ASINetworkQueue.m 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. //
  2. // ASINetworkQueue.m
  3. // Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest
  4. //
  5. // Created by Ben Copsey on 07/11/2008.
  6. // Copyright 2008-2009 All-Seeing Interactive. All rights reserved.
  7. //
  8. #import "ASINetworkQueue.h"
  9. #import "ASIHTTPRequest.h"
  10. // Private stuff
  11. @interface ASINetworkQueue ()
  12. - (void)resetProgressDelegate:(id *)progressDelegate;
  13. @property (assign) int requestsCount;
  14. @end
  15. @implementation ASINetworkQueue
  16. - (id)init
  17. {
  18. self = [super init];
  19. [self setShouldCancelAllRequestsOnFailure:YES];
  20. [self setMaxConcurrentOperationCount:4];
  21. [self setSuspended:YES];
  22. return self;
  23. }
  24. + (id)queue
  25. {
  26. return [[[self alloc] init] autorelease];
  27. }
  28. - (void)dealloc
  29. {
  30. //We need to clear the queue on any requests that haven't got around to cleaning up yet, as otherwise they'll try to let us know if something goes wrong, and we'll be long gone by then
  31. for (ASIHTTPRequest *request in [self operations]) {
  32. [request setQueue:nil];
  33. }
  34. [userInfo release];
  35. [super dealloc];
  36. }
  37. - (void)setSuspended:(BOOL)suspend
  38. {
  39. [super setSuspended:suspend];
  40. }
  41. - (void)reset
  42. {
  43. [self cancelAllOperations];
  44. [self setDelegate:nil];
  45. [self setDownloadProgressDelegate:nil];
  46. [self setUploadProgressDelegate:nil];
  47. [self setRequestDidStartSelector:NULL];
  48. [self setRequestDidReceiveResponseHeadersSelector:NULL];
  49. [self setRequestDidFailSelector:NULL];
  50. [self setRequestDidFinishSelector:NULL];
  51. [self setQueueDidFinishSelector:NULL];
  52. [self setSuspended:YES];
  53. }
  54. - (void)go
  55. {
  56. [self setSuspended:NO];
  57. }
  58. - (void)cancelAllOperations
  59. {
  60. [self setBytesUploadedSoFar:0];
  61. [self setTotalBytesToUpload:0];
  62. [self setBytesDownloadedSoFar:0];
  63. [self setTotalBytesToDownload:0];
  64. [super cancelAllOperations];
  65. }
  66. - (void)setUploadProgressDelegate:(id)newDelegate
  67. {
  68. uploadProgressDelegate = newDelegate;
  69. [self resetProgressDelegate:&uploadProgressDelegate];
  70. }
  71. - (void)setDownloadProgressDelegate:(id)newDelegate
  72. {
  73. downloadProgressDelegate = newDelegate;
  74. [self resetProgressDelegate:&downloadProgressDelegate];
  75. }
  76. - (void)resetProgressDelegate:(id *)progressDelegate
  77. {
  78. #if !TARGET_OS_IPHONE
  79. // If the uploadProgressDelegate is an NSProgressIndicator, we set its MaxValue to 1.0 so we can treat it similarly to UIProgressViews
  80. SEL selector = @selector(setMaxValue:);
  81. if ([*progressDelegate respondsToSelector:selector]) {
  82. double max = 1.0;
  83. [ASIHTTPRequest performSelector:selector onTarget:progressDelegate withObject:nil amount:&max callerToRetain:nil];
  84. }
  85. selector = @selector(setDoubleValue:);
  86. if ([*progressDelegate respondsToSelector:selector]) {
  87. double value = 0.0;
  88. [ASIHTTPRequest performSelector:selector onTarget:progressDelegate withObject:nil amount:&value callerToRetain:nil];
  89. }
  90. #else
  91. SEL selector = @selector(setProgress:);
  92. if ([*progressDelegate respondsToSelector:selector]) {
  93. float value = 0.0f;
  94. [ASIHTTPRequest performSelector:selector onTarget:progressDelegate withObject:nil amount:&value callerToRetain:nil];
  95. }
  96. #endif
  97. }
  98. - (void)addHEADOperation:(NSOperation *)operation
  99. {
  100. if ([operation isKindOfClass:[ASIHTTPRequest class]]) {
  101. ASIHTTPRequest *request = (ASIHTTPRequest *)operation;
  102. [request setRequestMethod:@"HEAD"];
  103. [request setQueuePriority:10];
  104. [request setShowAccurateProgress:YES];
  105. [request setQueue:self];
  106. // Important - we are calling NSOperation's add method - we don't want to add this as a normal request!
  107. [super addOperation:request];
  108. }
  109. }
  110. // Only add ASIHTTPRequests to this queue!!
  111. - (void)addOperation:(NSOperation *)operation
  112. {
  113. if (![operation isKindOfClass:[ASIHTTPRequest class]]) {
  114. [NSException raise:@"AttemptToAddInvalidRequest" format:@"Attempted to add an object that was not an ASIHTTPRequest to an ASINetworkQueue"];
  115. }
  116. [self setRequestsCount:[self requestsCount]+1];
  117. ASIHTTPRequest *request = (ASIHTTPRequest *)operation;
  118. if ([self showAccurateProgress]) {
  119. // Force the request to build its body (this may change requestMethod)
  120. [request buildPostBody];
  121. // If this is a GET request and we want accurate progress, perform a HEAD request first to get the content-length
  122. // We'll only do this before the queue is started
  123. // If requests are added after the queue is started they will probably move the overall progress backwards anyway, so there's no value performing the HEAD requests first
  124. // Instead, they'll update the total progress if and when they receive a content-length header
  125. if ([[request requestMethod] isEqualToString:@"GET"]) {
  126. if ([self isSuspended]) {
  127. ASIHTTPRequest *HEADRequest = [request HEADRequest];
  128. [self addHEADOperation:HEADRequest];
  129. [request addDependency:HEADRequest];
  130. if ([request shouldResetDownloadProgress]) {
  131. [self resetProgressDelegate:&downloadProgressDelegate];
  132. [request setShouldResetDownloadProgress:NO];
  133. }
  134. }
  135. }
  136. [request buildPostBody];
  137. [self request:nil incrementUploadSizeBy:(long long)[request postLength]];
  138. } else {
  139. [self request:nil incrementDownloadSizeBy:1];
  140. [self request:nil incrementUploadSizeBy:1];
  141. }
  142. // Tell the request not to increment the upload size when it starts, as we've already added its length
  143. if ([request shouldResetUploadProgress]) {
  144. [self resetProgressDelegate:&uploadProgressDelegate];
  145. [request setShouldResetUploadProgress:NO];
  146. }
  147. [request setShowAccurateProgress:[self showAccurateProgress]];
  148. [request setQueue:self];
  149. [super addOperation:request];
  150. }
  151. - (void)requestStarted:(ASIHTTPRequest *)request
  152. {
  153. if ([self requestDidStartSelector]) {
  154. [[self delegate] performSelector:[self requestDidStartSelector] withObject:request];
  155. }
  156. }
  157. - (void)request:(ASIHTTPRequest *)request didReceiveResponseHeaders:(NSDictionary *)responseHeaders
  158. {
  159. if ([self requestDidReceiveResponseHeadersSelector]) {
  160. [[self delegate] performSelector:[self requestDidReceiveResponseHeadersSelector] withObject:request withObject:responseHeaders];
  161. }
  162. }
  163. - (void)request:(ASIHTTPRequest *)request willRedirectToURL:(NSURL *)newURL
  164. {
  165. if ([self requestWillRedirectSelector]) {
  166. [[self delegate] performSelector:[self requestWillRedirectSelector] withObject:request withObject:newURL];
  167. }
  168. }
  169. - (void)requestFinished:(ASIHTTPRequest *)request
  170. {
  171. [self setRequestsCount:[self requestsCount]-1];
  172. if ([self requestDidFinishSelector]) {
  173. [[self delegate] performSelector:[self requestDidFinishSelector] withObject:request];
  174. }
  175. if ([self requestsCount] == 0) {
  176. if ([self queueDidFinishSelector]) {
  177. [[self delegate] performSelector:[self queueDidFinishSelector] withObject:self];
  178. }
  179. }
  180. }
  181. - (void)requestFailed:(ASIHTTPRequest *)request
  182. {
  183. [self setRequestsCount:[self requestsCount]-1];
  184. if ([self requestDidFailSelector]) {
  185. [[self delegate] performSelector:[self requestDidFailSelector] withObject:request];
  186. }
  187. if ([self requestsCount] == 0) {
  188. if ([self queueDidFinishSelector]) {
  189. [[self delegate] performSelector:[self queueDidFinishSelector] withObject:self];
  190. }
  191. }
  192. if ([self shouldCancelAllRequestsOnFailure] && [self requestsCount] > 0) {
  193. [self cancelAllOperations];
  194. }
  195. }
  196. - (void)request:(ASIHTTPRequest *)request didReceiveBytes:(long long)bytes
  197. {
  198. [self setBytesDownloadedSoFar:[self bytesDownloadedSoFar]+(unsigned long long)bytes];
  199. if ([self downloadProgressDelegate]) {
  200. [ASIHTTPRequest updateProgressIndicator:&downloadProgressDelegate withProgress:[self bytesDownloadedSoFar] ofTotal:[self totalBytesToDownload]];
  201. }
  202. }
  203. - (void)request:(ASIHTTPRequest *)request didSendBytes:(long long)bytes
  204. {
  205. [self setBytesUploadedSoFar:[self bytesUploadedSoFar]+(unsigned long long)bytes];
  206. if ([self uploadProgressDelegate]) {
  207. [ASIHTTPRequest updateProgressIndicator:&uploadProgressDelegate withProgress:[self bytesUploadedSoFar] ofTotal:[self totalBytesToUpload]];
  208. }
  209. }
  210. - (void)request:(ASIHTTPRequest *)request incrementDownloadSizeBy:(long long)newLength
  211. {
  212. [self setTotalBytesToDownload:[self totalBytesToDownload]+(unsigned long long)newLength];
  213. }
  214. - (void)request:(ASIHTTPRequest *)request incrementUploadSizeBy:(long long)newLength
  215. {
  216. [self setTotalBytesToUpload:[self totalBytesToUpload]+(unsigned long long)newLength];
  217. }
  218. // Since this queue takes over as the delegate for all requests it contains, it should forward authorisation requests to its own delegate
  219. - (void)authenticationNeededForRequest:(ASIHTTPRequest *)request
  220. {
  221. if ([[self delegate] respondsToSelector:@selector(authenticationNeededForRequest:)]) {
  222. [[self delegate] performSelector:@selector(authenticationNeededForRequest:) withObject:request];
  223. }
  224. }
  225. - (void)proxyAuthenticationNeededForRequest:(ASIHTTPRequest *)request
  226. {
  227. if ([[self delegate] respondsToSelector:@selector(proxyAuthenticationNeededForRequest:)]) {
  228. [[self delegate] performSelector:@selector(proxyAuthenticationNeededForRequest:) withObject:request];
  229. }
  230. }
  231. - (BOOL)respondsToSelector:(SEL)selector
  232. {
  233. // We handle certain methods differently because whether our delegate implements them or not can affect how the request should behave
  234. // If the delegate implements this, the request will stop to wait for credentials
  235. if (selector == @selector(authenticationNeededForRequest:)) {
  236. if ([[self delegate] respondsToSelector:@selector(authenticationNeededForRequest:)]) {
  237. return YES;
  238. }
  239. return NO;
  240. // If the delegate implements this, the request will to wait for credentials
  241. } else if (selector == @selector(proxyAuthenticationNeededForRequest:)) {
  242. if ([[self delegate] respondsToSelector:@selector(proxyAuthenticationNeededForRequest:)]) {
  243. return YES;
  244. }
  245. return NO;
  246. // If the delegate implements requestWillRedirectSelector, the request will stop to allow the delegate to change the url
  247. } else if (selector == @selector(request:willRedirectToURL:)) {
  248. if ([self requestWillRedirectSelector] && [[self delegate] respondsToSelector:[self requestWillRedirectSelector]]) {
  249. return YES;
  250. }
  251. return NO;
  252. }
  253. return [super respondsToSelector:selector];
  254. }
  255. #pragma mark NSCopying
  256. - (id)copyWithZone:(NSZone *)zone
  257. {
  258. ASINetworkQueue *newQueue = [[[self class] alloc] init];
  259. [newQueue setDelegate:[self delegate]];
  260. [newQueue setRequestDidStartSelector:[self requestDidStartSelector]];
  261. [newQueue setRequestWillRedirectSelector:[self requestWillRedirectSelector]];
  262. [newQueue setRequestDidReceiveResponseHeadersSelector:[self requestDidReceiveResponseHeadersSelector]];
  263. [newQueue setRequestDidFinishSelector:[self requestDidFinishSelector]];
  264. [newQueue setRequestDidFailSelector:[self requestDidFailSelector]];
  265. [newQueue setQueueDidFinishSelector:[self queueDidFinishSelector]];
  266. [newQueue setUploadProgressDelegate:[self uploadProgressDelegate]];
  267. [newQueue setDownloadProgressDelegate:[self downloadProgressDelegate]];
  268. [newQueue setShouldCancelAllRequestsOnFailure:[self shouldCancelAllRequestsOnFailure]];
  269. [newQueue setShowAccurateProgress:[self showAccurateProgress]];
  270. [newQueue setUserInfo:[[[self userInfo] copyWithZone:zone] autorelease]];
  271. return newQueue;
  272. }
  273. @synthesize requestsCount;
  274. @synthesize bytesUploadedSoFar;
  275. @synthesize totalBytesToUpload;
  276. @synthesize bytesDownloadedSoFar;
  277. @synthesize totalBytesToDownload;
  278. @synthesize shouldCancelAllRequestsOnFailure;
  279. @synthesize uploadProgressDelegate;
  280. @synthesize downloadProgressDelegate;
  281. @synthesize requestDidStartSelector;
  282. @synthesize requestDidReceiveResponseHeadersSelector;
  283. @synthesize requestWillRedirectSelector;
  284. @synthesize requestDidFinishSelector;
  285. @synthesize requestDidFailSelector;
  286. @synthesize queueDidFinishSelector;
  287. @synthesize delegate;
  288. @synthesize showAccurateProgress;
  289. @synthesize userInfo;
  290. @end