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