Merge branch 'master' into invalid-ssl

This commit is contained in:
Kevin Harwood 2013-03-25 17:11:42 -05:00
commit bcb8aea682
17 changed files with 719 additions and 329 deletions

View file

@ -1,22 +1,25 @@
Pod::Spec.new do |s| Pod::Spec.new do |s|
s.name = 'AFNetworking' s.name = 'AFNetworking'
s.version = '1.1.0' s.version = '1.2.0'
s.license = 'MIT' s.license = 'MIT'
s.summary = 'A delightful iOS and OS X networking framework.' s.summary = 'A delightful iOS and OS X networking framework.'
s.homepage = 'https://github.com/AFNetworking/AFNetworking' s.homepage = 'https://github.com/AFNetworking/AFNetworking'
s.authors = { 'Mattt Thompson' => 'm@mattt.me', 'Scott Raymond' => 'sco@gowalla.com' } s.authors = { 'Mattt Thompson' => 'm@mattt.me', 'Scott Raymond' => 'sco@gowalla.com' }
s.source = { :git => 'https://github.com/AFNetworking/AFNetworking.git', :tag => '1.1.0' } s.source = { :git => 'https://github.com/AFNetworking/AFNetworking.git', :tag => '1.2.0' }
s.source_files = 'AFNetworking' s.source_files = 'AFNetworking'
s.requires_arc = true s.requires_arc = true
s.ios.deployment_target = '5.0' s.ios.deployment_target = '5.0'
s.ios.frameworks = 'MobileCoreServices', 'SystemConfiguration' s.ios.frameworks = 'MobileCoreServices', 'SystemConfiguration', 'Security'
s.osx.deployment_target = '10.7' s.osx.deployment_target = '10.7'
s.osx.frameworks = 'CoreServices', 'SystemConfiguration' s.osx.frameworks = 'CoreServices', 'SystemConfiguration', 'Security'
s.prefix_header_contents = <<-EOS s.prefix_header_contents = <<-EOS
#import <Availability.h> #import <Availability.h>
#define _AFNETWORKING_PIN_SSL_CERTIFICATES_
#if __IPHONE_OS_VERSION_MIN_REQUIRED #if __IPHONE_OS_VERSION_MIN_REQUIRED
#import <SystemConfiguration/SystemConfiguration.h> #import <SystemConfiguration/SystemConfiguration.h>
#import <MobileCoreServices/MobileCoreServices.h> #import <MobileCoreServices/MobileCoreServices.h>

View file

@ -21,6 +21,7 @@
// THE SOFTWARE. // THE SOFTWARE.
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import "AFURLConnectionOperation.h"
#import <Availability.h> #import <Availability.h>
@ -137,6 +138,13 @@ typedef enum {
@property (readonly, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus; @property (readonly, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus;
#endif #endif
/**
Default SSL pinning mode for each `AFHTTPRequestOperation` which will be enqueued with `enqueueHTTPRequestOperation:`.
*/
#ifdef _AFNETWORKING_PIN_SSL_CERTIFICATES_
@property (nonatomic, assign) AFURLConnectionOperationSSLPinningMode defaultSSLPinningMode;
#endif
///--------------------------------------------- ///---------------------------------------------
/// @name Creating and Initializing HTTP Clients /// @name Creating and Initializing HTTP Clients
///--------------------------------------------- ///---------------------------------------------
@ -440,7 +448,7 @@ typedef enum {
///---------------- ///----------------
/** /**
### Network Reachability ## Network Reachability
The following constants are provided by `AFHTTPClient` as possible network reachability statuses. The following constants are provided by `AFHTTPClient` as possible network reachability statuses.
@ -471,7 +479,7 @@ typedef enum {
A key in the userInfo dictionary in a `AFNetworkingReachabilityDidChangeNotification` notification. A key in the userInfo dictionary in a `AFNetworkingReachabilityDidChangeNotification` notification.
The corresponding value is an `NSNumber` object representing the `AFNetworkReachabilityStatus` value for the current reachability status. The corresponding value is an `NSNumber` object representing the `AFNetworkReachabilityStatus` value for the current reachability status.
### Parameter Encoding ## Parameter Encoding
The following constants are provided by `AFHTTPClient` as possible methods for serializing parameters into query string or message body values. The following constants are provided by `AFHTTPClient` as possible methods for serializing parameters into query string or message body values.
@ -543,12 +551,44 @@ extern NSTimeInterval const kAFUploadStream3GSuggestedDelay;
@return `YES` if the file data was successfully appended, otherwise `NO`. @return `YES` if the file data was successfully appended, otherwise `NO`.
@discussion The filename and MIME type for this data in the form will be automatically generated, using `NSURLResponse` `-suggestedFilename` and `-MIMEType`, respectively. @discussion The filename and MIME type for this data in the form will be automatically generated, using the last path component of the `fileURL` and system associated MIME type for the `fileURL` extension, respectively.
*/ */
- (BOOL)appendPartWithFileURL:(NSURL *)fileURL - (BOOL)appendPartWithFileURL:(NSURL *)fileURL
name:(NSString *)name name:(NSString *)name
error:(NSError * __autoreleasing *)error; error:(NSError * __autoreleasing *)error;
/**
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 fileURL The URL corresponding to the file whose content will be appended to the form. This parameter must not be `nil`.
@param name The name to be associated with the specified data. This parameter must not be `nil`.
@param fileName The file name to be used in the `Content-Disposition` header. This parameter must not be `nil`.
@param mimeType The declared MIME type of the file data. This parameter must not be `nil`.
@param error If an error occurs, upon return contains an `NSError` object that describes the problem.
@return `YES` if the file data was successfully appended otherwise `NO`.
*/
- (BOOL)appendPartWithFileURL:(NSURL *)fileURL
name:(NSString *)name
fileName:(NSString *)fileName
mimeType:(NSString *)mimeType
error:(NSError * __autoreleasing *)error;
/**
Appends the HTTP header `Content-Disposition: file; filename=#{filename}; name=#{name}"` and `Content-Type: #{mimeType}`, followed by the data from the input stream and the multipart form boundary.
@param inputStream The input stream to be appended to the form data
@param name The name to be associated with the specified input stream. This parameter must not be `nil`.
@param fileName The filename to be associated with the specified input stream. This parameter must not be `nil`.
@param length The length of the specified input stream in bytes.
@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`.
*/
- (void)appendPartWithInputStream:(NSInputStream *)inputStream
name:(NSString *)name
fileName:(NSString *)fileName
length:(unsigned long long)length
mimeType:(NSString *)mimeType;
/** /**
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. 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.
@ -572,6 +612,7 @@ extern NSTimeInterval const kAFUploadStream3GSuggestedDelay;
- (void)appendPartWithFormData:(NSData *)data - (void)appendPartWithFormData:(NSData *)data
name:(NSString *)name; name:(NSString *)name;
/** /**
Appends HTTP headers, followed by the encoded data and the multipart form boundary. Appends HTTP headers, followed by the encoded data and the multipart form boundary.

View file

@ -81,7 +81,7 @@ static NSString * AFBase64EncodedStringFromString(NSString *string) {
} }
static NSString * AFPercentEscapedQueryStringPairMemberFromStringWithEncoding(NSString *string, NSStringEncoding encoding) { static NSString * AFPercentEscapedQueryStringPairMemberFromStringWithEncoding(NSString *string, NSStringEncoding encoding) {
static NSString * const kAFCharactersToBeEscaped = @":/?&=;+!@#$()~'"; static NSString * const kAFCharactersToBeEscaped = @":/?&=;+!@#$()~',*";
static NSString * const kAFCharactersToLeaveUnescaped = @"[]."; static NSString * const kAFCharactersToLeaveUnescaped = @"[].";
return (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge CFStringRef)string, (__bridge CFStringRef)kAFCharactersToLeaveUnescaped, (__bridge CFStringRef)kAFCharactersToBeEscaped, CFStringConvertNSStringEncodingToEncoding(encoding)); return (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge CFStringRef)string, (__bridge CFStringRef)kAFCharactersToLeaveUnescaped, (__bridge CFStringRef)kAFCharactersToBeEscaped, CFStringConvertNSStringEncodingToEncoding(encoding));
@ -146,10 +146,11 @@ NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
NSMutableArray *mutableQueryStringComponents = [NSMutableArray array]; NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];
if ([value isKindOfClass:[NSDictionary class]]) { if ([value isKindOfClass:[NSDictionary class]]) {
NSDictionary *dictionary = value;
// Sort dictionary keys to ensure consistent ordering in query string, which is important when deserializing potentially ambiguous sequences, such as an array of dictionaries // Sort dictionary keys to ensure consistent ordering in query string, which is important when deserializing potentially ambiguous sequences, such as an array of dictionaries
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(caseInsensitiveCompare:)]; NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(caseInsensitiveCompare:)];
[[[value allKeys] sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]] enumerateObjectsUsingBlock:^(id nestedKey, __unused NSUInteger idx, __unused BOOL *stop) { [[[dictionary allKeys] sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]] enumerateObjectsUsingBlock:^(id nestedKey, __unused NSUInteger idx, __unused BOOL *stop) {
id nestedValue = [value objectForKey:nestedKey]; id nestedValue = [dictionary objectForKey:nestedKey];
if (nestedValue) { if (nestedValue) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)]; [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)];
} }
@ -159,6 +160,11 @@ NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
[array enumerateObjectsUsingBlock:^(id nestedValue, __unused NSUInteger idx, __unused BOOL *stop) { [array enumerateObjectsUsingBlock:^(id nestedValue, __unused NSUInteger idx, __unused BOOL *stop) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)]; [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)];
}]; }];
} else if ([value isKindOfClass:[NSSet class]]) {
NSSet *set = value;
[set enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue(key, obj)];
}];
} else { } else {
[mutableQueryStringComponents addObject:[[AFQueryStringPair alloc] initWithField:key value:value]]; [mutableQueryStringComponents addObject:[[AFQueryStringPair alloc] initWithField:key value:value]];
} }
@ -234,9 +240,14 @@ NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
self.defaultHeaders = [NSMutableDictionary dictionary]; self.defaultHeaders = [NSMutableDictionary dictionary];
// Accept-Language HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4 // Accept-Language HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
NSString *preferredLanguageCodes = [[NSLocale preferredLanguages] componentsJoinedByString:@", "]; NSMutableArray *acceptLanguagesComponents = [NSMutableArray array];
[self setDefaultHeader:@"Accept-Language" value:[NSString stringWithFormat:@"%@, en-us;q=0.8", preferredLanguageCodes]]; [[NSLocale preferredLanguages] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
float q = 1.0f - (idx * 0.1f);
[acceptLanguagesComponents addObject:[NSString stringWithFormat:@"%@;q=%0.1g", obj, q]];
*stop = q <= 0.5f;
}];
[self setDefaultHeader:@"Accept-Language" value:[acceptLanguagesComponents componentsJoinedByString:@", "]];
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
// User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43 // User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43
[self setDefaultHeader:@"User-Agent" value:[NSString stringWithFormat:@"%@/%@ (%@; iOS %@; Scale/%0.2f)", [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString *)kCFBundleExecutableKey] ?: [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString *)kCFBundleIdentifierKey], (__bridge id)CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(), kCFBundleVersionKey) ?: [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString *)kCFBundleVersionKey], [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], ([[UIScreen mainScreen] respondsToSelector:@selector(scale)] ? [[UIScreen mainScreen] scale] : 1.0f)]]; [self setDefaultHeader:@"User-Agent" value:[NSString stringWithFormat:@"%@/%@ (%@; iOS %@; Scale/%0.2f)", [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString *)kCFBundleExecutableKey] ?: [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString *)kCFBundleIdentifierKey], (__bridge id)CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(), kCFBundleVersionKey) ?: [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString *)kCFBundleVersionKey], [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], ([[UIScreen mainScreen] respondsToSelector:@selector(scale)] ? [[UIScreen mainScreen] scale] : 1.0f)]];
@ -304,7 +315,8 @@ static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused targ
} }
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:[NSDictionary dictionaryWithObject:[NSNumber numberWithInteger:status] forKey:AFNetworkingReachabilityNotificationStatusItem]]; NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:[NSDictionary dictionaryWithObject:[NSNumber numberWithInteger:status] forKey:AFNetworkingReachabilityNotificationStatusItem]];
}); });
} }
@ -505,12 +517,12 @@ static void AFNetworkReachabilityReleaseCallback(const void *info) {
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{ {
AFHTTPRequestOperation *operation = nil; AFHTTPRequestOperation *operation = nil;
NSString *className = nil;
NSEnumerator *enumerator = [self.registeredHTTPOperationClassNames reverseObjectEnumerator]; for (NSString *className in self.registeredHTTPOperationClassNames) {
while (!operation && (className = [enumerator nextObject])) { Class operationClass = NSClassFromString(className);
Class op_class = NSClassFromString(className); if (operationClass && [operationClass canProcessRequest:urlRequest]) {
if (op_class && [op_class canProcessRequest:urlRequest]) { operation = [(AFHTTPRequestOperation *)[operationClass alloc] initWithRequest:urlRequest];
operation = [(AFHTTPRequestOperation *)[op_class alloc] initWithRequest:urlRequest]; break;
} }
} }
@ -521,6 +533,9 @@ static void AFNetworkReachabilityReleaseCallback(const void *info) {
[operation setCompletionBlockWithSuccess:success failure:failure]; [operation setCompletionBlockWithSuccess:success failure:failure];
operation.credential = self.defaultCredential; operation.credential = self.defaultCredential;
#ifdef _AFNETWORKING_PIN_SSL_CERTIFICATES_
operation.SSLPinningMode = self.defaultSSLPinningMode;
#endif
return operation; return operation;
} }
@ -534,7 +549,7 @@ static void AFNetworkReachabilityReleaseCallback(const void *info) {
- (void)cancelAllHTTPOperationsWithMethod:(NSString *)method - (void)cancelAllHTTPOperationsWithMethod:(NSString *)method
path:(NSString *)path path:(NSString *)path
{ {
NSString *URLStringToMatched = [[[self requestWithMethod:(method ?: @"GET") path:path parameters:nil] URL] absoluteString]; NSString *pathToBeMatched = [[[self requestWithMethod:(method ?: @"GET") path:path parameters:nil] URL] path];
for (NSOperation *operation in [self.operationQueue operations]) { for (NSOperation *operation in [self.operationQueue operations]) {
if (![operation isKindOfClass:[AFHTTPRequestOperation class]]) { if (![operation isKindOfClass:[AFHTTPRequestOperation class]]) {
@ -542,9 +557,9 @@ static void AFNetworkReachabilityReleaseCallback(const void *info) {
} }
BOOL hasMatchingMethod = !method || [method isEqualToString:[[(AFHTTPRequestOperation *)operation request] HTTPMethod]]; BOOL hasMatchingMethod = !method || [method isEqualToString:[[(AFHTTPRequestOperation *)operation request] HTTPMethod]];
BOOL hasMatchingURL = [[[[(AFHTTPRequestOperation *)operation request] URL] absoluteString] isEqualToString:URLStringToMatched]; BOOL hasMatchingPath = [[[[(AFHTTPRequestOperation *)operation request] URL] path] isEqual:pathToBeMatched];
if (hasMatchingMethod && hasMatchingURL) { if (hasMatchingMethod && hasMatchingPath) {
[operation cancel]; [operation cancel];
} }
} }
@ -581,9 +596,10 @@ static void AFNetworkReachabilityReleaseCallback(const void *info) {
for (AFHTTPRequestOperation *operation in operations) { for (AFHTTPRequestOperation *operation in operations) {
AFCompletionBlock originalCompletionBlock = [operation.completionBlock copy]; AFCompletionBlock originalCompletionBlock = [operation.completionBlock copy];
__weak AFHTTPRequestOperation *weakOperation = operation; __weak __typeof(&*operation)weakOperation = operation;
operation.completionBlock = ^{ operation.completionBlock = ^{
dispatch_queue_t queue = weakOperation.successCallbackQueue ?: dispatch_get_main_queue(); __strong __typeof(&*weakOperation)strongOperation = weakOperation;
dispatch_queue_t queue = strongOperation.successCallbackQueue ?: dispatch_get_main_queue();
dispatch_group_async(dispatchGroup, queue, ^{ dispatch_group_async(dispatchGroup, queue, ^{
if (originalCompletionBlock) { if (originalCompletionBlock) {
originalCompletionBlock(); originalCompletionBlock();
@ -729,7 +745,12 @@ static inline NSString * AFMultipartFormFinalBoundary() {
static inline NSString * AFContentTypeForPathExtension(NSString *extension) { static inline NSString * AFContentTypeForPathExtension(NSString *extension) {
#ifdef __UTTYPE__ #ifdef __UTTYPE__
NSString *UTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)extension, NULL); NSString *UTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)extension, NULL);
return (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)UTI, kUTTagClassMIMEType); NSString *contentType = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)UTI, kUTTagClassMIMEType);
if (!contentType) {
return @"application/octet-stream";
} else {
return contentType;
}
#else #else
return @"application/octet-stream"; return @"application/octet-stream";
#endif #endif
@ -755,9 +776,10 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
maxLength:(NSUInteger)length; maxLength:(NSUInteger)length;
@end @end
@interface AFMultipartBodyStream : NSInputStream <NSStreamDelegate> @interface AFMultipartBodyStreamProvider : NSObject
@property (nonatomic, assign) NSUInteger numberOfBytesInPacket; @property (nonatomic, assign) NSUInteger bufferLength;
@property (nonatomic, assign) NSTimeInterval delay; @property (nonatomic, assign) NSTimeInterval delay;
@property (nonatomic, readonly) NSInputStream *inputStream;
@property (nonatomic, readonly) unsigned long long contentLength; @property (nonatomic, readonly) unsigned long long contentLength;
@property (nonatomic, readonly, getter = isEmpty) BOOL empty; @property (nonatomic, readonly, getter = isEmpty) BOOL empty;
@ -770,7 +792,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
@interface AFStreamingMultipartFormData () @interface AFStreamingMultipartFormData ()
@property (readwrite, nonatomic, copy) NSMutableURLRequest *request; @property (readwrite, nonatomic, copy) NSMutableURLRequest *request;
@property (readwrite, nonatomic, strong) AFMultipartBodyStream *bodyStream; @property (readwrite, nonatomic, strong) AFMultipartBodyStreamProvider *bodyStream;
@property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding; @property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding;
@end @end
@ -789,7 +811,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
self.request = urlRequest; self.request = urlRequest;
self.stringEncoding = encoding; self.stringEncoding = encoding;
self.bodyStream = [[AFMultipartBodyStream alloc] initWithStringEncoding:encoding]; self.bodyStream = [[AFMultipartBodyStreamProvider alloc] initWithStringEncoding:encoding];
return self; return self;
} }
@ -801,6 +823,23 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
NSParameterAssert(fileURL); NSParameterAssert(fileURL);
NSParameterAssert(name); NSParameterAssert(name);
NSString *fileName = [fileURL lastPathComponent];
NSString *mimeType = AFContentTypeForPathExtension([fileURL pathExtension]);
return [self appendPartWithFileURL:fileURL name:name fileName:fileName mimeType:mimeType error:error];
}
- (BOOL)appendPartWithFileURL:(NSURL *)fileURL
name:(NSString *)name
fileName:(NSString *)fileName
mimeType:(NSString *)mimeType
error:(NSError * __autoreleasing *)error
{
NSParameterAssert(fileURL);
NSParameterAssert(name);
NSParameterAssert(fileName);
NSParameterAssert(mimeType);
if (![fileURL isFileURL]) { if (![fileURL isFileURL]) {
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:NSLocalizedStringFromTable(@"Expected URL to be a file URL", @"AFNetworking", nil) forKey:NSLocalizedFailureReasonErrorKey]; NSDictionary *userInfo = [NSDictionary dictionaryWithObject:NSLocalizedStringFromTable(@"Expected URL to be a file URL", @"AFNetworking", nil) forKey:NSLocalizedFailureReasonErrorKey];
if (error != NULL) { if (error != NULL) {
@ -818,8 +857,8 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
} }
NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary]; NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary];
[mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, [fileURL lastPathComponent]] forKey:@"Content-Disposition"]; [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"];
[mutableHeaders setValue:AFContentTypeForPathExtension([fileURL pathExtension]) forKey:@"Content-Type"]; [mutableHeaders setValue:mimeType forKey:@"Content-Type"];
AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init]; AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init];
bodyPart.stringEncoding = self.stringEncoding; bodyPart.stringEncoding = self.stringEncoding;
@ -834,6 +873,32 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
return YES; return YES;
} }
- (void)appendPartWithInputStream:(NSInputStream *)inputStream
name:(NSString *)name
fileName:(NSString *)fileName
length:(unsigned long long)length
mimeType:(NSString *)mimeType
{
NSParameterAssert(name);
NSParameterAssert(fileName);
NSParameterAssert(mimeType);
NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary];
[mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"];
[mutableHeaders setValue:mimeType forKey:@"Content-Type"];
AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init];
bodyPart.stringEncoding = self.stringEncoding;
bodyPart.headers = mutableHeaders;
bodyPart.body = inputStream;
bodyPart.bodyContentLength = length;
[self.bodyStream appendHTTPBodyPart:bodyPart];
}
- (void)appendPartWithFileData:(NSData *)data - (void)appendPartWithFileData:(NSData *)data
name:(NSString *)name name:(NSString *)name
fileName:(NSString *)fileName fileName:(NSString *)fileName
@ -878,7 +943,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
- (void)throttleBandwidthWithPacketSize:(NSUInteger)numberOfBytes - (void)throttleBandwidthWithPacketSize:(NSUInteger)numberOfBytes
delay:(NSTimeInterval)delay delay:(NSTimeInterval)delay
{ {
self.bodyStream.numberOfBytesInPacket = numberOfBytes; self.bodyStream.bufferLength = numberOfBytes;
self.bodyStream.delay = delay; self.bodyStream.delay = delay;
} }
@ -892,7 +957,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
[self.request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", kAFMultipartFormBoundary] forHTTPHeaderField:@"Content-Type"]; [self.request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", kAFMultipartFormBoundary] forHTTPHeaderField:@"Content-Type"];
[self.request setValue:[NSString stringWithFormat:@"%llu", [self.bodyStream contentLength]] forHTTPHeaderField:@"Content-Length"]; [self.request setValue:[NSString stringWithFormat:@"%llu", [self.bodyStream contentLength]] forHTTPHeaderField:@"Content-Length"];
[self.request setHTTPBodyStream:self.bodyStream]; [self.request setHTTPBodyStream:self.bodyStream.inputStream];
return self.request; return self.request;
} }
@ -901,24 +966,31 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
#pragma mark - #pragma mark -
@interface AFMultipartBodyStream () <NSCopying> @interface AFMultipartBodyStreamProvider () <NSCopying, NSStreamDelegate>
@property (nonatomic, assign) NSStreamStatus streamStatus;
@property (nonatomic, strong) NSError *streamError;
@property (nonatomic, assign) NSStringEncoding stringEncoding; @property (nonatomic, assign) NSStringEncoding stringEncoding;
@property (nonatomic, strong) NSMutableArray *HTTPBodyParts; @property (nonatomic, strong) NSMutableArray *HTTPBodyParts;
@property (nonatomic, strong) NSEnumerator *HTTPBodyPartEnumerator; @property (nonatomic, strong) NSEnumerator *HTTPBodyPartEnumerator;
@property (nonatomic, strong) AFHTTPBodyPart *currentHTTPBodyPart; @property (nonatomic, strong) AFHTTPBodyPart *currentHTTPBodyPart;
@property (nonatomic, strong) NSInputStream *inputStream;
@property (nonatomic, strong) NSOutputStream *outputStream;
@property (nonatomic, strong) NSMutableData *buffer;
@end @end
@implementation AFMultipartBodyStream static const NSUInteger AFMultipartBodyStreamProviderDefaultBufferLength = 4096;
@synthesize streamStatus = _streamStatus;
@synthesize streamError = _streamError; @implementation AFMultipartBodyStreamProvider {
@private
// Workaround for stream delegates being weakly referenced, but otherwise unowned
__strong id _self;
}
@synthesize stringEncoding = _stringEncoding; @synthesize stringEncoding = _stringEncoding;
@synthesize HTTPBodyParts = _HTTPBodyParts; @synthesize HTTPBodyParts = _HTTPBodyParts;
@synthesize HTTPBodyPartEnumerator = _HTTPBodyPartEnumerator; @synthesize HTTPBodyPartEnumerator = _HTTPBodyPartEnumerator;
@synthesize currentHTTPBodyPart = _currentHTTPBodyPart; @synthesize currentHTTPBodyPart = _currentHTTPBodyPart;
@synthesize numberOfBytesInPacket = _numberOfBytesInPacket; @synthesize inputStream = _inputStream;
@synthesize outputStream = _outputStream;
@synthesize buffer = _buffer;
@synthesize bufferLength = _numberOfBytesInPacket;
@synthesize delay = _delay; @synthesize delay = _delay;
- (id)initWithStringEncoding:(NSStringEncoding)encoding { - (id)initWithStringEncoding:(NSStringEncoding)encoding {
@ -929,11 +1001,18 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
self.stringEncoding = encoding; self.stringEncoding = encoding;
self.HTTPBodyParts = [NSMutableArray array]; self.HTTPBodyParts = [NSMutableArray array];
self.numberOfBytesInPacket = NSIntegerMax; self.bufferLength = NSIntegerMax;
self.buffer = [[NSMutableData alloc] init];
self.bufferLength = AFMultipartBodyStreamProviderDefaultBufferLength;
return self; return self;
} }
- (void)dealloc {
_outputStream.delegate = nil;
}
- (void)setInitialAndFinalBoundaries { - (void)setInitialAndFinalBoundaries {
if ([self.HTTPBodyParts count] > 0) { if ([self.HTTPBodyParts count] > 0) {
for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) { for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
@ -950,80 +1029,95 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
[self.HTTPBodyParts addObject:bodyPart]; [self.HTTPBodyParts addObject:bodyPart];
} }
- (NSInputStream *)inputStream {
if (_inputStream == nil) {
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreateBoundPair(NULL, &readStream, &writeStream, self.bufferLength);
_inputStream = CFBridgingRelease(readStream);
_outputStream = CFBridgingRelease(writeStream);
_outputStream.delegate = self;
if ([NSThread isMainThread]) {
[_outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
} else {
dispatch_sync(dispatch_get_main_queue(), ^{
[_outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
});
}
[_outputStream open];
_self = self;
}
return _inputStream;
}
- (BOOL)isEmpty { - (BOOL)isEmpty {
return [self.HTTPBodyParts count] == 0; return [self.HTTPBodyParts count] == 0;
} }
#pragma mark - NSInputStream #pragma mark - NSStreamDelegate
- (NSInteger)read:(uint8_t *)buffer - (void)stream:(NSStream *)stream
maxLength:(NSUInteger)length handleEvent:(NSStreamEvent)eventCode
{ {
if ([self streamStatus] == NSStreamStatusClosed) { if (eventCode & NSStreamEventHasSpaceAvailable) {
return 0; [self handleOutputStreamSpaceAvailable];
} }
NSInteger bytesRead = 0; }
while ((NSUInteger)bytesRead < MIN(length, self.numberOfBytesInPacket)) { - (void)handleOutputStreamSpaceAvailable {
if (!self.currentHTTPBodyPart || ![self.currentHTTPBodyPart hasBytesAvailable]) { while ([_outputStream hasSpaceAvailable]) {
if (!(self.currentHTTPBodyPart = [self.HTTPBodyPartEnumerator nextObject])) { if ([_buffer length] > 0) {
break; NSInteger numberOfBytesWritten = [_outputStream write:[_buffer bytes] maxLength:[_buffer length]];
if (numberOfBytesWritten < 0) {
[self close];
return;
} }
[_buffer replaceBytesInRange:NSMakeRange(0, numberOfBytesWritten) withBytes:NULL length:0];
} else { } else {
bytesRead += [self.currentHTTPBodyPart read:&buffer[bytesRead] maxLength:(length - (NSUInteger)bytesRead)]; if (!self.currentHTTPBodyPart) {
if (!self.HTTPBodyPartEnumerator) {
self.HTTPBodyPartEnumerator = [self.HTTPBodyParts objectEnumerator];
}
self.currentHTTPBodyPart = [self.HTTPBodyPartEnumerator nextObject];
}
if (!self.currentHTTPBodyPart) {
[self close];
return;
}
[_buffer setLength:self.bufferLength];
NSInteger numberOfBytesRead = [self.currentHTTPBodyPart read:[_buffer mutableBytes] maxLength:[_buffer length]];
if (numberOfBytesRead < 0) {
[self close];
return;
}
[_buffer setLength:numberOfBytesRead];
if(numberOfBytesRead == 0) {
self.currentHTTPBodyPart = nil;
}
if (self.delay > 0.0f) { if (self.delay > 0.0f) {
[NSThread sleepForTimeInterval:self.delay]; [NSThread sleepForTimeInterval:self.delay];
} }
} }
} }
return bytesRead;
}
- (BOOL)getBuffer:(__unused uint8_t **)buffer
length:(__unused NSUInteger *)len
{
return NO;
}
- (BOOL)hasBytesAvailable {
return [self streamStatus] == NSStreamStatusOpen;
}
#pragma mark - NSStream
- (void)open {
if (self.streamStatus == NSStreamStatusOpen) {
return;
}
self.streamStatus = NSStreamStatusOpen;
[self setInitialAndFinalBoundaries];
self.HTTPBodyPartEnumerator = [self.HTTPBodyParts objectEnumerator];
} }
- (void)close { - (void)close {
self.streamStatus = NSStreamStatusClosed; [_outputStream close];
_outputStream.delegate = nil;
_self = nil;
} }
- (id)propertyForKey:(__unused NSString *)key {
return nil;
}
- (BOOL)setProperty:(__unused id)property
forKey:(__unused NSString *)key
{
return NO;
}
- (void)scheduleInRunLoop:(__unused NSRunLoop *)aRunLoop
forMode:(__unused NSString *)mode
{}
- (void)removeFromRunLoop:(__unused NSRunLoop *)aRunLoop
forMode:(__unused NSString *)mode
{}
- (unsigned long long)contentLength { - (unsigned long long)contentLength {
unsigned long long length = 0; unsigned long long length = 0;
for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) { for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
@ -1033,26 +1127,10 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
return length; return length;
} }
#pragma mark - Undocumented CFReadStream Bridged Methods
- (void)_scheduleInCFRunLoop:(__unused CFRunLoopRef)aRunLoop
forMode:(__unused CFStringRef)aMode
{}
- (void)_unscheduleFromCFRunLoop:(__unused CFRunLoopRef)aRunLoop
forMode:(__unused CFStringRef)aMode
{}
- (BOOL)_setCFClientFlags:(__unused CFOptionFlags)inFlags
callback:(__unused CFReadStreamClientCallBack)inCallback
context:(__unused CFStreamClientContext *)inContext {
return NO;
}
#pragma mark - NSCopying #pragma mark - NSCopying
-(id)copyWithZone:(NSZone *)zone { - (id)copyWithZone:(NSZone *)zone {
AFMultipartBodyStream *bodyStreamCopy = [[[self class] allocWithZone:zone] initWithStringEncoding:self.stringEncoding]; AFMultipartBodyStreamProvider *bodyStreamCopy = [[[self class] allocWithZone:zone] initWithStringEncoding:self.stringEncoding];
for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) { for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
[bodyStreamCopy appendHTTPBodyPart:[bodyPart copy]]; [bodyStreamCopy appendHTTPBodyPart:[bodyPart copy]];
@ -1068,10 +1146,12 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
#pragma mark - #pragma mark -
typedef enum { typedef enum {
AFInitialPhase = 0,
AFEncapsulationBoundaryPhase = 1, AFEncapsulationBoundaryPhase = 1,
AFHeaderPhase = 2, AFHeaderPhase = 2,
AFBodyPhase = 3, AFBodyPhase = 3,
AFFinalBoundaryPhase = 4, AFFinalBoundaryPhase = 4,
AFCompletedPhase = 5,
} AFHTTPBodyPartReadPhase; } AFHTTPBodyPartReadPhase;
@interface AFHTTPBodyPart () <NSCopying> { @interface AFHTTPBodyPart () <NSCopying> {
@ -1118,6 +1198,8 @@ typedef enum {
_inputStream = [NSInputStream inputStreamWithData:self.body]; _inputStream = [NSInputStream inputStreamWithData:self.body];
} else if ([self.body isKindOfClass:[NSURL class]]) { } else if ([self.body isKindOfClass:[NSURL class]]) {
_inputStream = [NSInputStream inputStreamWithURL:self.body]; _inputStream = [NSInputStream inputStreamWithURL:self.body];
} else if ([self.body isKindOfClass:[NSInputStream class]]) {
_inputStream = self.body;
} }
} }
@ -1152,11 +1234,13 @@ typedef enum {
} }
- (BOOL)hasBytesAvailable { - (BOOL)hasBytesAvailable {
// Allows `read:maxLength:` to be called again if `AFMultipartFormFinalBoundary` doesn't fit into the avaiable buffer // Allows `read:maxLength:` to be called again if `AFMultipartFormFinalBoundary` doesn't fit into the available buffer
if (_phase == AFFinalBoundaryPhase) { if (_phase == AFFinalBoundaryPhase) {
return YES; return YES;
} }
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wcovered-switch-default"
switch (self.inputStream.streamStatus) { switch (self.inputStream.streamStatus) {
case NSStreamStatusNotOpen: case NSStreamStatusNotOpen:
case NSStreamStatusOpening: case NSStreamStatusOpening:
@ -1170,6 +1254,8 @@ typedef enum {
default: default:
return NO; return NO;
} }
#pragma clang diagnostic pop
} }
- (NSInteger)read:(uint8_t *)buffer - (NSInteger)read:(uint8_t *)buffer
@ -1227,7 +1313,12 @@ typedef enum {
return YES; return YES;
} }
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wcovered-switch-default"
switch (_phase) { switch (_phase) {
case AFInitialPhase:
_phase = AFEncapsulationBoundaryPhase;
break;
case AFEncapsulationBoundaryPhase: case AFEncapsulationBoundaryPhase:
_phase = AFHeaderPhase; _phase = AFHeaderPhase;
break; break;
@ -1241,10 +1332,13 @@ typedef enum {
_phase = AFFinalBoundaryPhase; _phase = AFFinalBoundaryPhase;
break; break;
case AFFinalBoundaryPhase: case AFFinalBoundaryPhase:
_phase = AFEncapsulationBoundaryPhase; case AFCompletedPhase:
default:
_phase = AFCompletedPhase;
break; break;
} }
_phaseReadOffset = 0; _phaseReadOffset = 0;
#pragma clang diagnostic pop
return YES; return YES;
} }

View file

@ -30,7 +30,6 @@
#define AF_CAST_TO_BLOCK __bridge void * #define AF_CAST_TO_BLOCK __bridge void *
#endif #endif
// We do a little bit of duck typing in this file which can trigger this warning. Turn it off for this source file.
#pragma clang diagnostic push #pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wstrict-selector-match" #pragma clang diagnostic ignored "-Wstrict-selector-match"
@ -108,18 +107,12 @@ static void AFSwizzleClassMethodWithClassAndSelectorUsingBlock(Class klass, SEL
@property (readwrite, nonatomic, strong) NSURLRequest *request; @property (readwrite, nonatomic, strong) NSURLRequest *request;
@property (readwrite, nonatomic, strong) NSHTTPURLResponse *response; @property (readwrite, nonatomic, strong) NSHTTPURLResponse *response;
@property (readwrite, nonatomic, strong) NSError *HTTPError; @property (readwrite, nonatomic, strong) NSError *HTTPError;
@property (readwrite, nonatomic, copy) NSString *HTTPResponseString;
@property (readwrite, nonatomic, assign) long long totalContentLength;
@property (readwrite, nonatomic, assign) long long offsetContentLength;
@end @end
@implementation AFHTTPRequestOperation @implementation AFHTTPRequestOperation
@synthesize HTTPError = _HTTPError; @synthesize HTTPError = _HTTPError;
@synthesize HTTPResponseString = _HTTPResponseString;
@synthesize successCallbackQueue = _successCallbackQueue; @synthesize successCallbackQueue = _successCallbackQueue;
@synthesize failureCallbackQueue = _failureCallbackQueue; @synthesize failureCallbackQueue = _failureCallbackQueue;
@synthesize totalContentLength = _totalContentLength;
@synthesize offsetContentLength = _offsetContentLength;
@dynamic request; @dynamic request;
@dynamic response; @dynamic response;
@ -169,23 +162,19 @@ static void AFSwizzleClassMethodWithClassAndSelectorUsingBlock(Class klass, SEL
} }
} }
- (NSString *)responseString { - (NSStringEncoding)responseStringEncoding {
// When no explicit charset parameter is provided by the sender, media subtypes of the "text" type are defined to have a default charset value of "ISO-8859-1" when received via HTTP. Data in character sets other than "ISO-8859-1" or its subsets MUST be labeled with an appropriate charset value. // When no explicit charset parameter is provided by the sender, media subtypes of the "text" type are defined to have a default charset value of "ISO-8859-1" when received via HTTP. Data in character sets other than "ISO-8859-1" or its subsets MUST be labeled with an appropriate charset value.
// See http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.4.1 // See http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.4.1
if (!self.HTTPResponseString && self.response && !self.response.textEncodingName && self.responseData) { if (self.response && !self.response.textEncodingName && self.responseData) {
NSString *type = nil; NSString *type = nil;
AFGetMediaTypeAndSubtypeWithString([[self.response allHeaderFields] valueForKey:@"Content-Type"], &type, nil); AFGetMediaTypeAndSubtypeWithString([[self.response allHeaderFields] valueForKey:@"Content-Type"], &type, nil);
if ([type isEqualToString:@"text"]) { if ([type isEqualToString:@"text"]) {
self.HTTPResponseString = [[NSString alloc] initWithData:self.responseData encoding:NSISOLatin1StringEncoding]; return NSISOLatin1StringEncoding;
} }
} }
if (self.HTTPResponseString) { return [super responseStringEncoding];
return self.HTTPResponseString;
} else {
return [super responseString];
}
} }
- (void)pause { - (void)pause {
@ -324,46 +313,6 @@ static void AFSwizzleClassMethodWithClassAndSelectorUsingBlock(Class klass, SEL
return [[self acceptableContentTypes] intersectsSet:AFContentTypesFromHTTPHeader([request valueForHTTPHeaderField:@"Accept"])]; return [[self acceptableContentTypes] intersectsSet:AFContentTypesFromHTTPHeader([request valueForHTTPHeaderField:@"Accept"])];
} }
#pragma mark - NSURLConnectionDelegate
- (void)connection:(__unused NSURLConnection *)connection
didReceiveResponse:(NSURLResponse *)response
{
self.response = (NSHTTPURLResponse *)response;
// Set Content-Range header if status code of response is 206 (Partial Content)
// See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.7
long long totalContentLength = self.response.expectedContentLength;
long long fileOffset = 0;
NSUInteger statusCode = ([self.response isKindOfClass:[NSHTTPURLResponse class]]) ? (NSUInteger)[self.response statusCode] : 200;
if (statusCode == 206) {
NSString *contentRange = [self.response.allHeaderFields valueForKey:@"Content-Range"];
if ([contentRange hasPrefix:@"bytes"]) {
NSArray *byteRanges = [contentRange componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@" -/"]];
if ([byteRanges count] == 4) {
fileOffset = [[byteRanges objectAtIndex:1] longLongValue];
totalContentLength = [[byteRanges objectAtIndex:2] longLongValue] ?: -1; // if this is "*", it's converted to 0, but -1 is default.
}
}
} else {
if ([self.outputStream propertyForKey:NSStreamFileCurrentOffsetKey]) {
[self.outputStream setProperty:[NSNumber numberWithInteger:0] forKey:NSStreamFileCurrentOffsetKey];
} else {
if ([[self.outputStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey] length] > 0) {
self.outputStream = [NSOutputStream outputStreamToMemory];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
for (NSString *runLoopMode in self.runLoopModes) {
[self.outputStream scheduleInRunLoop:runLoop forMode:runLoopMode];
}
}
}
}
self.offsetContentLength = MAX(fileOffset, 0);
self.totalContentLength = totalContentLength;
[self.outputStream open];
}
@end @end
#pragma clang diagnostic pop

View file

@ -22,11 +22,12 @@
#import "AFImageRequestOperation.h" #import "AFImageRequestOperation.h"
static dispatch_queue_t af_image_request_operation_processing_queue;
static dispatch_queue_t image_request_operation_processing_queue() { static dispatch_queue_t image_request_operation_processing_queue() {
if (af_image_request_operation_processing_queue == NULL) { static dispatch_queue_t af_image_request_operation_processing_queue;
af_image_request_operation_processing_queue = dispatch_queue_create("com.alamofire.networking.image-request.processing", 0); static dispatch_once_t onceToken;
} dispatch_once(&onceToken, ^{
af_image_request_operation_processing_queue = dispatch_queue_create("com.alamofire.networking.image-request.processing", DISPATCH_QUEUE_CONCURRENT);
});
return af_image_request_operation_processing_queue; return af_image_request_operation_processing_queue;
} }

View file

@ -22,11 +22,12 @@
#import "AFJSONRequestOperation.h" #import "AFJSONRequestOperation.h"
static dispatch_queue_t af_json_request_operation_processing_queue;
static dispatch_queue_t json_request_operation_processing_queue() { static dispatch_queue_t json_request_operation_processing_queue() {
if (af_json_request_operation_processing_queue == NULL) { static dispatch_queue_t af_json_request_operation_processing_queue;
af_json_request_operation_processing_queue = dispatch_queue_create("com.alamofire.networking.json-request.processing", 0); static dispatch_once_t onceToken;
} dispatch_once(&onceToken, ^{
af_json_request_operation_processing_queue = dispatch_queue_create("com.alamofire.networking.json-request.processing", DISPATCH_QUEUE_CONCURRENT);
});
return af_json_request_operation_processing_queue; return af_json_request_operation_processing_queue;
} }
@ -34,12 +35,14 @@ static dispatch_queue_t json_request_operation_processing_queue() {
@interface AFJSONRequestOperation () @interface AFJSONRequestOperation ()
@property (readwrite, nonatomic, strong) id responseJSON; @property (readwrite, nonatomic, strong) id responseJSON;
@property (readwrite, nonatomic, strong) NSError *JSONError; @property (readwrite, nonatomic, strong) NSError *JSONError;
@property (readwrite, nonatomic, strong) NSRecursiveLock *lock;
@end @end
@implementation AFJSONRequestOperation @implementation AFJSONRequestOperation
@synthesize responseJSON = _responseJSON; @synthesize responseJSON = _responseJSON;
@synthesize JSONReadingOptions = _JSONReadingOptions; @synthesize JSONReadingOptions = _JSONReadingOptions;
@synthesize JSONError = _JSONError; @synthesize JSONError = _JSONError;
@dynamic lock;
+ (instancetype)JSONRequestOperationWithRequest:(NSURLRequest *)urlRequest + (instancetype)JSONRequestOperationWithRequest:(NSURLRequest *)urlRequest
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, id JSON))success success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, id JSON))success
@ -61,6 +64,7 @@ static dispatch_queue_t json_request_operation_processing_queue() {
- (id)responseJSON { - (id)responseJSON {
[self.lock lock];
if (!_responseJSON && [self.responseData length] > 0 && [self isFinished] && !self.JSONError) { if (!_responseJSON && [self.responseData length] > 0 && [self isFinished] && !self.JSONError) {
NSError *error = nil; NSError *error = nil;
@ -77,6 +81,7 @@ static dispatch_queue_t json_request_operation_processing_queue() {
self.JSONError = error; self.JSONError = error;
} }
[self.lock unlock];
return _responseJSON; return _responseJSON;
} }

View file

@ -62,8 +62,8 @@ static NSTimeInterval const kAFNetworkActivityIndicatorInvisibilityDelay = 0.17;
return nil; return nil;
} }
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(incrementActivityCount) name:AFNetworkingOperationDidStartNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkingOperationDidStart:) name:AFNetworkingOperationDidStartNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(decrementActivityCount) name:AFNetworkingOperationDidFinishNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkingOperationDidFinish:) name:AFNetworkingOperationDidFinishNotification object:nil];
return self; return self;
} }
@ -126,6 +126,20 @@ static NSTimeInterval const kAFNetworkActivityIndicatorInvisibilityDelay = 0.17;
[self updateNetworkActivityIndicatorVisibilityDelayed]; [self updateNetworkActivityIndicatorVisibilityDelayed];
} }
- (void)networkingOperationDidStart:(NSNotification *)notification {
AFURLConnectionOperation *connectionOperation = [notification object];
if (connectionOperation.request.URL) {
[self incrementActivityCount];
}
}
- (void)networkingOperationDidFinish:(NSNotification *)notification {
AFURLConnectionOperation *connectionOperation = [notification object];
if (connectionOperation.request.URL) {
[self decrementActivityCount];
}
}
@end @end
#endif #endif

View file

@ -22,11 +22,12 @@
#import "AFPropertyListRequestOperation.h" #import "AFPropertyListRequestOperation.h"
static dispatch_queue_t af_property_list_request_operation_processing_queue;
static dispatch_queue_t property_list_request_operation_processing_queue() { static dispatch_queue_t property_list_request_operation_processing_queue() {
if (af_property_list_request_operation_processing_queue == NULL) { static dispatch_queue_t af_property_list_request_operation_processing_queue;
af_property_list_request_operation_processing_queue = dispatch_queue_create("com.alamofire.networking.property-list-request.processing", 0); static dispatch_once_t onceToken;
} dispatch_once(&onceToken, ^{
af_property_list_request_operation_processing_queue = dispatch_queue_create("com.alamofire.networking.property-list-request.processing", DISPATCH_QUEUE_CONCURRENT);
});
return af_property_list_request_operation_processing_queue; return af_property_list_request_operation_processing_queue;
} }

View file

@ -59,6 +59,14 @@
The built-in `completionBlock` provided by `NSOperation` allows for custom behavior to be executed after the request finishes. It is a common pattern for class constructors in subclasses to take callback block parameters, and execute them conditionally in the body of its `completionBlock`. Make sure to handle cancelled operations appropriately when setting a `completionBlock` (i.e. returning early before parsing response data). See the implementation of any of the `AFHTTPRequestOperation` subclasses for an example of this. The built-in `completionBlock` provided by `NSOperation` allows for custom behavior to be executed after the request finishes. It is a common pattern for class constructors in subclasses to take callback block parameters, and execute them conditionally in the body of its `completionBlock`. Make sure to handle cancelled operations appropriately when setting a `completionBlock` (i.e. returning early before parsing response data). See the implementation of any of the `AFHTTPRequestOperation` subclasses for an example of this.
Subclasses are strongly discouraged from overriding `setCompletionBlock:`, as `AFURLConnectionOperation`'s implementation includes a workaround to mitigate retain cycles, and what Apple rather ominously refers to as ["The Deallocation Problem"](http://developer.apple.com/library/ios/#technotes/tn2109/). Subclasses are strongly discouraged from overriding `setCompletionBlock:`, as `AFURLConnectionOperation`'s implementation includes a workaround to mitigate retain cycles, and what Apple rather ominously refers to as ["The Deallocation Problem"](http://developer.apple.com/library/ios/#technotes/tn2109/).
## SSL Pinning
Relying on the CA trust model to validate SSL certificates exposes your app to security vulnerabilities, such as man-in-the-middle attacks. For applications that connect to known servers, SSL certificate pinning provides an increased level of security, by checking server certificate validity against those specified in the app bundle.
SSL with certificate pinning is strongly recommended for any application that transmits sensitive information to an external webservice.
When `_AFNETWORKING_PIN_SSL_CERTIFICATES_` is defined and the Security framework is linked, connections will be validated on all matching certificates with a `.cer` extension in the bundle root.
## NSCoding & NSCopying Conformance ## NSCoding & NSCopying Conformance
@ -75,7 +83,21 @@
- A copy of an operation will not include the `outputStream` of the original. - A copy of an operation will not include the `outputStream` of the original.
- Operation copies do not include `completionBlock`. `completionBlock` often strongly captures a reference to `self`, which would otherwise have the unintuitive side-effect of pointing to the _original_ operation when copied. - Operation copies do not include `completionBlock`. `completionBlock` often strongly captures a reference to `self`, which would otherwise have the unintuitive side-effect of pointing to the _original_ operation when copied.
*/ */
@interface AFURLConnectionOperation : NSOperation <NSURLConnectionDelegate, NSCoding, NSCopying>
#ifdef _AFNETWORKING_PIN_SSL_CERTIFICATES_
typedef enum {
AFSSLPinningModeNone,
AFSSLPinningModePublicKey,
AFSSLPinningModeCertificate,
} AFURLConnectionOperationSSLPinningMode;
#endif
@interface AFURLConnectionOperation : NSOperation <NSURLConnectionDelegate,
#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_5_0) || \
(defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_8)
NSURLConnectionDataDelegate,
#endif
NSCoding, NSCopying>
///------------------------------- ///-------------------------------
/// @name Accessing Run Loop Modes /// @name Accessing Run Loop Modes
@ -131,7 +153,6 @@
*/ */
@property (readonly, nonatomic, assign) NSStringEncoding responseStringEncoding; @property (readonly, nonatomic, assign) NSStringEncoding responseStringEncoding;
///------------------------------- ///-------------------------------
/// @name Managing URL Credentials /// @name Managing URL Credentials
///------------------------------- ///-------------------------------
@ -150,6 +171,15 @@
*/ */
@property (nonatomic, strong) NSURLCredential *credential; @property (nonatomic, strong) NSURLCredential *credential;
/**
The pinning mode which will be used for SSL connections. `AFSSLPinningModePublicKey` by default.
@discussion To enable SSL Pinning, `#define _AFNETWORKING_PIN_SSL_CERTIFICATES_` in `Prefix.pch`. Also, make sure that the Security framework is linked with the binary. See the "SSL Pinning" section in the `AFURLConnectionOperation`" header for more information.
*/
#ifdef _AFNETWORKING_PIN_SSL_CERTIFICATES_
@property (nonatomic, assign) AFURLConnectionOperationSSLPinningMode SSLPinningMode;
#endif
///------------------------ ///------------------------
/// @name Accessing Streams /// @name Accessing Streams
///------------------------ ///------------------------
@ -290,6 +320,25 @@
///---------------- ///----------------
/** /**
## SSL Pinning Options
The following constants are provided by `AFURLConnectionOperation` as possible SSL Pinning options.
enum {
AFSSLPinningModeNone,
AFSSLPinningModePublicKey,
AFSSLPinningModeCertificate,
}
`AFSSLPinningModeNone`
Do not pin SSL connections
`AFSSLPinningModePublicKey`
Pin SSL connections to certificate public key (SPKI).
`AFSSLPinningModeCertificate`
Pin SSL connections to exact certificate. This may cause problems when your certificate expires and needs re-issuance.
## User info dictionary keys ## User info dictionary keys
These keys may exist in the user info dictionary, in addition to those defined for NSError. These keys may exist in the user info dictionary, in addition to those defined for NSError.

View file

@ -145,6 +145,9 @@ static inline BOOL AFStateTransitionIsValid(AFOperationState fromState, AFOperat
@dynamic inputStream; @dynamic inputStream;
@synthesize outputStream = _outputStream; @synthesize outputStream = _outputStream;
@synthesize credential = _credential; @synthesize credential = _credential;
#ifdef _AFNETWORKING_PIN_SSL_CERTIFICATES_
@synthesize SSLPinningMode = _SSLPinningMode;
#endif
@synthesize shouldUseCredentialStorage = _shouldUseCredentialStorage; @synthesize shouldUseCredentialStorage = _shouldUseCredentialStorage;
@synthesize userInfo = _userInfo; @synthesize userInfo = _userInfo;
@synthesize backgroundTaskIdentifier = _backgroundTaskIdentifier; @synthesize backgroundTaskIdentifier = _backgroundTaskIdentifier;
@ -167,7 +170,6 @@ static inline BOOL AFStateTransitionIsValid(AFOperationState fromState, AFOperat
+ (NSThread *)networkRequestThread { + (NSThread *)networkRequestThread {
static NSThread *_networkRequestThread = nil; static NSThread *_networkRequestThread = nil;
static dispatch_once_t oncePredicate; static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{ dispatch_once(&oncePredicate, ^{
_networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil]; _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
[_networkRequestThread start]; [_networkRequestThread start];
@ -176,24 +178,61 @@ static inline BOOL AFStateTransitionIsValid(AFOperationState fromState, AFOperat
return _networkRequestThread; return _networkRequestThread;
} }
#ifdef _AFNETWORKING_PIN_SSL_CERTIFICATES_
+ (NSArray *)pinnedCertificates { + (NSArray *)pinnedCertificates {
static NSArray *_pinnedCertificates = nil; static NSArray *_pinnedCertificates = nil;
static dispatch_once_t onceToken; static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ dispatch_once(&onceToken, ^{
NSBundle *bundle = [NSBundle bundleForClass:[self class]]; NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"."]; NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"."];
NSMutableArray *certificates = [NSMutableArray array];
NSMutableArray *certificates = [NSMutableArray arrayWithCapacity:[paths count]];
for (NSString *path in paths) { for (NSString *path in paths) {
NSData *certificateData = [NSData dataWithContentsOfFile:path]; NSData *certificateData = [NSData dataWithContentsOfFile:path];
[certificates addObject:certificateData]; [certificates addObject:certificateData];
} }
_pinnedCertificates = [[NSArray alloc] initWithArray:certificates]; _pinnedCertificates = [[NSArray alloc] initWithArray:certificates];
}); });
return _pinnedCertificates; return _pinnedCertificates;
} }
+ (NSArray *)pinnedPublicKeys {
static NSArray *_pinnedPublicKeys = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSArray *pinnedCertificates = [self pinnedCertificates];
NSMutableArray *publicKeys = [NSMutableArray arrayWithCapacity:[pinnedCertificates count]];
for (NSData *data in pinnedCertificates) {
SecCertificateRef allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)data);
NSCParameterAssert(allowedCertificate);
SecCertificateRef allowedCertificates[] = {allowedCertificate};
CFArrayRef certificates = CFArrayCreate(NULL, (const void **)allowedCertificates, 1, NULL);
SecPolicyRef policy = SecPolicyCreateBasicX509();
SecTrustRef allowedTrust = NULL;
OSStatus status = SecTrustCreateWithCertificates(certificates, policy, &allowedTrust);
NSAssert(status == noErr, @"SecTrustCreateWithCertificates error: %ld", (long int)status);
SecKeyRef allowedPublicKey = SecTrustCopyPublicKey(allowedTrust);
[publicKeys addObject:(__bridge_transfer id)allowedPublicKey];
CFRelease(allowedTrust);
CFRelease(policy);
CFRelease(certificates);
CFRelease(allowedCertificate);
}
_pinnedPublicKeys = [[NSArray alloc] initWithArray:publicKeys];
});
return _pinnedPublicKeys;
}
#endif
- (id)initWithRequest:(NSURLRequest *)urlRequest { - (id)initWithRequest:(NSURLRequest *)urlRequest {
self = [super init]; self = [super init];
if (!self) { if (!self) {
@ -344,19 +383,6 @@ static inline BOOL AFStateTransitionIsValid(AFOperationState fromState, AFOperat
[self didChangeValueForKey:oldStateKey]; [self didChangeValueForKey:oldStateKey];
[self didChangeValueForKey:newStateKey]; [self didChangeValueForKey:newStateKey];
[self.lock unlock]; [self.lock unlock];
dispatch_async(dispatch_get_main_queue(), ^{
switch (state) {
case AFOperationExecutingState:
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidStartNotification object:self];
break;
case AFOperationFinishedState:
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidFinishNotification object:self];
break;
default:
break;
}
});
} }
- (NSString *)responseString { - (NSString *)responseString {
@ -371,7 +397,7 @@ static inline BOOL AFStateTransitionIsValid(AFOperationState fromState, AFOperat
- (NSStringEncoding)responseStringEncoding { - (NSStringEncoding)responseStringEncoding {
[self.lock lock]; [self.lock lock];
if (!_responseStringEncoding) { if (!_responseStringEncoding && self.response) {
NSStringEncoding stringEncoding = NSUTF8StringEncoding; NSStringEncoding stringEncoding = NSUTF8StringEncoding;
if (self.response.textEncodingName) { if (self.response.textEncodingName) {
CFStringEncoding IANAEncoding = CFStringConvertIANACharSetNameToEncoding((__bridge CFStringRef)self.response.textEncodingName); CFStringEncoding IANAEncoding = CFStringConvertIANACharSetNameToEncoding((__bridge CFStringRef)self.response.textEncodingName);
@ -398,7 +424,8 @@ static inline BOOL AFStateTransitionIsValid(AFOperationState fromState, AFOperat
[self.connection performSelector:@selector(cancel) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]]; [self.connection performSelector:@selector(cancel) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidFinishNotification object:self]; NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter postNotificationName:AFNetworkingOperationDidFinishNotification object:self];
}); });
} }
@ -453,24 +480,34 @@ static inline BOOL AFStateTransitionIsValid(AFOperationState fromState, AFOperat
- (void)operationDidStart { - (void)operationDidStart {
[self.lock lock]; [self.lock lock];
if ([self isCancelled]) { if (! [self isCancelled]) {
[self finish];
} else {
self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO]; self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
for (NSString *runLoopMode in self.runLoopModes) { for (NSString *runLoopMode in self.runLoopModes) {
[self.connection scheduleInRunLoop:runLoop forMode:runLoopMode]; [self.connection scheduleInRunLoop:runLoop forMode:runLoopMode];
[self.outputStream scheduleInRunLoop:runLoop forMode:runLoopMode]; [self.outputStream scheduleInRunLoop:runLoop forMode:runLoopMode];
} }
[self.connection start]; [self.connection start];
} }
[self.lock unlock]; [self.lock unlock];
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidStartNotification object:self];
});
if ([self isCancelled]) {
[self finish];
}
} }
- (void)finish { - (void)finish {
self.state = AFOperationFinishedState; self.state = AFOperationFinishedState;
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidFinishNotification object:self];
});
} }
- (void)cancel { - (void)cancel {
@ -517,12 +554,57 @@ willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challe
NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust]; NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust];
[[challenge sender] useCredential:credential forAuthenticationChallenge:challenge]; [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
} else { } else {
[[challenge sender] cancelAuthenticationChallenge:challenge]; switch (self.SSLPinningMode) {
case AFSSLPinningModePublicKey: {
id publicKey = (__bridge_transfer id)SecTrustCopyPublicKey(serverTrust);
if ([[self.class pinnedPublicKeys] containsObject:publicKey]) {
NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust];
[[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
} else {
[[challenge sender] cancelAuthenticationChallenge:challenge];
}
break;
}
case AFSSLPinningModeCertificate: {
SecCertificateRef serverCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0);
NSData *serverCertificateData = (__bridge_transfer NSData *)SecCertificateCopyData(serverCertificate);
if ([[[self class] pinnedCertificates] containsObject:serverCertificateData]) {
NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust];
[[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
} else {
[[challenge sender] cancelAuthenticationChallenge:challenge];
}
break;
}
case AFSSLPinningModeNone: {
#ifdef _AFNETWORKING_ALLOW_INVALID_SSL_CERTIFICATES_
NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust];
[[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
#else
SecTrustResultType result = 0;
OSStatus status = SecTrustEvaluate(serverTrust, &result);
NSAssert(status == noErr, @"SecTrustEvaluate error: %ld", (long int)status);
if (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed) {
NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust];
[[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
} else {
[[challenge sender] cancelAuthenticationChallenge:challenge];
}
#endif
break;
}
}
} }
} }
} }
#endif #endif
- (BOOL)connection:(NSURLConnection *)connection - (BOOL)connection:(NSURLConnection *)connection
canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{ {
@ -631,18 +713,18 @@ didReceiveResponse:(NSURLResponse *)response
- (void)connection:(NSURLConnection __unused *)connection - (void)connection:(NSURLConnection __unused *)connection
didReceiveData:(NSData *)data didReceiveData:(NSData *)data
{ {
self.totalBytesRead += [data length];
if ([self.outputStream hasSpaceAvailable]) { if ([self.outputStream hasSpaceAvailable]) {
const uint8_t *dataBuffer = (uint8_t *) [data bytes]; const uint8_t *dataBuffer = (uint8_t *) [data bytes];
[self.outputStream write:&dataBuffer[0] maxLength:[data length]]; [self.outputStream write:&dataBuffer[0] maxLength:[data length]];
} }
if (self.downloadProgress) { dispatch_async(dispatch_get_main_queue(), ^{
dispatch_async(dispatch_get_main_queue(), ^{ self.totalBytesRead += [data length];
if (self.downloadProgress) {
self.downloadProgress([data length], self.totalBytesRead, self.response.expectedContentLength); self.downloadProgress([data length], self.totalBytesRead, self.response.expectedContentLength);
}); }
} });
} }
- (void)connectionDidFinishLoading:(NSURLConnection __unused *)connection { - (void)connectionDidFinishLoading:(NSURLConnection __unused *)connection {

View file

@ -24,11 +24,12 @@
#include <Availability.h> #include <Availability.h>
static dispatch_queue_t af_xml_request_operation_processing_queue;
static dispatch_queue_t xml_request_operation_processing_queue() { static dispatch_queue_t xml_request_operation_processing_queue() {
if (af_xml_request_operation_processing_queue == NULL) { static dispatch_queue_t af_xml_request_operation_processing_queue;
af_xml_request_operation_processing_queue = dispatch_queue_create("com.alamofire.networking.xml-request.processing", 0); static dispatch_once_t onceToken;
} dispatch_once(&onceToken, ^{
af_xml_request_operation_processing_queue = dispatch_queue_create("com.alamofire.networking.xml-request.processing", DISPATCH_QUEUE_CONCURRENT);
});
return af_xml_request_operation_processing_queue; return af_xml_request_operation_processing_queue;
} }

View file

@ -87,7 +87,6 @@ static char kAFImageRequestOperationObjectKey;
placeholderImage:(UIImage *)placeholderImage placeholderImage:(UIImage *)placeholderImage
{ {
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPShouldHandleCookies:NO];
[request addValue:@"image/*" forHTTPHeaderField:@"Accept"]; [request addValue:@"image/*" forHTTPHeaderField:@"Accept"];
[self setImageWithURLRequest:request placeholderImage:placeholderImage success:nil failure:nil]; [self setImageWithURLRequest:request placeholderImage:placeholderImage success:nil failure:nil];
@ -102,21 +101,22 @@ static char kAFImageRequestOperationObjectKey;
UIImage *cachedImage = [[[self class] af_sharedImageCache] cachedImageForRequest:urlRequest]; UIImage *cachedImage = [[[self class] af_sharedImageCache] cachedImageForRequest:urlRequest];
if (cachedImage) { if (cachedImage) {
self.image = cachedImage;
self.af_imageRequestOperation = nil;
if (success) { if (success) {
success(nil, nil, cachedImage); success(nil, nil, cachedImage);
} else {
self.image = cachedImage;
} }
self.af_imageRequestOperation = nil;
} else { } else {
self.image = placeholderImage; self.image = placeholderImage;
AFImageRequestOperation *requestOperation = [[AFImageRequestOperation alloc] initWithRequest:urlRequest]; AFImageRequestOperation *requestOperation = [[AFImageRequestOperation alloc] initWithRequest:urlRequest];
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { [requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
if ([[urlRequest URL] isEqual:[[self.af_imageRequestOperation request] URL]]) { if ([urlRequest isEqual:[self.af_imageRequestOperation request]]) {
if (success) { if (success) {
success(operation.request, operation.response, responseObject); success(operation.request, operation.response, responseObject);
} else { } else if (responseObject) {
self.image = responseObject; self.image = responseObject;
} }
@ -127,7 +127,7 @@ static char kAFImageRequestOperationObjectKey;
[[[self class] af_sharedImageCache] cacheImage:responseObject forRequest:urlRequest]; [[[self class] af_sharedImageCache] cacheImage:responseObject forRequest:urlRequest];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) { } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if ([[urlRequest URL] isEqual:[[self.af_imageRequestOperation request] URL]]) { if ([urlRequest isEqual:[self.af_imageRequestOperation request]]) {
if (failure) { if (failure) {
failure(operation.request, operation.response, error); failure(operation.request, operation.response, error);
} }

312
CHANGES
View file

@ -1,67 +1,195 @@
= 1.2.0 (2013-03-24)
* Add `SSLPinningMode` property to `AFHTTPClient` (Oliver Letterer, Kevin
Harwood, Adam Becevello, Dustin Barker, Mattt Thompson)
* Add single quote ("'"), comma (","), and asterix ("*") to escaped URL
encoding characters (Eric Florenzano, Marc Nijdam, Garrett Murray)
* Add `credential` property to `AFURLConnectionOperation` (Mattt Thompson)
* Add `-setDefaultCredential:` to `AFHTTPClient`
* Add `shouldUseCredentialStorage` property to `AFURLConnectionOperation`
(Mattt Thompson)
* Add support for repeated key value pairs in `AFHTTPClient` URL query string
(Nick Dawson)
* Add `AFMultipartFormData -
appendPartWithFileURL:name:fileName:mimeType:error` (Daniel Rodríguez Troitiño)
* Add `AFMultipartFormData -
appendPartWithInputStream:name:fileName:mimeType:` (@joein3d)
* Change SSL pinning to be runtime property on `AFURLConnectionOperation`
rather than defined by macro (Oliver Letterer)
* Change `AFMultipartBodyStream` to `AFMultipartBodyStreamProvider`, vending
one side of a bound CFStream pair rather than subclassing `NSInputStream` (Mike
Ash)
* Change default `Accept-Language` header in `AFHTTPClient` (@therigu, Mattt
Thompson)
* Change `AFHTTPClient` operation cancellation to be based on request URL path
rather than absolute URL string (Mattt Thompson)
* Change request operation subclass processing queues to use
`DISPATCH_QUEUE_CONCURRENT` (Mattt Thompson)
* Change `UIImageView+AFNetworking` to resolve asymmetry in cached image case
between success block provided and not provided (@Eveets, Mattt Thompson)
* Change `UIImageView+AFNetworking` to compare `NSURLRequest` instead of
`NSURL` to determine if previous request was equivalent (Cédric Luthi)
* Change `UIImageView+AFNetworking` to only set image if non-`nil` (Sean
Kovacs)
* Change indentation settings to four spaces at the project level (Cédric
Luthi)
* Change `AFNetworkActivityIndicatorManager` to only update if requests have a
non-`nil` URL (Cédric Luthi)
* Change `UIImageView+AFNetworking` to not do `setHTTPShouldHandleCookies`
(Konstantinos Vaggelakos)
* Fix request stream exhaustion error on authentication challenges (Alex
Burgel)
* Fix implementation to use `NSURL` methods instead of `CFURL` functions where
applicable (Cédric Luthi)
* Fix race condition in `UIImageView+AFNetworking` (Peyman)
* Fix `responseJSON`, `responseString`, and `responseStringEncoding` to be
threadsafe (Jon Parise, Mattt Thompson)
* Fix `AFContentTypeForPathExtension` to ensure non-`NULL` content return
value (Zach Waugh)
* Fix documentation for `appendPartWithFileURL:name:error:`
(Daniel Rodríguez Troitiño)
* Fix request operation subclass processing queues to initialize with
`dispatch_once` (Sasmito Adibowo)
* Fix posting of `AFNetworkingOperationDidStartNotification` and
`AFNetworkingOperationDidFinishNotification` to avoid crashes when logging in
response to notifications (Blake Watters)
* Fix ordering of registered operation consultation in `AFHTTPClient` (Joel
Parsons)
* Fix warning: multiple methods named 'postNotificationName:object:' found
[-Wstrict-selector-match] (Oliver Jones)
* Fix warning: multiple methods named 'objectForKey:' found
[-Wstrict-selector-match] (Oliver Jones)
* Fix warning: weak receiver may be unpredictably set to nil
[-Wreceiver-is-weak] (Oliver Jones)
* Fix missing #pragma clang diagnostic pop (Steven Fisher)
= 1.1.0 (2012-12-27) = 1.1.0 (2012-12-27)
* Add optional SSL certificate pinning with `#define _AFNETWORKING_PIN_SSL_CERTIFICATES_` (Dustin Barker) * Add optional SSL certificate pinning with `#define
_AFNETWORKING_PIN_SSL_CERTIFICATES_` (Dustin Barker)
* Add `responseStringEncoding` property to `AFURLConnectionOperation` (Mattt Thompson) * Add `responseStringEncoding` property to `AFURLConnectionOperation` (Mattt
Thompson)
* Add `userInfo` property to `AFURLConnectionOperation` (Mattt Thompson, Steven Fisher) * Add `userInfo` property to `AFURLConnectionOperation` (Mattt Thompson,
Steven Fisher)
* Change behavior to cause a failure when an operation is cancelled (Daniel Tull) * Change behavior to cause a failure when an operation is cancelled (Daniel
Tull)
* Change return type of class constructors to `instancetype` (@guykogus) * Change return type of class constructors to `instancetype` (@guykogus)
* Change notifications to always being posted on an asynchronously-dispatched block run on the main queue (Evadne Wu, Mattt Thompson) * Change notifications to always being posted on an asynchronously-dispatched
block run on the main queue (Evadne Wu, Mattt Thompson)
* Change from NSLocalizedString to NSLocalizedStringFromTable with AFNetworking.strings table for localized strings (Cédric Luthi) * Change from NSLocalizedString to NSLocalizedStringFromTable with
AFNetworking.strings table for localized strings (Cédric Luthi)
* Change `-appendPartWithHeaders:body:` to add assertion handler for existence of body data parameter (Jonathan Beilin) * Change `-appendPartWithHeaders:body:` to add assertion handler for existence
of body data parameter (Jonathan Beilin)
* Change `AFHTTPRequestOperation -responseString` to follow guidelines from RFC 2616 regarding the use of string encoding when none is specified in the response (Jorge Bernal) * Change `AFHTTPRequestOperation -responseString` to follow guidelines from
RFC 2616 regarding the use of string encoding when none is specified in the
response (Jorge Bernal)
* Change AFHTTPClient parameter serialization dictionary keys with `caseInsensitiveCompare:` to ensure * Change AFHTTPClient parameter serialization dictionary keys with
deterministic ordering of query string parameters, which may otherwise `caseInsensitiveCompare:` to ensure
cause ambiguous representations of nested parameters (James Coleman, deterministic ordering of query string parameters, which may otherwise
cause ambiguous representations of nested parameters (James Coleman,
Mattt Thompson) Mattt Thompson)
* Fix -Wstrict-selector-match warnings raised by Xcode 4.6DP3 (Jesse Collis, Cédric Luthi) * Fix -Wstrict-selector-match warnings raised by Xcode 4.6DP3 (Jesse Collis,
Cédric Luthi)
* Fix NSJSONSerialization crash with Unicode character escapes in JSON response (Mathijs Kadijk) * Fix NSJSONSerialization crash with Unicode character escapes in JSON
response (Mathijs Kadijk)
* Fix issue with early return in -startMonitoringNetworkReachability if network reachability object could not be created (i.e. invalid hostnames) (Basil Shkara) * Fix issue with early return in -startMonitoringNetworkReachability if
network reachability object could not be created (i.e. invalid hostnames)
(Basil Shkara)
* Fix retain cycles in AFImageRequestOperation.m and AFHTTPClient.m caused by strong references within blocks (Nick Forge) * Fix retain cycles in AFImageRequestOperation.m and AFHTTPClient.m caused by
strong references within blocks (Nick Forge)
* Fix issue caused by Rails behavior of returning a single space in head :ok responses, which is interpreted as invalid (Sebastian Ludwig) * Fix issue caused by Rails behavior of returning a single space in head :ok
responses, which is interpreted as invalid (Sebastian Ludwig)
* Fix issue in streaming multipart upload, where final encapsulation boundary would not be appended if it was larger than the available buffer, causing a potential timeout (Tomohisa Takaoka, David Kasper) * Fix issue in streaming multipart upload, where final encapsulation boundary
would not be appended if it was larger than the available buffer, causing a
potential timeout (Tomohisa Takaoka, David Kasper)
* Fix memory leak of network reachability callback block (Mattt Thompson) * Fix memory leak of network reachability callback block (Mattt Thompson)
* Fix `-initWithCoder:` for `AFURLConnectionOperation` and `AFHTTPClient` to cast scalar types (Mattt Thompson) * Fix `-initWithCoder:` for `AFURLConnectionOperation` and `AFHTTPClient` to
cast scalar types (Mattt Thompson)
* Fix bug in `-enqueueBatchOfHTTPRequestOperations:...` to by using `addOperations:waitUntilFinished:` instead of adding each operation individually. (Mattt Thompson) * Fix bug in `-enqueueBatchOfHTTPRequestOperations:...` to by using
`addOperations:waitUntilFinished:` instead of adding each operation
individually. (Mattt Thompson)
* Change `#warning` messages of checks for `CoreServices` and `MobileCoreServices` to message according to the build target platform (Mattt Thompson) * Change `#warning` messages of checks for `CoreServices` and
`MobileCoreServices` to message according to the build target platform (Mattt
Thompson)
* Change `AFQueryStringFromParametersWithEncoding` to create keys string representations using the description method as specified in documentation (Cédric Luthi) * Change `AFQueryStringFromParametersWithEncoding` to create keys string
representations using the description method as specified in documentation
(Cédric Luthi)
* Fix __unused keywords for better Xcode indexing (Christian Rasmussen) * Fix __unused keywords for better Xcode indexing (Christian Rasmussen)
* Fix warning: unused parameter 'x' [-Werror,-Wunused-parameter] (Oliver Jones) * Fix warning: unused parameter 'x' [-Werror,-Wunused-parameter] (Oliver Jones)
* Fix warning: property is assumed atomic by default [-Werror,-Wimplicit-atomic-properties] (Oliver Jones) * Fix warning: property is assumed atomic by default
[-Werror,-Wimplicit-atomic-properties] (Oliver Jones)
* Fix warning: weak receiver may be unpredictably null in ARC mode [-Werror,-Wreceiver-is-weak] (Oliver Jones)
* Fix warning: weak receiver may be unpredictably null in ARC mode
* Fix warning: multiple methods named 'selector' found [-Werror,-Wstrict-selector-match] (Oliver Jones) [-Werror,-Wreceiver-is-weak] (Oliver Jones)
* Fix warning: multiple methods named 'selector' found
[-Werror,-Wstrict-selector-match] (Oliver Jones)
* Fix warning: 'macro' is not defined, evaluates to 0 (Oliver Jones) * Fix warning: 'macro' is not defined, evaluates to 0 (Oliver Jones)
* Fix warning: atomic by default property 'X' has a user (Oliver Jones)defined getter (property should be marked 'atomic' if this is intended) [-Werror, -Wcustom-atomic-properties] (Oliver Jones) * Fix warning: atomic by default property 'X' has a user (Oliver Jones)defined
getter (property should be marked 'atomic' if this is intended) [-Werror,
* Fix warning: 'response' was marked unused but was used [-Werror,-Wused-but-marked-unused] (Oliver Jones) -Wcustom-atomic-properties] (Oliver Jones)
* Fix warning: enumeration value 'AFFinalBoundaryPhase' not explicitly handled in switch [-Werror,-Wswitch-enum] (Oliver Jones) * Fix warning: 'response' was marked unused but was used
[-Werror,-Wused-but-marked-unused] (Oliver Jones)
* Fix warning: enumeration value 'AFFinalBoundaryPhase' not explicitly handled
in switch [-Werror,-Wswitch-enum] (Oliver Jones)
= 1.0.1 / 2012-11-01 = 1.0.1 / 2012-11-01
@ -72,7 +200,7 @@ was not correctly calculated (Stan Chang Khin Boon)
placeholderImage:success:failure`:, it is now the responsibility of the placeholderImage:success:failure`:, it is now the responsibility of the
block to set the image of the image view (Mattt Thompson) block to set the image of the image view (Mattt Thompson)
* Add `JSONReadingOptions` property to `AFJSONRequestOperation` (Jeremy * Add `JSONReadingOptions` property to `AFJSONRequestOperation` (Jeremy
Foo, Mattt Thompson) Foo, Mattt Thompson)
* Using __weak self / __strong self pattern to break retain cycles in * Using __weak self / __strong self pattern to break retain cycles in
@ -80,10 +208,10 @@ block to set the image of the image view (Mattt Thompson)
* Fix parameter encoding to leave period (`.`) unescaped (Diego Torres) * Fix parameter encoding to leave period (`.`) unescaped (Diego Torres)
* Fixing last file component in multipart form part creation (Sylver * Fixing last file component in multipart form part creation (Sylver
Bruneau) Bruneau)
* Remove executable permission on AFHTTPClient source files (Andrew * Remove executable permission on AFHTTPClient source files (Andrew
Sardone) Sardone)
* Fix warning (error with -Werror) on implicit 64 to 32 conversion (Dan * Fix warning (error with -Werror) on implicit 64 to 32 conversion (Dan
@ -96,144 +224,144 @@ block to set the image of the image view (Mattt Thompson)
= 1.0 / 2012-10-15 = 1.0 / 2012-10-15
* AFNetworking now requires iOS 5 / Mac OSX 10.7 or higher (Mattt Thompson) * AFNetworking now requires iOS 5 / Mac OSX 10.7 or higher (Mattt Thompson)
* AFNetworking now uses Automatic Reference Counting (ARC) (Mattt Thompson) * AFNetworking now uses Automatic Reference Counting (ARC) (Mattt Thompson)
* AFNetworking raises compiler warnings for missing features when * AFNetworking raises compiler warnings for missing features when
SystemConfiguration or CoreServices / MobileCoreServices frameworks are not SystemConfiguration or CoreServices / MobileCoreServices frameworks are not
included in the project and imported in the precompiled headers (Mattt included in the project and imported in the precompiled headers (Mattt
Thompson) Thompson)
* AFNetworking now raises compiler error when not compiled with ARC (Steven * AFNetworking now raises compiler error when not compiled with ARC (Steven
Fisher) Fisher)
* Add `NSCoding` and `NSCopying` protocol conformance to * Add `NSCoding` and `NSCopying` protocol conformance to
`AFURLConnectionOperation` and `AFHTTPClient` (Mattt Thompson) `AFURLConnectionOperation` and `AFHTTPClient` (Mattt Thompson)
* Add substantial improvements HTTP multipart streaming support, having * Add substantial improvements HTTP multipart streaming support, having
files streamed directly from disk and read sequentially from a custom input files streamed directly from disk and read sequentially from a custom input
stream (Max Lansing, Stan Chang Khin Boon, Mattt Thompson) stream (Max Lansing, Stan Chang Khin Boon, Mattt Thompson)
* Add `AFMultipartFormData -throttleBandwidthWithPacketSize:delay:` as * Add `AFMultipartFormData -throttleBandwidthWithPacketSize:delay:` as
workaround to issues when uploading over 3G (Mattt Thompson) workaround to issues when uploading over 3G (Mattt Thompson)
* Add request and response to `userInfo` of errors returned from failing * Add request and response to `userInfo` of errors returned from failing
`AFHTTPRequestOperation` (Mattt Thompson) `AFHTTPRequestOperation` (Mattt Thompson)
* Add `userInfo` dictionary with current status in reachability changes * Add `userInfo` dictionary with current status in reachability changes
(Mattt Thompson) (Mattt Thompson)
* Add `Accept` header for image requests in `UIImageView` category (Bratley * Add `Accept` header for image requests in `UIImageView` category (Bratley
Lower) Lower)
* Add explicit declaration of `NSURLConnection` delegate methods so that * Add explicit declaration of `NSURLConnection` delegate methods so that
they can be overridden in subclasses (Mattt Thompson, Evan Grim) they can be overridden in subclasses (Mattt Thompson, Evan Grim)
* Add parameter validation to match conditions specified in documentation * Add parameter validation to match conditions specified in documentation
(Jason Brennan, Mattt Thompson) (Jason Brennan, Mattt Thompson)
* Add import to `UIKit` to avoid build errors from `UIDevice` references in * Add import to `UIKit` to avoid build errors from `UIDevice` references in
`User-Agent` default header (Blake Watters) `User-Agent` default header (Blake Watters)
* Remove `AFJSONUtilities` in favor of `NSJSONSerialization` (Mattt Thompson) * Remove `AFJSONUtilities` in favor of `NSJSONSerialization` (Mattt Thompson)
* Remove `extern` declaration of `AFURLEncodedStringFromStringWithEncoding` * Remove `extern` declaration of `AFURLEncodedStringFromStringWithEncoding`
function (`CFURLCreateStringByAddingPercentEscapes` should be used instead) function (`CFURLCreateStringByAddingPercentEscapes` should be used instead)
(Mattt Thompson) (Mattt Thompson)
* Remove `setHTTPShouldHandleCookies:NO` from `AFHTTPClient` (@phamsonha, * Remove `setHTTPShouldHandleCookies:NO` from `AFHTTPClient` (@phamsonha,
Mattt Thompson) Mattt Thompson)
* Remove `dispatch_retain` / `dispatch_release` with ARC in iOS 6 (Benoit * Remove `dispatch_retain` / `dispatch_release` with ARC in iOS 6 (Benoit
Bourdon) Bourdon)
* Fix threading issue with `AFNetworkActivityIndicatorManager` (Eric Patey) * Fix threading issue with `AFNetworkActivityIndicatorManager` (Eric Patey)
* Fix issue where `AFNetworkActivityIndicatorManager` count could become * Fix issue where `AFNetworkActivityIndicatorManager` count could become
negative (@ap4y) negative (@ap4y)
* Fix properties to explicitly set options to suppress warnings (Wen-Hao * Fix properties to explicitly set options to suppress warnings (Wen-Hao
Lue, Mattt Thompson) Lue, Mattt Thompson)
* Fix compiler warning caused by mismatched types in upload / download * Fix compiler warning caused by mismatched types in upload / download
progress blocks (Gareth du Plooy, tomas.a) progress blocks (Gareth du Plooy, tomas.a)
* Fix weak / strong variable relationships in `completionBlock` (Peter * Fix weak / strong variable relationships in `completionBlock` (Peter
Steinberger) Steinberger)
* Fix string formatting syntax warnings caused by type mismatch (David * Fix string formatting syntax warnings caused by type mismatch (David
Keegan, Steven Fisher, George Cox) Keegan, Steven Fisher, George Cox)
* Fix minor potential security vulnerability by explicitly using string * Fix minor potential security vulnerability by explicitly using string
format in NSError localizedDescription value in userInfo (Steven Fisher) format in NSError localizedDescription value in userInfo (Steven Fisher)
* Fix `AFURLConnectionOperation -pause` by adding state checks to prevent * Fix `AFURLConnectionOperation -pause` by adding state checks to prevent
likely memory issues when resuming (Mattt Thompson) likely memory issues when resuming (Mattt Thompson)
* Fix warning caused by miscast of type when * Fix warning caused by miscast of type when
`CLANG_WARN_IMPLICIT_SIGN_CONVERSION` is set (Steven Fisher) `CLANG_WARN_IMPLICIT_SIGN_CONVERSION` is set (Steven Fisher)
* Fix incomplete implementation warning in example code (Steven Fisher) * Fix incomplete implementation warning in example code (Steven Fisher)
* Fix warning caused by using `==` comparator on floats (Steven Fisher) * Fix warning caused by using `==` comparator on floats (Steven Fisher)
* Fix iOS 4 bug where file URLs return `NSURLResponse` rather than * Fix iOS 4 bug where file URLs return `NSURLResponse` rather than
`NSHTTPURLResponse` objects (Leo Lobato) `NSHTTPURLResponse` objects (Leo Lobato)
* Fix calculation of finished operations in batch operation progress * Fix calculation of finished operations in batch operation progress
callback (Mattt Thompson) callback (Mattt Thompson)
* Fix documentation typos (Steven Fisher, Matthias Wessendorf, * Fix documentation typos (Steven Fisher, Matthias Wessendorf,
jorge@miv.uk.com) jorge@miv.uk.com)
* Fix `hasAcceptableStatusCode` to return true after a network failure (Tony * Fix `hasAcceptableStatusCode` to return true after a network failure (Tony
Million) Million)
* Fix warning about missing prototype for private static method (Stephan * Fix warning about missing prototype for private static method (Stephan
Diederich) Diederich)
* Fix issue where `nil` content type resulted in unacceptable content type * Fix issue where `nil` content type resulted in unacceptable content type
(Mattt Thompson) (Mattt Thompson)
* Fix bug related to setup and scheduling of output stream (Stephen Tramer) * Fix bug related to setup and scheduling of output stream (Stephen Tramer)
* Fix AFContentTypesFromHTTPHeader to correctly handle comma-delimited * Fix AFContentTypesFromHTTPHeader to correctly handle comma-delimited
content types (Peyman, Mattt Thompson, @jsm174) content types (Peyman, Mattt Thompson, @jsm174)
* Fix crash caused by `_networkReachability` not being set to `NULL` after * Fix crash caused by `_networkReachability` not being set to `NULL` after
releasing (Blake Watters) releasing (Blake Watters)
* Fix Podspec to correctly import required headers and use ARC (Eloy Durán, * Fix Podspec to correctly import required headers and use ARC (Eloy Durán,
Blake Watters) Blake Watters)
* Fix query string parameter escaping to leave square brackets unescaped * Fix query string parameter escaping to leave square brackets unescaped
(Mattt Thompson) (Mattt Thompson)
* Fix query string parameter encoding of `NSNull` values (Daniel Rinser) * Fix query string parameter encoding of `NSNull` values (Daniel Rinser)
* Fix error caused by referencing `__IPHONE_OS_VERSION_MIN_REQUIRED` without * Fix error caused by referencing `__IPHONE_OS_VERSION_MIN_REQUIRED` without
importing `Availability.h` (Blake Watters) importing `Availability.h` (Blake Watters)
* Update example to use App.net API, as Twitter shut off its unauthorized * Update example to use App.net API, as Twitter shut off its unauthorized
access to the public timeline (Mattt Thompson) access to the public timeline (Mattt Thompson)
* Update `AFURLConnectionOperation` to replace `NSAutoReleasePool` with * Update `AFURLConnectionOperation` to replace `NSAutoReleasePool` with
`@autoreleasepool` (Mattt Thompson) `@autoreleasepool` (Mattt Thompson)
* Update `AFHTTPClient` operation queue to specify * Update `AFHTTPClient` operation queue to specify
`NSOperationQueueDefaultMaxConcurrentOperationCount` rather than `NSOperationQueueDefaultMaxConcurrentOperationCount` rather than
previously-defined constant (Mattt Thompson) previously-defined constant (Mattt Thompson)
* Update `AFHTTPClient -initWithBaseURL` to automatically append trailing * Update `AFHTTPClient -initWithBaseURL` to automatically append trailing
slash, so as to fix common issue where default path is not respected without slash, so as to fix common issue where default path is not respected without
trailing slash (Steven Fisher) trailing slash (Steven Fisher)
* Update default `AFHTTPClient` `User-Agent` header strings (Mattt Thompson, * Update default `AFHTTPClient` `User-Agent` header strings (Mattt Thompson,
Steven Fisher) Steven Fisher)
* Update icons for iOS example application (Mattt Thompson) * Update icons for iOS example application (Mattt Thompson)
* Update `numberOfCompletedOperations` variable in progress block to be * Update `numberOfCompletedOperations` variable in progress block to be
renamed to `numberOfFinishedOperations` (Mattt Thompson) renamed to `numberOfFinishedOperations` (Mattt Thompson)

View file

@ -21,6 +21,7 @@
F82EB080159A172000B10B56 /* AFPropertyListRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = F82EB077159A172000B10B56 /* AFPropertyListRequestOperation.m */; }; F82EB080159A172000B10B56 /* AFPropertyListRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = F82EB077159A172000B10B56 /* AFPropertyListRequestOperation.m */; };
F82EB081159A172000B10B56 /* AFURLConnectionOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = F82EB079159A172000B10B56 /* AFURLConnectionOperation.m */; }; F82EB081159A172000B10B56 /* AFURLConnectionOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = F82EB079159A172000B10B56 /* AFURLConnectionOperation.m */; };
F82EB082159A172000B10B56 /* AFXMLRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = F82EB07B159A172000B10B56 /* AFXMLRequestOperation.m */; }; F82EB082159A172000B10B56 /* AFXMLRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = F82EB07B159A172000B10B56 /* AFXMLRequestOperation.m */; };
F88812F216C533E9003C8B8C /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F88812F116C533E9003C8B8C /* Security.framework */; };
F8A847CF161F55A500940F39 /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8A847CE161F55A500940F39 /* CoreServices.framework */; }; F8A847CF161F55A500940F39 /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8A847CE161F55A500940F39 /* CoreServices.framework */; };
F8A847D2161F55AC00940F39 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8A847D1161F55AC00940F39 /* SystemConfiguration.framework */; }; F8A847D2161F55AC00940F39 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8A847D1161F55AC00940F39 /* SystemConfiguration.framework */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
@ -57,6 +58,7 @@
F82EB07A159A172000B10B56 /* AFXMLRequestOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AFXMLRequestOperation.h; path = ../AFNetworking/AFXMLRequestOperation.h; sourceTree = "<group>"; }; F82EB07A159A172000B10B56 /* AFXMLRequestOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AFXMLRequestOperation.h; path = ../AFNetworking/AFXMLRequestOperation.h; sourceTree = "<group>"; };
F82EB07B159A172000B10B56 /* AFXMLRequestOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AFXMLRequestOperation.m; path = ../AFNetworking/AFXMLRequestOperation.m; sourceTree = "<group>"; }; F82EB07B159A172000B10B56 /* AFXMLRequestOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AFXMLRequestOperation.m; path = ../AFNetworking/AFXMLRequestOperation.m; sourceTree = "<group>"; };
F877018B159A1CE700B45C0D /* AFNetworking Example.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = "AFNetworking Example.entitlements"; sourceTree = "<group>"; }; F877018B159A1CE700B45C0D /* AFNetworking Example.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = "AFNetworking Example.entitlements"; sourceTree = "<group>"; };
F88812F116C533E9003C8B8C /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
F8A847CE161F55A500940F39 /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = System/Library/Frameworks/CoreServices.framework; sourceTree = SDKROOT; }; F8A847CE161F55A500940F39 /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = System/Library/Frameworks/CoreServices.framework; sourceTree = SDKROOT; };
F8A847D1161F55AC00940F39 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; F8A847D1161F55AC00940F39 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */ /* End PBXFileReference section */
@ -66,6 +68,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
F88812F216C533E9003C8B8C /* Security.framework in Frameworks */,
F8A847D2161F55AC00940F39 /* SystemConfiguration.framework in Frameworks */, F8A847D2161F55AC00940F39 /* SystemConfiguration.framework in Frameworks */,
F8A847CF161F55A500940F39 /* CoreServices.framework in Frameworks */, F8A847CF161F55A500940F39 /* CoreServices.framework in Frameworks */,
F8129C001591061B009BFE23 /* Cocoa.framework in Frameworks */, F8129C001591061B009BFE23 /* Cocoa.framework in Frameworks */,
@ -78,6 +81,7 @@
F8129BF01591061B009BFE23 = { F8129BF01591061B009BFE23 = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
F88812F116C533E9003C8B8C /* Security.framework */,
F8A847D1161F55AC00940F39 /* SystemConfiguration.framework */, F8A847D1161F55AC00940F39 /* SystemConfiguration.framework */,
F8A847CE161F55A500940F39 /* CoreServices.framework */, F8A847CE161F55A500940F39 /* CoreServices.framework */,
F877018B159A1CE700B45C0D /* AFNetworking Example.entitlements */, F877018B159A1CE700B45C0D /* AFNetworking Example.entitlements */,
@ -86,7 +90,10 @@
F8129BFE1591061B009BFE23 /* Frameworks */, F8129BFE1591061B009BFE23 /* Frameworks */,
F8129BFC1591061B009BFE23 /* Products */, F8129BFC1591061B009BFE23 /* Products */,
); );
indentWidth = 4;
sourceTree = "<group>"; sourceTree = "<group>";
tabWidth = 4;
usesTabs = 0;
}; };
F8129BFC1591061B009BFE23 /* Products */ = { F8129BFC1591061B009BFE23 /* Products */ = {
isa = PBXGroup; isa = PBXGroup;
@ -206,7 +213,7 @@
F8129BF21591061B009BFE23 /* Project object */ = { F8129BF21591061B009BFE23 /* Project object */ = {
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastUpgradeCheck = 0450; LastUpgradeCheck = 0460;
}; };
buildConfigurationList = F8129BF51591061B009BFE23 /* Build configuration list for PBXProject "AFNetworking Mac Example" */; buildConfigurationList = F8129BF51591061B009BFE23 /* Build configuration list for PBXProject "AFNetworking Mac Example" */;
compatibilityVersion = "Xcode 3.2"; compatibilityVersion = "Xcode 3.2";
@ -265,6 +272,10 @@
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_64_BIT)"; ARCHS = "$(ARCHS_STANDARD_64_BIT)";
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
GCC_C_LANGUAGE_STANDARD = gnu99; GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO; GCC_DYNAMIC_NO_PIC = NO;
@ -292,6 +303,10 @@
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_64_BIT)"; ARCHS = "$(ARCHS_STANDARD_64_BIT)";
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = YES; COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_C_LANGUAGE_STANDARD = gnu99; GCC_C_LANGUAGE_STANDARD = gnu99;

View file

@ -9,6 +9,7 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
F8129C7415910C37009BFE23 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F8129C7215910C37009BFE23 /* AppDelegate.m */; }; F8129C7415910C37009BFE23 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F8129C7215910C37009BFE23 /* AppDelegate.m */; };
F818101615E6A0C600EF93C2 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50ABD6EC159FC2CE001BE42C /* MobileCoreServices.framework */; }; F818101615E6A0C600EF93C2 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50ABD6EC159FC2CE001BE42C /* MobileCoreServices.framework */; };
F88812F016C533D6003C8B8C /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8E469E013957DF100DB05C8 /* Security.framework */; };
F8A847C1161F51A300940F39 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F8A847C0161F51A300940F39 /* Default-568h@2x.png */; }; F8A847C1161F51A300940F39 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F8A847C0161F51A300940F39 /* Default-568h@2x.png */; };
F8A847C3161F523E00940F39 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = F8A847C2161F523E00940F39 /* Default.png */; }; F8A847C3161F523E00940F39 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = F8A847C2161F523E00940F39 /* Default.png */; };
F8A847C5161F524200940F39 /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F8A847C4161F524200940F39 /* Default@2x.png */; }; F8A847C5161F524200940F39 /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F8A847C4161F524200940F39 /* Default@2x.png */; };
@ -95,6 +96,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
F88812F016C533D6003C8B8C /* Security.framework in Frameworks */,
F8E469651395739D00DB05C8 /* UIKit.framework in Frameworks */, F8E469651395739D00DB05C8 /* UIKit.framework in Frameworks */,
F8E469671395739D00DB05C8 /* Foundation.framework in Frameworks */, F8E469671395739D00DB05C8 /* Foundation.framework in Frameworks */,
F8E469691395739D00DB05C8 /* CoreGraphics.framework in Frameworks */, F8E469691395739D00DB05C8 /* CoreGraphics.framework in Frameworks */,
@ -152,7 +154,10 @@
F8E469631395739D00DB05C8 /* Frameworks */, F8E469631395739D00DB05C8 /* Frameworks */,
F8E469611395739C00DB05C8 /* Products */, F8E469611395739C00DB05C8 /* Products */,
); );
indentWidth = 4;
sourceTree = "<group>"; sourceTree = "<group>";
tabWidth = 4;
usesTabs = 0;
}; };
F8E469611395739C00DB05C8 /* Products */ = { F8E469611395739C00DB05C8 /* Products */ = {
isa = PBXGroup; isa = PBXGroup;

View file

@ -26,9 +26,9 @@ extern NSString * const kUserProfileImageDidLoadNotification;
@interface User : NSObject @interface User : NSObject
@property (readonly) NSUInteger userID; @property (readonly, nonatomic) NSUInteger userID;
@property (readonly) NSString *username; @property (readonly, nonatomic) NSString *username;
@property (unsafe_unretained, readonly) NSURL *avatarImageURL; @property (readonly, nonatomic, unsafe_unretained) NSURL *avatarImageURL;
- (id)initWithAttributes:(NSDictionary *)attributes; - (id)initWithAttributes:(NSDictionary *)attributes;

View file

@ -1,5 +1,7 @@
#import <Availability.h> #import <Availability.h>
#define _AFNETWORKING_PIN_SSL_CERTIFICATES_
#if __IPHONE_OS_VERSION_MIN_REQUIRED #if __IPHONE_OS_VERSION_MIN_REQUIRED
#ifndef __IPHONE_3_0 #ifndef __IPHONE_3_0
#warning "This project uses features only available in iPhone SDK 3.0 and later." #warning "This project uses features only available in iPhone SDK 3.0 and later."