Source/TNLRequestConfiguration.m (664 lines of code) (raw):

// // TNLRequestConfiguration.m // TwitterNetworkLayer // // Created on 7/15/14. // Copyright © 2020 Twitter. All rights reserved. // #include <objc/message.h> #import "NSHTTPCookieStorage+TNLAdditions.h" #import "NSURLCache+TNLAdditions.h" #import "NSURLCredentialStorage+TNLAdditions.h" #import "NSURLSessionConfiguration+TNLAdditions.h" #import "TNL_Project.h" #import "TNLParameterCollection.h" #import "TNLPseudoURLProtocol.h" #import "TNLRequestConfiguration_Project.h" #import "TNLURLSessionManager.h" NS_ASSUME_NONNULL_BEGIN static const char kAssociatedConfigKey[] = "tnl.associated.config"; typedef struct { const NSTimeInterval idleTimeout; const NSTimeInterval attemptTimeout; const NSTimeInterval operationTimeout; } TNLRequestAnatomyTimeouts; static const TNLRequestAnatomyTimeouts kAnatomyTimeouts[] = { // TNLRequestAnatomySmallRequestSmallResponse { .idleTimeout = 30, .attemptTimeout = 30, .operationTimeout = 90 }, // TNLRequestAnatomySmallRequestLargeResponse (default) { .idleTimeout = 30, .attemptTimeout = 60, .operationTimeout = 180 }, // TNLRequestAnatomyLargeRequestSmallResponse { .idleTimeout = 30, .attemptTimeout = 60, .operationTimeout = 180 }, // TNLRequestAnatomyLargeRequestLargeResponse { .idleTimeout = 30, .attemptTimeout = 120, .operationTimeout = 360 }, // TNLRequestAnatomyVeryLargeRequestSmallResponse { .idleTimeout = 30, .attemptTimeout = 240, .operationTimeout = 480 }, // TNLRequestAnatomySmallRequestVeryLargeResponse { .idleTimeout = 30, .attemptTimeout = 180, .operationTimeout = 480 }, // TNLRequestAnatomySmallRequestStreamingResponse { .idleTimeout = 30, .attemptTimeout = NSTimeIntervalSince1970, .operationTimeout = NSTimeIntervalSince1970 }, }; static const NSInteger kMaxAnatomyTimeouts = TNLRequestAnatomySmallRequestStreamingResponse + 1; TNLStaticAssert((sizeof(kAnatomyTimeouts) / sizeof(kAnatomyTimeouts[0])) == kMaxAnatomyTimeouts, ANATOMY_TIMEOUT_COUNT_MISSMATCH); #define kConfigurationIdleTimeoutDefault (kAnatomyTimeouts[TNLRequestAnatomyDefault].idleTimeout) #define kConfigurationAttemptTimeoutDefault (kAnatomyTimeouts[TNLRequestAnatomyDefault].attemptTimeout) // Apple's default is 7 days (biasing towards background sessions). We'll bias towards foreground sessions. #define kConfigurationOperationTimeoutDefault (kAnatomyTimeouts[TNLRequestAnatomyDefault].operationTimeout) static const NSTimeInterval kConfigurationDeferrableIntervalDefault = 0.0; TNLStaticAssert(TNLResponseHashComputeAlgorithmNone == 0, ALGORITHM_NONE_WRONG_VALUE); #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" TNLStaticAssert(TNLResponseHashComputeAlgorithmMD2 == 1835294514, ALGORITHM_MD2_WRONG_VALUE); TNLStaticAssert(TNLResponseHashComputeAlgorithmMD4 == 1835294516, ALGORITHM_MD4_WRONG_VALUE); TNLStaticAssert(TNLResponseHashComputeAlgorithmMD5 == 1835294517, ALGORITHM_MD5_WRONG_VALUE); #pragma clang diagnostic pop TNLStaticAssert(TNLResponseHashComputeAlgorithmSHA1 == 1936220465, ALGORITHM_SHA1_WRONG_VALUE); TNLStaticAssert(TNLResponseHashComputeAlgorithmSHA256 == 1932670262, ALGORITHM_SHA256_WRONG_VALUE); TNLStaticAssert(TNLResponseHashComputeAlgorithmSHA512 == 1932865842, ALGORITHM_SHA512_WRONG_VALUE); @interface TNLRequestConfiguration () - (instancetype)initWithConfiguration:(nullable TNLRequestConfiguration *)config; - (instancetype)initWithIdleTimeout:(NSTimeInterval)idleTimeout attemptTimeout:(NSTimeInterval)attemptTimeout operationTimeout:(NSTimeInterval)operationTimeout; @end @implementation TNLRequestConfiguration #pragma mark @synthesize @synthesize retryPolicyProvider = _retryPolicyProvider; @synthesize contentEncoder = _contentEncoder; @synthesize additionalContentDecoders = _additionalContentDecoders; @synthesize URLCredentialStorage = _URLCredentialStorage; @synthesize URLCache = _URLCache; @synthesize sharedContainerIdentifier = _sharedContainerIdentifier; @synthesize cookieStorage = _cookieStorage; #pragma mark properties - (TNLRequestExecutionMode)executionMode { return _ivars.executionMode; } - (TNLRequestRedirectPolicy)redirectPolicy { return _ivars.redirectPolicy; } - (TNLResponseDataConsumptionMode)responseDataConsumptionMode { return _ivars.responseDataConsumptionMode; } - (TNLRequestProtocolOptions)protocolOptions { return _ivars.protocolOptions; } - (TNLRequestConnectivityOptions)connectivityOptions { return _ivars.connectivityOptions; } - (BOOL)contributeToExecutingNetworkConnectionsCount { return _ivars.contributeToExecutingNetworkConnectionsCount; } - (BOOL)skipHostSanitization { return _ivars.skipHostSanitization; } - (TNLResponseHashComputeAlgorithm)responseComputeHashAlgorithm { return _ivars.responseComputeHashAlgorithm; } - (NSTimeInterval)idleTimeout { return _ivars.idleTimeout; } - (NSTimeInterval)attemptTimeout { return _ivars.attemptTimeout; } - (NSTimeInterval)operationTimeout { return _ivars.operationTimeout; } - (NSTimeInterval)deferrableInterval { return _ivars.deferrableInterval; } - (NSURLRequestCachePolicy)cachePolicy { return _ivars.cachePolicy; } - (NSURLRequestNetworkServiceType)networkServiceType { return _ivars.networkServiceType; } - (NSHTTPCookieAcceptPolicy)cookieAcceptPolicy { return _ivars.cookieAcceptPolicy; } - (BOOL)shouldSetCookies { return _ivars.shouldSetCookies; } - (BOOL)allowsCellularAccess { return _ivars.allowsCellularAccess; } - (BOOL)isDiscretionary { return _ivars.discretionary; } - (BOOL)shouldLaunchAppForBackgroundEvents { return _ivars.shouldLaunchAppForBackgroundEvents; } - (NSURLSessionMultipathServiceType)multipathServiceType { #if TARGET_OS_IOS if (tnl_available_ios_11) { return _ivars.multipathServiceType; } #endif return 0; } - (BOOL)shouldUseExtendedBackgroundIdleMode { return _ivars.shouldUseExtendedBackgroundIdleMode; } #pragma mark Constructors + (instancetype)defaultConfiguration { return [[[self class] alloc] init]; } + (instancetype)configurationWithExpectedAnatomy:(TNLRequestAnatomy)anatomy { if (anatomy >= kMaxAnatomyTimeouts) { anatomy = TNLRequestAnatomyDefault; TNLLogWarning(@"Invalid TNLRequestAnatomy provided! Coersing to default."); } const TNLRequestAnatomyTimeouts timeouts = kAnatomyTimeouts[anatomy]; return [[[self class] alloc] initWithIdleTimeout:timeouts.idleTimeout attemptTimeout:timeouts.attemptTimeout operationTimeout:timeouts.operationTimeout]; } #pragma mark init - (instancetype)initWithConfiguration:(nullable TNLRequestConfiguration *)config { if (!config) { self = [self init]; } else if ((self = [super init])) { _retryPolicyProvider = config->_retryPolicyProvider; _contentEncoder = config->_contentEncoder; _additionalContentDecoders = [config->_additionalContentDecoders copy]; _URLCredentialStorage = config->_URLCredentialStorage; _URLCache = config->_URLCache; _cookieStorage = config->_cookieStorage; _sharedContainerIdentifier = [config->_sharedContainerIdentifier copy]; memcpy(&_ivars, &(config->_ivars), sizeof(_ivars)); if (_ivars.connectivityOptions != TNLRequestConnectivityOptionsNone) { if (![NSURLSessionConfiguration tnl_URLSessionCanUseWaitsForConnectivity]) { _ivars.connectivityOptions = TNLRequestConnectivityOptionsNone; TNL_LOG_WAITS_FOR_CONNECTIVITY_WARNING(); } } } return self; } - (instancetype)init { static NSURLSessionConfiguration *sTemplateConfig; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sTemplateConfig = [NSURLSessionConfiguration tnl_defaultSessionConfigurationWithNilPersistence]; }); TNLAssert(sTemplateConfig != nil); TNLAssert(!sTemplateConfig.URLCache); TNLAssert(!sTemplateConfig.URLCredentialStorage); TNLAssert(!sTemplateConfig.HTTPCookieStorage); return [self initWithSessionConfiguration:sTemplateConfig]; } - (instancetype)initWithIdleTimeout:(NSTimeInterval)idleTimeout attemptTimeout:(NSTimeInterval)attemptTimeout operationTimeout:(NSTimeInterval)operationTimeout { if (self = [self init]) { _ivars.idleTimeout = idleTimeout; _ivars.attemptTimeout = attemptTimeout; _ivars.operationTimeout = operationTimeout; } return self; } #pragma mark NSMutableCopying - (id)copyWithZone:(nullable NSZone *)zone { return self; } - (id)mutableCopyWithZone:(nullable NSZone *)zone { TNLMutableRequestConfiguration *config = [[TNLMutableRequestConfiguration allocWithZone:zone] initWithConfiguration:self]; return config; } #pragma mark Description - (NSString *)description { NSMutableDictionary *d = [NSMutableDictionary dictionary]; #define D_SET(prop) \ d[@"" #prop] = @(self.prop); D_SET(executionMode); D_SET(redirectPolicy); D_SET(responseDataConsumptionMode); D_SET(protocolOptions); D_SET(connectivityOptions); D_SET(contributeToExecutingNetworkConnectionsCount); D_SET(skipHostSanitization); D_SET(responseComputeHashAlgorithm); D_SET(attemptTimeout); D_SET(idleTimeout); D_SET(operationTimeout); D_SET(deferrableInterval); D_SET(cachePolicy); D_SET(networkServiceType); D_SET(cookieAcceptPolicy); D_SET(allowsCellularAccess); D_SET(isDiscretionary); D_SET(shouldLaunchAppForBackgroundEvents); D_SET(shouldSetCookies); D_SET(shouldUseExtendedBackgroundIdleMode); #if TARGET_OS_IOS if (tnl_available_ios_11) { D_SET(multipathServiceType); } #endif #undef D_SET #define D_SET(prop) \ if (self.prop) { \ d[@"" #prop] = self.prop; \ } D_SET(retryPolicyProvider); D_SET(contentEncoder); D_SET(additionalContentDecoders); D_SET(sharedContainerIdentifier); D_SET(URLCredentialStorage); D_SET(URLCache); D_SET(cookieStorage); #undef D_SET return [NSString stringWithFormat:@"<%@ %p: %@>", NSStringFromClass([self class]), self, d]; } #pragma mark Equivilence - (NSUInteger)hash { TNLMutableParameterCollection *params = TNLMutableParametersFromRequestConfiguration(self, nil, nil, nil); TNLMutableParametersStripURLCacheAndURLCredentialStorageAndCookieStorage(params); return params.hash + (NSUInteger)(self.responseComputeHashAlgorithm) + (NSUInteger)(self.contributeToExecutingNetworkConnectionsCount * 7) + (NSUInteger)(self.skipHostSanitization * 11) + (NSUInteger)(self.executionMode * 17); } - (BOOL)isEqual:(id)object { if ([super isEqual:object]) { return YES; } if (![object isKindOfClass:[TNLRequestConfiguration class]]) { return NO; } TNLRequestConfiguration *other = object; if (0 != memcmp(&_ivars, &(other->_ivars), sizeof(_ivars))) { return NO; } if (self.retryPolicyProvider != other.retryPolicyProvider) { return NO; } if (self.sharedContainerIdentifier != other.sharedContainerIdentifier && ![self.sharedContainerIdentifier isEqualToString:other.sharedContainerIdentifier]) { return NO; } if (self.URLCredentialStorage != other.URLCredentialStorage) { return NO; } if (self.URLCache != other.URLCache) { return NO; } if (self.cookieStorage != other.cookieStorage) { return NO; } return YES; } @end #pragma mark - TNLMutableRequestConfiguration @implementation TNLMutableRequestConfiguration @dynamic contributeToExecutingNetworkConnectionsCount; @dynamic skipHostSanitization; @dynamic responseComputeHashAlgorithm; @dynamic executionMode; @dynamic redirectPolicy; @dynamic responseDataConsumptionMode; @dynamic protocolOptions; @dynamic connectivityOptions; @dynamic retryPolicyProvider; @dynamic contentEncoder; @dynamic additionalContentDecoders; @dynamic idleTimeout; @dynamic attemptTimeout; @dynamic operationTimeout; @dynamic deferrableInterval; @dynamic cachePolicy; @dynamic networkServiceType; @dynamic cookieAcceptPolicy; @dynamic shouldSetCookies; @dynamic allowsCellularAccess; @dynamic discretionary; @dynamic sharedContainerIdentifier; @dynamic shouldLaunchAppForBackgroundEvents; @dynamic URLCredentialStorage; @dynamic URLCache; @dynamic cookieStorage; @dynamic multipathServiceType; @dynamic shouldUseExtendedBackgroundIdleMode; - (id)copyWithZone:(nullable NSZone *)zone { TNLRequestConfiguration *config = [[TNLRequestConfiguration allocWithZone:zone] initWithConfiguration:self]; return config; } - (void)setExecutionMode:(TNLRequestExecutionMode)executionMode { _ivars.executionMode = executionMode; } - (void)setRedirectPolicy:(TNLRequestRedirectPolicy)redirectPolicy { _ivars.redirectPolicy = redirectPolicy; } - (void)setResponseDataConsumptionMode:(TNLResponseDataConsumptionMode)responseDataConsumptionMode { _ivars.responseDataConsumptionMode = responseDataConsumptionMode; } - (void)setProtocolOptions:(TNLRequestProtocolOptions)protocolOptions { _ivars.protocolOptions = protocolOptions; } - (void)setConnectivityOptions:(TNLRequestConnectivityOptions)connectivityOptions { if (![NSURLSessionConfiguration tnl_URLSessionCanUseWaitsForConnectivity]) { TNL_LOG_WAITS_FOR_CONNECTIVITY_WARNING(); return; } _ivars.connectivityOptions = (connectivityOptions & 0xf); } - (void)setContributeToExecutingNetworkConnectionsCount:(BOOL)contributeToExecutingNetworkConnectionsCount { _ivars.contributeToExecutingNetworkConnectionsCount = (contributeToExecutingNetworkConnectionsCount != NO); } - (void)setSkipHostSanitization:(BOOL)skipHostSanitization { _ivars.skipHostSanitization = (skipHostSanitization != NO); } - (void)setResponseComputeHashAlgorithm:(TNLResponseHashComputeAlgorithm)responseComputeHashAlgorithm { _ivars.responseComputeHashAlgorithm = responseComputeHashAlgorithm; } - (void)setRetryPolicyProvider:(nullable id<TNLRequestRetryPolicyProvider>)retryPolicyProvider PROP_RETAIN_ASSIGN_IMP(retryPolicyProvider); - (void)setContentEncoder:(nullable id<TNLContentEncoder>)contentEncoder PROP_RETAIN_ASSIGN_IMP(contentEncoder); - (void)setAdditionalContentDecoders:(nullable NSArray<id<TNLContentDecoder>> *)additionalContentDecoders PROP_COPY_IMP(additionalContentDecoders); - (void)setIdleTimeout:(NSTimeInterval)idleTimeout { _ivars.idleTimeout = idleTimeout; } - (void)setAttemptTimeout:(NSTimeInterval)attemptTimeout { _ivars.attemptTimeout = attemptTimeout; } - (void)setOperationTimeout:(NSTimeInterval)operationTimeout { _ivars.operationTimeout = operationTimeout; } - (void)setDeferrableInterval:(NSTimeInterval)deferrableInterval { _ivars.deferrableInterval = deferrableInterval; } - (void)setCachePolicy:(NSURLRequestCachePolicy)cachePolicy { _ivars.cachePolicy = cachePolicy; } - (void)setNetworkServiceType:(NSURLRequestNetworkServiceType)networkServiceType { _ivars.networkServiceType = networkServiceType; } - (void)setCookieAcceptPolicy:(NSHTTPCookieAcceptPolicy)cookieAcceptPolicy { _ivars.cookieAcceptPolicy = cookieAcceptPolicy; } - (void)setShouldSetCookies:(BOOL)shouldSetCookies { _ivars.shouldSetCookies = shouldSetCookies; } - (void)setAllowsCellularAccess:(BOOL)allowsCellularAccess { _ivars.allowsCellularAccess = (allowsCellularAccess != NO); } - (void)setDiscretionary:(BOOL)discretionary { _ivars.discretionary = (discretionary != NO); } - (void)setSharedContainerIdentifier:(nullable NSString *)sharedContainerIdentifier { PROP_COPY_IMP(sharedContainerIdentifier); } - (void)setShouldLaunchAppForBackgroundEvents:(BOOL)shouldLaunchAppForBackgroundEvents { _ivars.shouldLaunchAppForBackgroundEvents = (shouldLaunchAppForBackgroundEvents != NO); } - (void)setURLCredentialStorage:(nullable NSURLCredentialStorage *)URLCredentialStorage PROP_RETAIN_ASSIGN_IMP(URLCredentialStorage); - (void)setURLCache:(nullable NSURLCache *)URLCache PROP_RETAIN_ASSIGN_IMP(URLCache); - (void)setCookieStorage:(nullable NSHTTPCookieStorage *)cookieStorage PROP_RETAIN_ASSIGN_IMP(cookieStorage); - (void)setMultipathServiceType:(NSURLSessionMultipathServiceType)multipathServiceType { #if TARGET_OS_IOS if (tnl_available_ios_11) { _ivars.multipathServiceType = multipathServiceType; } #endif } - (void)setShouldUseExtendedBackgroundIdleMode:(BOOL)shouldUseExtendedBackgroundIdleMode { _ivars.shouldUseExtendedBackgroundIdleMode = (shouldUseExtendedBackgroundIdleMode != NO); } - (void)configureAsLowPriority { self.discretionary = YES; self.deferrableInterval = TNLDeferrableIntervalForPriority(TNLPriorityLow); self.networkServiceType = NSURLNetworkServiceTypeBackground; } @end #pragma mark TNLRequestConfiguration @implementation TNLRequestConfiguration (Project) + (nullable instancetype)parseConfigurationFromIdentifier:(nullable NSString *)identifier { if (!identifier) { return nil; } NSURL *url = [NSURL URLWithString:identifier]; if (![url.scheme isEqualToString:TNLTwitterNetworkLayerURLScheme]) { return nil; } if (url.host.length == 0) { return nil; } NSArray *pathComponents = url.pathComponents; if (pathComponents.count != 3) { // 0 == '/' (root) // 1 == TNLRequestOperationQueue identifier // 2 == 'InApp' || 'Background' return nil; } NSString *version = [pathComponents[1] stringByReplacingOccurrencesOfString:@"_" withString:@"."]; NSString *modeStr = pathComponents[2]; TNLRequestExecutionMode mode = TNLRequestExecutionModeInApp; if ([modeStr isEqualToString:@"Background"]) { mode = TNLRequestExecutionModeBackground; } TNLParameterCollection *params = url.tnl_queryCollection; return [self configurationFromParameters:params executionMode:mode version:version]; } + (instancetype)configurationFromParameters:(nullable TNLParameterCollection *)params executionMode:(TNLRequestExecutionMode)mode version:(nullable NSString *)tnlVersion { TNLMutableRequestConfiguration *mConfig = [TNLMutableRequestConfiguration defaultConfiguration]; NSString *value; #define PULL_VALUE(key, target, accessor, type) \ do { \ value = params[key]; \ if (value) { \ mConfig.target = ( type )[value accessor]; \ } \ } while (0) PULL_VALUE(TNLRequestConfigurationPropertyKeyRedirectPolicy, redirectPolicy, integerValue, TNLRequestRedirectPolicy); PULL_VALUE(TNLRequestConfigurationPropertyKeyResponseDataConsumptionMode, responseDataConsumptionMode, integerValue, TNLResponseDataConsumptionMode); PULL_VALUE(TNLRequestConfigurationPropertyKeyProtocolOptions, protocolOptions, integerValue, TNLRequestProtocolOptions); PULL_VALUE(TNLRequestConfigurationPropertyKeyConnectivityOptions, connectivityOptions, integerValue, TNLRequestConnectivityOptions); PULL_VALUE(TNLRequestConfigurationPropertyKeyIdleTimeout, idleTimeout, doubleValue, NSTimeInterval); PULL_VALUE(TNLRequestConfigurationPropertyKeyAttemptTimeout, attemptTimeout, doubleValue, NSTimeInterval); PULL_VALUE(TNLRequestConfigurationPropertyKeyOperationTimeout, operationTimeout, doubleValue, NSTimeInterval); PULL_VALUE(TNLRequestConfigurationPropertyKeyDeferrableInterval, deferrableInterval, doubleValue, NSTimeInterval); PULL_VALUE(TNLRequestConfigurationPropertyKeyCachePolicy, cachePolicy, integerValue, NSURLRequestCachePolicy); PULL_VALUE(TNLRequestConfigurationPropertyKeyNetworkServiceType, networkServiceType, integerValue, NSURLRequestNetworkServiceType); PULL_VALUE(TNLRequestConfigurationPropertyKeyCookieAcceptPolicy, cookieAcceptPolicy, integerValue, NSHTTPCookieAcceptPolicy); PULL_VALUE(TNLRequestConfigurationPropertyKeyShouldSetCookies, shouldSetCookies, boolValue, BOOL); PULL_VALUE(TNLRequestConfigurationPropertyKeyAllowsCellularAccess, allowsCellularAccess, boolValue, BOOL); PULL_VALUE(TNLRequestConfigurationPropertyKeyDiscrectionary, discretionary, boolValue, BOOL); PULL_VALUE(TNLRequestConfigurationPropertyKeyShouldLaunchAppForBackgroundEvents, shouldLaunchAppForBackgroundEvents, boolValue, BOOL); PULL_VALUE(TNLRequestConfigurationPropertyKeyShouldUseExtendedBackgroundIdleMode, shouldUseExtendedBackgroundIdleMode, boolValue, BOOL); #if TARGET_OS_IOS if (tnl_available_ios_11) { PULL_VALUE(TNLRequestConfigurationPropertyKeyMultipathServiceType, multipathServiceType, integerValue, NSURLSessionMultipathServiceType); } #endif mConfig.sharedContainerIdentifier = params[TNLRequestConfigurationPropertyKeySharedContainerIdentifier]; // These cannot loaded from the params so it's best to have them be nil. // Having them be the default shared objects could hide that this constructor // doesn't actually load in these objects. mConfig.URLCredentialStorage = nil; mConfig.URLCache = nil; mConfig.cookieStorage = nil; #undef PULL_VALUE mConfig.executionMode = mode; return mConfig; } - (void)applyDefaultTimeouts { _ivars.idleTimeout = kConfigurationIdleTimeoutDefault; _ivars.attemptTimeout = kConfigurationAttemptTimeoutDefault; _ivars.operationTimeout = kConfigurationOperationTimeoutDefault; _ivars.deferrableInterval = kConfigurationDeferrableIntervalDefault; } @end #pragma mark - Functions NSURLCache * __nullable TNLUnwrappedURLCache(NSURLCache * __nullable cache) { if (cache == [NSURLCache tnl_sharedURLCacheProxy]) { return [NSURLCache sharedURLCache]; } return cache; } NSURLCredentialStorage * __nullable TNLUnwrappedURLCredentialStorage(NSURLCredentialStorage * __nullable storage) { if (storage == [NSURLCredentialStorage tnl_sharedCredentialStorageProxy]) { return [NSURLCredentialStorage sharedCredentialStorage]; } return storage; } NSHTTPCookieStorage * __nullable TNLUnwrappedCookieStorage(NSHTTPCookieStorage * __nullable storage) { if (storage == [NSHTTPCookieStorage tnl_sharedHTTPCookieStorageProxy]) { return [NSHTTPCookieStorage sharedHTTPCookieStorage]; } return storage; } void TNLMutableParametersStripURLCacheAndURLCredentialStorageAndCookieStorage(TNLMutableParameterCollection *params) { params[TNLRequestConfigurationPropertyKeyURLCredentialStorage] = nil; params[TNLRequestConfigurationPropertyKeyURLCache] = nil; params[TNLRequestConfigurationPropertyKeyCookieStorage] = nil; } TNLMutableParameterCollection * __nullable TNLMutableParametersFromRequestConfiguration(TNLRequestConfiguration *config, NSURLCache * __nullable canonicalCache, NSURLCredentialStorage * __nullable canonicalCredentialStorage, NSHTTPCookieStorage * __nullable canonicalCookieStorage) { if (!config) { return nil; } if (!canonicalCache) { canonicalCache = TNLUnwrappedURLCache(config.URLCache); } if (!canonicalCredentialStorage) { canonicalCredentialStorage = TNLUnwrappedURLCredentialStorage(config.URLCredentialStorage); } if (!canonicalCookieStorage) { canonicalCookieStorage = TNLUnwrappedCookieStorage(config.cookieStorage); } TNLMutableParameterCollection *params = [[TNLMutableParameterCollection alloc] init]; // Only for TNL layer (not NSURLSession) params[TNLRequestConfigurationPropertyKeyRedirectPolicy] = @(config.redirectPolicy); params[TNLRequestConfigurationPropertyKeyResponseDataConsumptionMode] = @(config.responseDataConsumptionMode); params[TNLRequestConfigurationPropertyKeyOperationTimeout] = @(config.operationTimeout); params[TNLRequestConfigurationPropertyKeyDeferrableInterval] = @(config.deferrableInterval); params[TNLRequestConfigurationPropertyKeyConnectivityOptions] = @(config.connectivityOptions); // For NSURLSession layer in the background params[TNLRequestConfigurationPropertyKeyIdleTimeout] = @(config.idleTimeout); params[TNLRequestConfigurationPropertyKeyAttemptTimeout] = @(config.attemptTimeout); params[TNLRequestConfigurationPropertyKeyShouldLaunchAppForBackgroundEvents] = @(config.shouldLaunchAppForBackgroundEvents); #if TARGET_OS_IOS if (tnl_available_ios_11) { const NSURLSessionMultipathServiceType type = config.multipathServiceType; if (type != 0) { params[TNLRequestConfigurationPropertyKeyMultipathServiceType] = @(type); } } #endif // Other properties params[TNLRequestConfigurationPropertyKeyProtocolOptions] = @(config.protocolOptions); params[TNLRequestConfigurationPropertyKeyConnectivityOptions] = @(config.connectivityOptions); params[TNLRequestConfigurationPropertyKeyCachePolicy] = @(config.cachePolicy); params[TNLRequestConfigurationPropertyKeyNetworkServiceType] = @(config.networkServiceType); params[TNLRequestConfigurationPropertyKeyAllowsCellularAccess] = @(config.allowsCellularAccess); params[TNLRequestConfigurationPropertyKeyDiscrectionary] = @(config.isDiscretionary); params[TNLRequestConfigurationPropertyKeyCookieAcceptPolicy] = @(config.cookieAcceptPolicy); params[TNLRequestConfigurationPropertyKeyShouldSetCookies] = @(config.shouldSetCookies); params[TNLRequestConfigurationPropertyKeyShouldUseExtendedBackgroundIdleMode] = @(config.shouldUseExtendedBackgroundIdleMode); id value; value = config.sharedContainerIdentifier; if (value) { params[TNLRequestConfigurationPropertyKeySharedContainerIdentifier] = value; } value = canonicalCredentialStorage ?: config.URLCredentialStorage; if (value) { params[TNLRequestConfigurationPropertyKeyURLCredentialStorage] = [NSString stringWithFormat:@"%@_%p", NSStringFromClass([value class]), value]; } value = canonicalCache ?: config.URLCache; if (value) { params[TNLRequestConfigurationPropertyKeyURLCache] = [NSString stringWithFormat:@"%@_%p", NSStringFromClass([value class]), value]; } value = canonicalCookieStorage ?: config.cookieStorage; if (value) { params[TNLRequestConfigurationPropertyKeyCookieStorage] = [NSString stringWithFormat:@"%@_%p", NSStringFromClass([value class]), value]; } /** Note: config.contributeToExecutingNetworkConnectionsCount, config.skipHostSanitization, config.responseComputeHashAlgorithm, config.contentEncoder, config.additionContentDecoders, config.retryPolicyProvider and config.executionMode are omitted on purpose. */ return params; } NSArray *TNLProtocolClassesForProtocolOptions(TNLRequestProtocolOptions options) { if (!options) { return nil; } NSMutableArray *protocols = [[NSMutableArray alloc] init]; if (TNL_BITMASK_HAS_SUBSET_FLAGS(options, TNLRequestProtocolOptionPseudo)) { [protocols addObject:[TNLPseudoURLProtocol class]]; } return protocols; } TNLRequestProtocolOptions TNLProtocolOptionsForProtocolClasses(NSArray * __nullable protocols) { TNLRequestProtocolOptions options = 0; for (Class c in protocols) { if ([c isSubclassOfClass:[TNLPseudoURLProtocol class]]) { options |= TNLRequestProtocolOptionPseudo; } } return options; } void TNLRequestConfigurationAssociateWithRequest(TNLRequestConfiguration *config, NSURLRequest *request) { TNLAssert(request); TNLAssert(config); if (request) { objc_setAssociatedObject(request, &kAssociatedConfigKey, config, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } } TNLRequestConfiguration * __nullable TNLRequestConfigurationGetAssociatedWithRequest(NSURLRequest *request) { TNLAssert(request); if (request) { return objc_getAssociatedObject(request, &kAssociatedConfigKey); } return nil; } NS_ASSUME_NONNULL_END