PPOTURLSession.m 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. //
  2. // PPOTSimpleURLConnection.m
  3. // Copyright © 2015 PayPal, Inc. All rights reserved.
  4. //
  5. #import <UIKit/UIKit.h>
  6. #import "PPOTURLSession.h"
  7. #import "PPOTMacros.h"
  8. #import "PPOTPinnedCertificates.h"
  9. #import "PPOTDevice.h"
  10. #import "PPOTVersion.h"
  11. #define STATUS_IS_FAIL(x) (x < 200 || x >= 300)
  12. @interface PPOTURLSession () <NSURLSessionDelegate>
  13. /// An optional array of pinned certificates, each an NSData instance
  14. /// consisting of DER encoded x509 certificates
  15. @property (nonatomic, nullable, strong) NSArray<NSData *> *pinnedCertificates;
  16. @property (nonatomic, strong, readwrite) NSURLSession *session;
  17. @property (nonatomic, strong, readwrite) NSURLSessionConfiguration *sessionConfig;
  18. @end
  19. @implementation PPOTURLSession
  20. + (PPOTURLSession *)session {
  21. return [PPOTURLSession sessionWithTimeoutIntervalForRequest:0];
  22. }
  23. + (PPOTURLSession *)sessionWithTimeoutIntervalForRequest:(NSTimeInterval)timeoutIntervalForRequest {
  24. PPOTURLSession *session = [[PPOTURLSession alloc] initWithTimeoutIntervalForRequest:timeoutIntervalForRequest];
  25. return session;
  26. }
  27. - (nonnull instancetype)initWithTimeoutIntervalForRequest:(NSTimeInterval)timeoutIntervalForRequest {
  28. self = [super init];
  29. if (self) {
  30. self.sessionConfig = [NSURLSessionConfiguration ephemeralSessionConfiguration];
  31. self.sessionConfig.requestCachePolicy = NSURLRequestReloadIgnoringCacheData;
  32. self.sessionConfig.HTTPShouldUsePipelining = YES;
  33. self.sessionConfig.HTTPAdditionalHeaders = @{ @"User-Agent": [PPOTURLSession computeUserAgent] };
  34. if (timeoutIntervalForRequest > 0) {
  35. self.sessionConfig.timeoutIntervalForRequest = timeoutIntervalForRequest;
  36. }
  37. self.session = [NSURLSession sessionWithConfiguration:self.sessionConfig
  38. delegate:self
  39. delegateQueue:nil];
  40. self.pinnedCertificates = [PPOTPinnedCertificates trustedCertificates];
  41. }
  42. return self;
  43. }
  44. - (void)sendRequest:(nonnull NSURLRequest *)request completionBlock:(nullable PPOTURLSessionCompletionBlock)completionBlock {
  45. NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request
  46. completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
  47. completionBlock(data, (NSHTTPURLResponse *)response, error);
  48. }];
  49. [task resume];
  50. }
  51. - (void)finishTasksAndInvalidate {
  52. [self.session finishTasksAndInvalidate];
  53. }
  54. + (NSString *)computeUserAgent {
  55. NSLocale *currentLocale = [NSLocale currentLocale];
  56. NSString *countryCode = [currentLocale objectForKey:NSLocaleCountryCode];
  57. NSString *language = [currentLocale objectForKey:NSLocaleLanguageCode];
  58. #ifdef DEBUG
  59. NSString *releaseMode = @"DEBUG";
  60. #else
  61. NSString *releaseMode = @"RELEASE";
  62. #endif
  63. // PayPalSDK/OneTouchCore-iOS 3.2.2-11-g8b1c0e3 (iPhone; CPU iPhone OS 8_4_1; en-US; iPhone (iPhone5,1); iPhone5,1; DEBUG)
  64. return [NSString stringWithFormat:@"PayPalSDK/OneTouchCore-iOS %@ (%@; CPU %@ %@; %@-%@; %@; %@; %@)",
  65. PayPalOTVersion(),
  66. [UIDevice currentDevice].model,
  67. [UIDevice currentDevice].systemName,
  68. [[UIDevice currentDevice].systemVersion stringByReplacingOccurrencesOfString:@"." withString:@"_"],
  69. language,
  70. countryCode,
  71. [PPOTDevice deviceName],
  72. [PPOTDevice hardwarePlatform],
  73. releaseMode
  74. ];
  75. }
  76. #pragma mark - NSURLSessionDelegate methods
  77. - (NSArray *)pinnedCertificateData {
  78. NSMutableArray *pinnedCertificates = [NSMutableArray array];
  79. for (NSData *certificateData in self.pinnedCertificates) {
  80. [pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
  81. }
  82. return pinnedCertificates;
  83. }
  84. - (void)URLSession:(__unused NSURLSession *)session
  85. didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
  86. completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler {
  87. if ([[[challenge protectionSpace] authenticationMethod] isEqualToString:NSURLAuthenticationMethodServerTrust]) {
  88. NSString *domain = challenge.protectionSpace.host;
  89. SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust];
  90. NSArray *policies = @[(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
  91. SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
  92. SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)self.pinnedCertificateData);
  93. SecTrustResultType result;
  94. OSStatus errorCode = SecTrustEvaluate(serverTrust, &result);
  95. BOOL evaluatesAsTrusted = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);
  96. if (errorCode == errSecSuccess && evaluatesAsTrusted) {
  97. NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust];
  98. completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
  99. } else {
  100. completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, NULL);
  101. }
  102. } else {
  103. completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, NULL);
  104. }
  105. }
  106. @end