From 5349a1d4ffd3f612f0cbc94beb8eb55d97e7b9e3 Mon Sep 17 00:00:00 2001 From: Mattt Thompson Date: Mon, 22 Aug 2011 19:16:31 -0500 Subject: [PATCH 1/5] Experimental implementation using a single, dedicated network thread, rather than spinning up new run loops for each request --- AFNetworking/AFHTTPRequestOperation.m | 43 ++++++++++++++++----------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/AFNetworking/AFHTTPRequestOperation.m b/AFNetworking/AFHTTPRequestOperation.m index bc3ca48..825706c 100644 --- a/AFNetworking/AFHTTPRequestOperation.m +++ b/AFNetworking/AFHTTPRequestOperation.m @@ -85,7 +85,6 @@ static inline BOOL AFHTTPOperationStateTransitionIsValid(AFHTTPOperationState fr @property (readwrite, nonatomic, copy) AFHTTPRequestOperationCompletionBlock completion; - (id)initWithRequest:(NSURLRequest *)urlRequest; -- (void)cleanup; @end @implementation AFHTTPRequestOperation @@ -102,6 +101,25 @@ static inline BOOL AFHTTPOperationStateTransitionIsValid(AFHTTPOperationState fr @synthesize progress = _progress; @synthesize completion = _completion; +static NSThread *_networkRequestThread = nil; + ++ (NSThread *)networkRequestThread { + if (!_networkRequestThread) { + _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil]; + [_networkRequestThread start]; + } + + return _networkRequestThread; +} + ++ (void)networkRequestThreadEntryPoint:(id)object { + do { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + [[NSRunLoop currentRunLoop] run]; + [pool drain]; + } while (YES); +} + + (id)operationWithRequest:(NSURLRequest *)urlRequest completion:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSData *data, NSError *error))completion { @@ -163,15 +181,6 @@ static inline BOOL AFHTTPOperationStateTransitionIsValid(AFHTTPOperationState fr [super dealloc]; } -- (void)cleanup { - [self.outputStream close]; - for (NSString *runLoopMode in self.runLoopModes) { - [self.connection unscheduleFromRunLoop:[NSRunLoop currentRunLoop] forMode:runLoopMode]; - [self.outputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:runLoopMode]; - } - CFRunLoopStop([[NSRunLoop currentRunLoop] getCFRunLoop]); -} - - (void)setProgressBlock:(void (^)(NSUInteger totalBytesWritten, NSUInteger totalBytesExpectedToWrite))block { self.progress = block; } @@ -198,7 +207,6 @@ static inline BOOL AFHTTPOperationStateTransitionIsValid(AFHTTPOperationState fr case AFHTTPOperationFinishedState: [[AFNetworkActivityIndicatorManager sharedManager] stopAnimating]; [[NSNotificationCenter defaultCenter] postNotificationName:AFHTTPOperationDidFinishNotification object:self]; - [self cleanup]; break; default: break; @@ -233,7 +241,11 @@ static inline BOOL AFHTTPOperationStateTransitionIsValid(AFHTTPOperationState fr } self.state = AFHTTPOperationExecutingState; - + + [self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:YES modes:[self.runLoopModes allObjects]]; +} + +- (void)operationDidStart { self.connection = [[[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO] autorelease]; NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; @@ -243,16 +255,13 @@ static inline BOOL AFHTTPOperationStateTransitionIsValid(AFHTTPOperationState fr } [self.connection start]; - - [runLoop run]; + } - (void)cancel { self.isCancelled = YES; - [self.connection cancel]; - - [self cleanup]; + [self.connection cancel]; } #pragma mark - AFHTTPRequestOperation From 59a98bb93741bd7b1c82f9ecffb577701971acdc Mon Sep 17 00:00:00 2001 From: Mattt Thompson Date: Tue, 23 Aug 2011 12:12:05 -0500 Subject: [PATCH 2/5] Experimental implementation of download progress block Renaming progressBlock to uploadProgressBlock --- AFNetworking/AFHTTPRequestOperation.h | 3 ++- AFNetworking/AFHTTPRequestOperation.m | 29 ++++++++++++++++++--------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/AFNetworking/AFHTTPRequestOperation.h b/AFNetworking/AFHTTPRequestOperation.h index 48f87a7..daee496 100644 --- a/AFNetworking/AFHTTPRequestOperation.h +++ b/AFNetworking/AFHTTPRequestOperation.h @@ -56,6 +56,7 @@ extern NSString * const AFHTTPOperationDidFinishNotification; outputStream:(NSOutputStream *)outputStream completion:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error))completion; -- (void)setProgressBlock:(void (^)(NSUInteger totalBytesWritten, NSUInteger totalBytesExpectedToWrite))block; +- (void)setUploadProgressBlock:(void (^)(NSUInteger totalBytesWritten, NSUInteger totalBytesExpectedToWrite))block; +- (void)setDownloadProgressBlock:(void (^)(NSUInteger totalBytesRead, NSUInteger totalBytesExpectedToRead))block; @end diff --git a/AFNetworking/AFHTTPRequestOperation.m b/AFNetworking/AFHTTPRequestOperation.m index 825706c..495192c 100644 --- a/AFNetworking/AFHTTPRequestOperation.m +++ b/AFNetworking/AFHTTPRequestOperation.m @@ -33,7 +33,7 @@ typedef enum { NSString * const AFHTTPOperationDidStartNotification = @"com.alamofire.http-operation.start"; NSString * const AFHTTPOperationDidFinishNotification = @"com.alamofire.http-operation.finish"; -typedef void (^AFHTTPRequestOperationProgressBlock)(NSUInteger totalBytesWritten, NSUInteger totalBytesExpectedToWrite); +typedef void (^AFHTTPRequestOperationProgressBlock)(NSUInteger totalBytes, NSUInteger totalBytesExpected); typedef void (^AFHTTPRequestOperationCompletionBlock)(NSURLRequest *request, NSHTTPURLResponse *response, NSData *data, NSError *error); static inline NSString * AFKeyPathFromOperationState(AFHTTPOperationState state) { @@ -81,7 +81,8 @@ static inline BOOL AFHTTPOperationStateTransitionIsValid(AFHTTPOperationState fr @property (nonatomic, assign) BOOL isCancelled; @property (readwrite, nonatomic, retain) NSMutableData *dataAccumulator; @property (readwrite, nonatomic, retain) NSOutputStream *outputStream; -@property (readwrite, nonatomic, copy) AFHTTPRequestOperationProgressBlock progress; +@property (readwrite, nonatomic, copy) AFHTTPRequestOperationProgressBlock uploadProgress; +@property (readwrite, nonatomic, copy) AFHTTPRequestOperationProgressBlock downloadProgress; @property (readwrite, nonatomic, copy) AFHTTPRequestOperationCompletionBlock completion; - (id)initWithRequest:(NSURLRequest *)urlRequest; @@ -98,7 +99,8 @@ static inline BOOL AFHTTPOperationStateTransitionIsValid(AFHTTPOperationState fr @synthesize responseBody = _responseBody; @synthesize dataAccumulator = _dataAccumulator; @synthesize outputStream = _outputStream; -@synthesize progress = _progress; +@synthesize uploadProgress = _uploadProgress; +@synthesize downloadProgress = _downloadProgress; @synthesize completion = _completion; static NSThread *_networkRequestThread = nil; @@ -175,16 +177,21 @@ static NSThread *_networkRequestThread = nil; [_connection release]; _connection = nil; - [_progress release]; + [_uploadProgress release]; + [_downloadProgress release]; [_completion release]; - [_progress release]; [super dealloc]; } -- (void)setProgressBlock:(void (^)(NSUInteger totalBytesWritten, NSUInteger totalBytesExpectedToWrite))block { - self.progress = block; +- (void)setUploadProgressBlock:(void (^)(NSUInteger totalBytesWritten, NSUInteger totalBytesExpectedToWrite))block { + self.uploadProgress = block; } +- (void)setDownloadProgressBlock:(void (^)(NSUInteger totalBytesRead, NSUInteger totalBytesExpectedToRead))block { + self.downloadProgress = block; +} + + - (void)setState:(AFHTTPOperationState)state { if (!AFHTTPOperationStateTransitionIsValid(self.state, state)) { return; @@ -301,6 +308,10 @@ didReceiveResponse:(NSURLResponse *)response } } else { [self.dataAccumulator appendData:data]; + + if (self.downloadProgress) { + self.downloadProgress([self.dataAccumulator length], self.response.expectedContentLength); + } } } @@ -332,8 +343,8 @@ didReceiveResponse:(NSURLResponse *)response totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite { - if (self.progress) { - self.progress(totalBytesWritten, totalBytesExpectedToWrite); + if (self.uploadProgress) { + self.uploadProgress(totalBytesWritten, totalBytesExpectedToWrite); } } From 8de22e8f99ea788b836e7b2aa05ea6bcc37f51c1 Mon Sep 17 00:00:00 2001 From: Mattt Thompson Date: Tue, 23 Aug 2011 21:15:25 -0500 Subject: [PATCH 3/5] Adding support for download progress block when an output block is used --- AFNetworking/AFHTTPRequestOperation.m | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/AFNetworking/AFHTTPRequestOperation.m b/AFNetworking/AFHTTPRequestOperation.m index 495192c..37eee24 100644 --- a/AFNetworking/AFHTTPRequestOperation.m +++ b/AFNetworking/AFHTTPRequestOperation.m @@ -79,6 +79,7 @@ static inline BOOL AFHTTPOperationStateTransitionIsValid(AFHTTPOperationState fr @interface AFHTTPRequestOperation () @property (nonatomic, assign) AFHTTPOperationState state; @property (nonatomic, assign) BOOL isCancelled; +@property (readwrite, nonatomic, assign) NSUInteger totalBytesRead; @property (readwrite, nonatomic, retain) NSMutableData *dataAccumulator; @property (readwrite, nonatomic, retain) NSOutputStream *outputStream; @property (readwrite, nonatomic, copy) AFHTTPRequestOperationProgressBlock uploadProgress; @@ -97,6 +98,7 @@ static inline BOOL AFHTTPOperationStateTransitionIsValid(AFHTTPOperationState fr @synthesize response = _response; @synthesize error = _error; @synthesize responseBody = _responseBody; +@synthesize totalBytesRead = _totalBytesRead; @synthesize dataAccumulator = _dataAccumulator; @synthesize outputStream = _outputStream; @synthesize uploadProgress = _uploadProgress; @@ -301,6 +303,8 @@ didReceiveResponse:(NSURLResponse *)response - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { + self.totalBytesRead += [data length]; + if (self.outputStream) { if ([self.outputStream hasSpaceAvailable]) { const uint8_t *dataBuffer = [data bytes]; @@ -308,10 +312,10 @@ didReceiveResponse:(NSURLResponse *)response } } else { [self.dataAccumulator appendData:data]; + } - if (self.downloadProgress) { - self.downloadProgress([self.dataAccumulator length], self.response.expectedContentLength); - } + if (self.downloadProgress) { + self.downloadProgress(self.totalBytesRead, self.response.expectedContentLength); } } @@ -348,7 +352,9 @@ totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite } } -- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse { +- (NSCachedURLResponse *)connection:(NSURLConnection *)connection + willCacheResponse:(NSCachedURLResponse *)cachedResponse +{ if ([self isCancelled]) { return nil; } From 79e4d0e823ede1acd8cdadeb3ed727fabfb1e59d Mon Sep 17 00:00:00 2001 From: Mattt Thompson Date: Wed, 24 Aug 2011 14:28:56 -0500 Subject: [PATCH 4/5] Removing unnecessary performSelectorOnMainThread for -finish calls --- AFNetworking/AFHTTPRequestOperation.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AFNetworking/AFHTTPRequestOperation.m b/AFNetworking/AFHTTPRequestOperation.m index 37eee24..1ad535c 100644 --- a/AFNetworking/AFHTTPRequestOperation.m +++ b/AFNetworking/AFHTTPRequestOperation.m @@ -329,7 +329,7 @@ didReceiveResponse:(NSURLResponse *)response [_dataAccumulator release]; _dataAccumulator = nil; } - [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO]; + [self finish]; } - (void)connection:(NSURLConnection *)connection @@ -339,7 +339,7 @@ didReceiveResponse:(NSURLResponse *)response self.error = error; - [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO]; + [self finish]; } - (void)connection:(NSURLConnection *)connection From f8d3550aafc414ec199062d55cfaaca0d5660b2e Mon Sep 17 00:00:00 2001 From: Mattt Thompson Date: Wed, 24 Aug 2011 15:21:17 -0500 Subject: [PATCH 5/5] Experimental implementation of HTTP Basic auth helper in AFRestClient --- AFNetworking/AFRestClient.h | 1 + AFNetworking/AFRestClient.m | 8 ++++++++ AFNetworking/NSData+AFNetworking.h | 1 + AFNetworking/NSData+AFNetworking.m | 29 +++++++++++++++++++++++++++++ 4 files changed, 39 insertions(+) diff --git a/AFNetworking/AFRestClient.h b/AFNetworking/AFRestClient.h index 57c54b5..7470810 100644 --- a/AFNetworking/AFRestClient.h +++ b/AFNetworking/AFRestClient.h @@ -38,6 +38,7 @@ - (NSString *)defaultValueForHeader:(NSString *)header; - (void)setDefaultHeader:(NSString *)header value:(NSString *)value; +- (void)setAuthorizationHeaderWithUsername:(NSString *)username password:(NSString *)password; - (void)setAuthorizationHeaderWithToken:(NSString *)token; - (void)clearAuthorizationHeader; diff --git a/AFNetworking/AFRestClient.m b/AFNetworking/AFRestClient.m index 2062c55..d699e59 100644 --- a/AFNetworking/AFRestClient.m +++ b/AFNetworking/AFRestClient.m @@ -23,6 +23,8 @@ #import "AFRestClient.h" #import "AFJSONRequestOperation.h" +#import "NSData+AFNetworking.h" + static NSStringEncoding const kAFRestClientStringEncoding = NSUTF8StringEncoding; @interface AFRestClient () @@ -86,6 +88,12 @@ static NSStringEncoding const kAFRestClientStringEncoding = NSUTF8StringEncoding [self.defaultHeaders setObject:value forKey:header]; } +- (void)setAuthorizationHeaderWithUsername:(NSString *)username password:(NSString *)password { + NSString *authHeader = [NSString stringWithFormat:@"%@:%@", username, password]; + NSString *encodedAuthHeader = [[NSData dataWithBytes:[authHeader UTF8String] length:[authHeader length]] base64EncodedString]; + [self setDefaultHeader:@"Authorization" value:[NSString stringWithFormat:@"Basic %@", encodedAuthHeader]]; +} + - (void)setAuthorizationHeaderWithToken:(NSString *)token { [self setDefaultHeader:@"Authorization" value:[NSString stringWithFormat:@"Token token=\"%@\"", token]]; } diff --git a/AFNetworking/NSData+AFNetworking.h b/AFNetworking/NSData+AFNetworking.h index 02851e9..4fe6c36 100644 --- a/AFNetworking/NSData+AFNetworking.h +++ b/AFNetworking/NSData+AFNetworking.h @@ -26,6 +26,7 @@ extern NSString * const kAFZlibErrorDomain; @interface NSData (AFNetworking) +- (NSString *)base64EncodedString; - (NSData *)dataByGZipCompressingWithError:(NSError **)error; - (NSData *)dataByGZipDecompressingDataWithError:(NSError **)error; diff --git a/AFNetworking/NSData+AFNetworking.m b/AFNetworking/NSData+AFNetworking.m index c90c6b1..9d2fad2 100644 --- a/AFNetworking/NSData+AFNetworking.m +++ b/AFNetworking/NSData+AFNetworking.m @@ -25,6 +25,8 @@ NSString * const kAFZlibErrorDomain = @"com.alamofire.zlib.error"; +static char Base64EncodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + static inline NSUInteger NSDataEstimatedCompressedLength(NSData *data) { return [data length] / 2; } @@ -88,6 +90,33 @@ typedef enum { @implementation NSData (AFNetworking) +- (NSString *)base64EncodedString { + NSUInteger length = [self length]; + NSMutableData *mutableData = [NSMutableData dataWithLength:((length + 2) / 3) * 4]; + + uint8_t *input = (uint8_t *)[self bytes]; + uint8_t *output = (uint8_t *)[mutableData mutableBytes]; + + for (NSUInteger i = 0; i < length; i += 3) { + NSUInteger value = 0; + for (NSUInteger j = i; j < (i + 3); j++) { + value <<= 8; + + if (j < length) { + value |= (0xFF & input[j]); + } + } + + NSInteger idx = (i / 3) * 4; + output[idx + 0] = Base64EncodingTable[(value >> 18) & 0x3F]; + output[idx + 1] = Base64EncodingTable[(value >> 12) & 0x3F]; + output[idx + 2] = (i + 1) < length ? Base64EncodingTable[(value >> 6) & 0x3F] : '='; + output[idx + 3] = (i + 2) < length ? Base64EncodingTable[(value >> 0) & 0x3F] : '='; + } + + return [[[NSString alloc] initWithData:mutableData encoding:NSASCIIStringEncoding] autorelease]; +} + - (NSData *)dataByGZipCompressingWithError:(NSError **)error { return [NSData dataByTransformingData:self usingGZipOperation:GzipDeflate error:error]; }