2011-07-27 15:14:15 -05:00
// AFHTTPOperation . m
//
// Copyright ( c ) 2011 Gowalla ( http : // gowalla . com / )
//
// Permission is hereby granted , free of charge , to any person obtaining a copy
// of this software and associated documentation files ( the "Software" ) , to deal
// in the Software without restriction , including without limitation the rights
// to use , copy , modify , merge , publish , distribute , sublicense , and / or sell
// copies of the Software , and to permit persons to whom the Software is
// furnished to do so , subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software .
//
// THE SOFTWARE IS PROVIDED "AS IS" , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
// IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
// LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM ,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE .
# import "AFHTTPRequestOperation.h"
2012-03-02 10:42:24 -08:00
# import < objc / runtime . h >
2011-07-27 15:14:15 -05:00
2012-03-25 12:23:40 -07:00
NSString * const kAFNetworkingIncompleteDownloadDirectoryName = @ "Incomplete" ;
2012-03-02 11:18:20 -08:00
NSSet * AFContentTypesFromHTTPHeader ( NSString * string ) {
static NSCharacterSet * _skippedCharacterSet = nil ;
static dispatch_once _t onceToken ;
dispatch_once ( & onceToken , ^ {
_skippedCharacterSet = [ NSCharacterSet characterSetWithCharactersInString : @ " ," ] ;
} ) ;
2012-03-03 13:19:53 -08:00
if ( ! string ) {
return nil ;
}
2012-03-02 11:18:20 -08:00
NSScanner * scanner = [ NSScanner scannerWithString : string ] ;
scanner . charactersToBeSkipped = _skippedCharacterSet ;
NSMutableSet * mutableContentTypes = [ NSMutableSet set ] ;
while ( ! [ scanner isAtEnd ] ) {
NSString * contentType = nil ;
if ( [ scanner scanUpToString : @ ";" intoString : & contentType ] ) {
[ scanner scanUpToString : @ "," intoString : nil ] ;
}
if ( contentType ) {
[ mutableContentTypes addObject : contentType ] ;
}
}
return [ NSSet setWithSet : mutableContentTypes ] ;
}
2012-03-02 10:42:24 -08:00
static void AFSwizzleClassMethodWithClassAndSelectorUsingBlock ( Class klass , SEL selector , void * block ) {
Method originalMethod = class_getClassMethod ( klass , selector ) ;
IMP implementation = imp_implementationWithBlock ( block ) ;
2012-03-12 20:22:46 -07:00
class_replaceMethod ( objc_getMetaClass ( [ NSStringFromClass ( klass ) UTF8String ] ) , selector , implementation , method_getTypeEncoding ( originalMethod ) ) ;
2012-03-02 10:42:24 -08:00
}
2012-01-20 14:58:48 -08:00
static NSString * AFStringFromIndexSet ( NSIndexSet * indexSet ) {
NSMutableString * string = [ NSMutableString string ] ;
NSRange range = NSMakeRange ( [ indexSet firstIndex ] , 1 ) ;
while ( range . location ! = NSNotFound ) {
NSUInteger nextIndex = [ indexSet indexGreaterThanIndex : range . location ] ;
while ( nextIndex = = range . location + range . length ) {
range . length + + ;
nextIndex = [ indexSet indexGreaterThanIndex : nextIndex ] ;
}
if ( string . length ) {
[ string appendString : @ "," ] ;
}
if ( range . length = = 1 ) {
[ string appendFormat : @ "%u" , range . location ] ;
} else {
NSUInteger firstIndex = range . location ;
NSUInteger lastIndex = firstIndex + range . length - 1 ;
[ string appendFormat : @ "%u-%u" , firstIndex , lastIndex ] ;
}
range . location = nextIndex ;
range . length = 1 ;
}
return string ;
}
2012-04-06 03:00:33 -07:00
NSString * AFCreateIncompleteDownloadDirectoryPath ( void ) {
static NSString * incompleteDownloadPath ;
static dispatch_once _t onceToken ;
dispatch_once ( & onceToken , ^ {
NSString * tempDirectory = NSTemporaryDirectory ( ) ;
incompleteDownloadPath = [ [ tempDirectory stringByAppendingPathComponent : kAFNetworkingIncompleteDownloadDirectoryName ] retain ] ;
NSError * error = nil ;
NSFileManager * fileMan = [ [ NSFileManager alloc ] init ] ;
if ( ! [ fileMan createDirectoryAtPath : incompleteDownloadPath withIntermediateDirectories : YES attributes : nil error : & error ] ) {
NSLog ( @ "Failed to create incomplete downloads directory at %@" , incompleteDownloadPath ) ;
}
[ fileMan release ] ;
} ) ;
return incompleteDownloadPath ;
}
2012-01-20 14:58:48 -08:00
# pragma mark -
2011-07-27 15:14:15 -05:00
@ interface AFHTTPRequestOperation ( )
2012-03-25 12:23:40 -07:00
@ property ( readwrite , nonatomic , retain ) NSURLRequest * request ;
2012-03-27 11:01:20 -07:00
@ property ( readwrite , nonatomic , retain ) NSHTTPURLResponse * response ;
2011-10-24 13:32:42 -05:00
@ property ( readwrite , nonatomic , retain ) NSError * HTTPError ;
2012-03-25 12:23:40 -07:00
@ property ( readwrite , nonatomic , copy ) NSString * responseFilePath ;
2011-07-27 15:14:15 -05:00
@ end
@ implementation AFHTTPRequestOperation
2011-10-24 13:32:42 -05:00
@ synthesize HTTPError = _HTTPError ;
2012-03-25 12:23:40 -07:00
@ synthesize responseFilePath = _responseFilePath ;
2012-02-06 13:29:00 -08:00
@ synthesize successCallbackQueue = _successCallbackQueue ;
@ synthesize failureCallbackQueue = _failureCallbackQueue ;
2012-03-25 12:23:40 -07:00
@ dynamic request ;
@ dynamic response ;
2011-10-04 00:13:12 -05:00
2011-07-27 15:14:15 -05:00
- ( void ) dealloc {
2011-10-05 14:14:52 -05:00
[ _HTTPError release ] ;
2012-02-20 20:22:46 -08:00
if ( _successCallbackQueue ) {
dispatch_release ( _successCallbackQueue ) ;
_successCallbackQueue = NULL ;
}
if ( _failureCallbackQueue ) {
dispatch_release ( _failureCallbackQueue ) ;
_failureCallbackQueue = NULL ;
}
2011-07-27 15:14:15 -05:00
[ super dealloc ] ;
}
2011-10-04 00:13:12 -05:00
- ( NSError * ) error {
2011-10-24 13:32:42 -05:00
if ( self . response && ! self . HTTPError ) {
2011-10-04 00:13:12 -05:00
if ( ! [ self hasAcceptableStatusCode ] ) {
NSMutableDictionary * userInfo = [ NSMutableDictionary dictionary ] ;
2012-03-02 10:42:24 -08:00
[ userInfo setValue : [ NSString stringWithFormat : NSLocalizedString ( @ "Expected status code in (%@), got %d" , nil ) , AFStringFromIndexSet ( [ [ self class ] acceptableStatusCodes ] ) , [ self . response statusCode ] ] forKey : NSLocalizedDescriptionKey ] ;
2011-10-04 00:13:12 -05:00
[ userInfo setValue : [ self . request URL ] forKey : NSURLErrorFailingURLErrorKey ] ;
2011-10-24 13:32:42 -05:00
self . HTTPError = [ [ [ NSError alloc ] initWithDomain : AFNetworkingErrorDomain code : NSURLErrorBadServerResponse userInfo : userInfo ] autorelease ] ;
2012-01-20 10:42:38 -08:00
} else if ( [ self . responseData length ] > 0 && ! [ self hasAcceptableContentType ] ) { // Don ' t invalidate content type if there is no content
2011-10-04 00:13:12 -05:00
NSMutableDictionary * userInfo = [ NSMutableDictionary dictionary ] ;
2012-03-02 10:42:24 -08:00
[ userInfo setValue : [ NSString stringWithFormat : NSLocalizedString ( @ "Expected content type %@, got %@" , nil ) , [ [ self class ] acceptableContentTypes ] , [ self . response MIMEType ] ] forKey : NSLocalizedDescriptionKey ] ;
2011-10-04 00:13:12 -05:00
[ userInfo setValue : [ self . request URL ] forKey : NSURLErrorFailingURLErrorKey ] ;
2011-10-24 13:32:42 -05:00
self . HTTPError = [ [ [ NSError alloc ] initWithDomain : AFNetworkingErrorDomain code : NSURLErrorCannotDecodeContentData userInfo : userInfo ] autorelease ] ;
2011-08-05 13:52:20 -05:00
}
2011-09-15 14:26:55 -05:00
}
2012-01-20 10:42:38 -08:00
if ( self . HTTPError ) {
return self . HTTPError ;
2011-10-24 13:32:42 -05:00
} else {
return [ super error ] ;
}
2011-07-27 15:14:15 -05:00
}
2012-03-27 11:01:20 -07:00
- ( void ) pause {
unsigned long long offset = 0 ;
if ( [ self . outputStream propertyForKey : NSStreamFileCurrentOffsetKey ] ) {
offset = [ [ self . outputStream propertyForKey : NSStreamFileCurrentOffsetKey ] unsignedLongLongValue ] ;
} else {
offset = [ [ self . outputStream propertyForKey : NSStreamDataWrittenToMemoryStreamKey ] length ] ;
}
2012-04-06 03:00:33 -07:00
2012-03-27 11:01:20 -07:00
NSMutableURLRequest * mutableURLRequest = [ [ self . request mutableCopy ] autorelease ] ;
if ( [ [ self . response allHeaderFields ] valueForKey : @ "ETag" ] ) {
[ mutableURLRequest setValue : [ [ self . response allHeaderFields ] valueForKey : @ "ETag" ] forHTTPHeaderField : @ "If-Range" ] ;
}
[ mutableURLRequest setValue : [ NSString stringWithFormat : @ "bytes=%llu-" , offset ] forHTTPHeaderField : @ "Range" ] ;
self . request = mutableURLRequest ;
[ super pause ] ;
}
2011-10-04 00:13:12 -05:00
- ( BOOL ) hasAcceptableStatusCode {
2012-03-02 10:42:24 -08:00
return ! [ [ self class ] acceptableStatusCodes ] || [ [ [ self class ] acceptableStatusCodes ] containsIndex : [ self . response statusCode ] ] ;
2011-08-14 19:53:48 -05:00
}
2011-10-04 00:13:12 -05:00
- ( BOOL ) hasAcceptableContentType {
2012-03-02 10:42:24 -08:00
return ! [ [ self class ] acceptableContentTypes ] || [ [ [ self class ] acceptableContentTypes ] containsObject : [ self . response MIMEType ] ] ;
2011-08-03 11:31:00 -05:00
}
2012-02-06 13:29:00 -08:00
- ( void ) setSuccessCallbackQueue : ( dispatch_queue _t ) successCallbackQueue {
if ( successCallbackQueue ! = _successCallbackQueue ) {
if ( _successCallbackQueue ) {
dispatch_release ( _successCallbackQueue ) ;
}
2012-04-06 03:00:33 -07:00
2012-02-06 13:29:00 -08:00
if ( successCallbackQueue ) {
dispatch_retain ( successCallbackQueue ) ;
_successCallbackQueue = successCallbackQueue ;
}
}
}
- ( void ) setFailureCallbackQueue : ( dispatch_queue _t ) failureCallbackQueue {
if ( failureCallbackQueue ! = _failureCallbackQueue ) {
if ( _failureCallbackQueue ) {
dispatch_release ( _failureCallbackQueue ) ;
}
if ( failureCallbackQueue ) {
dispatch_retain ( failureCallbackQueue ) ;
_failureCallbackQueue = failureCallbackQueue ;
}
}
}
2011-10-24 13:08:58 -05:00
- ( void ) setCompletionBlockWithSuccess : ( void ( ^ ) ( AFHTTPRequestOperation * operation , id responseObject ) ) success
failure : ( void ( ^ ) ( AFHTTPRequestOperation * operation , NSError * error ) ) failure
2011-10-05 15:44:51 -05:00
{
2011-10-24 13:08:58 -05:00
self . completionBlock = ^ {
if ( [ self isCancelled ] ) {
2011-10-24 09:48:19 -05:00
return ;
}
2011-10-24 13:08:58 -05:00
if ( self . error ) {
2012-02-20 20:22:46 -08:00
if ( failure ) {
dispatch_async ( self . failureCallbackQueue ? self . failureCallbackQueue : dispatch_get _main _queue ( ) , ^ {
failure ( self , self . error ) ;
} ) ;
}
2011-10-24 09:48:19 -05:00
} else {
2012-02-20 20:22:46 -08:00
if ( success ) {
dispatch_async ( self . successCallbackQueue ? self . successCallbackQueue : dispatch_get _main _queue ( ) , ^ {
2012-03-09 12:44:29 -08:00
success ( self , self . responseData ) ;
2012-02-20 20:22:46 -08:00
} ) ;
}
2011-10-24 09:48:19 -05:00
}
} ;
2011-10-24 13:08:58 -05:00
}
# pragma mark - AFHTTPClientOperation
2012-03-02 10:42:24 -08:00
+ ( NSIndexSet * ) acceptableStatusCodes {
return [ NSIndexSet indexSetWithIndexesInRange : NSMakeRange ( 200 , 100 ) ] ;
}
+ ( void ) addAcceptableStatusCodes : ( NSIndexSet * ) statusCodes {
NSMutableIndexSet * mutableStatusCodes = [ [ [ NSMutableIndexSet alloc ] initWithIndexSet : [ self acceptableStatusCodes ] ] autorelease ] ;
[ mutableStatusCodes addIndexes : statusCodes ] ;
AFSwizzleClassMethodWithClassAndSelectorUsingBlock ( [ self class ] , @ selector ( acceptableStatusCodes ) , ^ ( id _self ) {
return mutableStatusCodes ;
} ) ;
}
+ ( NSSet * ) acceptableContentTypes {
return nil ;
}
+ ( void ) addAcceptableContentTypes : ( NSSet * ) contentTypes {
NSMutableSet * mutableContentTypes = [ [ [ NSMutableSet alloc ] initWithSet : [ self acceptableContentTypes ] copyItems : YES ] autorelease ] ;
[ mutableContentTypes unionSet : contentTypes ] ;
AFSwizzleClassMethodWithClassAndSelectorUsingBlock ( [ self class ] , @ selector ( acceptableContentTypes ) , ^ ( id _self ) {
return mutableContentTypes ;
} ) ;
}
2011-10-24 13:08:58 -05:00
+ ( BOOL ) canProcessRequest : ( NSURLRequest * ) request {
2012-03-03 13:19:53 -08:00
if ( ! [ [ self class ] isEqual : [ AFHTTPRequestOperation class ] ] ) {
return YES ;
}
return [ [ self acceptableContentTypes ] intersectsSet : AFContentTypesFromHTTPHeader ( [ request valueForHTTPHeaderField : @ "Accept" ] ) ] ;
2012-02-06 13:43:43 -08:00
}
2012-03-25 12:23:40 -07:00
# pragma mark - NSURLConnectionDelegate
- ( void ) connection : ( NSURLConnection * ) connection
didReceiveResponse : ( NSURLResponse * ) response
{
2012-03-27 11:01:20 -07:00
self . response = ( NSHTTPURLResponse * ) response ;
2012-03-25 12:23:40 -07:00
2012-04-06 03:00:33 -07:00
// 206 = Partial Content .
2012-03-27 11:01:20 -07:00
if ( [ self . response statusCode ] ! = 206 ) {
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 ] ;
2012-03-25 12:23:40 -07:00
}
}
}
2012-03-27 11:01:20 -07:00
[ self . outputStream open ] ;
2012-03-25 12:23:40 -07:00
}
2011-07-27 15:14:15 -05:00
@ end