Compare commits

...

12 commits

Author SHA1 Message Date
Mattt Thompson
5266874bcc Bumping version to 1.3.4 2014-04-15 13:50:51 -07:00
Mattt Thompson
e7bc1679c7 Applying equivalent patch for 4c20a5ea38a50f119c9e8ac3d91fb199192a1d57, which fixes potential non-terminating loop in connection:didReceiveData: 2014-04-15 13:20:56 -07:00
Mattt Thompson
b3d44e2fc3 Applying equivalent patch for 830cc8df3ca6a564b7675672279c7715a56b2718, addressing a security vulnerability caused by known multipart form boundaries 2014-04-15 13:15:30 -07:00
Mattt Thompson
55ef458688 Merge pull request #1993 from minorblend/af_1.x
call SecTrustEvaluate before calling SecTrustGetCertificateCount
2014-04-15 11:00:40 -07:00
Josh Chung
5c237a66eb call SecTrustEvaluate before calling SecTrustGetCertificateCount
SecTrustGetCertificateCount's documentation says SecTrustEvaluate must be called before calling it. Without calling it, SecTrustGetCertificateCount returns 0 in OS X 10.7.
2014-04-14 17:13:24 +09:00
Mattt Thompson
d74e568cf2 Merge pull request #1888 from indragiek/1.x
Same fix as 6c7250e for 1.x branch
2014-03-11 03:05:26 -07:00
Indragie Karunaratne
ab08e149e2 Same fix as 6c7250e for 1.x branch
Wrapping OSStatus and NSCAssert with NS_BLOCK_ASSERTIONS macro to prevent unused variable
2014-03-10 22:09:02 -06:00
Mattt Thompson
4fa43bd41c Merge pull request #1866 from CFKevinRef/1.x
Check CFStringTransform() call for success before using result
2014-03-05 08:44:23 -08:00
Kevin Cassidy Jr
24816f52a4 Check CFStringTransform() call for success before using result 2014-02-28 14:00:04 -05:00
Mattt Thompson
353163c09c Merge pull request #1764 from doo/ake-fix-warnings
Fix two warnings that show up recently
2014-01-21 08:28:17 -08:00
Alexander Kempgen
6aa3346e98 Don't use NSUInteger as format argument, add explicit cast instead 2014-01-21 16:07:21 +01:00
Alexander Kempgen
a89fede6dd Remove unused variable 'kAFStreamToStreamBufferSize' 2014-01-21 16:07:21 +01:00
7 changed files with 86 additions and 43 deletions

View file

@ -1,11 +1,11 @@
Pod::Spec.new do |s| Pod::Spec.new do |s|
s.name = 'AFNetworking' s.name = 'AFNetworking'
s.version = '1.3.3' s.version = '1.3.4'
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.3.3' } s.source = { :git => 'https://github.com/AFNetworking/AFNetworking.git', :tag => '1.3.4' }
s.source_files = 'AFNetworking' s.source_files = 'AFNetworking'
s.requires_arc = true s.requires_arc = true
@ -28,4 +28,4 @@ Pod::Spec.new do |s|
#import <Security/Security.h> #import <Security/Security.h>
#endif #endif
EOS EOS
end end

View file

@ -272,8 +272,9 @@ NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
if (userAgent) { if (userAgent) {
if (![userAgent canBeConvertedToEncoding:NSASCIIStringEncoding]) { if (![userAgent canBeConvertedToEncoding:NSASCIIStringEncoding]) {
NSMutableString *mutableUserAgent = [userAgent mutableCopy]; NSMutableString *mutableUserAgent = [userAgent mutableCopy];
CFStringTransform((__bridge CFMutableStringRef)(mutableUserAgent), NULL, kCFStringTransformToLatin, false); if (CFStringTransform((__bridge CFMutableStringRef)(mutableUserAgent), NULL, kCFStringTransformToLatin, false)) {
userAgent = mutableUserAgent; userAgent = mutableUserAgent;
}
} }
[self setDefaultHeader:@"User-Agent" value:userAgent]; [self setDefaultHeader:@"User-Agent" value:userAgent];
} }
@ -758,22 +759,22 @@ static void AFNetworkReachabilityReleaseCallback(const void *info) {
#pragma mark - #pragma mark -
static NSString * const kAFMultipartFormBoundary = @"Boundary+0xAbCdEfGbOuNdArY"; static NSString * AFCreateMultipartFormBoundary() {
return [NSString stringWithFormat:@"Boundary+%08X%08X", arc4random(), arc4random()];
}
static NSString * const kAFMultipartFormCRLF = @"\r\n"; static NSString * const kAFMultipartFormCRLF = @"\r\n";
static NSInteger const kAFStreamToStreamBufferSize = 1024 * 1024; //1 meg default static inline NSString * AFMultipartFormInitialBoundary(NSString *boundary) {
return [NSString stringWithFormat:@"--%@%@", boundary, kAFMultipartFormCRLF];
static inline NSString * AFMultipartFormInitialBoundary() {
return [NSString stringWithFormat:@"--%@%@", kAFMultipartFormBoundary, kAFMultipartFormCRLF];
} }
static inline NSString * AFMultipartFormEncapsulationBoundary() { static inline NSString * AFMultipartFormEncapsulationBoundary(NSString *boundary) {
return [NSString stringWithFormat:@"%@--%@%@", kAFMultipartFormCRLF, kAFMultipartFormBoundary, kAFMultipartFormCRLF]; return [NSString stringWithFormat:@"%@--%@%@", kAFMultipartFormCRLF, boundary, kAFMultipartFormCRLF];
} }
static inline NSString * AFMultipartFormFinalBoundary() { static inline NSString * AFMultipartFormFinalBoundary(NSString *boundary) {
return [NSString stringWithFormat:@"%@--%@--%@", kAFMultipartFormCRLF, kAFMultipartFormBoundary, kAFMultipartFormCRLF]; return [NSString stringWithFormat:@"%@--%@--%@", kAFMultipartFormCRLF, boundary, kAFMultipartFormCRLF];
} }
static inline NSString * AFContentTypeForPathExtension(NSString *extension) { static inline NSString * AFContentTypeForPathExtension(NSString *extension) {
@ -800,6 +801,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
@property (nonatomic, assign) unsigned long long bodyContentLength; @property (nonatomic, assign) unsigned long long bodyContentLength;
@property (nonatomic, strong) NSInputStream *inputStream; @property (nonatomic, strong) NSInputStream *inputStream;
@property (nonatomic, copy) NSString *boundary;
@property (nonatomic, assign) BOOL hasInitialBoundary; @property (nonatomic, assign) BOOL hasInitialBoundary;
@property (nonatomic, assign) BOOL hasFinalBoundary; @property (nonatomic, assign) BOOL hasFinalBoundary;
@ -826,6 +828,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
@interface AFStreamingMultipartFormData () @interface AFStreamingMultipartFormData ()
@property (readwrite, nonatomic, copy) NSMutableURLRequest *request; @property (readwrite, nonatomic, copy) NSMutableURLRequest *request;
@property (nonatomic, copy) NSString *boundary;
@property (readwrite, nonatomic, strong) AFMultipartBodyStream *bodyStream; @property (readwrite, nonatomic, strong) AFMultipartBodyStream *bodyStream;
@property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding; @property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding;
@end @end
@ -845,6 +848,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
self.request = urlRequest; self.request = urlRequest;
self.stringEncoding = encoding; self.stringEncoding = encoding;
self.boundary = AFCreateMultipartFormBoundary();
self.bodyStream = [[AFMultipartBodyStream alloc] initWithStringEncoding:encoding]; self.bodyStream = [[AFMultipartBodyStream alloc] initWithStringEncoding:encoding];
return self; return self;
@ -897,6 +901,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init]; AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init];
bodyPart.stringEncoding = self.stringEncoding; bodyPart.stringEncoding = self.stringEncoding;
bodyPart.headers = mutableHeaders; bodyPart.headers = mutableHeaders;
bodyPart.boundary = self.boundary;
bodyPart.body = fileURL; bodyPart.body = fileURL;
NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[fileURL path] error:nil]; NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[fileURL path] error:nil];
@ -926,6 +931,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init]; AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init];
bodyPart.stringEncoding = self.stringEncoding; bodyPart.stringEncoding = self.stringEncoding;
bodyPart.headers = mutableHeaders; bodyPart.headers = mutableHeaders;
bodyPart.boundary = self.boundary;
bodyPart.body = inputStream; bodyPart.body = inputStream;
bodyPart.bodyContentLength = length; bodyPart.bodyContentLength = length;
@ -968,6 +974,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init]; AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init];
bodyPart.stringEncoding = self.stringEncoding; bodyPart.stringEncoding = self.stringEncoding;
bodyPart.headers = headers; bodyPart.headers = headers;
bodyPart.boundary = self.boundary;
bodyPart.bodyContentLength = [body length]; bodyPart.bodyContentLength = [body length];
bodyPart.body = body; bodyPart.body = body;
@ -989,7 +996,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
// Reset the initial and final boundaries to ensure correct Content-Length // Reset the initial and final boundaries to ensure correct Content-Length
[self.bodyStream setInitialAndFinalBoundaries]; [self.bodyStream setInitialAndFinalBoundaries];
[self.request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", kAFMultipartFormBoundary] forHTTPHeaderField:@"Content-Type"]; [self.request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", self.boundary] 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];
@ -1253,7 +1260,7 @@ typedef enum {
- (unsigned long long)contentLength { - (unsigned long long)contentLength {
unsigned long long length = 0; unsigned long long length = 0;
NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ? AFMultipartFormInitialBoundary() : AFMultipartFormEncapsulationBoundary()) dataUsingEncoding:self.stringEncoding]; NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ? AFMultipartFormInitialBoundary(self.boundary) : AFMultipartFormEncapsulationBoundary(self.boundary)) dataUsingEncoding:self.stringEncoding];
length += [encapsulationBoundaryData length]; length += [encapsulationBoundaryData length];
NSData *headersData = [[self stringForHeaders] dataUsingEncoding:self.stringEncoding]; NSData *headersData = [[self stringForHeaders] dataUsingEncoding:self.stringEncoding];
@ -1261,7 +1268,7 @@ typedef enum {
length += _bodyContentLength; length += _bodyContentLength;
NSData *closingBoundaryData = ([self hasFinalBoundary] ? [AFMultipartFormFinalBoundary() dataUsingEncoding:self.stringEncoding] : [NSData data]); NSData *closingBoundaryData = ([self hasFinalBoundary] ? [AFMultipartFormFinalBoundary(self.boundary) dataUsingEncoding:self.stringEncoding] : [NSData data]);
length += [closingBoundaryData length]; length += [closingBoundaryData length];
return length; return length;
@ -1297,7 +1304,7 @@ typedef enum {
NSUInteger totalNumberOfBytesRead = 0; NSUInteger totalNumberOfBytesRead = 0;
if (_phase == AFEncapsulationBoundaryPhase) { if (_phase == AFEncapsulationBoundaryPhase) {
NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ? AFMultipartFormInitialBoundary() : AFMultipartFormEncapsulationBoundary()) dataUsingEncoding:self.stringEncoding]; NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ? AFMultipartFormInitialBoundary(self.boundary) : AFMultipartFormEncapsulationBoundary(self.boundary)) dataUsingEncoding:self.stringEncoding];
totalNumberOfBytesRead += [self readData:encapsulationBoundaryData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)]; totalNumberOfBytesRead += [self readData:encapsulationBoundaryData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)];
} }
@ -1322,7 +1329,7 @@ typedef enum {
} }
if (_phase == AFFinalBoundaryPhase) { if (_phase == AFFinalBoundaryPhase) {
NSData *closingBoundaryData = ([self hasFinalBoundary] ? [AFMultipartFormFinalBoundary() dataUsingEncoding:self.stringEncoding] : [NSData data]); NSData *closingBoundaryData = ([self hasFinalBoundary] ? [AFMultipartFormFinalBoundary(self.boundary) dataUsingEncoding:self.stringEncoding] : [NSData data]);
totalNumberOfBytesRead += [self readData:closingBoundaryData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)]; totalNumberOfBytesRead += [self readData:closingBoundaryData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)];
} }

View file

@ -149,8 +149,8 @@ static void AFSwizzleClassMethodWithClassAndSelectorUsingBlock(Class klass, SEL
[userInfo setValue:self.response forKey:AFNetworkingOperationFailingURLResponseErrorKey]; [userInfo setValue:self.response forKey:AFNetworkingOperationFailingURLResponseErrorKey];
if (![self hasAcceptableStatusCode]) { if (![self hasAcceptableStatusCode]) {
NSUInteger statusCode = ([self.response isKindOfClass:[NSHTTPURLResponse class]]) ? (NSUInteger)[self.response statusCode] : 200; NSInteger statusCode = ([self.response isKindOfClass:[NSHTTPURLResponse class]]) ? [self.response statusCode] : 200;
[userInfo setValue:[NSString stringWithFormat:NSLocalizedStringFromTable(@"Expected status code in (%@), got %d", @"AFNetworking", nil), AFStringFromIndexSet([[self class] acceptableStatusCodes]), statusCode] forKey:NSLocalizedDescriptionKey]; [userInfo setValue:[NSString stringWithFormat:NSLocalizedStringFromTable(@"Expected status code in (%@), got %ld", @"AFNetworking", nil), AFStringFromIndexSet([[self class] acceptableStatusCodes]), (long)statusCode] forKey:NSLocalizedDescriptionKey];
self.HTTPError = [[NSError alloc] initWithDomain:AFNetworkingErrorDomain code:NSURLErrorBadServerResponse userInfo:userInfo]; self.HTTPError = [[NSError alloc] initWithDomain:AFNetworkingErrorDomain code:NSURLErrorBadServerResponse userInfo:userInfo];
} else if (![self hasAcceptableContentType]) { } else if (![self hasAcceptableContentType]) {
// Don't invalidate content type if there is no content // Don't invalidate content type if there is no content

View file

@ -108,8 +108,13 @@ static inline BOOL AFStateTransitionIsValid(AFOperationState fromState, AFOperat
static NSData *AFSecKeyGetData(SecKeyRef key) { static NSData *AFSecKeyGetData(SecKeyRef key) {
CFDataRef data = NULL; CFDataRef data = NULL;
#if defined(NS_BLOCK_ASSERTIONS)
SecItemExport(key, kSecFormatUnknown, kSecItemPemArmour, NULL, &data);
#else
OSStatus status = SecItemExport(key, kSecFormatUnknown, kSecItemPemArmour, NULL, &data); OSStatus status = SecItemExport(key, kSecFormatUnknown, kSecItemPemArmour, NULL, &data);
NSCAssert(status == errSecSuccess, @"SecItemExport error: %ld", (long int)status); NSCAssert(status == errSecSuccess, @"SecItemExport error: %ld", (long int)status);
#endif
NSCParameterAssert(data); NSCParameterAssert(data);
return (__bridge_transfer NSData *)data; return (__bridge_transfer NSData *)data;
@ -587,6 +592,7 @@ willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challe
SecTrustRef serverTrust = challenge.protectionSpace.serverTrust; SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
SecPolicyRef policy = SecPolicyCreateBasicX509(); SecPolicyRef policy = SecPolicyCreateBasicX509();
SecTrustEvaluate(serverTrust, NULL);
CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust); CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:certificateCount]; NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:certificateCount];
@ -733,16 +739,20 @@ didReceiveResponse:(NSURLResponse *)response
while (totalNumberOfBytesWritten < length) { while (totalNumberOfBytesWritten < length) {
numberOfBytesWritten = [self.outputStream write:&dataBuffer[0] maxLength:length]; numberOfBytesWritten = [self.outputStream write:&dataBuffer[0] maxLength:length];
if (numberOfBytesWritten == -1) { if (numberOfBytesWritten == -1) {
[self.connection cancel]; break;
[self performSelector:@selector(connection:didFailWithError:) withObject:self.connection withObject:self.outputStream.streamError];
return;
} else {
totalNumberOfBytesWritten += numberOfBytesWritten;
} }
totalNumberOfBytesWritten += numberOfBytesWritten;
} }
break; break;
} }
if (self.outputStream.streamError) {
[self.connection cancel];
[self performSelector:@selector(connection:didFailWithError:) withObject:self.connection withObject:self.outputStream.streamError];
return;
}
} }
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{

56
CHANGES
View file

@ -1,45 +1,73 @@
= 1.3.4 (2014-04-15)
* Fix `AFHTTPMultipartBodyStream` to randomly generate form boundary, to
prevent attack based on a known value (Mathias Bynens, Tom Van Goethem, Mattt
Thompson)
* Fix potential non-terminating loop in `connection:didReceiveData:` (Mattt
Thompson)
* Fix SSL certificate validation to provide a human readable Warning when
SSL Pinning fails (Maximillian Dornseif)
* Fix SSL certificate validation to assert that no impossible pinning
configuration exists (Maximillian Dornseif)
* Fix to check `CFStringTransform()` call for success before using result
(Kevin Cassidy Jr)
* Fix to prevent unused assertion results with macros (Indragie Karunaratne)
* Fix to call call `SecTrustEvaluate` before calling
`SecTrustGetCertificateCount` in SSL certificate validation (Josh Chung)
* Fix to add explicit cast to `NSUInteger` in format string (Alexander
Kempgen)
* Remove unused variable `kAFStreamToStreamBufferSize` (Alexander Kempgen)
= 1.3.3 (2013-09-25) = 1.3.3 (2013-09-25)
* Add stream error handling to `AFMultipartBodyStream` (Nicolas Bachschmidt, * Add stream error handling to `AFMultipartBodyStream` (Nicolas Bachschmidt,
Mattt Thompson) Mattt Thompson)
* Add stream error handling to `AFURLConnectionOperation * Add stream error handling to `AFURLConnectionOperation
-connection:didReceiveData:` (Ian Duggan, Mattt Thompson) -connection:didReceiveData:` (Ian Duggan, Mattt Thompson)
* Fix parameter query string encoding of square brackets according to RFC * Fix parameter query string encoding of square brackets according to RFC
3986 (Kra Larivain) 3986 (Kra Larivain)
* Fix AFHTTPBodyPart determination of end of input stream data (Brian Croom) * Fix AFHTTPBodyPart determination of end of input stream data (Brian Croom)
* Fix unit test timeouts (Carson McDonald) * Fix unit test timeouts (Carson McDonald)
* Fix truncated `User-Agent` header field when app contained non-ASCII * Fix truncated `User-Agent` header field when app contained non-ASCII
characters (Diego Torres) characters (Diego Torres)
* Fix outdated link in documentation (Jonas Schmid) * Fix outdated link in documentation (Jonas Schmid)
* Fix `AFHTTPRequestOperation` `HTTPError` property to be thread-safe * Fix `AFHTTPRequestOperation` `HTTPError` property to be thread-safe
(Oliver Letterer, Mattt Thompson) (Oliver Letterer, Mattt Thompson)
* Fix API compatibility with iOS 5 (Blake Watters, Mattt Thompson) * Fix API compatibility with iOS 5 (Blake Watters, Mattt Thompson)
* Fix potential race condition in `AFURLConnectionOperation * Fix potential race condition in `AFURLConnectionOperation
-cancelConnection` (@mm-jkolb, Mattt Thompson) -cancelConnection` (@mm-jkolb, Mattt Thompson)
* Remove implementation of `connection:needNewBodyStream:` delegate method * Remove implementation of `connection:needNewBodyStream:` delegate method
in `AFURLConnectionOperation`, which fixes stream errors on authentication in `AFURLConnectionOperation`, which fixes stream errors on authentication
challenges (Mattt Thompson) challenges (Mattt Thompson)
* Fix calculation of network reachability from flags (Tracy Pesin, Mattt * Fix calculation of network reachability from flags (Tracy Pesin, Mattt
Thompson) Thompson)
* Update AFHTTPClient documentation to clarify scope of `parameterEncoding` * Update AFHTTPClient documentation to clarify scope of `parameterEncoding`
property (Thomas Catterall) property (Thomas Catterall)
* Update `UIImageView` category to allow for nested calls to * Update `UIImageView` category to allow for nested calls to
`setImageWithURLRequest:` (Philippe Converset) `setImageWithURLRequest:` (Philippe Converset)
* Change `UIImageView` category to accept invalid SSL certificates when * Change `UIImageView` category to accept invalid SSL certificates when
`_AFNETWORKING_ALLOW_INVALID_SSL_CERTIFICATES_` is defined (Flávio Caetano) `_AFNETWORKING_ALLOW_INVALID_SSL_CERTIFICATES_` is defined (Flávio Caetano)
* Change to replace #pragma clang with cast (Cédric Luthi) * Change to replace #pragma clang with cast (Cédric Luthi)

View file

@ -1,4 +1,4 @@
Copyright (c) 2011 Gowalla (http://gowalla.com/) Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com/)
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View file

@ -2,8 +2,6 @@
<img src="https://raw.github.com/AFNetworking/AFNetworking/assets/afnetworking-logo.png" alt="AFNetworking" title="AFNetworking"> <img src="https://raw.github.com/AFNetworking/AFNetworking/assets/afnetworking-logo.png" alt="AFNetworking" title="AFNetworking">
</p> </p>
[![Build Status](https://travis-ci.org/AFNetworking/AFNetworking.png?branch=master)](https://travis-ci.org/AFNetworking/AFNetworking)
AFNetworking is a delightful networking library for iOS and Mac OS X. It's built on top of [NSURLConnection](http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/Reference/Reference.html), [NSOperation](http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/NSOperation_class/Reference/Reference.html), and other familiar Foundation technologies. It has a modular architecture with well-designed, feature-rich APIs that are a joy to use. For example, here's how easy it is to get JSON from a URL: AFNetworking is a delightful networking library for iOS and Mac OS X. It's built on top of [NSURLConnection](http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/Reference/Reference.html), [NSOperation](http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/NSOperation_class/Reference/Reference.html), and other familiar Foundation technologies. It has a modular architecture with well-designed, feature-rich APIs that are a joy to use. For example, here's how easy it is to get JSON from a URL:
```objective-c ```objective-c