PageRenderTime 3ms CodeModel.GetById 62ms app.highlight 34ms RepoModel.GetById 0ms app.codeStats 0ms

/Vendor/HTTPRiot/HRRequestOperation.m

https://gitlab.com/lisit1003/uservoice-ios-sdk
Objective C | 321 lines | 244 code | 60 blank | 17 comment | 48 complexity | 67e1fc2428286f9fe14bb0b95c1cb08d MD5 | raw file
  1//
  2//  HRRequestOperation.m
  3//  HTTPRiot
  4//
  5//  Created by Justin Palmer on 1/30/09.
  6//  Copyright 2009 LabratRevenge LLC.. All rights reserved.
  7//
  8
  9#import "HRRequestOperation.h"
 10#import "HRFormatJSON.h"
 11#import "UVUtils.h"
 12#import "HROperationQueue.h"
 13
 14@interface HRRequestOperation (PrivateMethods)
 15- (NSMutableURLRequest *)http;
 16- (NSArray *)formattedResults:(NSData *)data;
 17- (void)setDefaultHeadersForRequest:(NSMutableURLRequest *)request;
 18- (void)setAuthHeadersForRequest:(NSMutableURLRequest *)request;
 19- (NSMutableURLRequest *)configuredRequest;
 20- (NSURL *)composedURL;
 21+ (id)handleResponse:(NSHTTPURLResponse *)response error:(NSError * __autoreleasing *)error;
 22+ (NSString *)buildQueryStringFromParams:(NSDictionary *)params;
 23- (void)finish;
 24@end
 25
 26@implementation HRRequestOperation
 27
 28- (id)initWithMethod:(HRRequestMethod)method path:(NSString*)urlPath options:(NSDictionary*)opts object:(id)obj {
 29                 
 30    if(self = [super init]) {
 31        _isExecuting    = NO;
 32        _isFinished     = NO;
 33        _isCancelled    = NO;
 34        _requestMethod  = method;
 35        _path           = [urlPath copy];
 36        _options        = opts;
 37        _object         = obj;
 38        _timeout        = 120.0;
 39        _delegate       = [opts valueForKey:kHRClassAttributesDelegateKey];
 40        _formatter      = [HRFormatJSON class];
 41    }
 42
 43    return self;
 44}
 45
 46
 47////////////////////////////////////////////////////////////////////////////////////////////////////
 48#pragma mark - Concurrent NSOperation Methods
 49- (void)start {
 50    // Snow Leopard Fix. See http://www.dribin.org/dave/blog/archives/2009/09/13/snowy_concurrent_operations/
 51    if (![NSThread isMainThread]) {
 52        [self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
 53        return;
 54    }
 55    
 56    [self willChangeValueForKey:@"isExecuting"];
 57    _isExecuting = YES;
 58    [self didChangeValueForKey:@"isExecuting"];
 59    
 60    NSURLRequest *request = [self configuredRequest];
 61    //HRLOG(@"FETCHING:%@ \nHEADERS:%@", [[request URL] absoluteString], [request allHTTPHeaderFields]);
 62    _connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
 63    
 64    if(_connection) {
 65        _responseData = [NSMutableData new];        
 66    } else {
 67        [self finish];
 68    }    
 69}
 70
 71- (void)finish {
 72    _connection = nil;
 73    _responseData = nil;
 74
 75    [self willChangeValueForKey:@"isExecuting"];
 76    [self willChangeValueForKey:@"isFinished"];
 77
 78    _isExecuting = NO;
 79    _isFinished = YES;
 80
 81    [self didChangeValueForKey:@"isExecuting"];
 82    [self didChangeValueForKey:@"isFinished"];
 83}
 84
 85- (void)cancel {
 86    //HRLOG(@"SHOULD CANCEL");
 87    [self willChangeValueForKey:@"isCancelled"];
 88    
 89    [_connection cancel];    
 90    _isCancelled = YES;
 91    
 92    [self didChangeValueForKey:@"isCancelled"];
 93    
 94    [self finish];
 95}
 96
 97- (BOOL)isExecuting {
 98   return _isExecuting;
 99}
100
101- (BOOL)isFinished {
102   return _isFinished;
103}
104
105- (BOOL)isCancelled {
106   return _isCancelled;
107}
108
109- (BOOL)isConcurrent {
110    return YES;
111}
112
113////////////////////////////////////////////////////////////////////////////////////////////////////
114#pragma mark - NSURLConnection delegates
115- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {    
116    //HRLOG(@"Server responded with:%i, %@", [response statusCode], [NSHTTPURLResponse localizedStringForStatusCode:[response statusCode]]);
117    
118    if ([self.delegate respondsToSelector:@selector(restConnection:didReceiveResponse:object:)]) {
119        dispatch_async(dispatch_get_main_queue(), ^{
120            [self.delegate restConnection:connection didReceiveResponse:response object:self->_object];
121        });
122    }
123    
124    NSError *error = nil;
125    [[self class] handleResponse:(NSHTTPURLResponse *)response error:&error];
126    
127    if(error) {
128        if([self.delegate respondsToSelector:@selector(restConnection:didReceiveError:response:object:)]) {
129            dispatch_async(dispatch_get_main_queue(), ^{
130                [self.delegate restConnection:connection didReceiveError:error response:response object:self->_object];
131            });
132            [connection cancel];
133            [self finish];
134        }
135    }
136    
137    [_responseData setLength:0];
138}
139
140- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {   
141    [_responseData appendData:data];
142}
143
144- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {  
145    //HRLOG(@"Connection failed: %@", [error localizedDescription]);
146    if([self.delegate respondsToSelector:@selector(restConnection:didFailWithError:object:)]) {        
147        dispatch_async(dispatch_get_main_queue(), ^{
148            [self.delegate restConnection:connection didFailWithError:error object:self->_object];
149        });
150    }
151    
152    [self finish];
153}
154
155- (void)connectionDidFinishLoading:(NSURLConnection *)connection {    
156    id results = [NSNull null];
157    NSError *parseError = nil;
158    if([_responseData length] > 0) {
159        results = [[self formatter] decode:_responseData error:&parseError];
160                
161        if(parseError) {
162            NSString *rawString = [[NSString alloc] initWithData:_responseData encoding:NSUTF8StringEncoding];
163            if([self.delegate respondsToSelector:@selector(restConnection:didReceiveParseError:responseBody:object:)]) {
164                dispatch_async(dispatch_get_main_queue(), ^{
165                    [self.delegate restConnection:connection didReceiveParseError:parseError responseBody:rawString object:self->_object];
166                });
167            }
168            [self finish];
169            return;
170        }
171    }
172
173    if([self.delegate respondsToSelector:@selector(restConnection:didReturnResource:object:)]) {        
174        dispatch_async(dispatch_get_main_queue(), ^{
175            [self.delegate restConnection:connection didReturnResource:results object:self->_object];
176        });
177    }
178        
179    [self finish];
180}
181
182////////////////////////////////////////////////////////////////////////////////////////////////////
183#pragma mark - Configuration
184
185- (void)setDefaultHeadersForRequest:(NSMutableURLRequest *)request {
186    NSDictionary *headers = [[self options] valueForKey:kHRClassAttributesHeadersKey];
187    [request setValue:[[self formatter] mimeType] forHTTPHeaderField:@"Content-Type"];  
188    [request addValue:[[self formatter] mimeType] forHTTPHeaderField:@"Accept"];
189    if(headers) {
190        for(NSString *header in headers) {
191            NSString *value = [headers valueForKey:header];
192            if([header isEqualToString:@"Accept"]) {
193                [request addValue:value forHTTPHeaderField:header];
194            } else {
195                [request setValue:value forHTTPHeaderField:header];
196            }
197        }        
198    }
199}
200
201- (void)setAuthHeadersForRequest:(NSMutableURLRequest *)request {
202    NSDictionary *authDict = [_options valueForKey:kHRClassAttributesBasicAuthKey];
203    NSString *username = [authDict valueForKey:kHRClassAttributesUsernameKey];
204    NSString *password = [authDict valueForKey:kHRClassAttributesPasswordKey];
205    
206    if(username || password) {
207        NSString *userPass = [NSString stringWithFormat:@"%@:%@", username, password];
208        NSData   *upData = [userPass dataUsingEncoding:NSUTF8StringEncoding];
209        NSString *encodedUserPass = [UVUtils encodeData64:upData];
210        NSString *basicHeader = [NSString stringWithFormat:@"Basic %@", encodedUserPass];
211        [request setValue:basicHeader forHTTPHeaderField:@"Authorization"];
212    }
213}
214
215- (NSMutableURLRequest *)configuredRequest {
216    NSMutableURLRequest *request = [NSMutableURLRequest new];
217    [request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
218    [request setTimeoutInterval:_timeout];
219    [request setHTTPShouldHandleCookies:YES];
220    [self setDefaultHeadersForRequest:request];
221    [self setAuthHeadersForRequest:request];
222    
223    NSURL *composedURL = [self composedURL];
224    NSDictionary *params = [[self options] valueForKey:kHRClassAttributesParamsKey];
225    id body = [[self options] valueForKey:kHRClassAttributesBodyKey];
226    NSString *queryString = [[self class] buildQueryStringFromParams:params];
227    
228    if(_requestMethod == HRRequestMethodGet || _requestMethod == HRRequestMethodDelete) {
229        NSString *urlString = [[composedURL absoluteString] stringByAppendingString:queryString];
230        NSURL *url = [NSURL URLWithString:urlString];
231        [request setURL:url];
232        
233        if(_requestMethod == HRRequestMethodGet) {
234            [request setHTTPMethod:@"GET"];
235        } else {
236            [request setHTTPMethod:@"DELETE"];
237        }
238            
239    } else if(_requestMethod == HRRequestMethodPost || _requestMethod == HRRequestMethodPut) {
240        
241        NSData *bodyData = nil;   
242        if([body isKindOfClass:[NSDictionary class]]) {
243            bodyData = [[UVUtils toQueryString:body] dataUsingEncoding:NSUTF8StringEncoding];
244        } else if([body isKindOfClass:[NSString class]]) {
245            bodyData = [body dataUsingEncoding:NSUTF8StringEncoding];
246        } else if([body isKindOfClass:[NSData class]]) {
247            bodyData = body;
248        } else {
249            [NSException exceptionWithName:@"InvalidBodyData"
250                                    reason:@"The body must be an NSDictionary, NSString, or NSData"
251                                  userInfo:nil];
252        }
253            
254        [request setHTTPBody:bodyData];
255        [request setURL:composedURL];
256        
257        if(_requestMethod == HRRequestMethodPost)
258            [request setHTTPMethod:@"POST"];
259        else
260            [request setHTTPMethod:@"PUT"];
261            
262    }
263    
264    return request;
265}
266
267- (NSURL *)composedURL {
268    NSURL *tmpURI = [NSURL URLWithString:_path];
269    NSURL *baseURL = [_options objectForKey:kHRClassAttributesBaseURLKey];
270
271    if([tmpURI host] == nil && [baseURL host] == nil)
272        [NSException raise:@"UnspecifiedHost" format:@"host wasn't provided in baseURL or path"];
273    
274    if([tmpURI host])
275        return tmpURI;
276        
277    return [NSURL URLWithString:[[baseURL absoluteString] stringByAppendingString:_path]];
278}
279
280////////////////////////////////////////////////////////////////////////////////////////////////////
281#pragma mark - Class Methods
282+ (HRRequestOperation *)requestWithMethod:(HRRequestMethod)method path:(NSString*)urlPath options:(NSDictionary*)requestOptions object:(id)obj {
283    id operation = [[self alloc] initWithMethod:method path:urlPath options:requestOptions object:obj];
284    // NSLog(@"%@", urlPath);
285    [[HROperationQueue sharedOperationQueue] addOperation:operation];
286    return operation;
287}
288
289+ (id)handleResponse:(NSHTTPURLResponse *)response error:(NSError * __autoreleasing *)error {
290    NSInteger code = [response statusCode];
291    NSUInteger ucode = [[NSNumber numberWithInteger:code] unsignedIntValue];
292    NSRange okRange = NSMakeRange(200, 201);
293    
294    if(NSLocationInRange(ucode, okRange)) {
295        return response;
296    }
297
298    if(error != nil) {
299        NSDictionary *headers = [response allHeaderFields];
300        NSString *errorReason = [NSString stringWithFormat:@"%d Error: ", (int)code];
301        NSString *errorDescription = [NSHTTPURLResponse localizedStringForStatusCode:code];
302        NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
303            errorReason, NSLocalizedFailureReasonErrorKey,
304            errorDescription, NSLocalizedDescriptionKey, 
305            headers, kHRClassAttributesHeadersKey, 
306            [[response URL] absoluteString], @"url", nil];
307        *error = [NSError errorWithDomain:HTTPRiotErrorDomain code:code userInfo:userInfo];
308    }
309
310    return nil;
311}
312
313+ (NSString *)buildQueryStringFromParams:(NSDictionary *)theParams {
314    if(theParams) {
315        if([theParams count] > 0)
316            return [NSString stringWithFormat:@"?%@", [UVUtils toQueryString:theParams]];
317    }
318    
319    return @"";
320}
321@end