diff --git a/AFNetworking/AFHTTPClient.h b/AFNetworking/AFHTTPClient.h index 27f9737..c8d1282 100755 --- a/AFNetworking/AFHTTPClient.h +++ b/AFNetworking/AFHTTPClient.h @@ -24,7 +24,7 @@ @class AFHTTPRequestOperation; @protocol AFHTTPClientOperation; -@protocol AFMultipartFormData; +@protocol AFStreamingMultipartFormData; /** Posted when network reachability changes. @@ -42,10 +42,10 @@ extern NSString * const AFNetworkingReachabilityDidChangeNotification; */ #ifdef _SYSTEMCONFIGURATION_H typedef enum { - AFNetworkReachabilityStatusUnknown = -1, - AFNetworkReachabilityStatusNotReachable = 0, - AFNetworkReachabilityStatusReachableViaWWAN = 1, - AFNetworkReachabilityStatusReachableViaWiFi = 2, + AFNetworkReachabilityStatusUnknown = -1, + AFNetworkReachabilityStatusNotReachable = 0, + AFNetworkReachabilityStatusReachableViaWWAN = 1, + AFNetworkReachabilityStatusReachableViaWiFi = 2, } AFNetworkReachabilityStatus; #endif @@ -53,9 +53,9 @@ typedef enum { Specifies the method used to encode parameters into request body. */ typedef enum { - AFFormURLParameterEncoding, - AFJSONParameterEncoding, - AFPropertyListParameterEncoding, + AFFormURLParameterEncoding, + AFJSONParameterEncoding, + AFPropertyListParameterEncoding, } AFHTTPClientParameterEncoding; /** @@ -116,14 +116,14 @@ extern NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *paramete Both `requestWithMethod:path:parameters` and `multipartFormRequestWithMethod:path:parameters:constructingBodyWithBlock:` construct URLs from the path relative to the `baseURL`, using `NSURL +URLWithString:relativeToURL:`. Below are a few examples of how `baseURL` and relative paths interract: - NSURL *baseURL = [NSURL URLWithString:@"http://example.com/v1/"]; - [NSURL URLWithString:@"foo" relativeToURL:baseURL]; // http://example.com/v1/foo - [NSURL URLWithString:@"foo?bar=baz" relativeToURL:baseURL]; // http://example.com/v1/foo?bar=baz - [NSURL URLWithString:@"/foo" relativeToURL:baseURL]; // http://example.com/foo - [NSURL URLWithString:@"foo/" relativeToURL:baseURL]; // http://example.com/v1/foo - [NSURL URLWithString:@"/foo/" relativeToURL:baseURL]; // http://example.com/foo/ - [NSURL URLWithString:@"http://example2.com/" relativeToURL:baseURL]; // http://example2.com/ - + NSURL *baseURL = [NSURL URLWithString:@"http://example.com/v1/"]; + [NSURL URLWithString:@"foo" relativeToURL:baseURL]; // http://example.com/v1/foo + [NSURL URLWithString:@"foo?bar=baz" relativeToURL:baseURL]; // http://example.com/v1/foo?bar=baz + [NSURL URLWithString:@"/foo" relativeToURL:baseURL]; // http://example.com/foo + [NSURL URLWithString:@"foo/" relativeToURL:baseURL]; // http://example.com/v1/foo + [NSURL URLWithString:@"/foo/" relativeToURL:baseURL]; // http://example.com/foo/ + [NSURL URLWithString:@"http://example2.com/" relativeToURL:baseURL]; // http://example2.com/ + */ @interface AFHTTPClient : NSObject @@ -143,7 +143,7 @@ extern NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *paramete /** The `AFHTTPClientParameterEncoding` value corresponding to how parameters are encoded into a request body. This is `AFFormURLParameterEncoding` by default. - + @warning JSON encoding will automatically use JSONKit, SBJSON, YAJL, or NextiveJSON, if provided. Otherwise, the built-in `NSJSONSerialization` class is used, if available (iOS 5.0 and Mac OS 10.7). If the build target does not either support `NSJSONSerialization` or include a third-party JSON library, a runtime exception will be thrown when attempting to encode parameters as JSON. */ @property (nonatomic, assign) AFHTTPClientParameterEncoding parameterEncoding; @@ -155,8 +155,8 @@ extern NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *paramete /** The reachability status from the device to the current `baseURL` of the `AFHTTPClient`. - - @warning This property requires the `SystemConfiguration` framework. Add it in the active target's "Link Binary With Library" build phase, and add `#import ` to the header prefix of the project (Prefix.pch). + + @warning This property requires the `SystemConfiguration` framework. Add it in the active target's "Link Binary With Library" build phase, and add `#import ` to the header prefix of the project (Prefix.pch). */ #ifdef _SYSTEMCONFIGURATION_H @property (readonly, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus; @@ -170,7 +170,7 @@ extern NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *paramete Creates and initializes an `AFHTTPClient` object with the specified base URL. @param url The base URL for the HTTP client. This argument must not be nil. - + @return The newly-initialized HTTP client */ + (AFHTTPClient *)clientWithBaseURL:(NSURL *)url; @@ -280,7 +280,7 @@ extern NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *paramete @param method The HTTP method for the request, such as `GET`, `POST`, `PUT`, or `DELETE`. @param path The path to be appended to the HTTP client's base URL and used as the request URL. @param parameters The parameters to be either set as a query string for `GET` requests, or the request HTTP body. - + @return An `NSMutableURLRequest` object */ - (NSMutableURLRequest *)requestWithMethod:(NSString *)method @@ -294,7 +294,7 @@ extern NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *paramete @param path The path to be appended to the HTTP client's base URL and used as the request URL. @param parameters The parameters to be encoded and set in the request HTTP body. @param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `AFMultipartFormData` protocol. This can be used to upload files, encode HTTP body as JSON or XML, or specify multiple values for the same parameter, as one might for array values. - + @discussion The multipart form data is constructed synchronously in the specified block, so in cases where large amounts of data are being added to the request, you should consider performing this method in the background. Likewise, the form data is constructed in-memory, so it may be advantageous to instead write parts of the form data to a file and stream the request body using the `HTTPBodyStream` property of `NSURLRequest`. @warning An exception will be raised if the specified method is not `POST`, `PUT` or `DELETE`. @@ -304,7 +304,7 @@ extern NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *paramete - (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters - constructingBodyWithBlock:(void (^)(id formData))block; + constructingBodyWithBlock:(void (^)(id formData))block; ///------------------------------- /// @name Creating HTTP Operations @@ -457,15 +457,10 @@ extern NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *paramete @see `AFHTTPClient -multipartFormRequestWithMethod:path:parameters:constructingBodyWithBlock:` */ -@protocol AFMultipartFormData -/** - Appends HTTP headers, followed by the encoded data and the multipart form boundary. - - @param headers The HTTP headers to be appended to the form data. - @param body The data to be encoded and appended to the form data. - */ -- (void)appendPartWithHeaders:(NSDictionary *)headers body:(NSData *)body; + +@protocol AFStreamingMultipartFormData + /** Appends the HTTP headers `Content-Disposition: form-data; name=#{name}"`, followed by the encoded data and the multipart form boundary. @@ -473,17 +468,8 @@ extern NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *paramete @param data The data to be encoded and appended to the form data. @param name The name to be associated with the specified data. This parameter must not be `nil`. */ -- (void)appendPartWithFormData:(NSData *)data name:(NSString *)name; -/** - Appends the HTTP header `Content-Disposition: file; filename=#{filename}; name=#{name}"` and `Content-Type: #{mimeType}`, followed by the encoded file data and the multipart form boundary. - - @param data The data to be encoded and appended to the form data. - @param name The name to be associated with the specified data. This parameter must not be `nil`. - @param mimeType The MIME type of the specified data. (For example, the MIME type for a JPEG image is image/jpeg.) For a list of valid MIME types, see http://www.iana.org/assignments/media-types/. This parameter must not be `nil`. - @param filename The filename to be associated with the specified data. This parameter must not be `nil`. - */ -- (void)appendPartWithFileData:(NSData *)data name:(NSString *)name fileName:(NSString *)fileName mimeType:(NSString *)mimeType; +- (void)appendPartWithFormData:(NSData *)data name:(NSString *)name; /** Appends the HTTP header `Content-Disposition: file; filename=#{generated filename}; name=#{name}"` and `Content-Type: #{generated mimeType}`, followed by the encoded file data and the multipart form boundary. @@ -496,34 +482,8 @@ extern NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *paramete @discussion The filename and MIME type for this data in the form will be automatically generated, using `NSURLResponse` `-suggestedFilename` and `-MIMEType`, respectively. */ + - (BOOL)appendPartWithFileURL:(NSURL *)fileURL name:(NSString *)name error:(NSError **)error; -/** - Appends the HTTP header `Content-Disposition: file; filename=#{generated filename}; name=#{name}"` and `Content-Type: #{mimeType}`, followed by the encoded file data and the multipart form boundary, using NSInputStream to read input. - - @param fileURL The URL corresponding to the file whose content will be appended to the form. - @param name The name to be associated with the specified data. This parameter must not be `nil`. - @param mimeType The MIME type of the specified data. (For example, the MIME type for a JPEG image is image/jpeg.) For a list of valid MIME types, see http://www.iana.org/assignments/media-types/. This parameter must not be `nil`. - - @discussion The filename is generated from the last component of the streamingURL parameter. The size of the buffer used to copy from streamingURL to the output stream is defined by the constant kAFStreamToStreamBufferSize. - */ -- (void)appendPartWithStreamingURL:(NSURL *)streamingURL - name:(NSString *)name - mimeType:(NSString *)mimeType; - -/** - Appends encoded data to the form data. - - @param data The data to be encoded and appended to the form data. - */ -- (void)appendData:(NSData *)data; - -/** - Appends a string to the form data. - - @param string The string to be encoded and appended to the form data. - */ -- (void)appendString:(NSString *)string; - @end diff --git a/AFNetworking/AFHTTPClient.m b/AFNetworking/AFHTTPClient.m index 1d456eb..1460d69 100755 --- a/AFNetworking/AFHTTPClient.m +++ b/AFNetworking/AFHTTPClient.m @@ -30,6 +30,9 @@ #if __IPHONE_OS_VERSION_MIN_REQUIRED #import +#import +#else +#import #endif #ifdef _SYSTEMCONFIGURATION_H @@ -41,10 +44,10 @@ #import #endif + NSString * const AFNetworkingReachabilityDidChangeNotification = @"com.alamofire.networking.reachability.change"; -@interface AFMultipartFormData : NSObject - +@interface AFStreamingMultipartFormData : NSObject - (id)initWithURLRequest:(NSMutableURLRequest *)request stringEncoding:(NSStringEncoding)encoding; @@ -52,6 +55,17 @@ NSString * const AFNetworkingReachabilityDidChangeNotification = @"com.alamofire @end +@interface AFMultipartBodyStream : NSInputStream + + +-(id)initWithStringEncoding:(NSStringEncoding)encoding; +-(BOOL)addFileFromURL:(NSURL *)fileURL name:(NSString *)name error:(NSError **)error; +-(void)addFormData:(NSData *)data name:(NSString *)name; +-(BOOL)empty; +-(NSUInteger)contentLength; + +@end + #pragma mark - #ifdef _SYSTEMCONFIGURATION_H @@ -66,37 +80,37 @@ typedef void (^AFCompletionBlock)(void); static NSUInteger const kAFHTTPClientDefaultMaxConcurrentOperationCount = 4; static NSString * AFBase64EncodedStringFromString(NSString *string) { - NSData *data = [NSData dataWithBytes:[string UTF8String] length:[string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]]; - NSUInteger length = [data length]; - NSMutableData *mutableData = [NSMutableData dataWithLength:((length + 2) / 3) * 4]; - - uint8_t *input = (uint8_t *)[data 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]); - } - } - - static uint8_t const kAFBase64EncodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - - NSUInteger idx = (i / 3) * 4; - output[idx + 0] = kAFBase64EncodingTable[(value >> 18) & 0x3F]; - output[idx + 1] = kAFBase64EncodingTable[(value >> 12) & 0x3F]; - output[idx + 2] = (i + 1) < length ? kAFBase64EncodingTable[(value >> 6) & 0x3F] : '='; - output[idx + 3] = (i + 2) < length ? kAFBase64EncodingTable[(value >> 0) & 0x3F] : '='; + NSData *data = [NSData dataWithBytes:[string UTF8String] length:[string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]]; + NSUInteger length = [data length]; + NSMutableData *mutableData = [NSMutableData dataWithLength:((length + 2) / 3) * 4]; + + uint8_t *input = (uint8_t *)[data 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]); + } } - return [[[NSString alloc] initWithData:mutableData encoding:NSASCIIStringEncoding] autorelease]; + static uint8_t const kAFBase64EncodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + NSUInteger idx = (i / 3) * 4; + output[idx + 0] = kAFBase64EncodingTable[(value >> 18) & 0x3F]; + output[idx + 1] = kAFBase64EncodingTable[(value >> 12) & 0x3F]; + output[idx + 2] = (i + 1) < length ? kAFBase64EncodingTable[(value >> 6) & 0x3F] : '='; + output[idx + 3] = (i + 2) < length ? kAFBase64EncodingTable[(value >> 0) & 0x3F] : '='; + } + + return [[[NSString alloc] initWithData:mutableData encoding:NSASCIIStringEncoding] autorelease]; } NSString * AFURLEncodedStringFromStringWithEncoding(NSString *string, NSStringEncoding encoding) { - static NSString * const kAFLegalCharactersToBeEscaped = @"?!@#$^&%*+=,:;'\"`<>()[]{}/\\|~ "; - + static NSString * const kAFLegalCharactersToBeEscaped = @"?!@#$^&%*+=,:;'\"`<>()[]{}/\\|~ "; + return [(NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)string, NULL, (CFStringRef)kAFLegalCharactersToBeEscaped, CFStringConvertNSStringEncodingToEncoding(encoding)) autorelease]; } @@ -104,8 +118,8 @@ NSString * AFURLEncodedStringFromStringWithEncoding(NSString *string, NSStringEn @interface AFQueryStringComponent : NSObject { @private - NSString *_key; - NSString *_value; + NSString *_key; + NSString *_value; } @property (readwrite, nonatomic, retain) id key; @@ -121,25 +135,25 @@ NSString * AFURLEncodedStringFromStringWithEncoding(NSString *string, NSStringEn @synthesize value = _value; - (id)initWithKey:(id)key value:(id)value { - self = [super init]; - if (!self) { - return nil; - } - - self.key = key; - self.value = value; - - return self; + self = [super init]; + if (!self) { + return nil; + } + + self.key = key; + self.value = value; + + return self; } - (void)dealloc { - [_key release]; - [_value release]; - [super dealloc]; + [_key release]; + [_value release]; + [super dealloc]; } - (NSString *)URLEncodedStringValueWithEncoding:(NSStringEncoding)stringEncoding { - return [NSString stringWithFormat:@"%@=%@", self.key, AFURLEncodedStringFromStringWithEncoding([self.value description], stringEncoding)]; + return [NSString stringWithFormat:@"%@=%@", self.key, AFURLEncodedStringFromStringWithEncoding([self.value description], stringEncoding)]; } @end @@ -151,69 +165,69 @@ extern NSArray * AFQueryStringComponentsFromKeyAndDictionaryValue(NSString *key, extern NSArray * AFQueryStringComponentsFromKeyAndArrayValue(NSString *key, NSArray *value); NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *parameters, NSStringEncoding stringEncoding) { - NSMutableArray *mutableComponents = [NSMutableArray array]; - for (AFQueryStringComponent *component in AFQueryStringComponentsFromKeyAndValue(nil, parameters)) { - [mutableComponents addObject:[component URLEncodedStringValueWithEncoding:stringEncoding]]; - } - - return [mutableComponents componentsJoinedByString:@"&"]; + NSMutableArray *mutableComponents = [NSMutableArray array]; + for (AFQueryStringComponent *component in AFQueryStringComponentsFromKeyAndValue(nil, parameters)) { + [mutableComponents addObject:[component URLEncodedStringValueWithEncoding:stringEncoding]]; + } + + return [mutableComponents componentsJoinedByString:@"&"]; } NSArray * AFQueryStringComponentsFromKeyAndValue(NSString *key, id value) { - NSMutableArray *mutableQueryStringComponents = [NSMutableArray array]; - - if([value isKindOfClass:[NSDictionary class]]) { - [mutableQueryStringComponents addObjectsFromArray:AFQueryStringComponentsFromKeyAndDictionaryValue(key, value)]; - } else if([value isKindOfClass:[NSArray class]]) { - [mutableQueryStringComponents addObjectsFromArray:AFQueryStringComponentsFromKeyAndArrayValue(key, value)]; - } else { - [mutableQueryStringComponents addObject:[[[AFQueryStringComponent alloc] initWithKey:key value:value] autorelease]]; - } - - return mutableQueryStringComponents; + NSMutableArray *mutableQueryStringComponents = [NSMutableArray array]; + + if([value isKindOfClass:[NSDictionary class]]) { + [mutableQueryStringComponents addObjectsFromArray:AFQueryStringComponentsFromKeyAndDictionaryValue(key, value)]; + } else if([value isKindOfClass:[NSArray class]]) { + [mutableQueryStringComponents addObjectsFromArray:AFQueryStringComponentsFromKeyAndArrayValue(key, value)]; + } else { + [mutableQueryStringComponents addObject:[[[AFQueryStringComponent alloc] initWithKey:key value:value] autorelease]]; + } + + return mutableQueryStringComponents; } NSArray * AFQueryStringComponentsFromKeyAndDictionaryValue(NSString *key, NSDictionary *value){ - NSMutableArray *mutableQueryStringComponents = [NSMutableArray array]; - - [value enumerateKeysAndObjectsUsingBlock:^(id nestedKey, id nestedValue, BOOL *stop) { - [mutableQueryStringComponents addObjectsFromArray:AFQueryStringComponentsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)]; - }]; - - return mutableQueryStringComponents; + NSMutableArray *mutableQueryStringComponents = [NSMutableArray array]; + + [value enumerateKeysAndObjectsUsingBlock:^(id nestedKey, id nestedValue, BOOL *stop) { + [mutableQueryStringComponents addObjectsFromArray:AFQueryStringComponentsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)]; + }]; + + return mutableQueryStringComponents; } NSArray * AFQueryStringComponentsFromKeyAndArrayValue(NSString *key, NSArray *value) { - NSMutableArray *mutableQueryStringComponents = [NSMutableArray array]; - - [value enumerateObjectsUsingBlock:^(id nestedValue, NSUInteger idx, BOOL *stop) { - [mutableQueryStringComponents addObjectsFromArray:AFQueryStringComponentsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)]; - }]; - - return mutableQueryStringComponents; + NSMutableArray *mutableQueryStringComponents = [NSMutableArray array]; + + [value enumerateObjectsUsingBlock:^(id nestedValue, NSUInteger idx, BOOL *stop) { + [mutableQueryStringComponents addObjectsFromArray:AFQueryStringComponentsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)]; + }]; + + return mutableQueryStringComponents; } static NSString * AFJSONStringFromParameters(NSDictionary *parameters) { - NSError *error = nil; - NSData *JSONData = AFJSONEncode(parameters, &error); - - if (!error) { - return [[[NSString alloc] initWithData:JSONData encoding:NSUTF8StringEncoding] autorelease]; - } else { - return nil; - } + NSError *error = nil; + NSData *JSONData = AFJSONEncode(parameters, &error); + + if (!error) { + return [[[NSString alloc] initWithData:JSONData encoding:NSUTF8StringEncoding] autorelease]; + } else { + return nil; + } } static NSString * AFPropertyListStringFromParameters(NSDictionary *parameters) { - NSString *propertyListString = nil; - NSError *error = nil; - - NSData *propertyListData = [NSPropertyListSerialization dataWithPropertyList:parameters format:NSPropertyListXMLFormat_v1_0 options:0 error:&error]; - if (!error) { - propertyListString = [[[NSString alloc] initWithData:propertyListData encoding:NSUTF8StringEncoding] autorelease]; - } - - return propertyListString; + NSString *propertyListString = nil; + NSError *error = nil; + + NSData *propertyListData = [NSPropertyListSerialization dataWithPropertyList:parameters format:NSPropertyListXMLFormat_v1_0 options:0 error:&error]; + if (!error) { + propertyListString = [[[NSString alloc] initWithData:propertyListData encoding:NSUTF8StringEncoding] autorelease]; + } + + return propertyListString; } @interface AFHTTPClient () @@ -247,177 +261,177 @@ static NSString * AFPropertyListStringFromParameters(NSDictionary *parameters) { #endif + (AFHTTPClient *)clientWithBaseURL:(NSURL *)url { - return [[[self alloc] initWithBaseURL:url] autorelease]; + return [[[self alloc] initWithBaseURL:url] autorelease]; } - (id)initWithBaseURL:(NSURL *)url { - self = [super init]; - if (!self) { - return nil; - } - - self.baseURL = url; - - self.stringEncoding = NSUTF8StringEncoding; - self.parameterEncoding = AFFormURLParameterEncoding; + self = [super init]; + if (!self) { + return nil; + } + + self.baseURL = url; + + self.stringEncoding = NSUTF8StringEncoding; + self.parameterEncoding = AFFormURLParameterEncoding; - self.registeredHTTPOperationClassNames = [NSMutableArray array]; - + self.registeredHTTPOperationClassNames = [NSMutableArray array]; + self.defaultHeaders = [NSMutableDictionary dictionary]; - + // Accept-Encoding HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3 [self setDefaultHeader:@"Accept-Encoding" value:@"gzip"]; // Accept-Language HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4 NSString *preferredLanguageCodes = [[NSLocale preferredLanguages] componentsJoinedByString:@", "]; [self setDefaultHeader:@"Accept-Language" value:[NSString stringWithFormat:@"%@, en-us;q=0.8", preferredLanguageCodes]]; - + #if __IPHONE_OS_VERSION_MIN_REQUIRED - // User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43 - [self setDefaultHeader:@"User-Agent" value:[NSString stringWithFormat:@"%@/%@ (%@, %@ %@, %@, Scale/%f)", [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString *)kCFBundleIdentifierKey], [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString *)kCFBundleVersionKey], @"unknown", [[UIDevice currentDevice] systemName], [[UIDevice currentDevice] systemVersion], [[UIDevice currentDevice] model], ([[UIScreen mainScreen] respondsToSelector:@selector(scale)] ? [[UIScreen mainScreen] scale] : 1.0)]]; + // User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43 + [self setDefaultHeader:@"User-Agent" value:[NSString stringWithFormat:@"%@/%@ (%@, %@ %@, %@, Scale/%f)", [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString *)kCFBundleIdentifierKey], [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString *)kCFBundleVersionKey], @"unknown", [[UIDevice currentDevice] systemName], [[UIDevice currentDevice] systemVersion], [[UIDevice currentDevice] model], ([[UIScreen mainScreen] respondsToSelector:@selector(scale)] ? [[UIScreen mainScreen] scale] : 1.0)]]; #elif __MAC_OS_X_VERSION_MIN_REQUIRED - [self setDefaultHeader:@"User-Agent" value:[NSString stringWithFormat:@"%@/%@ (%@)", [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString *)kCFBundleIdentifierKey], [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString *)kCFBundleVersionKey], @"unknown"]]; + [self setDefaultHeader:@"User-Agent" value:[NSString stringWithFormat:@"%@/%@ (%@)", [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString *)kCFBundleIdentifierKey], [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString *)kCFBundleVersionKey], @"unknown"]]; #endif - + #ifdef _SYSTEMCONFIGURATION_H - self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown; - [self startMonitoringNetworkReachability]; + self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown; + [self startMonitoringNetworkReachability]; #endif - - self.operationQueue = [[[NSOperationQueue alloc] init] autorelease]; + + self.operationQueue = [[[NSOperationQueue alloc] init] autorelease]; [self.operationQueue setMaxConcurrentOperationCount:kAFHTTPClientDefaultMaxConcurrentOperationCount]; - - return self; + + return self; } - (void)dealloc { #ifdef _SYSTEMCONFIGURATION_H - [self stopMonitoringNetworkReachability]; - [_networkReachabilityStatusBlock release]; + [self stopMonitoringNetworkReachability]; + [_networkReachabilityStatusBlock release]; #endif - - [_baseURL release]; - [_registeredHTTPOperationClassNames release]; - [_defaultHeaders release]; - [_operationQueue release]; - - [super dealloc]; + + [_baseURL release]; + [_registeredHTTPOperationClassNames release]; + [_defaultHeaders release]; + [_operationQueue release]; + + [super dealloc]; } - (NSString *)description { - return [NSString stringWithFormat:@"<%@: %p, baseURL: %@, defaultHeaders: %@, registeredOperationClasses: %@, operationQueue: %@>", NSStringFromClass([self class]), self, [self.baseURL absoluteString], self.defaultHeaders, self.registeredHTTPOperationClassNames, self.operationQueue]; + return [NSString stringWithFormat:@"<%@: %p, baseURL: %@, defaultHeaders: %@, registeredOperationClasses: %@, operationQueue: %@>", NSStringFromClass([self class]), self, [self.baseURL absoluteString], self.defaultHeaders, self.registeredHTTPOperationClassNames, self.operationQueue]; } #pragma mark - #ifdef _SYSTEMCONFIGURATION_H static BOOL AFURLHostIsIPAddress(NSURL *url) { - struct sockaddr_in sa_in; - struct sockaddr_in6 sa_in6; - - return [url host] && (inet_pton(AF_INET, [[url host] UTF8String], &sa_in) == 1 || inet_pton(AF_INET6, [[url host] UTF8String], &sa_in6) == 1); + struct sockaddr_in sa_in; + struct sockaddr_in6 sa_in6; + + return [url host] && (inet_pton(AF_INET, [[url host] UTF8String], &sa_in) == 1 || inet_pton(AF_INET6, [[url host] UTF8String], &sa_in6) == 1); } static AFNetworkReachabilityStatus AFNetworkReachabilityStatusForFlags(SCNetworkReachabilityFlags flags) { - BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) != 0); - BOOL needsConnection = ((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0); - BOOL isNetworkReachable = (isReachable && !needsConnection); - - AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusUnknown; - if(isNetworkReachable == NO){ - status = AFNetworkReachabilityStatusNotReachable; - } + BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) != 0); + BOOL needsConnection = ((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0); + BOOL isNetworkReachable = (isReachable && !needsConnection); + + AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusUnknown; + if(isNetworkReachable == NO){ + status = AFNetworkReachabilityStatusNotReachable; + } #if TARGET_OS_IPHONE - else if((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0){ - status = AFNetworkReachabilityStatusReachableViaWWAN; - } + else if((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0){ + status = AFNetworkReachabilityStatusReachableViaWWAN; + } #endif - else { - status = AFNetworkReachabilityStatusReachableViaWiFi; - } - - return status; + else { + status = AFNetworkReachabilityStatusReachableViaWiFi; + } + + return status; } static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) { - AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags); - AFNetworkReachabilityStatusBlock block = (AFNetworkReachabilityStatusBlock)info; - if (block) { - block(status); - } - - [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingReachabilityDidChangeNotification object:[NSNumber numberWithInt:status]]; + AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags); + AFNetworkReachabilityStatusBlock block = (AFNetworkReachabilityStatusBlock)info; + if (block) { + block(status); + } + + [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingReachabilityDidChangeNotification object:[NSNumber numberWithInt:status]]; } static const void * AFNetworkReachabilityRetainCallback(const void *info) { - return [(AFNetworkReachabilityStatusBlock)info copy]; + return [(AFNetworkReachabilityStatusBlock)info copy]; } static void AFNetworkReachabilityReleaseCallback(const void *info) { - [(AFNetworkReachabilityStatusBlock)info release]; + [(AFNetworkReachabilityStatusBlock)info release]; } - (void)startMonitoringNetworkReachability { - [self stopMonitoringNetworkReachability]; - - if (!self.baseURL) { - return; - } - - self.networkReachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [[self.baseURL host] UTF8String]); - - AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status){ - self.networkReachabilityStatus = status; - if (self.networkReachabilityStatusBlock) { - self.networkReachabilityStatusBlock(status); - } - }; - - SCNetworkReachabilityContext context = {0, callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL}; - SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context); - SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), (CFStringRef)NSRunLoopCommonModes); - - /* Network reachability monitoring does not establish a baseline for IP addresses as it does for hostnames, so if the base URL host is an IP address, the initial reachability callback is manually triggered. - */ - if (AFURLHostIsIPAddress(self.baseURL)) { - SCNetworkReachabilityFlags flags; - SCNetworkReachabilityGetFlags(self.networkReachability, &flags); - dispatch_async(dispatch_get_main_queue(), ^{ - AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags); - callback(status); - }); + [self stopMonitoringNetworkReachability]; + + if (!self.baseURL) { + return; + } + + self.networkReachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [[self.baseURL host] UTF8String]); + + AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status){ + self.networkReachabilityStatus = status; + if (self.networkReachabilityStatusBlock) { + self.networkReachabilityStatusBlock(status); } + }; + + SCNetworkReachabilityContext context = {0, callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL}; + SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context); + SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), (CFStringRef)NSRunLoopCommonModes); + + /* Network reachability monitoring does not establish a baseline for IP addresses as it does for hostnames, so if the base URL host is an IP address, the initial reachability callback is manually triggered. + */ + if (AFURLHostIsIPAddress(self.baseURL)) { + SCNetworkReachabilityFlags flags; + SCNetworkReachabilityGetFlags(self.networkReachability, &flags); + dispatch_async(dispatch_get_main_queue(), ^{ + AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags); + callback(status); + }); + } } - (void)stopMonitoringNetworkReachability { - if (_networkReachability) { - SCNetworkReachabilityUnscheduleFromRunLoop(_networkReachability, CFRunLoopGetMain(), (CFStringRef)NSRunLoopCommonModes); - CFRelease(_networkReachability); - } + if (_networkReachability) { + SCNetworkReachabilityUnscheduleFromRunLoop(_networkReachability, CFRunLoopGetMain(), (CFStringRef)NSRunLoopCommonModes); + CFRelease(_networkReachability); + } } - (void)setReachabilityStatusChangeBlock:(void (^)(AFNetworkReachabilityStatus status))block { - self.networkReachabilityStatusBlock = block; + self.networkReachabilityStatusBlock = block; } #endif #pragma mark - - (BOOL)registerHTTPOperationClass:(Class)operationClass { - if (![operationClass isSubclassOfClass:[AFHTTPRequestOperation class]]) { - return NO; - } - - NSString *className = NSStringFromClass(operationClass); - [self.registeredHTTPOperationClassNames removeObject:className]; - [self.registeredHTTPOperationClassNames insertObject:className atIndex:0]; - - return YES; + if (![operationClass isSubclassOfClass:[AFHTTPRequestOperation class]]) { + return NO; + } + + NSString *className = NSStringFromClass(operationClass); + [self.registeredHTTPOperationClassNames removeObject:className]; + [self.registeredHTTPOperationClassNames insertObject:className atIndex:0]; + + return YES; } - (void)unregisterHTTPOperationClass:(Class)operationClass { - NSString *className = NSStringFromClass(operationClass); - [self.registeredHTTPOperationClassNames removeObject:className]; + NSString *className = NSStringFromClass(operationClass); + [self.registeredHTTPOperationClassNames removeObject:className]; } #pragma mark - @@ -432,11 +446,11 @@ static void AFNetworkReachabilityReleaseCallback(const void *info) { - (void)setAuthorizationHeaderWithUsername:(NSString *)username password:(NSString *)password { NSString *basicAuthCredentials = [NSString stringWithFormat:@"%@:%@", username, password]; - [self setDefaultHeader:@"Authorization" value:[NSString stringWithFormat:@"Basic %@", AFBase64EncodedStringFromString(basicAuthCredentials)]]; + [self setDefaultHeader:@"Authorization" value:[NSString stringWithFormat:@"Basic %@", AFBase64EncodedStringFromString(basicAuthCredentials)]]; } - (void)setAuthorizationHeaderWithToken:(NSString *)token { - [self setDefaultHeader:@"Authorization" value:[NSString stringWithFormat:@"Token token=\"%@\"", token]]; + [self setDefaultHeader:@"Authorization" value:[NSString stringWithFormat:@"Token token=\"%@\"", token]]; } - (void)clearAuthorizationHeader { @@ -449,164 +463,161 @@ static void AFNetworkReachabilityReleaseCallback(const void *info) { path:(NSString *)path parameters:(NSDictionary *)parameters { - NSURL *url = [NSURL URLWithString:path relativeToURL:self.baseURL]; + NSURL *url = [NSURL URLWithString:path relativeToURL:self.baseURL]; NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] initWithURL:url] autorelease]; - [request setHTTPMethod:method]; - [request setAllHTTPHeaderFields:self.defaultHeaders]; - - if ([method isEqualToString:@"GET"] || [method isEqualToString:@"HEAD"]) { - [request setHTTPShouldUsePipelining:YES]; - } + [request setHTTPMethod:method]; + [request setAllHTTPHeaderFields:self.defaultHeaders]; - if (parameters) { - if ([method isEqualToString:@"GET"] || [method isEqualToString:@"HEAD"] || [method isEqualToString:@"DELETE"]) { - url = [NSURL URLWithString:[[url absoluteString] stringByAppendingFormat:[path rangeOfString:@"?"].location == NSNotFound ? @"?%@" : @"&%@", AFQueryStringFromParametersWithEncoding(parameters, self.stringEncoding)]]; - [request setURL:url]; - } else { - NSString *charset = (NSString *)CFStringConvertEncodingToIANACharSetName(CFStringConvertNSStringEncodingToEncoding(self.stringEncoding)); - switch (self.parameterEncoding) { - case AFFormURLParameterEncoding:; - [request setValue:[NSString stringWithFormat:@"application/x-www-form-urlencoded; charset=%@", charset] forHTTPHeaderField:@"Content-Type"]; - [request setHTTPBody:[AFQueryStringFromParametersWithEncoding(parameters, self.stringEncoding) dataUsingEncoding:self.stringEncoding]]; - break; - case AFJSONParameterEncoding:; - [request setValue:[NSString stringWithFormat:@"application/json; charset=%@", charset] forHTTPHeaderField:@"Content-Type"]; - [request setHTTPBody:[AFJSONStringFromParameters(parameters) dataUsingEncoding:self.stringEncoding]]; - break; - case AFPropertyListParameterEncoding:; - [request setValue:[NSString stringWithFormat:@"application/x-plist; charset=%@", charset] forHTTPHeaderField:@"Content-Type"]; - [request setHTTPBody:[AFPropertyListStringFromParameters(parameters) dataUsingEncoding:self.stringEncoding]]; - break; - } - } + if (parameters) { + if ([method isEqualToString:@"GET"] || [method isEqualToString:@"HEAD"] || [method isEqualToString:@"DELETE"]) { + url = [NSURL URLWithString:[[url absoluteString] stringByAppendingFormat:[path rangeOfString:@"?"].location == NSNotFound ? @"?%@" : @"&%@", AFQueryStringFromParametersWithEncoding(parameters, self.stringEncoding)]]; + [request setURL:url]; + } else { + NSString *charset = (NSString *)CFStringConvertEncodingToIANACharSetName(CFStringConvertNSStringEncodingToEncoding(self.stringEncoding)); + switch (self.parameterEncoding) { + case AFFormURLParameterEncoding:; + [request setValue:[NSString stringWithFormat:@"application/x-www-form-urlencoded; charset=%@", charset] forHTTPHeaderField:@"Content-Type"]; + [request setHTTPBody:[AFQueryStringFromParametersWithEncoding(parameters, self.stringEncoding) dataUsingEncoding:self.stringEncoding]]; + break; + case AFJSONParameterEncoding:; + [request setValue:[NSString stringWithFormat:@"application/json; charset=%@", charset] forHTTPHeaderField:@"Content-Type"]; + [request setHTTPBody:[AFJSONStringFromParameters(parameters) dataUsingEncoding:self.stringEncoding]]; + break; + case AFPropertyListParameterEncoding:; + [request setValue:[NSString stringWithFormat:@"application/x-plist; charset=%@", charset] forHTTPHeaderField:@"Content-Type"]; + [request setHTTPBody:[AFPropertyListStringFromParameters(parameters) dataUsingEncoding:self.stringEncoding]]; + break; + } } - + } + return request; } - (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters - constructingBodyWithBlock:(void (^)(id formData))block + constructingBodyWithBlock:(void (^)(id formData))block { - NSMutableURLRequest *request = [self requestWithMethod:method path:path parameters:nil]; - __block AFMultipartFormData *formData = [[[AFMultipartFormData alloc] initWithURLRequest:request stringEncoding:self.stringEncoding] autorelease]; - - if (parameters) { - for (AFQueryStringComponent *component in AFQueryStringComponentsFromKeyAndValue(nil, parameters)) { - NSData *data = nil; - if ([component.value isKindOfClass:[NSData class]]) { - data = component.value; - } else { - data = [[component.value description] dataUsingEncoding:self.stringEncoding]; - } - - if (data) { - [formData appendPartWithFormData:data name:[component.key description]]; - } - } + NSMutableURLRequest *request = [self requestWithMethod:method path:path parameters:nil]; + + __block AFStreamingMultipartFormData * formData = [[[AFStreamingMultipartFormData alloc] initWithURLRequest:request stringEncoding:self.stringEncoding] autorelease]; + + // __block AFMultipartFormData *formData = [[[AFMultipartFormData alloc] initWithURLRequest:request stringEncoding:self.stringEncoding] autorelease]; + + for (AFQueryStringComponent *component in AFQueryStringComponentsFromKeyAndValue(nil, parameters)) { + NSData *data = nil; + if ([component.value isKindOfClass:[NSData class]]) { + data = component.value; + } else { + data = [[component.value description] dataUsingEncoding:self.stringEncoding]; } - if (block) { - block(formData); + if (data) { + [formData appendPartWithFormData:data name:[component.key description]]; } - - return [formData requestByFinalizingMultipartFormData]; + } + + if (block) { + block(formData); + } + + return [formData requestByFinalizingMultipartFormData]; } - (AFHTTPRequestOperation *)HTTPRequestOperationWithRequest:(NSURLRequest *)urlRequest success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure { - AFHTTPRequestOperation *operation = nil; - NSString *className = nil; - NSEnumerator *enumerator = [self.registeredHTTPOperationClassNames reverseObjectEnumerator]; - while (!operation && (className = [enumerator nextObject])) { - Class op_class = NSClassFromString(className); - if (op_class && [op_class canProcessRequest:urlRequest]) { - operation = [[(AFHTTPRequestOperation *)[op_class alloc] initWithRequest:urlRequest] autorelease]; - } + AFHTTPRequestOperation *operation = nil; + NSString *className = nil; + NSEnumerator *enumerator = [self.registeredHTTPOperationClassNames reverseObjectEnumerator]; + while (!operation && (className = [enumerator nextObject])) { + Class op_class = NSClassFromString(className); + if (op_class && [op_class canProcessRequest:urlRequest]) { + operation = [[(AFHTTPRequestOperation *)[op_class alloc] initWithRequest:urlRequest] autorelease]; } - - if (!operation) { - operation = [[[AFHTTPRequestOperation alloc] initWithRequest:urlRequest] autorelease]; - } - - [operation setCompletionBlockWithSuccess:success failure:failure]; - - return operation; + } + + if (!operation) { + operation = [[[AFHTTPRequestOperation alloc] initWithRequest:urlRequest] autorelease]; + } + + [operation setCompletionBlockWithSuccess:success failure:failure]; + + return operation; } #pragma mark - - (void)enqueueHTTPRequestOperation:(AFHTTPRequestOperation *)operation { - [self.operationQueue addOperation:operation]; + [self.operationQueue addOperation:operation]; } - (void)cancelAllHTTPOperationsWithMethod:(NSString *)method path:(NSString *)path { - for (NSOperation *operation in [self.operationQueue operations]) { - if (![operation isKindOfClass:[AFHTTPRequestOperation class]]) { - continue; - } - - if ((!method || [method isEqualToString:[[(AFHTTPRequestOperation *)operation request] HTTPMethod]]) && [path isEqualToString:[[[(AFHTTPRequestOperation *)operation request] URL] path]]) { - [operation cancel]; - } + for (NSOperation *operation in [self.operationQueue operations]) { + if (![operation isKindOfClass:[AFHTTPRequestOperation class]]) { + continue; } + + if ((!method || [method isEqualToString:[[(AFHTTPRequestOperation *)operation request] HTTPMethod]]) && [path isEqualToString:[[[(AFHTTPRequestOperation *)operation request] URL] path]]) { + [operation cancel]; + } + } } - (void)enqueueBatchOfHTTPRequestOperationsWithRequests:(NSArray *)requests progressBlock:(void (^)(NSUInteger numberOfCompletedOperations, NSUInteger totalNumberOfOperations))progressBlock completionBlock:(void (^)(NSArray *operations))completionBlock { - NSMutableArray *mutableOperations = [NSMutableArray array]; - for (NSURLRequest *request in requests) { - AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:nil failure:nil]; - [mutableOperations addObject:operation]; - } - - [self enqueueBatchOfHTTPRequestOperations:mutableOperations progressBlock:progressBlock completionBlock:completionBlock]; + NSMutableArray *mutableOperations = [NSMutableArray array]; + for (NSURLRequest *request in requests) { + AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:nil failure:nil]; + [mutableOperations addObject:operation]; + } + + [self enqueueBatchOfHTTPRequestOperations:mutableOperations progressBlock:progressBlock completionBlock:completionBlock]; } - (void)enqueueBatchOfHTTPRequestOperations:(NSArray *)operations progressBlock:(void (^)(NSUInteger numberOfCompletedOperations, NSUInteger totalNumberOfOperations))progressBlock completionBlock:(void (^)(NSArray *operations))completionBlock { - __block dispatch_group_t dispatchGroup = dispatch_group_create(); - NSBlockOperation *batchedOperation = [NSBlockOperation blockOperationWithBlock:^{ - dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^{ - if (completionBlock) { - completionBlock(operations); - } - }); - dispatch_release(dispatchGroup); - }]; - - NSPredicate *finishedOperationPredicate = [NSPredicate predicateWithFormat:@"isFinished == YES"]; - - for (AFHTTPRequestOperation *operation in operations) { - AFCompletionBlock originalCompletionBlock = [[operation.completionBlock copy] autorelease]; - operation.completionBlock = ^{ - dispatch_queue_t queue = operation.successCallbackQueue ? operation.successCallbackQueue : dispatch_get_main_queue(); - dispatch_group_async(dispatchGroup, queue, ^{ - if (originalCompletionBlock) { - originalCompletionBlock(); - } - - if (progressBlock) { - progressBlock([[operations filteredArrayUsingPredicate:finishedOperationPredicate] count], [operations count]); - } - - dispatch_group_leave(dispatchGroup); - }); - }; + __block dispatch_group_t dispatchGroup = dispatch_group_create(); + NSBlockOperation *batchedOperation = [NSBlockOperation blockOperationWithBlock:^{ + dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^{ + if (completionBlock) { + completionBlock(operations); + } + }); + dispatch_release(dispatchGroup); + }]; + + NSPredicate *finishedOperationPredicate = [NSPredicate predicateWithFormat:@"isFinished == YES"]; + + for (AFHTTPRequestOperation *operation in operations) { + AFCompletionBlock originalCompletionBlock = [[operation.completionBlock copy] autorelease]; + operation.completionBlock = ^{ + dispatch_queue_t queue = operation.successCallbackQueue ? operation.successCallbackQueue : dispatch_get_main_queue(); + dispatch_group_async(dispatchGroup, queue, ^{ + if (originalCompletionBlock) { + originalCompletionBlock(); + } - dispatch_group_enter(dispatchGroup); - [batchedOperation addDependency:operation]; + if (progressBlock) { + progressBlock([[operations filteredArrayUsingPredicate:finishedOperationPredicate] count], [operations count]); + } - [self enqueueHTTPRequestOperation:operation]; - } - [self.operationQueue addOperation:batchedOperation]; + dispatch_group_leave(dispatchGroup); + }); + }; + + dispatch_group_enter(dispatchGroup); + [batchedOperation addDependency:operation]; + + [self enqueueHTTPRequestOperation:operation]; + } + [self.operationQueue addOperation:batchedOperation]; } #pragma mark - @@ -617,8 +628,8 @@ static void AFNetworkReachabilityReleaseCallback(const void *info) { failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure { NSURLRequest *request = [self requestWithMethod:@"GET" path:path parameters:parameters]; - AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:success failure:failure]; - [self enqueueHTTPRequestOperation:operation]; + AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:success failure:failure]; + [self enqueueHTTPRequestOperation:operation]; } - (void)postPath:(NSString *)path @@ -628,7 +639,7 @@ static void AFNetworkReachabilityReleaseCallback(const void *info) { { NSURLRequest *request = [self requestWithMethod:@"POST" path:path parameters:parameters]; AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:success failure:failure]; - [self enqueueHTTPRequestOperation:operation]; + [self enqueueHTTPRequestOperation:operation]; } - (void)putPath:(NSString *)path @@ -638,7 +649,7 @@ static void AFNetworkReachabilityReleaseCallback(const void *info) { { NSURLRequest *request = [self requestWithMethod:@"PUT" path:path parameters:parameters]; AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:success failure:failure]; - [self enqueueHTTPRequestOperation:operation]; + [self enqueueHTTPRequestOperation:operation]; } - (void)deletePath:(NSString *)path @@ -648,7 +659,7 @@ static void AFNetworkReachabilityReleaseCallback(const void *info) { { NSURLRequest *request = [self requestWithMethod:@"DELETE" path:path parameters:parameters]; AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:success failure:failure]; - [self enqueueHTTPRequestOperation:operation]; + [self enqueueHTTPRequestOperation:operation]; } - (void)patchPath:(NSString *)path @@ -656,9 +667,9 @@ static void AFNetworkReachabilityReleaseCallback(const void *info) { success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure { - NSURLRequest *request = [self requestWithMethod:@"PATCH" path:path parameters:parameters]; + NSURLRequest *request = [self requestWithMethod:@"PATCH" path:path parameters:parameters]; AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:success failure:failure]; - [self enqueueHTTPRequestOperation:operation]; + [self enqueueHTTPRequestOperation:operation]; } @end @@ -668,18 +679,18 @@ static void AFNetworkReachabilityReleaseCallback(const void *info) { static NSString * const kAFMultipartTemporaryFileDirectoryName = @"com.alamofire.uploads"; static NSString * AFMultipartTemporaryFileDirectoryPath() { - static NSString *multipartTemporaryFilePath = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - multipartTemporaryFilePath = [[NSTemporaryDirectory() stringByAppendingPathComponent:kAFMultipartTemporaryFileDirectoryName] copy]; - - NSError *error = nil; - if(![[NSFileManager defaultManager] createDirectoryAtPath:multipartTemporaryFilePath withIntermediateDirectories:YES attributes:nil error:&error]) { - NSLog(@"Failed to create multipary temporary file directory at %@", multipartTemporaryFilePath); - } - }); + static NSString *multipartTemporaryFilePath = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + multipartTemporaryFilePath = [[NSTemporaryDirectory() stringByAppendingPathComponent:kAFMultipartTemporaryFileDirectoryName] copy]; - return multipartTemporaryFilePath; + NSError *error = nil; + if(![[NSFileManager defaultManager] createDirectoryAtPath:multipartTemporaryFilePath withIntermediateDirectories:YES attributes:nil error:&error]) { + NSLog(@"Failed to create multipary temporary file directory at %@", multipartTemporaryFilePath); + } + }); + + return multipartTemporaryFilePath; } static NSString * const kAFMultipartFormBoundary = @"Boundary+0xAbCdEfGbOuNdArY"; @@ -689,203 +700,468 @@ static NSString * const kAFMultipartFormCRLF = @"\r\n"; static NSInteger const kAFStreamToStreamBufferSize = 1024*1024; //1 meg default static inline NSString * AFMultipartFormInitialBoundary() { - return [NSString stringWithFormat:@"--%@%@", kAFMultipartFormBoundary, kAFMultipartFormCRLF]; + return [NSString stringWithFormat:@"--%@%@", kAFMultipartFormBoundary, kAFMultipartFormCRLF]; } static inline NSString * AFMultipartFormEncapsulationBoundary() { - return [NSString stringWithFormat:@"%@--%@%@", kAFMultipartFormCRLF, kAFMultipartFormBoundary, kAFMultipartFormCRLF]; + return [NSString stringWithFormat:@"%@--%@%@", kAFMultipartFormCRLF, kAFMultipartFormBoundary, kAFMultipartFormCRLF]; } static inline NSString * AFMultipartFormFinalBoundary() { - return [NSString stringWithFormat:@"%@--%@--%@", kAFMultipartFormCRLF, kAFMultipartFormBoundary, kAFMultipartFormCRLF]; + return [NSString stringWithFormat:@"%@--%@--%@", kAFMultipartFormCRLF, kAFMultipartFormBoundary, kAFMultipartFormCRLF]; +} + +#pragma mark -- +#pragma mark AFStreamingMultipartFormData + + +@interface AFStreamingMultipartFormData () { + AFMultipartBodyStream * bodyStream; } -@interface AFMultipartFormData () @property (readwrite, nonatomic, retain) NSMutableURLRequest *request; @property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding; -@property (readwrite, nonatomic, retain) NSOutputStream *outputStream; -@property (readwrite, nonatomic, copy) NSString *temporaryFilePath; + @end -@implementation AFMultipartFormData +@implementation AFStreamingMultipartFormData + @synthesize request = _request; @synthesize stringEncoding = _stringEncoding; -@synthesize outputStream = _outputStream; -@synthesize temporaryFilePath = _temporaryFilePath; - (id)initWithURLRequest:(NSMutableURLRequest *)request stringEncoding:(NSStringEncoding)encoding { - self = [super init]; - if (!self) { - return nil; - } - - self.request = request; - self.stringEncoding = encoding; - - self.temporaryFilePath = [AFMultipartTemporaryFileDirectoryPath() stringByAppendingPathComponent:[NSString stringWithFormat:@"%u", [[self.request URL] hash]]]; - self.outputStream = [NSOutputStream outputStreamToFileAtPath:self.temporaryFilePath append:NO]; - - NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; - [self.outputStream scheduleInRunLoop:runLoop forMode:NSRunLoopCommonModes]; - [self.outputStream open]; - - return self; + self = [super init]; + if (!self) { + return nil; + } + self.request = request; + self.stringEncoding = encoding; + bodyStream = [[AFMultipartBodyStream alloc] initWithStringEncoding:encoding]; + return self; } - (void)dealloc { - [_request release]; - - if (_outputStream) { - [_outputStream close]; - [_outputStream release]; - _outputStream = nil; - } - - [_temporaryFilePath release]; - [super dealloc]; + [bodyStream release]; + [super dealloc]; +} + +-(void)appendPartWithFormData:(NSData *)data name:(NSString *)name { + [bodyStream addFormData:data name:name]; +} + +-(BOOL)appendPartWithFileURL:(NSURL *)fileURL name:(NSString *)name error:(NSError **)error { + return [bodyStream addFileFromURL:fileURL name:name error:error]; } - (NSMutableURLRequest *)requestByFinalizingMultipartFormData { - // Close the stream and return the original request if no data has been written - if ([[self.outputStream propertyForKey:NSStreamFileCurrentOffsetKey] integerValue] == 0) { - [self.outputStream close]; - - return self.request; - } - - [self appendData:[AFMultipartFormFinalBoundary() dataUsingEncoding:self.stringEncoding]]; - - [self.request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", kAFMultipartFormBoundary] forHTTPHeaderField:@"Content-Type"]; - [self.request setValue:[[self.outputStream propertyForKey:NSStreamFileCurrentOffsetKey] stringValue] forHTTPHeaderField:@"Content-Length"]; - - [self.outputStream close]; - [self.request setHTTPBodyStream:[NSInputStream inputStreamWithFileAtPath:self.temporaryFilePath]]; - + //return the original request if no data has been added + if ([bodyStream empty]) { return self.request; -} - -#pragma mark - AFMultipartFormData - -- (void)appendBoundary { - if ([[self.outputStream propertyForKey:NSStreamFileCurrentOffsetKey] integerValue] == 0) { - [self appendString:AFMultipartFormInitialBoundary()]; - } else { - [self appendString:AFMultipartFormEncapsulationBoundary()]; - } -} - -- (void)appendPartWithHeaders:(NSDictionary *)headers - body:(NSData *)body -{ - [self appendBoundary]; - - for (NSString *field in [headers allKeys]) { - [self appendString:[NSString stringWithFormat:@"%@: %@%@", field, [headers valueForKey:field], kAFMultipartFormCRLF]]; - } - - [self appendString:kAFMultipartFormCRLF]; - [self appendData:body]; -} - -- (void)appendPartWithFormData:(NSData *)data - name:(NSString *)name -{ - NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary]; - [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"", name] forKey:@"Content-Disposition"]; - - [self appendPartWithHeaders:mutableHeaders body:data]; -} - -- (NSMutableDictionary *)fileHeadersWithName:(NSString *)name - fileName:(NSString *)fileName - mimeType:(NSString *)mimeType { - NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary]; - [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"]; - [mutableHeaders setValue:mimeType forKey:@"Content-Type"]; - return mutableHeaders; -} - -- (void)appendPartWithFileData:(NSData *)data - name:(NSString *)name - fileName:(NSString *)fileName - mimeType:(NSString *)mimeType -{ - - [self appendPartWithHeaders:[self fileHeadersWithName:name fileName:fileName mimeType:mimeType] body:data]; -} - -- (void)appendPartWithStreamingURL:(NSURL *)streamingURL - name:(NSString *)name - mimeType:(NSString *)mimeType -{ - - NSString * fileName = [[streamingURL pathComponents] objectAtIndex:([[streamingURL pathComponents] count] - 1)]; - [self appendPartWithHeaders:[self fileHeadersWithName:name fileName:fileName mimeType:mimeType] body:nil]; - - NSInputStream * inputStream = [NSInputStream inputStreamWithURL:streamingURL]; - [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] - forMode:NSRunLoopCommonModes]; - [inputStream open]; - - void * dataBuffer = malloc(kAFStreamToStreamBufferSize); - NSInteger bytesRead = [inputStream read:dataBuffer maxLength:kAFStreamToStreamBufferSize]; - while (bytesRead > 0) { - NSData * tempData = [NSData dataWithBytesNoCopy:dataBuffer length:bytesRead freeWhenDone:NO]; - [self appendData:tempData]; - bytesRead = [inputStream read:dataBuffer maxLength:kAFStreamToStreamBufferSize]; } + [self.request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", kAFMultipartFormBoundary] forHTTPHeaderField:@"Content-Type"]; + [self.request setValue:[NSString stringWithFormat:@"%d",[bodyStream contentLength]] forHTTPHeaderField:@"Content-Length"]; + [self.request setHTTPBodyStream:bodyStream]; + return self.request; +} + +-(void)appendData:(NSData *)data { - [inputStream close]; - free(dataBuffer); -} - -- (BOOL)appendPartWithFileURL:(NSURL *)fileURL - name:(NSString *)name - error:(NSError **)error -{ - if (![fileURL isFileURL]) { - NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; - [userInfo setValue:fileURL forKey:NSURLErrorFailingURLErrorKey]; - [userInfo setValue:NSLocalizedString(@"Expected URL to be a file URL", nil) forKey:NSLocalizedFailureReasonErrorKey]; - if (error != NULL) { - *error = [[[NSError alloc] initWithDomain:AFNetworkingErrorDomain code:NSURLErrorBadURL userInfo:userInfo] autorelease]; - } - - return NO; - } - - NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:fileURL]; - [request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData]; - - NSURLResponse *response = nil; - NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:error]; - - if (data && response) { - [self appendPartWithFileData:data name:name fileName:[response suggestedFilename] mimeType:[response MIMEType]]; - - return YES; - } else { - return NO; - } -} - -- (void)appendString:(NSString *)string { - [self appendData:[string dataUsingEncoding:self.stringEncoding]]; -} - -- (void)appendData:(NSData *)data { - if ([data length] == 0) { - return; - } - - if ([self.outputStream hasSpaceAvailable]) { - const uint8_t *dataBuffer = (uint8_t *) [data bytes]; - [self.outputStream write:&dataBuffer[0] maxLength:[data length]]; - } else { - NSLog(@"Failed to append to outputStream!"); - } } @end + +#pragma mark -- +#pragma mark AFMultipartBodyStream + +@interface AFMultipartBodyStream () { + //all undocumented nsstream/nsinputstream/CFReadStream bull shit + CFReadStreamClientCallBack copiedCallback; + CFStreamClientContext copiedContext; + CFOptionFlags requestedEvents; + NSStreamStatus streamStatus; + id delegate; + + NSMutableArray * fileNames; + NSMutableDictionary * fileURLs; + NSMutableDictionary * fileHeaders; + NSMutableArray * formNames; + NSMutableDictionary * formDatas; + NSMutableDictionary * formHeaders; + NSUInteger readElementCursor; + NSUInteger readOffsetCursor; + NSUInteger readHeaderOffsetCursor; + NSInputStream * currentFileStream; + NSStringEncoding stringEncoding; +} + +@end + +@implementation AFMultipartBodyStream + + +-(id)init { + self = [super init]; + fileNames = [[NSMutableArray alloc] init]; + fileURLs = [[NSMutableDictionary alloc] init]; + fileHeaders = [[NSMutableDictionary alloc] init]; + formNames = [[NSMutableArray alloc] init]; + formDatas = [[NSMutableDictionary alloc] init]; + formHeaders = [[NSMutableDictionary alloc] init]; + currentFileStream = NULL; + stringEncoding = NSUTF8StringEncoding; + streamStatus = NSStreamStatusNotOpen; + [self resetCursors]; + [self setDelegate:self]; + return self; +} + +-(id)initWithStringEncoding:(NSStringEncoding)encoding { + self = [self init]; + stringEncoding = encoding; + return self; +} + +-(void)resetCursors { + readElementCursor = 0; + readOffsetCursor = 0; + readHeaderOffsetCursor = 0; +} + +-(void)dealloc { + [fileNames release]; + [fileURLs release]; + [fileHeaders release]; + [formNames release]; + [formDatas release]; + [formHeaders release]; + if (currentFileStream) { + [currentFileStream close]; + [currentFileStream release]; + currentFileStream = NULL; + } + [super dealloc]; +} + +-(BOOL)empty { + if (([fileURLs count] + [formDatas count]) == 0) + return YES; + else + return NO; +} + + +-(BOOL)addFileFromURL:(NSURL *)fileURL name:(NSString *)name error:(NSError **)error { + assert([self streamStatus] == NSStreamStatusNotOpen); + + NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; + [userInfo setValue:fileURL forKey:NSURLErrorFailingURLErrorKey]; + + if (![fileURL isFileURL]) { + [userInfo setValue:NSLocalizedString(@"Expected URL to be a file URL", nil) forKey:NSLocalizedFailureReasonErrorKey]; + if (error != NULL) { + *error = [[[NSError alloc] initWithDomain:AFNetworkingErrorDomain code:NSURLErrorBadURL userInfo:userInfo] autorelease]; + } + return NO; + } + + if ([fileURL checkResourceIsReachableAndReturnError:error] == NO) { + [userInfo setValue:NSLocalizedString(@"File URL not reachable.", nil) forKey:NSLocalizedFailureReasonErrorKey]; + if (error != NULL) { + *error = [[[NSError alloc] initWithDomain:AFNetworkingErrorDomain code:NSURLErrorBadURL userInfo:userInfo] autorelease]; + } + + return NO; + } + + [fileNames addObject:name]; + [fileURLs setObject:fileURL forKey:name]; + [self generateHeaders]; + + return YES; +} + +-(void)addFormData:(NSData *)data name:(NSString *)name { + assert([self streamStatus] == NSStreamStatusNotOpen); + [formNames addObject:name]; + [formDatas setObject:data forKey:name]; + [self generateHeaders]; +} + +-(void)generateHeaders { + [formHeaders removeAllObjects]; + for (NSString * formName in formNames) { + [formHeaders setObject:[self headersDataForForm:formName] forKey:formName]; + } + [fileHeaders removeAllObjects]; + for (NSString * fileName in fileNames) { + [fileHeaders setObject:[self headersDataForFile:fileName] forKey:fileName]; + } +} + +-(NSString *)stringForHeaders:(NSDictionary *)headers { + NSMutableString * headerString = [NSMutableString string]; + for (NSString *field in [headers allKeys]) { + [headerString appendString:[NSString stringWithFormat:@"%@: %@%@", field, [headers valueForKey:field], kAFMultipartFormCRLF]]; + } + return [NSString stringWithString:headerString]; +} + +-(NSData *)headersDataForDict:(NSDictionary *)headersDict { + NSMutableString * headerString = [NSMutableString string]; + if ([formHeaders count] == 0 && [fileHeaders count] == 0) { + [headerString appendString:AFMultipartFormInitialBoundary()]; + } else { + [headerString appendString:AFMultipartFormEncapsulationBoundary()]; + } + [headerString appendString:[self stringForHeaders:headersDict]]; + [headerString appendString:kAFMultipartFormCRLF]; + + return [headerString dataUsingEncoding:stringEncoding]; +} + +-(NSData *)headersDataForFile:(NSString *)name { + NSURL * fileURL = [fileURLs objectForKey:name]; + NSString * fileName = [[fileURL pathComponents] objectAtIndex:([[fileURL pathComponents] count] - 1)]; + NSMutableDictionary * mutableHeaders = [NSMutableDictionary dictionary]; + [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"]; + CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (CFStringRef)[fileURL pathExtension], NULL); + NSString * MIMEType = [(NSString*) UTTypeCopyPreferredTagWithClass (UTI, kUTTagClassMIMEType) autorelease]; + CFRelease(UTI); + [mutableHeaders setValue:MIMEType forKey:@"Content-Type"]; + return [self headersDataForDict:mutableHeaders]; +} + +-(NSData *)headersDataForForm:(NSString *)name { + return [self headersDataForDict:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"form-data; name=\"%@\"", name] forKey:@"Content-Disposition"]]; +} + +-(NSInteger)readData:(NSData *)data intoBuffer:(uint8_t *)buffer maxLength:(NSUInteger)len offsetCursor:(NSUInteger*)offsetCursorPtr { + NSUInteger bytesAvailable = [data length] - *offsetCursorPtr; + if (len > bytesAvailable) { + [data getBytes:buffer range:NSMakeRange(*offsetCursorPtr, bytesAvailable)]; + *offsetCursorPtr += bytesAvailable; + return bytesAvailable; + } else { + [data getBytes:buffer range:NSMakeRange(*offsetCursorPtr, len)]; + *offsetCursorPtr += len; + return len; + } +} + +-(void)nextElement { + readOffsetCursor = 0; + readHeaderOffsetCursor = 0; + readElementCursor += 1; + + if (currentFileStream) { + [currentFileStream close]; + [currentFileStream release]; + currentFileStream = NULL; + } +} + +-(NSUInteger)contentLength { + NSUInteger total = 0; + for (NSString * formName in formNames) { + total += [[formHeaders objectForKey:formName] length]; + total += [[formDatas objectForKey:formName] length]; + } + for (NSString * fileName in fileNames) { + NSError * error; + NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[[fileURLs objectForKey:fileName] path] error:&error]; + total += [[fileHeaders objectForKey:fileName] length]; + total += [[fileAttributes objectForKey:NSFileSize] longValue]; + } + total += [[self finalBoundaryData] length]; + return total; +} + +-(NSUInteger)totalElements { + return [formDatas count] + [fileURLs count]; +} + +-(NSData *)finalBoundaryData { + return [AFMultipartFormFinalBoundary() dataUsingEncoding:stringEncoding]; +} + +#pragma mark - NSStream subclass overrides + +-(void)open { + streamStatus = NSStreamStatusOpen; +} + +-(void)close { + streamStatus = NSStreamStatusClosed; + [self resetCursors]; +} + +- (id )delegate { + return delegate; +} + +- (void)setDelegate:(id)aDelegate { + if (aDelegate == nil) { + delegate = self; + } + else { + delegate = aDelegate; + } +} + +- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode { +} + +- (void)removeFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode { +} + +- (id)propertyForKey:(NSString *)key { + return nil; +} + +- (BOOL)setProperty:(id)property forKey:(NSString *)key { + return NO; +} + + +- (NSStreamStatus)streamStatus { + return streamStatus; +} + +- (NSError *)streamError { + return nil; +} + +#pragma mark - NSInputStream subclass overrides + +-(NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)len { + assert ([self streamStatus] == NSStreamStatusOpen); + + NSUInteger bytesRead = 0; + NSUInteger readFileCursor = (readElementCursor - [formNames count]); + + if (readElementCursor < [formNames count]) { + //reading from formDatas + NSString * currentFormName = [formNames objectAtIndex:readElementCursor]; + NSData * currentData = [formDatas objectForKey:currentFormName]; + NSData * headersData = [formHeaders objectForKey:currentFormName]; + + if (readHeaderOffsetCursor < [headersData length]) { + bytesRead = [self readData:headersData intoBuffer:buffer maxLength:len offsetCursor:&readHeaderOffsetCursor]; + } else { + bytesRead = [self readData:currentData intoBuffer:buffer maxLength:len offsetCursor:&readOffsetCursor]; + } + if (readOffsetCursor == [currentData length]) { + [self nextElement]; + } + } + else if (readElementCursor >= [formNames count] && readFileCursor < [fileNames count]) { + //reading from files + NSString * currentFileName = [fileNames objectAtIndex:readFileCursor]; + NSURL * currentFileURL = [fileURLs objectForKey:currentFileName]; + NSData * headersData = [fileHeaders objectForKey:currentFileName]; + + assert(headersData != NULL); + + if (readHeaderOffsetCursor < [headersData length]) { + bytesRead = [self readData:headersData intoBuffer:buffer maxLength:len offsetCursor:&readHeaderOffsetCursor]; + } else { + if (!currentFileStream) { + currentFileStream = [[NSInputStream inputStreamWithURL:currentFileURL] retain]; + currentFileStream.delegate = self; + [currentFileStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; + [currentFileStream open]; + } + + bytesRead = [currentFileStream read:buffer maxLength:len]; + + if (![currentFileStream hasBytesAvailable] || bytesRead == 0) { + [self nextElement]; + } + } + } + else { + //add final boundary + bytesRead = [self readData:[self finalBoundaryData] intoBuffer:buffer maxLength:len offsetCursor:&readOffsetCursor]; + if (readOffsetCursor == [[self finalBoundaryData] length]) { + [self nextElement]; + } + } + + if (bytesRead < len && readElementCursor <= [self totalElements]) { + //recurse to fill out the buffer, this is critical if the above read operations produce a zero length buffer for some reason (empty form data, end of file) because returning zero from this method will result in the stream being closed. + bytesRead += [self read:buffer+bytesRead maxLength:len-bytesRead]; + } + + + //doesn't seem to make a diff if we do the callbacks or not for the HTTP request use anyway, + //and it sometimes crashes if the callback reciever or context have been released. + // if (CFReadStreamGetStatus((CFReadStreamRef)self) == kCFStreamStatusOpen) { + // double delayInSeconds = 0; + // dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC); + // dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ + // if (copiedCallback && (requestedEvents & kCFStreamEventHasBytesAvailable)) { + // NSLog(@"callback kCFStreamEventHasBytesAvailable... bytes read: %d", bytesRead); + // copiedCallback((CFReadStreamRef)self, kCFStreamEventHasBytesAvailable, &copiedContext); + // } + // }); + // } + + assert(bytesRead > 0); //really should never return zero from this call + return bytesRead; +} + +-(BOOL)hasBytesAvailable { + if ([self totalElements] == 0) { + return NO; + } else if (readElementCursor < ([self totalElements] + 1)) { + return YES; + } else { + return NO; + } +} + +-(BOOL)getBuffer:(uint8_t **)buffer length:(NSUInteger *)len { + return NO; +} + +#pragma mark - Undocumented CFReadStream bridged methods + +- (void)_scheduleInCFRunLoop:(CFRunLoopRef)aRunLoop forMode:(CFStringRef)aMode { +} + +- (BOOL)_setCFClientFlags:(CFOptionFlags)inFlags + callback:(CFReadStreamClientCallBack)inCallback + context:(CFStreamClientContext *)inContext { + + if (inCallback != NULL) { + requestedEvents = inFlags; + copiedCallback = inCallback; + memcpy(&copiedContext, inContext, sizeof(CFStreamClientContext)); + if (copiedContext.info && copiedContext.retain) { + copiedContext.retain(copiedContext.info); + } + copiedCallback((CFReadStreamRef)self, kCFStreamEventHasBytesAvailable, &copiedContext); + } + else { + requestedEvents = kCFStreamEventNone; + copiedCallback = NULL; + if (copiedContext.info && copiedContext.release) { + copiedContext.release(copiedContext.info); + } + memset(&copiedContext, 0, sizeof(CFStreamClientContext)); + } + + return YES; + +} + +- (void)_unscheduleFromCFRunLoop:(CFRunLoopRef)aRunLoop forMode:(CFStringRef)aMode { +} + + + +@end + + diff --git a/Example/AFNetworking iOS Example.xcodeproj/project.pbxproj b/Example/AFNetworking iOS Example.xcodeproj/project.pbxproj index 7abdbab..bb836e0 100644 --- a/Example/AFNetworking iOS Example.xcodeproj/project.pbxproj +++ b/Example/AFNetworking iOS Example.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 50ABD6ED159FC2CE001BE42C /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50ABD6EC159FC2CE001BE42C /* MobileCoreServices.framework */; }; F8129C7415910C37009BFE23 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F8129C7215910C37009BFE23 /* AppDelegate.m */; }; F8D0701B14310F4A00653FD3 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8E469E213957DF700DB05C8 /* SystemConfiguration.framework */; }; F8D0701C14310F4F00653FD3 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8E469E013957DF100DB05C8 /* Security.framework */; }; @@ -39,6 +40,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 50ABD6EC159FC2CE001BE42C /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; }; F8129C3815910830009BFE23 /* Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Prefix.pch; sourceTree = SOURCE_ROOT; }; F8129C7215910C37009BFE23 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = SOURCE_ROOT; }; F8129C7315910C37009BFE23 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = SOURCE_ROOT; }; @@ -95,6 +97,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 50ABD6ED159FC2CE001BE42C /* MobileCoreServices.framework in Frameworks */, F8E469651395739D00DB05C8 /* UIKit.framework in Frameworks */, F8E469671395739D00DB05C8 /* Foundation.framework in Frameworks */, F8E469691395739D00DB05C8 /* CoreGraphics.framework in Frameworks */, @@ -142,6 +145,7 @@ F8E469551395739C00DB05C8 = { isa = PBXGroup; children = ( + 50ABD6EC159FC2CE001BE42C /* MobileCoreServices.framework */, F8E469B71395759C00DB05C8 /* Networking Extensions */, F8E4696A1395739D00DB05C8 /* Classes */, F8E469ED1395812A00DB05C8 /* Images */,