From 485ba203683e11c2d686d415129dd5eeae655036 Mon Sep 17 00:00:00 2001 From: Cameron Sabol Date: Tue, 12 Jun 2018 15:11:40 -0700 Subject: [PATCH 1/4] Uses shared NSURLSession singleton instead of creating our own Adds method to add default stripe headers to any request Setting the headers on requests instead of re-instantiating NSURLSession with new default headers avoids memory accumulation from the security framework (see https://developer.apple.com/library/archive/qa/qa1727/_index.html) --- Stripe/STPAPIClient+Private.h | 4 +++- Stripe/STPAPIClient.m | 30 ++++++++++++++++++++++-------- Stripe/STPAPIRequest.m | 6 +++--- Tests/Tests/STPAPIClientTest.m | 12 ++++++------ 4 files changed, 34 insertions(+), 18 deletions(-) diff --git a/Stripe/STPAPIClient+Private.h b/Stripe/STPAPIClient+Private.h index a2da35d378a..a523c93097c 100644 --- a/Stripe/STPAPIClient+Private.h +++ b/Stripe/STPAPIClient+Private.h @@ -24,7 +24,9 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, strong, readwrite) NSURL *apiURL; -@property (nonatomic, strong, readwrite) NSURLSession *urlSession; +@property (nonatomic, strong, readonly) NSURLSession *urlSession; + +- (NSMutableURLRequest *)configuredRequestForURL:(NSURL *)url; @end diff --git a/Stripe/STPAPIClient.m b/Stripe/STPAPIClient.m index 980f32604fa..8943f91d29a 100644 --- a/Stripe/STPAPIClient.m +++ b/Stripe/STPAPIClient.m @@ -124,27 +124,41 @@ - (instancetype)initWithConfiguration:(STPPaymentConfiguration *)configuration { _stripeAccount = configuration.stripeAccount; _sourcePollers = [NSMutableDictionary dictionary]; _sourcePollersQueue = dispatch_queue_create("com.stripe.sourcepollers", DISPATCH_QUEUE_SERIAL); - _urlSession = [NSURLSession sessionWithConfiguration:[self sessionConfiguration]]; + _urlSession = [NSURLSession sharedSession]; } return self; } -- (NSURLSessionConfiguration *)sessionConfiguration { +- (NSMutableURLRequest *)configuredRequestForURL:(NSURL *)url { + NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; + [[self defaultHeaders] enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSString * _Nonnull obj, __unused BOOL * _Nonnull stop) { + [request setValue:obj forHTTPHeaderField:key]; + }]; + return request; +} + +- (NSDictionary *)defaultHeaders { NSMutableDictionary *additionalHeaders = [NSMutableDictionary new]; additionalHeaders[@"X-Stripe-User-Agent"] = [self.class stripeUserAgentDetails]; additionalHeaders[@"Stripe-Version"] = APIVersion; additionalHeaders[@"Authorization"] = [@"Bearer " stringByAppendingString:self.apiKey ?: @""]; additionalHeaders[@"Stripe-Account"] = self.stripeAccount; - NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; - sessionConfiguration.HTTPAdditionalHeaders = additionalHeaders; - return sessionConfiguration; + return [additionalHeaders copy]; +} + +- (void)setUrlSession:(NSURLSession *)urlSession +{ + if (urlSession != _urlSession) { + [_urlSession invalidateAndCancel]; + _urlSession = urlSession; + } } - (void)setApiKey:(NSString *)apiKey { _apiKey = apiKey; // Regenerate url session configuration - self.urlSession = [NSURLSession sessionWithConfiguration:[self sessionConfiguration]]; +// self.urlSession = [NSURLSession sessionWithConfiguration:[self sessionConfiguration]]; } - (void)setPublishableKey:(NSString *)publishableKey { @@ -160,7 +174,7 @@ - (void)setStripeAccount:(NSString *)stripeAccount { _stripeAccount = stripeAccount; // Regenerate url session configuration - self.urlSession = [NSURLSession sessionWithConfiguration:[self sessionConfiguration]]; +// self.urlSession = [NSURLSession sessionWithConfiguration:[self sessionConfiguration]]; } - (void)createTokenWithParameters:(NSDictionary *)parameters @@ -342,7 +356,7 @@ - (void)uploadImage:(UIImage *)image NSString *boundary = [STPMultipartFormDataEncoder generateBoundary]; NSData *data = [STPMultipartFormDataEncoder multipartFormDataForParts:@[purposePart, imagePart] boundary:boundary]; - NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:FileUploadURL]]; + NSMutableURLRequest *request = [self configuredRequestForURL:[NSURL URLWithString:FileUploadURL]]; [request setHTTPMethod:@"POST"]; [request stp_setMultipartFormData:data boundary:boundary]; diff --git a/Stripe/STPAPIRequest.m b/Stripe/STPAPIRequest.m index 0538d22f92b..f550f09e719 100644 --- a/Stripe/STPAPIRequest.m +++ b/Stripe/STPAPIRequest.m @@ -42,7 +42,7 @@ + (NSURLSessionDataTask *)postWithAPIClient:(STPAPIClient *)apiClient NSURL *url = [apiClient.apiURL URLByAppendingPathComponent:endpoint]; // Setup request - NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; + NSMutableURLRequest *request = [apiClient configuredRequestForURL:url]; request.HTTPMethod = HTTPMethodPOST; [request stp_setFormPayload:parameters]; @@ -66,7 +66,7 @@ + (NSURLSessionDataTask *)getWithAPIClient:(STPAPIClient *)apiClient NSURL *url = [apiClient.apiURL URLByAppendingPathComponent:endpoint]; // Setup request - NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; + NSMutableURLRequest *request = [apiClient configuredRequestForURL:url]; [request stp_addParametersToURL:parameters]; request.HTTPMethod = HTTPMethodGET; @@ -98,7 +98,7 @@ + (NSURLSessionDataTask *)deleteWithAPIClient:(STPAPIClient *)apiClient NSURL *url = [apiClient.apiURL URLByAppendingPathComponent:endpoint]; // Setup request - NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; + NSMutableURLRequest *request = [apiClient configuredRequestForURL:url]; [request stp_addParametersToURL:parameters]; request.HTTPMethod = HTTPMethodDELETE; diff --git a/Tests/Tests/STPAPIClientTest.m b/Tests/Tests/STPAPIClientTest.m index d3022ca2837..5a23088214c 100644 --- a/Tests/Tests/STPAPIClientTest.m +++ b/Tests/Tests/STPAPIClientTest.m @@ -34,25 +34,25 @@ - (void)testSetDefaultPublishableKey { - (void)testInitWithPublishableKey { STPAPIClient *sut = [[STPAPIClient alloc] initWithPublishableKey:@"pk_foo"]; - NSString *authHeader = sut.urlSession.configuration.HTTPAdditionalHeaders[@"Authorization"]; + NSString *authHeader = [sut configuredRequestForURL:[NSURL URLWithString:@"https://www.stripe.com"]].allHTTPHeaderFields[@"Authorization"]; XCTAssertEqualObjects(authHeader, @"Bearer pk_foo"); } - (void)testSetPublishableKey { STPAPIClient *sut = [[STPAPIClient alloc] initWithPublishableKey:@"pk_foo"]; - NSString *authHeader = sut.urlSession.configuration.HTTPAdditionalHeaders[@"Authorization"]; + NSString *authHeader = [sut configuredRequestForURL:[NSURL URLWithString:@"https://www.stripe.com"]].allHTTPHeaderFields[@"Authorization"]; XCTAssertEqualObjects(authHeader, @"Bearer pk_foo"); sut.publishableKey = @"pk_bar"; - authHeader = sut.urlSession.configuration.HTTPAdditionalHeaders[@"Authorization"]; + authHeader = [sut configuredRequestForURL:[NSURL URLWithString:@"https://www.stripe.com"]].allHTTPHeaderFields[@"Authorization"]; XCTAssertEqualObjects(authHeader, @"Bearer pk_bar"); } - (void)testSetStripeAccount { STPAPIClient *sut = [[STPAPIClient alloc] initWithPublishableKey:@"pk_foo"]; - NSString *accountHeader = sut.urlSession.configuration.HTTPAdditionalHeaders[@"Stripe-Account"]; + NSString *accountHeader = [sut configuredRequestForURL:[NSURL URLWithString:@"https://www.stripe.com"]].allHTTPHeaderFields[@"Stripe-Account"]; XCTAssertNil(accountHeader); sut.stripeAccount = @"acct_123"; - accountHeader = sut.urlSession.configuration.HTTPAdditionalHeaders[@"Stripe-Account"]; + accountHeader = [sut configuredRequestForURL:[NSURL URLWithString:@"https://www.stripe.com"]].allHTTPHeaderFields[@"Stripe-Account"]; XCTAssertEqualObjects(accountHeader, @"acct_123"); } @@ -63,7 +63,7 @@ - (void)testInitWithConfiguration { STPAPIClient *sut = [[STPAPIClient alloc] initWithConfiguration:config]; XCTAssertEqualObjects(sut.publishableKey, config.publishableKey); XCTAssertEqualObjects(sut.stripeAccount, config.stripeAccount); - NSString *accountHeader = sut.urlSession.configuration.HTTPAdditionalHeaders[@"Stripe-Account"]; + NSString *accountHeader = [sut configuredRequestForURL:[NSURL URLWithString:@"https://www.stripe.com"]].allHTTPHeaderFields[@"Stripe-Account"]; XCTAssertEqualObjects(accountHeader, @"acct_123"); } From 70bf6f294f5b82aafa6f6fb7b69330e6c07754fb Mon Sep 17 00:00:00 2001 From: Cameron Sabol Date: Tue, 12 Jun 2018 15:36:00 -0700 Subject: [PATCH 2/4] Use private NSURLSession instance. --- Stripe/STPAPIClient.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Stripe/STPAPIClient.m b/Stripe/STPAPIClient.m index 8943f91d29a..b466cc56910 100644 --- a/Stripe/STPAPIClient.m +++ b/Stripe/STPAPIClient.m @@ -124,7 +124,7 @@ - (instancetype)initWithConfiguration:(STPPaymentConfiguration *)configuration { _stripeAccount = configuration.stripeAccount; _sourcePollers = [NSMutableDictionary dictionary]; _sourcePollersQueue = dispatch_queue_create("com.stripe.sourcepollers", DISPATCH_QUEUE_SERIAL); - _urlSession = [NSURLSession sharedSession]; + _urlSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; } return self; } From 2c02f547d9932da8758e58efaf94056812e7bb4a Mon Sep 17 00:00:00 2001 From: Cameron Sabol Date: Mon, 18 Jun 2018 15:54:37 -0700 Subject: [PATCH 3/4] Deletes unused code --- Stripe/STPAPIClient.m | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/Stripe/STPAPIClient.m b/Stripe/STPAPIClient.m index b466cc56910..b6cd4bd8167 100644 --- a/Stripe/STPAPIClient.m +++ b/Stripe/STPAPIClient.m @@ -146,21 +146,6 @@ - (NSMutableURLRequest *)configuredRequestForURL:(NSURL *)url { return [additionalHeaders copy]; } -- (void)setUrlSession:(NSURLSession *)urlSession -{ - if (urlSession != _urlSession) { - [_urlSession invalidateAndCancel]; - _urlSession = urlSession; - } -} - -- (void)setApiKey:(NSString *)apiKey { - _apiKey = apiKey; - - // Regenerate url session configuration -// self.urlSession = [NSURLSession sessionWithConfiguration:[self sessionConfiguration]]; -} - - (void)setPublishableKey:(NSString *)publishableKey { self.configuration.publishableKey = [publishableKey copy]; self.apiKey = [publishableKey copy]; @@ -170,13 +155,6 @@ - (NSString *)publishableKey { return self.configuration.publishableKey; } -- (void)setStripeAccount:(NSString *)stripeAccount { - _stripeAccount = stripeAccount; - - // Regenerate url session configuration -// self.urlSession = [NSURLSession sessionWithConfiguration:[self sessionConfiguration]]; -} - - (void)createTokenWithParameters:(NSDictionary *)parameters completion:(STPTokenCompletionBlock)completion { NSCAssert(parameters != nil, @"'parameters' is required to create a token"); From d61bb1e6abc118cdb8b3ceced590ffd74736b35c Mon Sep 17 00:00:00 2001 From: Cameron Sabol Date: Mon, 18 Jun 2018 16:32:19 -0700 Subject: [PATCH 4/4] Fixes tests. --- Tests/Tests/STPAPIRequestTest.m | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Tests/Tests/STPAPIRequestTest.m b/Tests/Tests/STPAPIRequestTest.m index 3c3705705ce..190fb743d67 100644 --- a/Tests/Tests/STPAPIRequestTest.m +++ b/Tests/Tests/STPAPIRequestTest.m @@ -49,6 +49,14 @@ - (void)testPostWithAPIClient { STPAPIClient *apiClientMock = OCMClassMock([STPAPIClient class]); OCMStub([apiClientMock apiURL]).andReturn([NSURL URLWithString:@"https://api.stripe.com"]); OCMStub([apiClientMock urlSession]).andReturn(urlSessionMock); + OCMStub([apiClientMock configuredRequestForURL:[OCMArg isKindOfClass:[NSURL class]]]).andDo(^(NSInvocation *invocation) + { + NSURL *urlArg; + [invocation getArgument:&urlArg atIndex:2]; + NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:urlArg]; + [invocation setReturnValue:&request]; + [invocation retainArguments]; + }); id apiRequestMock = OCMClassMock([STPAPIRequest class]); OCMStub([apiRequestMock parseResponse:[OCMArg any] @@ -117,6 +125,14 @@ - (void)testGetWithAPIClient { STPAPIClient *apiClientMock = OCMClassMock([STPAPIClient class]); OCMStub([apiClientMock apiURL]).andReturn([NSURL URLWithString:@"https://api.stripe.com"]); OCMStub([apiClientMock urlSession]).andReturn(urlSessionMock); + OCMStub([apiClientMock configuredRequestForURL:[OCMArg isKindOfClass:[NSURL class]]]).andDo(^(NSInvocation *invocation) + { + NSURL *urlArg; + [invocation getArgument:&urlArg atIndex:2]; + NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:urlArg]; + [invocation setReturnValue:&request]; + [invocation retainArguments]; + }); id apiRequestMock = OCMClassMock([STPAPIRequest class]); OCMStub([apiRequestMock parseResponse:[OCMArg any] @@ -185,6 +201,14 @@ - (void)testDeleteWithAPIClient { STPAPIClient *apiClientMock = OCMClassMock([STPAPIClient class]); OCMStub([apiClientMock apiURL]).andReturn([NSURL URLWithString:@"https://api.stripe.com"]); OCMStub([apiClientMock urlSession]).andReturn(urlSessionMock); + OCMStub([apiClientMock configuredRequestForURL:[OCMArg isKindOfClass:[NSURL class]]]).andDo(^(NSInvocation *invocation) + { + NSURL *urlArg; + [invocation getArgument:&urlArg atIndex:2]; + NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:urlArg]; + [invocation setReturnValue:&request]; + [invocation retainArguments]; + }); id apiRequestMock = OCMClassMock([STPAPIRequest class]); OCMStub([apiRequestMock parseResponse:[OCMArg any]