3 // Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest
5 // Created by Ben Copsey on 07/11/2008.
6 // Copyright 2008-2009 All-Seeing Interactive. All rights reserved.
9 #import "ASINetworkQueue.h"
10 #import "ASIHTTPRequest.h"
13 @interface ASINetworkQueue ()
14 - (void)resetProgressDelegate:(id *)progressDelegate;
15 @property (assign) int requestsCount;
18 @implementation ASINetworkQueue
23 [self setShouldCancelAllRequestsOnFailure:YES];
24 [self setMaxConcurrentOperationCount:4];
25 [self setSuspended:YES];
32 return [[[self alloc] init] autorelease];
37 //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
38 for (ASIHTTPRequest *request in [self operations]) {
39 [request setQueue:nil];
45 - (void)setSuspended:(BOOL)suspend
47 [super setSuspended:suspend];
52 [self cancelAllOperations];
53 [self setDelegate:nil];
54 [self setDownloadProgressDelegate:nil];
55 [self setUploadProgressDelegate:nil];
56 [self setRequestDidStartSelector:NULL];
57 [self setRequestDidReceiveResponseHeadersSelector:NULL];
58 [self setRequestDidFailSelector:NULL];
59 [self setRequestDidFinishSelector:NULL];
60 [self setQueueDidFinishSelector:NULL];
61 [self setSuspended:YES];
67 [self setSuspended:NO];
70 - (void)cancelAllOperations
72 [self setBytesUploadedSoFar:0];
73 [self setTotalBytesToUpload:0];
74 [self setBytesDownloadedSoFar:0];
75 [self setTotalBytesToDownload:0];
76 [super cancelAllOperations];
79 - (void)setUploadProgressDelegate:(id)newDelegate
81 uploadProgressDelegate = newDelegate;
82 [self resetProgressDelegate:&uploadProgressDelegate];
86 - (void)setDownloadProgressDelegate:(id)newDelegate
88 downloadProgressDelegate = newDelegate;
89 [self resetProgressDelegate:&downloadProgressDelegate];
92 - (void)resetProgressDelegate:(id *)progressDelegate
95 // If the uploadProgressDelegate is an NSProgressIndicator, we set its MaxValue to 1.0 so we can treat it similarly to UIProgressViews
96 SEL selector = @selector(setMaxValue:);
97 if ([*progressDelegate respondsToSelector:selector]) {
99 [ASIHTTPRequest performSelector:selector onTarget:progressDelegate withObject:nil amount:&max callerToRetain:nil];
101 selector = @selector(setDoubleValue:);
102 if ([*progressDelegate respondsToSelector:selector]) {
104 [ASIHTTPRequest performSelector:selector onTarget:progressDelegate withObject:nil amount:&value callerToRetain:nil];
107 SEL selector = @selector(setProgress:);
108 if ([*progressDelegate respondsToSelector:selector]) {
110 [ASIHTTPRequest performSelector:selector onTarget:progressDelegate withObject:nil amount:&value callerToRetain:nil];
115 - (void)addHEADOperation:(NSOperation *)operation
117 if ([operation isKindOfClass:[ASIHTTPRequest class]]) {
119 ASIHTTPRequest *request = (ASIHTTPRequest *)operation;
120 [request setRequestMethod:@"HEAD"];
121 [request setQueuePriority:10];
122 [request setShowAccurateProgress:YES];
123 [request setQueue:self];
125 // Important - we are calling NSOperation's add method - we don't want to add this as a normal request!
126 [super addOperation:request];
130 // Only add ASIHTTPRequests to this queue!!
131 - (void)addOperation:(NSOperation *)operation
133 if (![operation isKindOfClass:[ASIHTTPRequest class]]) {
134 [NSException raise:@"AttemptToAddInvalidRequest" format:@"Attempted to add an object that was not an ASIHTTPRequest to an ASINetworkQueue"];
137 [self setRequestsCount:[self requestsCount]+1];
139 ASIHTTPRequest *request = (ASIHTTPRequest *)operation;
141 if ([self showAccurateProgress]) {
143 // Force the request to build its body (this may change requestMethod)
144 [request buildPostBody];
146 // If this is a GET request and we want accurate progress, perform a HEAD request first to get the content-length
147 // We'll only do this before the queue is started
148 // 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
149 // Instead, they'll update the total progress if and when they receive a content-length header
150 if ([[request requestMethod] isEqualToString:@"GET"]) {
151 if ([self isSuspended]) {
152 ASIHTTPRequest *HEADRequest = [request HEADRequest];
153 [self addHEADOperation:HEADRequest];
154 [request addDependency:HEADRequest];
155 if ([request shouldResetDownloadProgress]) {
156 [self resetProgressDelegate:&downloadProgressDelegate];
157 [request setShouldResetDownloadProgress:NO];
161 [request buildPostBody];
162 [self request:nil incrementUploadSizeBy:[request postLength]];
166 [self request:nil incrementDownloadSizeBy:1];
167 [self request:nil incrementUploadSizeBy:1];
169 // Tell the request not to increment the upload size when it starts, as we've already added its length
170 if ([request shouldResetUploadProgress]) {
171 [self resetProgressDelegate:&uploadProgressDelegate];
172 [request setShouldResetUploadProgress:NO];
175 [request setShowAccurateProgress:[self showAccurateProgress]];
177 [request setQueue:self];
178 [super addOperation:request];
182 - (void)requestStarted:(ASIHTTPRequest *)request
184 if ([self requestDidStartSelector]) {
185 [[self delegate] performSelector:[self requestDidStartSelector] withObject:request];
189 - (void)request:(ASIHTTPRequest *)request didReceiveResponseHeaders:(NSDictionary *)responseHeaders
191 if ([self requestDidReceiveResponseHeadersSelector]) {
192 [[self delegate] performSelector:[self requestDidReceiveResponseHeadersSelector] withObject:request withObject:responseHeaders];
196 - (void)request:(ASIHTTPRequest *)request willRedirectToURL:(NSURL *)newURL
198 if ([self requestWillRedirectSelector]) {
199 [[self delegate] performSelector:[self requestWillRedirectSelector] withObject:request withObject:newURL];
203 - (void)requestFinished:(ASIHTTPRequest *)request
205 [self setRequestsCount:[self requestsCount]-1];
206 if ([self requestDidFinishSelector]) {
207 [[self delegate] performSelector:[self requestDidFinishSelector] withObject:request];
209 if ([self requestsCount] == 0) {
210 if ([self queueDidFinishSelector]) {
211 [[self delegate] performSelector:[self queueDidFinishSelector] withObject:self];
216 - (void)requestFailed:(ASIHTTPRequest *)request
218 [self setRequestsCount:[self requestsCount]-1];
219 if ([self requestDidFailSelector]) {
220 [[self delegate] performSelector:[self requestDidFailSelector] withObject:request];
222 if ([self requestsCount] == 0) {
223 if ([self queueDidFinishSelector]) {
224 [[self delegate] performSelector:[self queueDidFinishSelector] withObject:self];
227 if ([self shouldCancelAllRequestsOnFailure] && [self requestsCount] > 0) {
228 [self cancelAllOperations];
234 - (void)request:(ASIHTTPRequest *)request didReceiveBytes:(long long)bytes
236 [self setBytesDownloadedSoFar:[self bytesDownloadedSoFar]+bytes];
237 if ([self downloadProgressDelegate]) {
238 [ASIHTTPRequest updateProgressIndicator:&downloadProgressDelegate withProgress:[self bytesDownloadedSoFar] ofTotal:[self totalBytesToDownload]];
242 - (void)request:(ASIHTTPRequest *)request didSendBytes:(long long)bytes
244 [self setBytesUploadedSoFar:[self bytesUploadedSoFar]+bytes];
245 if ([self uploadProgressDelegate]) {
246 [ASIHTTPRequest updateProgressIndicator:&uploadProgressDelegate withProgress:[self bytesUploadedSoFar] ofTotal:[self totalBytesToUpload]];
250 - (void)request:(ASIHTTPRequest *)request incrementDownloadSizeBy:(long long)newLength
252 [self setTotalBytesToDownload:[self totalBytesToDownload]+newLength];
255 - (void)request:(ASIHTTPRequest *)request incrementUploadSizeBy:(long long)newLength
257 [self setTotalBytesToUpload:[self totalBytesToUpload]+newLength];
261 // Since this queue takes over as the delegate for all requests it contains, it should forward authorisation requests to its own delegate
262 - (void)authenticationNeededForRequest:(ASIHTTPRequest *)request
264 if ([[self delegate] respondsToSelector:@selector(authenticationNeededForRequest:)]) {
265 [[self delegate] performSelector:@selector(authenticationNeededForRequest:) withObject:request];
269 - (void)proxyAuthenticationNeededForRequest:(ASIHTTPRequest *)request
271 if ([[self delegate] respondsToSelector:@selector(proxyAuthenticationNeededForRequest:)]) {
272 [[self delegate] performSelector:@selector(proxyAuthenticationNeededForRequest:) withObject:request];
277 - (BOOL)respondsToSelector:(SEL)selector
279 // We handle certain methods differently because whether our delegate implements them or not can affect how the request should behave
281 // If the delegate implements this, the request will stop to wait for credentials
282 if (selector == @selector(authenticationNeededForRequest:)) {
283 if ([[self delegate] respondsToSelector:@selector(authenticationNeededForRequest:)]) {
288 // If the delegate implements this, the request will to wait for credentials
289 } else if (selector == @selector(proxyAuthenticationNeededForRequest:)) {
290 if ([[self delegate] respondsToSelector:@selector(proxyAuthenticationNeededForRequest:)]) {
295 // If the delegate implements requestWillRedirectSelector, the request will stop to allow the delegate to change the url
296 } else if (selector == @selector(request:willRedirectToURL:)) {
297 if ([self requestWillRedirectSelector] && [[self delegate] respondsToSelector:[self requestWillRedirectSelector]]) {
302 return [super respondsToSelector:selector];
305 #pragma mark NSCopying
307 - (id)copyWithZone:(NSZone *)zone
309 ASINetworkQueue *newQueue = [[[self class] alloc] init];
310 [newQueue setDelegate:[self delegate]];
311 [newQueue setRequestDidStartSelector:[self requestDidStartSelector]];
312 [newQueue setRequestWillRedirectSelector:[self requestWillRedirectSelector]];
313 [newQueue setRequestDidReceiveResponseHeadersSelector:[self requestDidReceiveResponseHeadersSelector]];
314 [newQueue setRequestDidFinishSelector:[self requestDidFinishSelector]];
315 [newQueue setRequestDidFailSelector:[self requestDidFailSelector]];
316 [newQueue setQueueDidFinishSelector:[self queueDidFinishSelector]];
317 [newQueue setUploadProgressDelegate:[self uploadProgressDelegate]];
318 [newQueue setDownloadProgressDelegate:[self downloadProgressDelegate]];
319 [newQueue setShouldCancelAllRequestsOnFailure:[self shouldCancelAllRequestsOnFailure]];
320 [newQueue setShowAccurateProgress:[self showAccurateProgress]];
321 [newQueue setUserInfo:[[[self userInfo] copyWithZone:zone] autorelease]];
326 @synthesize requestsCount;
327 @synthesize bytesUploadedSoFar;
328 @synthesize totalBytesToUpload;
329 @synthesize bytesDownloadedSoFar;
330 @synthesize totalBytesToDownload;
331 @synthesize shouldCancelAllRequestsOnFailure;
332 @synthesize uploadProgressDelegate;
333 @synthesize downloadProgressDelegate;
334 @synthesize requestDidStartSelector;
335 @synthesize requestDidReceiveResponseHeadersSelector;
336 @synthesize requestWillRedirectSelector;
337 @synthesize requestDidFinishSelector;
338 @synthesize requestDidFailSelector;
339 @synthesize queueDidFinishSelector;
340 @synthesize delegate;
341 @synthesize showAccurateProgress;
342 @synthesize userInfo;