Statistics
| Branch: | Revision:

root / asi-http-request-with-pithos / Classes / ASINetworkQueue.m @ be116d22

History | View | Annotate | Download (11.1 kB)

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