// // RKResponse.m // RKFramework // // Created by Blake Watters on 7/28/09. // Copyright 2009 Two Toasters. All rights reserved. // #import "RKResponse.h" #import "RKNotifications.h" #import "RKNetwork.h" #import "RKLog.h" #import "RKParserRegistry.h" // Set Logging Component #undef RKLogComponent #define RKLogComponent lcl_cRestKitNetwork extern NSString* cacheResponseCodeKey; extern NSString* cacheMIMETypeKey; extern NSString* cacheURLKey; @implementation RKResponse @synthesize body = _body, request = _request, failureError = _failureError; - (id)init { self = [super init]; if (self) { _body = [[NSMutableData alloc] init]; _failureError = nil; _loading = NO; _responseHeaders = nil; } return self; } - (id)initWithRequest:(RKRequest*)request { self = [self init]; if (self) { // We don't retain here as we're letting RKRequestQueue manage // request ownership _request = request; } return self; } - (id)initWithRequest:(RKRequest*)request body:(NSData*)body headers:(NSDictionary*)headers { self = [self initWithRequest:request]; if (self) { [_body release]; _body = [[NSMutableData dataWithData:body] retain]; _responseHeaders = [headers retain]; } return self; } - (id)initWithSynchronousRequest:(RKRequest*)request URLResponse:(NSHTTPURLResponse*)URLResponse body:(NSData*)body error:(NSError*)error { self = [super init]; if (self) { _request = request; _httpURLResponse = [URLResponse retain]; _failureError = [error retain]; _body = [[NSMutableData dataWithData:body] retain]; _loading = NO; } return self; } - (void)dealloc { [_httpURLResponse release]; _httpURLResponse = nil; [_body release]; _body = nil; [_failureError release]; _failureError = nil; [_responseHeaders release]; _responseHeaders = nil; [super dealloc]; } // Handle basic auth - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { if ([challenge previousFailureCount] == 0) { NSURLCredential *newCredential; newCredential = [NSURLCredential credentialWithUser:[NSString stringWithFormat:@"%@", _request.username] password:[NSString stringWithFormat:@"%@", _request.password] persistence:RKNetworkGetGlobalCredentialPersistence()]; [[challenge sender] useCredential:newCredential forAuthenticationChallenge:challenge]; } else { [[challenge sender] cancelAuthenticationChallenge:challenge]; } } - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { [_body appendData:data]; } - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response { RKLogDebug(@"NSHTTPURLResponse Status Code: %d", [response statusCode]); RKLogDebug(@"Headers: %@", [response allHeaderFields]); RKLogTrace(@"Read response body: %@", [self bodyAsString]); _httpURLResponse = [response retain]; } - (void)connectionDidFinishLoading:(NSURLConnection *)connection { [_request didFinishLoad:self]; } - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { _failureError = [error retain]; [_request didFailLoadWithError:_failureError]; } // In the event that the url request is a post, this delegate method will be called before // either connection:didReceiveData: or connection:didReceiveResponse: // However this method is only called if there is payload data to be sent. // Therefore, we ensure the delegate recieves the did start loading here and // in connection:didReceiveResponse: to ensure that the RKRequestDelegate // callbacks get called in the correct order. - (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite { if ([[_request delegate] respondsToSelector:@selector(request:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:)]) { [[_request delegate] request:_request didSendBodyData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite]; } } - (NSString*)localizedStatusCodeString { return [NSHTTPURLResponse localizedStringForStatusCode:[self statusCode]]; } - (NSData*)body { return _body; } - (NSString*)bodyAsString { return [[[NSString alloc] initWithData:self.body encoding:NSUTF8StringEncoding] autorelease]; } - (id)bodyAsJSON { [NSException raise:nil format:@"Reimplemented as parsedBody"]; return nil; } - (id)parsedBody:(NSError**)error { id parser = [[RKParserRegistry sharedRegistry] parserForMIMEType:[self MIMEType]]; id object = [parser objectFromString:[self bodyAsString] error:error]; if (object == nil) { RKLogError(@"Unable to parse response body: %@", [*error localizedDescription]); return nil; } return object; } - (NSString*)failureErrorDescription { if ([self isFailure]) { return [_failureError localizedDescription]; } else { return nil; } } - (BOOL)wasLoadedFromCache { return (_responseHeaders != nil); } - (NSURL*)URL { if ([self wasLoadedFromCache]) { return [NSURL URLWithString:[_responseHeaders valueForKey:cacheURLKey]]; } return [_httpURLResponse URL]; } - (NSString*)MIMEType { if ([self wasLoadedFromCache]) { return [_responseHeaders valueForKey:cacheMIMETypeKey]; } return [_httpURLResponse MIMEType]; } - (NSInteger)statusCode { if ([self wasLoadedFromCache]) { return [[_responseHeaders valueForKey:cacheResponseCodeKey] intValue]; } return [_httpURLResponse statusCode]; } - (NSDictionary*)allHeaderFields { if ([self wasLoadedFromCache]) { return _responseHeaders; } return [_httpURLResponse allHeaderFields]; } - (NSArray*)cookies { return [NSHTTPCookie cookiesWithResponseHeaderFields:self.allHeaderFields forURL:self.URL]; } - (BOOL)isFailure { return (nil != _failureError); } - (BOOL)isInvalid { return ([self statusCode] < 100 || [self statusCode] > 600); } - (BOOL)isInformational { return ([self statusCode] >= 100 && [self statusCode] < 200); } - (BOOL)isSuccessful { return (([self statusCode] >= 200 && [self statusCode] < 300) || ([self wasLoadedFromCache])); } - (BOOL)isRedirection { return ([self statusCode] >= 300 && [self statusCode] < 400); } - (BOOL)isClientError { return ([self statusCode] >= 400 && [self statusCode] < 500); } - (BOOL)isServerError { return ([self statusCode] >= 500 && [self statusCode] < 600); } - (BOOL)isError { return ([self isClientError] || [self isServerError]); } - (BOOL)isOK { return ([self statusCode] == 200); } - (BOOL)isCreated { return ([self statusCode] == 201); } - (BOOL)isNotModified { return ([self statusCode] == 304); } - (BOOL)isUnauthorized { return ([self statusCode] == 401); } - (BOOL)isForbidden { return ([self statusCode] == 403); } - (BOOL)isNotFound { return ([self statusCode] == 404); } - (BOOL)isConflict { return ([self statusCode] == 409); } - (BOOL)isGone { return ([self statusCode] == 410); } - (BOOL)isUnprocessableEntity { return ([self statusCode] == 422); } - (BOOL)isRedirect { return ([self statusCode] == 301 || [self statusCode] == 302 || [self statusCode] == 303 || [self statusCode] == 307); } - (BOOL)isEmpty { return ([self statusCode] == 201 || [self statusCode] == 204 || [self statusCode] == 304); } - (BOOL)isServiceUnavailable { return ([self statusCode] == 503); } - (NSString*)contentType { return ([[self allHeaderFields] objectForKey:@"Content-Type"]); } - (NSString*)contentLength { return ([[self allHeaderFields] objectForKey:@"Content-Length"]); } - (NSString*)location { return ([[self allHeaderFields] objectForKey:@"Location"]); } - (BOOL)isHTML { NSString* contentType = [self contentType]; return (contentType && ([contentType rangeOfString:@"text/html" options:NSCaseInsensitiveSearch|NSAnchoredSearch].length > 0 || [self isXHTML])); } - (BOOL)isXHTML { NSString* contentType = [self contentType]; return (contentType && [contentType rangeOfString:@"application/xhtml+xml" options:NSCaseInsensitiveSearch|NSAnchoredSearch].length > 0); } - (BOOL)isXML { NSString* contentType = [self contentType]; return (contentType && [contentType rangeOfString:@"application/xml" options:NSCaseInsensitiveSearch|NSAnchoredSearch].length > 0); } - (BOOL)isJSON { NSString* contentType = [self contentType]; return (contentType && [contentType rangeOfString:@"application/json" options:NSCaseInsensitiveSearch|NSAnchoredSearch].length > 0); } @end