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 bc3ca48..1ad535c 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) { @@ -79,13 +79,14 @@ 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 progress; +@property (readwrite, nonatomic, copy) AFHTTPRequestOperationProgressBlock uploadProgress; +@property (readwrite, nonatomic, copy) AFHTTPRequestOperationProgressBlock downloadProgress; @property (readwrite, nonatomic, copy) AFHTTPRequestOperationCompletionBlock completion; - (id)initWithRequest:(NSURLRequest *)urlRequest; -- (void)cleanup; @end @implementation AFHTTPRequestOperation @@ -97,11 +98,32 @@ 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 progress = _progress; +@synthesize uploadProgress = _uploadProgress; +@synthesize downloadProgress = _downloadProgress; @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 { @@ -157,25 +179,21 @@ static inline BOOL AFHTTPOperationStateTransitionIsValid(AFHTTPOperationState fr [_connection release]; _connection = nil; - [_progress release]; + [_uploadProgress release]; + [_downloadProgress release]; [_completion release]; - [_progress release]; [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)setUploadProgressBlock:(void (^)(NSUInteger totalBytesWritten, NSUInteger totalBytesExpectedToWrite))block { + self.uploadProgress = block; } -- (void)setProgressBlock:(void (^)(NSUInteger totalBytesWritten, NSUInteger totalBytesExpectedToWrite))block { - self.progress = block; +- (void)setDownloadProgressBlock:(void (^)(NSUInteger totalBytesRead, NSUInteger totalBytesExpectedToRead))block { + self.downloadProgress = block; } + - (void)setState:(AFHTTPOperationState)state { if (!AFHTTPOperationStateTransitionIsValid(self.state, state)) { return; @@ -198,7 +216,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 +250,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 +264,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 @@ -285,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]; @@ -293,6 +313,10 @@ didReceiveResponse:(NSURLResponse *)response } else { [self.dataAccumulator appendData:data]; } + + if (self.downloadProgress) { + self.downloadProgress(self.totalBytesRead, self.response.expectedContentLength); + } } - (void)connectionDidFinishLoading:(NSURLConnection *)connection { @@ -305,7 +329,7 @@ didReceiveResponse:(NSURLResponse *)response [_dataAccumulator release]; _dataAccumulator = nil; } - [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO]; + [self finish]; } - (void)connection:(NSURLConnection *)connection @@ -315,7 +339,7 @@ didReceiveResponse:(NSURLResponse *)response self.error = error; - [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO]; + [self finish]; } - (void)connection:(NSURLConnection *)connection @@ -323,12 +347,14 @@ didReceiveResponse:(NSURLResponse *)response totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite { - if (self.progress) { - self.progress(totalBytesWritten, totalBytesExpectedToWrite); + if (self.uploadProgress) { + self.uploadProgress(totalBytesWritten, totalBytesExpectedToWrite); } } -- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse { +- (NSCachedURLResponse *)connection:(NSURLConnection *)connection + willCacheResponse:(NSCachedURLResponse *)cachedResponse +{ if ([self isCancelled]) { return nil; } 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]; }