Add session manager pools.

pull/1/head
Matthew Chen 6 years ago
parent 5fd6b52eb7
commit 280b9378b5

@ -25,18 +25,161 @@ BOOL IsNSErrorNetworkFailure(NSError *_Nullable error)
&& error.code == TSNetworkManagerErrorFailedConnection); && error.code == TSNetworkManagerErrorFailedConnection);
} }
#pragma mark -
@interface OWSSessionManager : NSObject
@property (nonatomic) AFHTTPSessionManager *sessionManager;
@property (nonatomic, readonly) NSDictionary *defaultHeaders;
@end
#pragma mark -
@implementation OWSSessionManager
- (instancetype)init
{
self = [super init];
if (!self) {
return self;
}
self.sessionManager = [OWSSignalService sharedInstance].signalServiceSessionManager;
self.sessionManager.completionQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// NOTE: We could enable HTTPShouldUsePipelining here.
// Make a copy of the default headers for this session manager.
_defaultHeaders = [self.sessionManager.requestSerializer.HTTPRequestHeaders copy];
return self;
}
- (void)performRequest:(TSRequest *)request
success:(TSNetworkManagerSuccess)success
failure:(TSNetworkManagerFailure)failure
{
OWSAssertDebug(request);
OWSAssertDebug(success);
OWSAssertDebug(failure);
// Clear all headers so that we don't retain headers from previous requests.
for (NSString *headerField in self.sessionManager.requestSerializer.HTTPRequestHeaders.allKeys.copy) {
[self.sessionManager.requestSerializer setValue:nil forHTTPHeaderField:headerField];
}
// Apply the default headers for this session manager.
for (NSString *headerField in self.defaultHeaders) {
NSString *headerValue = self.defaultHeaders[headerField];
[self.sessionManager.requestSerializer setValue:headerValue forHTTPHeaderField:headerField];
}
if (request.shouldHaveAuthorizationHeaders) {
[self.sessionManager.requestSerializer setAuthorizationHeaderFieldWithUsername:request.authUsername
password:request.authPassword];
}
// Honor the request's headers.
for (NSString *headerField in request.allHTTPHeaderFields) {
NSString *headerValue = request.allHTTPHeaderFields[headerField];
[self.sessionManager.requestSerializer setValue:headerValue forHTTPHeaderField:headerField];
}
if ([request.HTTPMethod isEqualToString:@"GET"]) {
[self.sessionManager GET:request.URL.absoluteString
parameters:request.parameters
progress:nil
success:success
failure:failure];
} else if ([request.HTTPMethod isEqualToString:@"POST"]) {
[self.sessionManager POST:request.URL.absoluteString
parameters:request.parameters
progress:nil
success:success
failure:failure];
} else if ([request.HTTPMethod isEqualToString:@"PUT"]) {
[self.sessionManager PUT:request.URL.absoluteString
parameters:request.parameters
success:success
failure:failure];
} else if ([request.HTTPMethod isEqualToString:@"DELETE"]) {
[self.sessionManager DELETE:request.URL.absoluteString
parameters:request.parameters
success:success
failure:failure];
} else {
OWSLogError(@"Trying to perform HTTP operation with unknown verb: %@", request.HTTPMethod);
}
}
@end
#pragma mark -
@interface OWSSessionManagerPool : NSObject
@property (nonatomic) NSMutableArray<OWSSessionManager *> *pool;
@end
#pragma mark -
@implementation OWSSessionManagerPool
- (instancetype)init
{
self = [super init];
if (!self) {
return self;
}
self.pool = [NSMutableArray new];
return self;
}
- (OWSSessionManager *)get
{
OWSSessionManager *_Nullable sessionManager = [self.pool lastObject];
if (sessionManager) {
OWSLogVerbose(@"Cache hit.");
[self.pool removeLastObject];
} else {
OWSLogVerbose(@"Cache miss.");
sessionManager = [OWSSessionManager new];
}
OWSAssertDebug(sessionManager);
return sessionManager;
}
- (void)returnToPool:(OWSSessionManager *)sessionManager
{
OWSAssertDebug(sessionManager);
const NSUInteger kMaxPoolSize = 3;
if (self.pool.count >= kMaxPoolSize) {
// Discard
return;
}
[self.pool addObject:sessionManager];
}
@end
#pragma mark -
@interface TSNetworkManager () @interface TSNetworkManager ()
// This property should only be accessed on udSerialQueue. // These properties should only be accessed on serialQueue.
@property (atomic, readonly) AFHTTPSessionManager *udSessionManager; @property (atomic, readonly) OWSSessionManagerPool *udSessionManagerPool;
@property (atomic, readonly) NSDictionary *udSessionManagerDefaultHeaders; @property (atomic, readonly) OWSSessionManagerPool *nonUdSessionManagerPool;
@property (atomic, readonly) dispatch_queue_t udSerialQueue; @property (atomic, readonly) dispatch_queue_t serialQueue;
typedef void (^failureBlock)(NSURLSessionDataTask *task, NSError *error); typedef void (^failureBlock)(NSURLSessionDataTask *task, NSError *error);
@end @end
#pragma mark -
@implementation TSNetworkManager @implementation TSNetworkManager
#pragma mark - Dependencies #pragma mark - Dependencies
@ -48,8 +191,7 @@ typedef void (^failureBlock)(NSURLSessionDataTask *task, NSError *error);
#pragma mark - #pragma mark -
@synthesize udSessionManager = _udSessionManager; @synthesize serialQueue = _serialQueue;
@synthesize udSerialQueue = _udSerialQueue;
#pragma mark - Singleton #pragma mark - Singleton
@ -67,7 +209,9 @@ typedef void (^failureBlock)(NSURLSessionDataTask *task, NSError *error);
return self; return self;
} }
_udSerialQueue = dispatch_queue_create("org.whispersystems.networkManager.udQueue", DISPATCH_QUEUE_SERIAL); _udSessionManagerPool = [OWSSessionManagerPool new];
_nonUdSessionManagerPool = [OWSSessionManagerPool new];
_serialQueue = dispatch_queue_create("org.whispersystems.networkManager", DISPATCH_QUEUE_SERIAL);
OWSSingletonAssert(); OWSSingletonAssert();
@ -92,148 +236,100 @@ typedef void (^failureBlock)(NSURLSessionDataTask *task, NSError *error);
OWSAssertDebug(successBlock); OWSAssertDebug(successBlock);
OWSAssertDebug(failureBlock); OWSAssertDebug(failureBlock);
if (request.isUDRequest) { dispatch_async(self.serialQueue, ^{
dispatch_async(self.udSerialQueue, ^{ if (request.isUDRequest) {
[self makeUDRequestSync:request success:successBlock failure:failureBlock]; [self makeUDRequestSync:request success:successBlock failure:failureBlock];
}); } else {
} else {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self makeRequestSync:request completionQueue:completionQueue success:successBlock failure:failureBlock]; [self makeRequestSync:request completionQueue:completionQueue success:successBlock failure:failureBlock];
}); }
} });
} }
- (void)makeRequestSync:(TSRequest *)request - (void)makeRequestSync:(TSRequest *)request
completionQueue:(dispatch_queue_t)completionQueue completionQueue:(dispatch_queue_t)completionQueue
success:(TSNetworkManagerSuccess)successBlock success:(TSNetworkManagerSuccess)successParam
failure:(TSNetworkManagerFailure)failureBlock failure:(TSNetworkManagerFailure)failureParam
{ {
OWSAssertDebug(request); OWSAssertDebug(request);
OWSAssertDebug(successBlock); OWSAssertDebug(successParam);
OWSAssertDebug(failureBlock); OWSAssertDebug(failureParam);
OWSLogInfo(@"Making Non-UD request: %@", request); OWSLogInfo(@"Making Non-UD request: %@", request);
// TODO: Remove this logging when the call connection issues have been resolved.
TSNetworkManagerSuccess success = ^(NSURLSessionDataTask *task, _Nullable id responseObject) {
OWSLogInfo(@"Non-UD request succeeded : %@", request);
if (request.shouldHaveAuthorizationHeaders) { OWSSessionManagerPool *sessionManagerPool = self.nonUdSessionManagerPool;
[TSNetworkManager.tsAccountManager setIsDeregistered:NO]; OWSSessionManager *sessionManager = [sessionManagerPool get];
}
successBlock(task, responseObject); TSNetworkManagerSuccess success = ^(NSURLSessionDataTask *task, _Nullable id responseObject) {
dispatch_async(self.serialQueue, ^{
[sessionManagerPool returnToPool:sessionManager];
});
[OutageDetection.sharedManager reportConnectionSuccess]; dispatch_async(completionQueue, ^{
}; OWSLogInfo(@"Non-UD request succeeded : %@", request);
TSNetworkManagerFailure failure = [TSNetworkManager errorPrettifyingForFailureBlock:failureBlock request:request];
AFHTTPSessionManager *sessionManager = [OWSSignalService sharedInstance].signalServiceSessionManager; if (request.shouldHaveAuthorizationHeaders) {
// [OWSSignalService signalServiceSessionManager] always returns a new instance of [TSNetworkManager.tsAccountManager setIsDeregistered:NO];
// session manager, so its safe to reconfigure it here. }
sessionManager.completionQueue = completionQueue;
if (request.shouldHaveAuthorizationHeaders) { successParam(task, responseObject);
[sessionManager.requestSerializer setAuthorizationHeaderFieldWithUsername:request.authUsername
password:request.authPassword];
}
// Honor the request's headers. [OutageDetection.sharedManager reportConnectionSuccess];
for (NSString *headerField in request.allHTTPHeaderFields) { });
NSString *headerValue = request.allHTTPHeaderFields[headerField]; };
[sessionManager.requestSerializer setValue:headerValue forHTTPHeaderField:headerField]; TSNetworkManagerSuccess failure = ^(NSURLSessionDataTask *task, NSError *error) {
} dispatch_async(self.serialQueue, ^{
[sessionManagerPool returnToPool:sessionManager];
[self performRequest:request sessionManager:sessionManager success:success failure:failure]; });
}
// This method should only be invoked on udSerialQueue. // TODO: Refactor this.
- (AFHTTPSessionManager *)udSessionManager [TSNetworkManager
{ errorPrettifyingForFailureBlock:^(NSURLSessionDataTask *task, NSError *error) {
if (!_udSessionManager) { dispatch_async(completionQueue, ^{
AFHTTPSessionManager *udSessionManager = [OWSSignalService sharedInstance].signalServiceSessionManager; failureParam(task, error);
udSessionManager.completionQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); });
// NOTE: We could enable HTTPShouldUsePipelining here. }
_udSessionManager = udSessionManager; request:request](task, error);
// Make a copy of the default headers for this session manager. };
_udSessionManagerDefaultHeaders = [udSessionManager.requestSerializer.HTTPRequestHeaders copy];
}
return _udSessionManager; [sessionManager performRequest:request success:success failure:failure];
} }
- (void)makeUDRequestSync:(TSRequest *)request - (void)makeUDRequestSync:(TSRequest *)request
success:(TSNetworkManagerSuccess)successBlock success:(TSNetworkManagerSuccess)successParam
failure:(TSNetworkManagerFailure)failureBlock failure:(TSNetworkManagerFailure)failureParam
{ {
OWSAssertDebug(request); OWSAssertDebug(request);
OWSAssert(!request.shouldHaveAuthorizationHeaders); OWSAssert(!request.shouldHaveAuthorizationHeaders);
OWSAssertDebug(successBlock); OWSAssertDebug(successParam);
OWSAssertDebug(failureBlock); OWSAssertDebug(failureParam);
OWSLogInfo(@"Making UD request: %@", request); OWSLogInfo(@"Making UD request: %@", request);
OWSSessionManagerPool *sessionManagerPool = self.udSessionManagerPool;
OWSSessionManager *sessionManager = [sessionManagerPool get];
TSNetworkManagerSuccess success = ^(NSURLSessionDataTask *task, _Nullable id responseObject) { TSNetworkManagerSuccess success = ^(NSURLSessionDataTask *task, _Nullable id responseObject) {
OWSLogInfo(@"UD request succeeded : %@", request); OWSLogInfo(@"UD request succeeded : %@", request);
successBlock(task, responseObject); dispatch_async(self.serialQueue, ^{
[sessionManagerPool returnToPool:sessionManager];
});
successParam(task, responseObject);
[OutageDetection.sharedManager reportConnectionSuccess]; [OutageDetection.sharedManager reportConnectionSuccess];
}; };
TSNetworkManagerFailure failure = [TSNetworkManager errorPrettifyingForFailureBlock:failureBlock request:request]; TSNetworkManagerSuccess failure = ^(NSURLSessionDataTask *task, NSError *error) {
dispatch_async(self.serialQueue, ^{
AFHTTPSessionManager *sessionManager = self.udSessionManager; [sessionManagerPool returnToPool:sessionManager];
});
// Clear all headers so that we don't retain headers from previous requests.
for (NSString *headerField in sessionManager.requestSerializer.HTTPRequestHeaders.allKeys.copy) {
[sessionManager.requestSerializer setValue:nil forHTTPHeaderField:headerField];
}
// Apply the default headers for this session manager.
for (NSString *headerField in self.udSessionManagerDefaultHeaders) {
NSString *headerValue = self.udSessionManagerDefaultHeaders[headerField];
[sessionManager.requestSerializer setValue:headerValue forHTTPHeaderField:headerField];
}
// Honor the request's headers.
for (NSString *headerField in request.allHTTPHeaderFields) {
NSString *headerValue = request.allHTTPHeaderFields[headerField];
[sessionManager.requestSerializer setValue:headerValue forHTTPHeaderField:headerField];
}
[self performRequest:request sessionManager:sessionManager success:success failure:failure];
}
- (void)performRequest:(TSRequest *)request // TODO: Refactor this.
sessionManager:(AFHTTPSessionManager *)sessionManager [TSNetworkManager errorPrettifyingForFailureBlock:failureParam request:request](task, error);
success:(TSNetworkManagerSuccess)success };
failure:(TSNetworkManagerFailure)failure
{
OWSAssertDebug(request);
OWSAssertDebug(sessionManager);
OWSAssertDebug(success);
OWSAssertDebug(failure);
if ([request.HTTPMethod isEqualToString:@"GET"]) { [sessionManager performRequest:request success:success failure:failure];
[sessionManager GET:request.URL.absoluteString
parameters:request.parameters
progress:nil
success:success
failure:failure];
} else if ([request.HTTPMethod isEqualToString:@"POST"]) {
[sessionManager POST:request.URL.absoluteString
parameters:request.parameters
progress:nil
success:success
failure:failure];
} else if ([request.HTTPMethod isEqualToString:@"PUT"]) {
[sessionManager PUT:request.URL.absoluteString parameters:request.parameters success:success failure:failure];
} else if ([request.HTTPMethod isEqualToString:@"DELETE"]) {
[sessionManager DELETE:request.URL.absoluteString
parameters:request.parameters
success:success
failure:failure];
} else {
OWSLogError(@"Trying to perform HTTP operation with unknown verb: %@", request.HTTPMethod);
}
} }
#ifdef DEBUG #ifdef DEBUG

Loading…
Cancel
Save