summaryrefslogtreecommitdiff
path: root/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.m
diff options
context:
space:
mode:
Diffstat (limited to 'platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.m')
-rw-r--r--platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.m789
1 files changed, 789 insertions, 0 deletions
diff --git a/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.m b/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.m
new file mode 100644
index 0000000000..cfe609a2de
--- /dev/null
+++ b/platform/ios/test/OHHTTPStubs/OHHTTPStubs/Pods/AFNetworking/AFNetworking/AFURLConnectionOperation.m
@@ -0,0 +1,789 @@
+// AFURLConnectionOperation.m
+//
+// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#import "AFURLConnectionOperation.h"
+
+#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
+#import <UIKit/UIKit.h>
+#endif
+
+#if !__has_feature(objc_arc)
+#error AFNetworking must be built with ARC.
+// You can turn on ARC for only AFNetworking files by adding -fobjc-arc to the build phase for each of its files.
+#endif
+
+typedef NS_ENUM(NSInteger, AFOperationState) {
+ AFOperationPausedState = -1,
+ AFOperationReadyState = 1,
+ AFOperationExecutingState = 2,
+ AFOperationFinishedState = 3,
+};
+
+#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && !defined(AF_APP_EXTENSIONS)
+typedef UIBackgroundTaskIdentifier AFBackgroundTaskIdentifier;
+#else
+typedef id AFBackgroundTaskIdentifier;
+#endif
+
+static dispatch_group_t url_request_operation_completion_group() {
+ static dispatch_group_t af_url_request_operation_completion_group;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ af_url_request_operation_completion_group = dispatch_group_create();
+ });
+
+ return af_url_request_operation_completion_group;
+}
+
+static dispatch_queue_t url_request_operation_completion_queue() {
+ static dispatch_queue_t af_url_request_operation_completion_queue;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ af_url_request_operation_completion_queue = dispatch_queue_create("com.alamofire.networking.operation.queue", DISPATCH_QUEUE_CONCURRENT );
+ });
+
+ return af_url_request_operation_completion_queue;
+}
+
+static NSString * const kAFNetworkingLockName = @"com.alamofire.networking.operation.lock";
+
+NSString * const AFNetworkingOperationDidStartNotification = @"com.alamofire.networking.operation.start";
+NSString * const AFNetworkingOperationDidFinishNotification = @"com.alamofire.networking.operation.finish";
+
+typedef void (^AFURLConnectionOperationProgressBlock)(NSUInteger bytes, long long totalBytes, long long totalBytesExpected);
+typedef void (^AFURLConnectionOperationAuthenticationChallengeBlock)(NSURLConnection *connection, NSURLAuthenticationChallenge *challenge);
+typedef NSCachedURLResponse * (^AFURLConnectionOperationCacheResponseBlock)(NSURLConnection *connection, NSCachedURLResponse *cachedResponse);
+typedef NSURLRequest * (^AFURLConnectionOperationRedirectResponseBlock)(NSURLConnection *connection, NSURLRequest *request, NSURLResponse *redirectResponse);
+
+static inline NSString * AFKeyPathFromOperationState(AFOperationState state) {
+ switch (state) {
+ case AFOperationReadyState:
+ return @"isReady";
+ case AFOperationExecutingState:
+ return @"isExecuting";
+ case AFOperationFinishedState:
+ return @"isFinished";
+ case AFOperationPausedState:
+ return @"isPaused";
+ default: {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunreachable-code"
+ return @"state";
+#pragma clang diagnostic pop
+ }
+ }
+}
+
+static inline BOOL AFStateTransitionIsValid(AFOperationState fromState, AFOperationState toState, BOOL isCancelled) {
+ switch (fromState) {
+ case AFOperationReadyState:
+ switch (toState) {
+ case AFOperationPausedState:
+ case AFOperationExecutingState:
+ return YES;
+ case AFOperationFinishedState:
+ return isCancelled;
+ default:
+ return NO;
+ }
+ case AFOperationExecutingState:
+ switch (toState) {
+ case AFOperationPausedState:
+ case AFOperationFinishedState:
+ return YES;
+ default:
+ return NO;
+ }
+ case AFOperationFinishedState:
+ return NO;
+ case AFOperationPausedState:
+ return toState == AFOperationReadyState;
+ default: {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunreachable-code"
+ switch (toState) {
+ case AFOperationPausedState:
+ case AFOperationReadyState:
+ case AFOperationExecutingState:
+ case AFOperationFinishedState:
+ return YES;
+ default:
+ return NO;
+ }
+ }
+#pragma clang diagnostic pop
+ }
+}
+
+@interface AFURLConnectionOperation ()
+@property (readwrite, nonatomic, assign) AFOperationState state;
+@property (readwrite, nonatomic, strong) NSRecursiveLock *lock;
+@property (readwrite, nonatomic, strong) NSURLConnection *connection;
+@property (readwrite, nonatomic, strong) NSURLRequest *request;
+@property (readwrite, nonatomic, strong) NSURLResponse *response;
+@property (readwrite, nonatomic, strong) NSError *error;
+@property (readwrite, nonatomic, strong) NSData *responseData;
+@property (readwrite, nonatomic, copy) NSString *responseString;
+@property (readwrite, nonatomic, assign) NSStringEncoding responseStringEncoding;
+@property (readwrite, nonatomic, assign) long long totalBytesRead;
+@property (readwrite, nonatomic, assign) AFBackgroundTaskIdentifier backgroundTaskIdentifier;
+@property (readwrite, nonatomic, copy) AFURLConnectionOperationProgressBlock uploadProgress;
+@property (readwrite, nonatomic, copy) AFURLConnectionOperationProgressBlock downloadProgress;
+@property (readwrite, nonatomic, copy) AFURLConnectionOperationAuthenticationChallengeBlock authenticationChallenge;
+@property (readwrite, nonatomic, copy) AFURLConnectionOperationCacheResponseBlock cacheResponse;
+@property (readwrite, nonatomic, copy) AFURLConnectionOperationRedirectResponseBlock redirectResponse;
+
+- (void)operationDidStart;
+- (void)finish;
+- (void)cancelConnection;
+@end
+
+@implementation AFURLConnectionOperation
+@synthesize outputStream = _outputStream;
+
++ (void)networkRequestThreadEntryPoint:(id)__unused object {
+ @autoreleasepool {
+ [[NSThread currentThread] setName:@"AFNetworking"];
+
+ NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
+ [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
+ [runLoop run];
+ }
+}
+
++ (NSThread *)networkRequestThread {
+ static NSThread *_networkRequestThread = nil;
+ static dispatch_once_t oncePredicate;
+ dispatch_once(&oncePredicate, ^{
+ _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
+ [_networkRequestThread start];
+ });
+
+ return _networkRequestThread;
+}
+
+- (instancetype)initWithRequest:(NSURLRequest *)urlRequest {
+ NSParameterAssert(urlRequest);
+
+ self = [super init];
+ if (!self) {
+ return nil;
+ }
+
+ _state = AFOperationReadyState;
+
+ self.lock = [[NSRecursiveLock alloc] init];
+ self.lock.name = kAFNetworkingLockName;
+
+ self.runLoopModes = [NSSet setWithObject:NSRunLoopCommonModes];
+
+ self.request = urlRequest;
+
+ self.shouldUseCredentialStorage = YES;
+
+ self.securityPolicy = [AFSecurityPolicy defaultPolicy];
+
+ return self;
+}
+
+- (void)dealloc {
+ if (_outputStream) {
+ [_outputStream close];
+ _outputStream = nil;
+ }
+
+#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && !defined(AF_APP_EXTENSIONS)
+ if (_backgroundTaskIdentifier) {
+ [[UIApplication sharedApplication] endBackgroundTask:_backgroundTaskIdentifier];
+ _backgroundTaskIdentifier = UIBackgroundTaskInvalid;
+ }
+#endif
+}
+
+#pragma mark -
+
+- (void)setResponseData:(NSData *)responseData {
+ [self.lock lock];
+ if (!responseData) {
+ _responseData = nil;
+ } else {
+ _responseData = [NSData dataWithBytes:responseData.bytes length:responseData.length];
+ }
+ [self.lock unlock];
+}
+
+- (NSString *)responseString {
+ [self.lock lock];
+ if (!_responseString && self.response && self.responseData) {
+ self.responseString = [[NSString alloc] initWithData:self.responseData encoding:self.responseStringEncoding];
+ }
+ [self.lock unlock];
+
+ return _responseString;
+}
+
+- (NSStringEncoding)responseStringEncoding {
+ [self.lock lock];
+ if (!_responseStringEncoding && self.response) {
+ NSStringEncoding stringEncoding = NSUTF8StringEncoding;
+ if (self.response.textEncodingName) {
+ CFStringEncoding IANAEncoding = CFStringConvertIANACharSetNameToEncoding((__bridge CFStringRef)self.response.textEncodingName);
+ if (IANAEncoding != kCFStringEncodingInvalidId) {
+ stringEncoding = CFStringConvertEncodingToNSStringEncoding(IANAEncoding);
+ }
+ }
+
+ self.responseStringEncoding = stringEncoding;
+ }
+ [self.lock unlock];
+
+ return _responseStringEncoding;
+}
+
+- (NSInputStream *)inputStream {
+ return self.request.HTTPBodyStream;
+}
+
+- (void)setInputStream:(NSInputStream *)inputStream {
+ NSMutableURLRequest *mutableRequest = [self.request mutableCopy];
+ mutableRequest.HTTPBodyStream = inputStream;
+ self.request = mutableRequest;
+}
+
+- (NSOutputStream *)outputStream {
+ if (!_outputStream) {
+ self.outputStream = [NSOutputStream outputStreamToMemory];
+ }
+
+ return _outputStream;
+}
+
+- (void)setOutputStream:(NSOutputStream *)outputStream {
+ [self.lock lock];
+ if (outputStream != _outputStream) {
+ if (_outputStream) {
+ [_outputStream close];
+ }
+
+ _outputStream = outputStream;
+ }
+ [self.lock unlock];
+}
+
+#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && !defined(AF_APP_EXTENSIONS)
+- (void)setShouldExecuteAsBackgroundTaskWithExpirationHandler:(void (^)(void))handler {
+ [self.lock lock];
+ if (!self.backgroundTaskIdentifier) {
+ UIApplication *application = [UIApplication sharedApplication];
+ __weak __typeof(self)weakSelf = self;
+ self.backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^{
+ __strong __typeof(weakSelf)strongSelf = weakSelf;
+
+ if (handler) {
+ handler();
+ }
+
+ if (strongSelf) {
+ [strongSelf cancel];
+
+ [application endBackgroundTask:strongSelf.backgroundTaskIdentifier];
+ strongSelf.backgroundTaskIdentifier = UIBackgroundTaskInvalid;
+ }
+ }];
+ }
+ [self.lock unlock];
+}
+#endif
+
+#pragma mark -
+
+- (void)setState:(AFOperationState)state {
+ if (!AFStateTransitionIsValid(self.state, state, [self isCancelled])) {
+ return;
+ }
+
+ [self.lock lock];
+ NSString *oldStateKey = AFKeyPathFromOperationState(self.state);
+ NSString *newStateKey = AFKeyPathFromOperationState(state);
+
+ [self willChangeValueForKey:newStateKey];
+ [self willChangeValueForKey:oldStateKey];
+ _state = state;
+ [self didChangeValueForKey:oldStateKey];
+ [self didChangeValueForKey:newStateKey];
+ [self.lock unlock];
+}
+
+- (void)pause {
+ if ([self isPaused] || [self isFinished] || [self isCancelled]) {
+ return;
+ }
+
+ [self.lock lock];
+ if ([self isExecuting]) {
+ [self performSelector:@selector(operationDidPause) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
+ [notificationCenter postNotificationName:AFNetworkingOperationDidFinishNotification object:self];
+ });
+ }
+
+ self.state = AFOperationPausedState;
+ [self.lock unlock];
+}
+
+- (void)operationDidPause {
+ [self.lock lock];
+ [self.connection cancel];
+ [self.lock unlock];
+}
+
+- (BOOL)isPaused {
+ return self.state == AFOperationPausedState;
+}
+
+- (void)resume {
+ if (![self isPaused]) {
+ return;
+ }
+
+ [self.lock lock];
+ self.state = AFOperationReadyState;
+
+ [self start];
+ [self.lock unlock];
+}
+
+#pragma mark -
+
+- (void)setUploadProgressBlock:(void (^)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite))block {
+ self.uploadProgress = block;
+}
+
+- (void)setDownloadProgressBlock:(void (^)(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead))block {
+ self.downloadProgress = block;
+}
+
+- (void)setWillSendRequestForAuthenticationChallengeBlock:(void (^)(NSURLConnection *connection, NSURLAuthenticationChallenge *challenge))block {
+ self.authenticationChallenge = block;
+}
+
+- (void)setCacheResponseBlock:(NSCachedURLResponse * (^)(NSURLConnection *connection, NSCachedURLResponse *cachedResponse))block {
+ self.cacheResponse = block;
+}
+
+- (void)setRedirectResponseBlock:(NSURLRequest * (^)(NSURLConnection *connection, NSURLRequest *request, NSURLResponse *redirectResponse))block {
+ self.redirectResponse = block;
+}
+
+#pragma mark - NSOperation
+
+- (void)setCompletionBlock:(void (^)(void))block {
+ [self.lock lock];
+ if (!block) {
+ [super setCompletionBlock:nil];
+ } else {
+ __weak __typeof(self)weakSelf = self;
+ [super setCompletionBlock:^ {
+ __strong __typeof(weakSelf)strongSelf = weakSelf;
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu"
+ dispatch_group_t group = strongSelf.completionGroup ?: url_request_operation_completion_group();
+ dispatch_queue_t queue = strongSelf.completionQueue ?: dispatch_get_main_queue();
+#pragma clang diagnostic pop
+
+ dispatch_group_async(group, queue, ^{
+ block();
+ });
+
+ dispatch_group_notify(group, url_request_operation_completion_queue(), ^{
+ [strongSelf setCompletionBlock:nil];
+ });
+ }];
+ }
+ [self.lock unlock];
+}
+
+- (BOOL)isReady {
+ return self.state == AFOperationReadyState && [super isReady];
+}
+
+- (BOOL)isExecuting {
+ return self.state == AFOperationExecutingState;
+}
+
+- (BOOL)isFinished {
+ return self.state == AFOperationFinishedState;
+}
+
+- (BOOL)isConcurrent {
+ return YES;
+}
+
+- (void)start {
+ [self.lock lock];
+ if ([self isCancelled]) {
+ [self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
+ } else if ([self isReady]) {
+ self.state = AFOperationExecutingState;
+
+ [self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
+ }
+ [self.lock unlock];
+}
+
+- (void)operationDidStart {
+ [self.lock lock];
+ if (![self isCancelled]) {
+ self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];
+
+ NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
+ for (NSString *runLoopMode in self.runLoopModes) {
+ [self.connection scheduleInRunLoop:runLoop forMode:runLoopMode];
+ [self.outputStream scheduleInRunLoop:runLoop forMode:runLoopMode];
+ }
+
+ [self.outputStream open];
+ [self.connection start];
+ }
+ [self.lock unlock];
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidStartNotification object:self];
+ });
+}
+
+- (void)finish {
+ [self.lock lock];
+ self.state = AFOperationFinishedState;
+ [self.lock unlock];
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidFinishNotification object:self];
+ });
+}
+
+- (void)cancel {
+ [self.lock lock];
+ if (![self isFinished] && ![self isCancelled]) {
+ [super cancel];
+
+ if ([self isExecuting]) {
+ [self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
+ }
+ }
+ [self.lock unlock];
+}
+
+- (void)cancelConnection {
+ NSDictionary *userInfo = nil;
+ if ([self.request URL]) {
+ userInfo = [NSDictionary dictionaryWithObject:[self.request URL] forKey:NSURLErrorFailingURLErrorKey];
+ }
+ NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:userInfo];
+
+ if (![self isFinished]) {
+ if (self.connection) {
+ [self.connection cancel];
+ [self performSelector:@selector(connection:didFailWithError:) withObject:self.connection withObject:error];
+ } else {
+ // Accomodate race condition where `self.connection` has not yet been set before cancellation
+ self.error = error;
+ [self finish];
+ }
+ }
+}
+
+#pragma mark -
+
++ (NSArray *)batchOfRequestOperations:(NSArray *)operations
+ progressBlock:(void (^)(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations))progressBlock
+ completionBlock:(void (^)(NSArray *operations))completionBlock
+{
+ if (!operations || [operations count] == 0) {
+ return @[[NSBlockOperation blockOperationWithBlock:^{
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if (completionBlock) {
+ completionBlock(@[]);
+ }
+ });
+ }]];
+ }
+
+ __block dispatch_group_t group = dispatch_group_create();
+ NSBlockOperation *batchedOperation = [NSBlockOperation blockOperationWithBlock:^{
+ dispatch_group_notify(group, dispatch_get_main_queue(), ^{
+ if (completionBlock) {
+ completionBlock(operations);
+ }
+ });
+ }];
+
+ for (AFURLConnectionOperation *operation in operations) {
+ operation.completionGroup = group;
+ void (^originalCompletionBlock)(void) = [operation.completionBlock copy];
+ __weak __typeof(operation)weakOperation = operation;
+ operation.completionBlock = ^{
+ __strong __typeof(weakOperation)strongOperation = weakOperation;
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu"
+ dispatch_queue_t queue = strongOperation.completionQueue ?: dispatch_get_main_queue();
+#pragma clang diagnostic pop
+ dispatch_group_async(group, queue, ^{
+ if (originalCompletionBlock) {
+ originalCompletionBlock();
+ }
+
+ NSUInteger numberOfFinishedOperations = [[operations indexesOfObjectsPassingTest:^BOOL(id op, NSUInteger __unused idx, BOOL __unused *stop) {
+ return [op isFinished];
+ }] count];
+
+ if (progressBlock) {
+ progressBlock(numberOfFinishedOperations, [operations count]);
+ }
+
+ dispatch_group_leave(group);
+ });
+ };
+
+ dispatch_group_enter(group);
+ [batchedOperation addDependency:operation];
+ }
+
+ return [operations arrayByAddingObject:batchedOperation];
+}
+
+#pragma mark - NSObject
+
+- (NSString *)description {
+ [self.lock lock];
+ NSString *description = [NSString stringWithFormat:@"<%@: %p, state: %@, cancelled: %@ request: %@, response: %@>", NSStringFromClass([self class]), self, AFKeyPathFromOperationState(self.state), ([self isCancelled] ? @"YES" : @"NO"), self.request, self.response];
+ [self.lock unlock];
+ return description;
+}
+
+#pragma mark - NSURLConnectionDelegate
+
+- (void)connection:(NSURLConnection *)connection
+willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
+{
+ if (self.authenticationChallenge) {
+ self.authenticationChallenge(connection, challenge);
+ return;
+ }
+
+ if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
+ if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
+ NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
+ [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
+ } else {
+ [[challenge sender] cancelAuthenticationChallenge:challenge];
+ }
+ } else {
+ if ([challenge previousFailureCount] == 0) {
+ if (self.credential) {
+ [[challenge sender] useCredential:self.credential forAuthenticationChallenge:challenge];
+ } else {
+ [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
+ }
+ } else {
+ [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
+ }
+ }
+}
+
+- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection __unused *)connection {
+ return self.shouldUseCredentialStorage;
+}
+
+- (NSURLRequest *)connection:(NSURLConnection *)connection
+ willSendRequest:(NSURLRequest *)request
+ redirectResponse:(NSURLResponse *)redirectResponse
+{
+ if (self.redirectResponse) {
+ return self.redirectResponse(connection, request, redirectResponse);
+ } else {
+ return request;
+ }
+}
+
+- (void)connection:(NSURLConnection __unused *)connection
+ didSendBodyData:(NSInteger)bytesWritten
+ totalBytesWritten:(NSInteger)totalBytesWritten
+totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
+{
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if (self.uploadProgress) {
+ self.uploadProgress((NSUInteger)bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
+ }
+ });
+}
+
+- (void)connection:(NSURLConnection __unused *)connection
+didReceiveResponse:(NSURLResponse *)response
+{
+ self.response = response;
+}
+
+- (void)connection:(NSURLConnection __unused *)connection
+ didReceiveData:(NSData *)data
+{
+ NSUInteger length = [data length];
+ while (YES) {
+ NSInteger totalNumberOfBytesWritten = 0;
+ if ([self.outputStream hasSpaceAvailable]) {
+ const uint8_t *dataBuffer = (uint8_t *)[data bytes];
+
+ NSInteger numberOfBytesWritten = 0;
+ while (totalNumberOfBytesWritten < (NSInteger)length) {
+ numberOfBytesWritten = [self.outputStream write:&dataBuffer[(NSUInteger)totalNumberOfBytesWritten] maxLength:(length - (NSUInteger)totalNumberOfBytesWritten)];
+ if (numberOfBytesWritten == -1) {
+ break;
+ }
+
+ totalNumberOfBytesWritten += numberOfBytesWritten;
+ }
+
+ break;
+ }
+
+ if (self.outputStream.streamError) {
+ [self.connection cancel];
+ [self performSelector:@selector(connection:didFailWithError:) withObject:self.connection withObject:self.outputStream.streamError];
+ return;
+ }
+ }
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ self.totalBytesRead += (long long)length;
+
+ if (self.downloadProgress) {
+ self.downloadProgress(length, self.totalBytesRead, self.response.expectedContentLength);
+ }
+ });
+}
+
+- (void)connectionDidFinishLoading:(NSURLConnection __unused *)connection {
+ self.responseData = [self.outputStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
+
+ [self.outputStream close];
+ if (self.responseData) {
+ self.outputStream = nil;
+ }
+
+ self.connection = nil;
+
+ [self finish];
+}
+
+- (void)connection:(NSURLConnection __unused *)connection
+ didFailWithError:(NSError *)error
+{
+ self.error = error;
+
+ [self.outputStream close];
+ if (self.responseData) {
+ self.outputStream = nil;
+ }
+
+ self.connection = nil;
+
+ [self finish];
+}
+
+- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
+ willCacheResponse:(NSCachedURLResponse *)cachedResponse
+{
+ if (self.cacheResponse) {
+ return self.cacheResponse(connection, cachedResponse);
+ } else {
+ if ([self isCancelled]) {
+ return nil;
+ }
+
+ return cachedResponse;
+ }
+}
+
+#pragma mark - NSSecureCoding
+
++ (BOOL)supportsSecureCoding {
+ return YES;
+}
+
+- (id)initWithCoder:(NSCoder *)decoder {
+ NSURLRequest *request = [decoder decodeObjectOfClass:[NSURLRequest class] forKey:NSStringFromSelector(@selector(request))];
+
+ self = [self initWithRequest:request];
+ if (!self) {
+ return nil;
+ }
+
+ self.state = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(state))] integerValue];
+ self.response = [decoder decodeObjectOfClass:[NSHTTPURLResponse class] forKey:NSStringFromSelector(@selector(response))];
+ self.error = [decoder decodeObjectOfClass:[NSError class] forKey:NSStringFromSelector(@selector(error))];
+ self.responseData = [decoder decodeObjectOfClass:[NSData class] forKey:NSStringFromSelector(@selector(responseData))];
+ self.totalBytesRead = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(totalBytesRead))] longLongValue];
+
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+ [self pause];
+
+ [coder encodeObject:self.request forKey:NSStringFromSelector(@selector(request))];
+
+ switch (self.state) {
+ case AFOperationExecutingState:
+ case AFOperationPausedState:
+ [coder encodeInteger:AFOperationReadyState forKey:NSStringFromSelector(@selector(state))];
+ break;
+ default:
+ [coder encodeInteger:self.state forKey:NSStringFromSelector(@selector(state))];
+ break;
+ }
+
+ [coder encodeObject:self.response forKey:NSStringFromSelector(@selector(response))];
+ [coder encodeObject:self.error forKey:NSStringFromSelector(@selector(error))];
+ [coder encodeObject:self.responseData forKey:NSStringFromSelector(@selector(responseData))];
+ [coder encodeInt64:self.totalBytesRead forKey:NSStringFromSelector(@selector(totalBytesRead))];
+}
+
+#pragma mark - NSCopying
+
+- (id)copyWithZone:(NSZone *)zone {
+ AFURLConnectionOperation *operation = [(AFURLConnectionOperation *)[[self class] allocWithZone:zone] initWithRequest:self.request];
+
+ operation.uploadProgress = self.uploadProgress;
+ operation.downloadProgress = self.downloadProgress;
+ operation.authenticationChallenge = self.authenticationChallenge;
+ operation.cacheResponse = self.cacheResponse;
+ operation.redirectResponse = self.redirectResponse;
+ operation.completionQueue = self.completionQueue;
+ operation.completionGroup = self.completionGroup;
+
+ return operation;
+}
+
+@end