Skip to content

Commit

Permalink
Merge pull request #253 from pubnub/fix/pt125486401
Browse files Browse the repository at this point in the history
Fixed timeout issue which caused by recently added shared NSURLSessionConfiguration configuration
  • Loading branch information
parfeon committed Jul 8, 2016
2 parents 0da22a3 + aed67af commit 91a5176
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 47 deletions.
95 changes: 64 additions & 31 deletions PubNub/Misc/Categories/NSURLSessionConfiguration+PNConfiguration.m
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ @interface NSURLSessionConfiguration (PNConfigurationProtected)

#pragma mark - Misc

/**
@brief Retrieve reference on list of previusly created configuration instances.
@since 4.4.1
@return Dictionary where each configuration mapped to it's identifier.
*/
+ (NSMutableDictionary<NSString *, NSURLSessionConfiguration *> *)pn_configurations;

/**
@brief Allow to filter up passed list of prtocol classes from names which can intersect with \c Apple's
protocols.
Expand Down Expand Up @@ -57,24 +66,25 @@ @implementation NSURLSessionConfiguration (PNConfiguration)

#pragma mark - Initialization and Configuration

+ (instancetype)pn_ephemeralSessionConfiguration {
+ (instancetype)pn_ephemeralSessionConfigurationWithIdentifier:(NSString *)identifier; {

static NSURLSessionConfiguration *_sharedSessionConfiguration;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSMutableDictionary *sessionConfigurations = [self pn_configurations];
if (sessionConfigurations[identifier] == nil) {

_sharedSessionConfiguration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
_sharedSessionConfiguration.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
_sharedSessionConfiguration.URLCache = nil;
_sharedSessionConfiguration.HTTPAdditionalHeaders = [self pn_defaultHeaders];
});
NSURLSessionConfiguration *sessionConfiguration;
sessionConfiguration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
sessionConfiguration.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
sessionConfiguration.URLCache = nil;
sessionConfiguration.HTTPAdditionalHeaders = [self pn_defaultHeaders];
sessionConfigurations[identifier] = sessionConfiguration;
}

return _sharedSessionConfiguration;
return sessionConfigurations[identifier];
}

+ (NSDictionary<NSString *, id> *)pn_HTTPAdditionalHeaders {

NSURLSessionConfiguration *configuration = [self pn_ephemeralSessionConfiguration];
NSURLSessionConfiguration *configuration = [self pn_configurations].allValues.firstObject;
NSMutableDictionary *headers = [configuration.HTTPAdditionalHeaders mutableCopy];
[headers removeObjectsForKeys:@[@"Accept", @"Accept-Encoding", @"User-Agent", @"Connection"]];

Expand All @@ -83,77 +93,100 @@ + (instancetype)pn_ephemeralSessionConfiguration {

+ (void)pn_setHTTPAdditionalHeaders:(NSDictionary<NSString *, id> *)HTTPAdditionalHeaders {

NSURLSessionConfiguration *configuration = [self pn_ephemeralSessionConfiguration];
NSMutableDictionary *headers = [HTTPAdditionalHeaders mutableCopy];
[headers removeObjectsForKeys:@[@"Accept", @"Accept-Encoding", @"User-Agent", @"Connection"]];
if (headers.count) {

[headers addEntriesFromDictionary:configuration.HTTPAdditionalHeaders];
NSArray<NSURLSessionConfiguration *> *configurations = [self pn_configurations].allValues;
NSMutableDictionary *customHeaders = [HTTPAdditionalHeaders mutableCopy];
[customHeaders removeObjectsForKeys:@[@"Accept", @"Accept-Encoding", @"User-Agent", @"Connection"]];

// Compose resulting HTTP headers holder.
NSMutableDictionary *headers = [[self pn_defaultHeaders] mutableCopy];
[headers addEntriesFromDictionary:customHeaders];

for (NSURLSessionConfiguration *configuration in configurations) {

configuration.HTTPAdditionalHeaders = headers;
}
else { configuration.HTTPAdditionalHeaders = [self pn_defaultHeaders]; }
}

+ (NSURLRequestNetworkServiceType)pn_networkServiceType {

NSURLSessionConfiguration *configuration = [self pn_ephemeralSessionConfiguration];
NSURLSessionConfiguration *configuration = [self pn_configurations].allValues.firstObject;

return configuration.networkServiceType;
}

+ (void)pn_setNetworkServiceType:(NSURLRequestNetworkServiceType)networkServiceType {

NSURLSessionConfiguration *configuration = [self pn_ephemeralSessionConfiguration];
configuration.networkServiceType = networkServiceType;
for (NSURLSessionConfiguration *configuration in [self pn_configurations].allValues) {

configuration.networkServiceType = networkServiceType;
}
}

+ (BOOL)pn_allowsCellularAccess {

NSURLSessionConfiguration *configuration = [self pn_ephemeralSessionConfiguration];
NSURLSessionConfiguration *configuration = [self pn_configurations].allValues.firstObject;

return configuration.allowsCellularAccess;
}

+ (void)pn_setAllowsCellularAccess:(BOOL)allowsCellularAccess {

NSURLSessionConfiguration *configuration = [self pn_ephemeralSessionConfiguration];
configuration.allowsCellularAccess = allowsCellularAccess;
for (NSURLSessionConfiguration *configuration in [self pn_configurations].allValues) {

configuration.allowsCellularAccess = allowsCellularAccess;
}
}

+ (NSArray<Class> *)pn_protocolClasses {

NSURLSessionConfiguration *configuration = [self pn_ephemeralSessionConfiguration];
NSURLSessionConfiguration *configuration = [self pn_configurations].allValues.firstObject;

return [self pn_filteredProtocolClasses:configuration.protocolClasses];
}

+ (void)pn_setProtocolClasses:(NSArray<Class> *)protocolClasses {

NSURLSessionConfiguration *configuration = [self pn_ephemeralSessionConfiguration];
NSArray<Class> *classes = [self pn_configurations].allValues.firstObject.protocolClasses;

// Append user-provided protocol classes to system-provided.
NSMutableArray *currentProtocolClasses = [NSMutableArray arrayWithArray:configuration.protocolClasses];
NSMutableArray *currentProtocolClasses = [NSMutableArray arrayWithArray:classes];
[currentProtocolClasses removeObjectsInArray:[self pn_protocolClasses]];
[currentProtocolClasses addObjectsFromArray:[self pn_filteredProtocolClasses:protocolClasses]];
configuration.protocolClasses = [currentProtocolClasses copy];

classes = [currentProtocolClasses copy];
for (NSURLSessionConfiguration *configuration in [self pn_configurations].allValues) {

configuration.protocolClasses = classes;
}
}

+ (NSDictionary<NSString *, id> *)pn_connectionProxyDictionary {

NSURLSessionConfiguration *configuration = [self pn_ephemeralSessionConfiguration];
NSURLSessionConfiguration *configuration = [self pn_configurations].allValues.firstObject;

return configuration.connectionProxyDictionary;
}

+ (void)pn_setConnectionProxyDictionary:(NSDictionary<NSString *, id> *)connectionProxyDictionary {

NSURLSessionConfiguration *configuration = [self pn_ephemeralSessionConfiguration];
configuration.connectionProxyDictionary = connectionProxyDictionary;
for (NSURLSessionConfiguration *configuration in [self pn_configurations].allValues) {

configuration.connectionProxyDictionary = connectionProxyDictionary;
}
}


#pragma mark - Misc

+ (NSMutableDictionary<NSString *, NSURLSessionConfiguration *> *)pn_configurations {

static NSMutableDictionary *_sharedSessionConfigurations;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ _sharedSessionConfigurations = [NSMutableDictionary new]; });

return _sharedSessionConfigurations;
}

+ (NSArray<Class> *)pn_filteredProtocolClasses:(NSArray<Class> *)protocolClasses {

NSArray<Class> *protocols = (protocolClasses.count ? protocolClasses : nil);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ NS_ASSUME_NONNULL_BEGIN
@since 4.4.0
@param identifier Unique identifier to identify session configuration among other.
@return Configured and ready to use \c NSURLSession configuration instance.
*/
+ (instancetype)pn_ephemeralSessionConfiguration;
+ (instancetype)pn_ephemeralSessionConfigurationWithIdentifier:(NSString *)identifier;

#pragma mark -

Expand Down
27 changes: 15 additions & 12 deletions PubNub/Network/PNNetwork.m
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,13 @@ @interface PNNetwork () <NSURLSessionDelegate>
*/
@property (nonatomic, strong) PNConfiguration *configuration;

/**
@brief Stores reference on unique \b PubNub network manager instance identifier.
@since 4.4.1
*/
@property (nonatomic, copy) NSString *identifier;

/**
@brief Stores whether \b PubNub network manager configured for long-poll request processing or not.
@discussion This property taken into account when manager need to invalidate underlying \a NSURLSession and
Expand Down Expand Up @@ -176,16 +183,13 @@ changed at any moment (invalidated instances can't be used and SDK should instan
@param maximumConnections Maximum simultaneously connections (requests) which can be opened.
@param longPollEnabled Whether \b PubNub network manager should be configured for long-poll requests or
not. This option affect the way how network manager handle reset.
@param queue Reference on GCD queue which should be used for callbacks and as working queue for
underlying logic.
@return 4.0
@since Initialized and ready to use \b PubNub network manager.
*/
- (instancetype)initForClient:(PubNub *)client requestTimeout:(NSTimeInterval)timeout
maximumConnections:(NSInteger)maximumConnections longPoll:(BOOL)longPollEnabled
workingQueue:(dispatch_queue_t)queue;
maximumConnections:(NSInteger)maximumConnections longPoll:(BOOL)longPollEnabled;


#pragma mark - Request helper
Expand Down Expand Up @@ -473,23 +477,21 @@ + (void)ddSetLogLevel:(DDLogLevel)logLevel {
+ (instancetype)networkForClient:(PubNub *)client requestTimeout:(NSTimeInterval)timeout
maximumConnections:(NSInteger)maximumConnections longPoll:(BOOL)longPollEnabled {

dispatch_queue_t queue = dispatch_queue_create("com.pubnub.network", DISPATCH_QUEUE_CONCURRENT);
return [[self alloc] initForClient:client requestTimeout:timeout
maximumConnections:maximumConnections longPoll:longPollEnabled
workingQueue:queue];
return [[self alloc] initForClient:client requestTimeout:timeout maximumConnections:maximumConnections
longPoll:longPollEnabled];
}

- (instancetype)initForClient:(PubNub *)client requestTimeout:(NSTimeInterval)timeout
maximumConnections:(NSInteger)maximumConnections longPoll:(BOOL)longPollEnabled
workingQueue:(dispatch_queue_t)queue {
maximumConnections:(NSInteger)maximumConnections longPoll:(BOOL)longPollEnabled {

// Check whether initialization was successful or not.
if ((self = [super init])) {

_client = client;
_configuration = client.configuration;
_forLongPollRequests = longPollEnabled;
_processingQueue = queue;
_identifier = [[NSString stringWithFormat:@"com.pubnub.network.%p", self] copy];
_processingQueue = dispatch_queue_create([_identifier UTF8String], DISPATCH_QUEUE_CONCURRENT);;
_serializer = [PNNetworkResponseSerializer new];
_baseURL = [self requestBaseURL];
_lock = OS_SPINLOCK_INIT;
Expand Down Expand Up @@ -785,7 +787,8 @@ - (NSURLSessionConfiguration *)configurationWithRequestTimeout:(NSTimeInterval)t

// Prepare base configuration with predefined timeout values and maximum connections
// to same host (basically how many requests can be handled at once).
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration pn_ephemeralSessionConfiguration];
NSURLSessionConfiguration *configuration = nil;
configuration = [NSURLSessionConfiguration pn_ephemeralSessionConfigurationWithIdentifier:self.identifier];
configuration.HTTPShouldUsePipelining = !self.forLongPollRequests;
configuration.timeoutIntervalForRequest = timeout;
configuration.HTTPMaximumConnectionsPerHost = maximumConnections;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
buildConfiguration = "Release"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
Expand Down
43 changes: 41 additions & 2 deletions Tests/iOS Tests/Tests/NSURLSessionConfigurationCategoryTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,9 @@ - (void)setUp {
// Forward method call to the super class.
[super setUp];

self.configuration = [NSURLSessionConfiguration pn_ephemeralSessionConfiguration];
self.configuration = [NSURLSessionConfiguration pn_ephemeralSessionConfigurationWithIdentifier:@"testConfiguration"];
self.originalProtocolClasses = [self.configuration.protocolClasses copy];

// Reset shared session configuration instance.
[NSURLSessionConfiguration pn_setHTTPAdditionalHeaders:nil];
[NSURLSessionConfiguration pn_setNetworkServiceType:NSURLNetworkServiceTypeDefault];
Expand All @@ -84,6 +84,45 @@ - (void)tearDown {
self.configuration.protocolClasses = self.originalProtocolClasses;
}

/**
@brief Checking what changes inside of two configuration for non-shared values won't affect second instance.
*/
- (void)testConfigurationsScopedModification {

NSURLSessionConfiguration *configuration1 = nil;
NSURLSessionConfiguration *configuration2 = nil;

// Setup first configuration data.
configuration1 = [NSURLSessionConfiguration pn_ephemeralSessionConfigurationWithIdentifier:@"testConfiguration1"];
configuration1.HTTPShouldUsePipelining = YES;
configuration1.timeoutIntervalForRequest = 10.f;
configuration1.HTTPMaximumConnectionsPerHost = 3;

// Setup second configuration data.
configuration2 = [NSURLSessionConfiguration pn_ephemeralSessionConfigurationWithIdentifier:@"testConfiguration2"];
configuration2.HTTPShouldUsePipelining = NO;
configuration2.timeoutIntervalForRequest = 310.f;
configuration2.HTTPMaximumConnectionsPerHost = 1;

// Set custom HTTP header.
NSDictionary *customHeaders = @{@"X-Powered-By": @"PubNub"};
[NSURLSessionConfiguration pn_setHTTPAdditionalHeaders:customHeaders];

XCTAssertNotEqual(configuration1.HTTPShouldUsePipelining, configuration2.HTTPShouldUsePipelining,
@"HTTPShouldUsePipelining value expected to be different for independent configuration "
"instances.");
XCTAssertNotEqual(configuration1.timeoutIntervalForRequest, configuration2.timeoutIntervalForRequest,
@"Request timeout value expected to be different for independent configuration "
"instances.");
XCTAssertNotEqual(configuration1.HTTPMaximumConnectionsPerHost, configuration2.HTTPMaximumConnectionsPerHost,
@"HTTPMaximumConnectionsPerHost value expected to be different for independent "
"configuration instances.");

XCTAssertEqualObjects(configuration1.HTTPAdditionalHeaders, configuration2.HTTPAdditionalHeaders,
@"Independent configurations expected to has same value for shared additional HTTP "
"headers.");
}

- (void)testDefaultSessionConfiguration {

XCTAssertEqual(self.configuration.requestCachePolicy, NSURLRequestReloadIgnoringLocalCacheData,
Expand Down

0 comments on commit 91a5176

Please sign in to comment.