Skip to content
Newer
Older
100644 344 lines (290 sloc) 11.1 KB
76d2a5b @mstroeck
mstroeck authored Aug 28, 2011
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
9 #import "ASINetworkQueue.h"
10 #import "ASIHTTPRequest.h"
11
12 // Private stuff
13 @interface ASINetworkQueue ()
14 - (void)resetProgressDelegate:(id *)progressDelegate;
15 @property (assign) int requestsCount;
16 @end
17
18 @implementation ASINetworkQueue
19
20 - (id)init
21 {
22 self = [super init];
23 [self setShouldCancelAllRequestsOnFailure:YES];
24 [self setMaxConcurrentOperationCount:4];
25 [self setSuspended:YES];
26
27 return self;
28 }
29
30 + (id)queue
31 {
32 return [[[self alloc] init] autorelease];
33 }
34
35 - (void)dealloc
36 {
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];
40 }
41 [userInfo release];
42 [super dealloc];
43 }
44
45 - (void)setSuspended:(BOOL)suspend
46 {
47 [super setSuspended:suspend];
48 }
49
50 - (void)reset
51 {
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];
62 }
63
64
65 - (void)go
66 {
67 [self setSuspended:NO];
68 }
69
70 - (void)cancelAllOperations
71 {
72 [self setBytesUploadedSoFar:0];
73 [self setTotalBytesToUpload:0];
74 [self setBytesDownloadedSoFar:0];
75 [self setTotalBytesToDownload:0];
76 [super cancelAllOperations];
77 }
78
79 - (void)setUploadProgressDelegate:(id)newDelegate
80 {
81 uploadProgressDelegate = newDelegate;
82 [self resetProgressDelegate:&uploadProgressDelegate];
83
84 }
85
86 - (void)setDownloadProgressDelegate:(id)newDelegate
87 {
88 downloadProgressDelegate = newDelegate;
89 [self resetProgressDelegate:&downloadProgressDelegate];
90 }
91
92 - (void)resetProgressDelegate:(id *)progressDelegate
93 {
94 #if !TARGET_OS_IPHONE
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]) {
98 double max = 1.0;
99 [ASIHTTPRequest performSelector:selector onTarget:progressDelegate withObject:nil amount:&max callerToRetain:nil];
100 }
101 selector = @selector(setDoubleValue:);
102 if ([*progressDelegate respondsToSelector:selector]) {
103 double value = 0.0;
104 [ASIHTTPRequest performSelector:selector onTarget:progressDelegate withObject:nil amount:&value callerToRetain:nil];
105 }
106 #else
107 SEL selector = @selector(setProgress:);
108 if ([*progressDelegate respondsToSelector:selector]) {
109 float value = 0.0f;
110 [ASIHTTPRequest performSelector:selector onTarget:progressDelegate withObject:nil amount:&value callerToRetain:nil];
111 }
112 #endif
113 }
114
115 - (void)addHEADOperation:(NSOperation *)operation
116 {
117 if ([operation isKindOfClass:[ASIHTTPRequest class]]) {
118
119 ASIHTTPRequest *request = (ASIHTTPRequest *)operation;
120 [request setRequestMethod:@"HEAD"];
121 [request setQueuePriority:10];
122 [request setShowAccurateProgress:YES];
123 [request setQueue:self];
124
125 // Important - we are calling NSOperation's add method - we don't want to add this as a normal request!
126 [super addOperation:request];
127 }
128 }
129
130 // Only add ASIHTTPRequests to this queue!!
131 - (void)addOperation:(NSOperation *)operation
132 {
133 if (![operation isKindOfClass:[ASIHTTPRequest class]]) {
134 [NSException raise:@"AttemptToAddInvalidRequest" format:@"Attempted to add an object that was not an ASIHTTPRequest to an ASINetworkQueue"];
135 }
136
137 [self setRequestsCount:[self requestsCount]+1];
138
139 ASIHTTPRequest *request = (ASIHTTPRequest *)operation;
140
141 if ([self showAccurateProgress]) {
142
143 // Force the request to build its body (this may change requestMethod)
144 [request buildPostBody];
145
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];
158 }
159 }
160 }
161 [request buildPostBody];
162 [self request:nil incrementUploadSizeBy:[request postLength]];
163
164
165 } else {
166 [self request:nil incrementDownloadSizeBy:1];
167 [self request:nil incrementUploadSizeBy:1];
168 }
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];
173 }
174
175 [request setShowAccurateProgress:[self showAccurateProgress]];
176
177 [request setQueue:self];
178 [super addOperation:request];
179
180 }
181
182 - (void)requestStarted:(ASIHTTPRequest *)request
183 {
184 if ([self requestDidStartSelector]) {
185 [[self delegate] performSelector:[self requestDidStartSelector] withObject:request];
186 }
187 }
188
189 - (void)request:(ASIHTTPRequest *)request didReceiveResponseHeaders:(NSDictionary *)responseHeaders
190 {
191 if ([self requestDidReceiveResponseHeadersSelector]) {
192 [[self delegate] performSelector:[self requestDidReceiveResponseHeadersSelector] withObject:request withObject:responseHeaders];
193 }
194 }
195
196 - (void)request:(ASIHTTPRequest *)request willRedirectToURL:(NSURL *)newURL
197 {
198 if ([self requestWillRedirectSelector]) {
199 [[self delegate] performSelector:[self requestWillRedirectSelector] withObject:request withObject:newURL];
200 }
201 }
202
203 - (void)requestFinished:(ASIHTTPRequest *)request
204 {
205 [self setRequestsCount:[self requestsCount]-1];
206 if ([self requestDidFinishSelector]) {
207 [[self delegate] performSelector:[self requestDidFinishSelector] withObject:request];
208 }
209 if ([self requestsCount] == 0) {
210 if ([self queueDidFinishSelector]) {
211 [[self delegate] performSelector:[self queueDidFinishSelector] withObject:self];
212 }
213 }
214 }
215
216 - (void)requestFailed:(ASIHTTPRequest *)request
217 {
218 [self setRequestsCount:[self requestsCount]-1];
219 if ([self requestDidFailSelector]) {
220 [[self delegate] performSelector:[self requestDidFailSelector] withObject:request];
221 }
222 if ([self requestsCount] == 0) {
223 if ([self queueDidFinishSelector]) {
224 [[self delegate] performSelector:[self queueDidFinishSelector] withObject:self];
225 }
226 }
227 if ([self shouldCancelAllRequestsOnFailure] && [self requestsCount] > 0) {
228 [self cancelAllOperations];
229 }
230
231 }
232
233
234 - (void)request:(ASIHTTPRequest *)request didReceiveBytes:(long long)bytes
235 {
236 [self setBytesDownloadedSoFar:[self bytesDownloadedSoFar]+bytes];
237 if ([self downloadProgressDelegate]) {
238 [ASIHTTPRequest updateProgressIndicator:&downloadProgressDelegate withProgress:[self bytesDownloadedSoFar] ofTotal:[self totalBytesToDownload]];
239 }
240 }
241
242 - (void)request:(ASIHTTPRequest *)request didSendBytes:(long long)bytes
243 {
244 [self setBytesUploadedSoFar:[self bytesUploadedSoFar]+bytes];
245 if ([self uploadProgressDelegate]) {
246 [ASIHTTPRequest updateProgressIndicator:&uploadProgressDelegate withProgress:[self bytesUploadedSoFar] ofTotal:[self totalBytesToUpload]];
247 }
248 }
249
250 - (void)request:(ASIHTTPRequest *)request incrementDownloadSizeBy:(long long)newLength
251 {
252 [self setTotalBytesToDownload:[self totalBytesToDownload]+newLength];
253 }
254
255 - (void)request:(ASIHTTPRequest *)request incrementUploadSizeBy:(long long)newLength
256 {
257 [self setTotalBytesToUpload:[self totalBytesToUpload]+newLength];
258 }
259
260
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
263 {
264 if ([[self delegate] respondsToSelector:@selector(authenticationNeededForRequest:)]) {
265 [[self delegate] performSelector:@selector(authenticationNeededForRequest:) withObject:request];
266 }
267 }
268
269 - (void)proxyAuthenticationNeededForRequest:(ASIHTTPRequest *)request
270 {
271 if ([[self delegate] respondsToSelector:@selector(proxyAuthenticationNeededForRequest:)]) {
272 [[self delegate] performSelector:@selector(proxyAuthenticationNeededForRequest:) withObject:request];
273 }
274 }
275
276
277 - (BOOL)respondsToSelector:(SEL)selector
278 {
279 // We handle certain methods differently because whether our delegate implements them or not can affect how the request should behave
280
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:)]) {
284 return YES;
285 }
286 return NO;
287
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:)]) {
291 return YES;
292 }
293 return NO;
294
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]]) {
298 return YES;
299 }
300 return NO;
301 }
302 return [super respondsToSelector:selector];
303 }
304
305 #pragma mark NSCopying
306
307 - (id)copyWithZone:(NSZone *)zone
308 {
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]];
322 return newQueue;
323 }
324
325
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;
343 @end
Something went wrong with that request. Please try again.