This repository has been archived by the owner on Sep 24, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathASINetworkQueue.m
366 lines (310 loc) · 11.8 KB
/
ASINetworkQueue.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
//
// ASINetworkQueue.m
// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest
//
// Created by Ben Copsey on 07/11/2008.
// Copyright 2008-2009 All-Seeing Interactive. All rights reserved.
//
#import "ASINetworkQueue.h"
#import "ASIHTTPRequest.h"
// Private stuff
@interface ASINetworkQueue ()
@property (assign) int requestsCount;
@end
@implementation ASINetworkQueue
- (id)init
{
self = [super init];
[self setShouldCancelAllRequestsOnFailure:YES];
[self setMaxConcurrentOperationCount:4];
[self setSuspended:YES];
return self;
}
+ (id)queue
{
return [[[self alloc] init] autorelease];
}
- (void)dealloc
{
//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
for (ASIHTTPRequest *request in [self operations]) {
[request setQueue:nil];
}
[userInfo release];
[super dealloc];
}
- (BOOL)isNetworkActive
{
return ([self requestsCount] > 0 && ![self isSuspended]);
}
- (void)updateNetworkActivityIndicator
{
#if TARGET_OS_IPHONE
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:[self isNetworkActive]];
#endif
}
- (void)setSuspended:(BOOL)suspend
{
[super setSuspended:suspend];
[self updateNetworkActivityIndicator];
}
- (void)go
{
if (![self showAccurateProgress]) {
if ([self downloadProgressDelegate]) {
[self incrementDownloadSizeBy:[self requestsCount]];
}
if ([self uploadProgressDelegate]) {
[self incrementUploadSizeBy:[self requestsCount]];
}
}
[self setSuspended:NO];
}
- (void)cancelAllOperations
{
[self setRequestsCount:0];
[self setBytesUploadedSoFar:0];
[self setTotalBytesToUpload:0];
[self setBytesDownloadedSoFar:0];
[self setTotalBytesToDownload:0];
[super cancelAllOperations];
[self updateNetworkActivityIndicator];
}
- (void)setUploadProgressDelegate:(id)newDelegate
{
uploadProgressDelegate = newDelegate;
#if !TARGET_OS_IPHONE
// If the uploadProgressDelegate is an NSProgressIndicator, we set it's MaxValue to 1.0 so we can treat it similarly to UIProgressViews
SEL selector = @selector(setMaxValue:);
if ([[self uploadProgressDelegate] respondsToSelector:selector]) {
double max = 1.0;
NSMethodSignature *signature = [[[self uploadProgressDelegate] class] instanceMethodSignatureForSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setSelector:selector];
[invocation setArgument:&max atIndex:2];
[invocation invokeWithTarget:[self uploadProgressDelegate]];
}
#endif
}
- (void)setDownloadProgressDelegate:(id)newDelegate
{
downloadProgressDelegate = newDelegate;
#if !TARGET_OS_IPHONE
// If the downloadProgressDelegate is an NSProgressIndicator, we set it's MaxValue to 1.0 so we can treat it similarly to UIProgressViews
SEL selector = @selector(setMaxValue:);
if ([[self downloadProgressDelegate] respondsToSelector:selector]) {
double max = 1.0;
NSMethodSignature *signature = [[[self downloadProgressDelegate] class] instanceMethodSignatureForSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setSelector:@selector(setMaxValue:)];
[invocation setArgument:&max atIndex:2];
[invocation invokeWithTarget:[self downloadProgressDelegate]];
}
#endif
}
- (void)addHEADOperation:(NSOperation *)operation
{
if ([operation isKindOfClass:[ASIHTTPRequest class]]) {
ASIHTTPRequest *request = (ASIHTTPRequest *)operation;
[request setRequestMethod:@"HEAD"];
[request setQueuePriority:10];
[request setShowAccurateProgress:YES];
[request setQueue:self];
// Important - we are calling NSOperation's add method - we don't want to add this as a normal request!
[super addOperation:request];
}
}
// Only add ASIHTTPRequests to this queue!!
- (void)addOperation:(NSOperation *)operation
{
if (![operation isKindOfClass:[ASIHTTPRequest class]]) {
[NSException raise:@"AttemptToAddInvalidRequest" format:@"Attempted to add an object that was not an ASIHTTPRequest to an ASINetworkQueue"];
}
[self setRequestsCount:[self requestsCount]+1];
ASIHTTPRequest *request = (ASIHTTPRequest *)operation;
if ([self showAccurateProgress]) {
// If this is a GET request and we want accurate progress, perform a HEAD request first to get the content-length
// We'll only do this before the queue is started
// 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
// Instead, they'll update the total progress if and when they recieve a content-length header
if ([[request requestMethod] isEqualToString:@"GET"] && [self isSuspended]) {
ASIHTTPRequest *HEADRequest = [request HEADRequest];
[self addHEADOperation:HEADRequest];
//Tell the request not to reset the progress indicator when it gets a content-length, as we will get the length from the HEAD request
[request setShouldResetProgressIndicators:NO];
[request addDependency:HEADRequest];
// If we want to track uploading for this request accurately, we need to add the size of the post content to the total
} else if (uploadProgressDelegate) {
[request buildPostBody];
[self setTotalBytesToUpload:[self totalBytesToUpload]+[request postLength]];
}
}
[request setShowAccurateProgress:[self showAccurateProgress]];
[request setQueue:self];
[super addOperation:request];
[self updateNetworkActivityIndicator];
}
- (void)requestDidStart:(ASIHTTPRequest *)request
{
if ([self requestDidStartSelector]) {
[[self delegate] performSelector:[self requestDidStartSelector] withObject:request];
}
}
- (void)requestDidFail:(ASIHTTPRequest *)request
{
[self setRequestsCount:[self requestsCount]-1];
[self updateNetworkActivityIndicator];
if ([self requestDidFailSelector]) {
[[self delegate] performSelector:[self requestDidFailSelector] withObject:request];
}
if ([self shouldCancelAllRequestsOnFailure] && [self requestsCount] > 0) {
[self cancelAllOperations];
}
if ([self requestsCount] == 0) {
if ([self queueDidFinishSelector]) {
[[self delegate] performSelector:[self queueDidFinishSelector] withObject:self];
}
}
}
- (void)requestDidFinish:(ASIHTTPRequest *)request
{
[self setRequestsCount:[self requestsCount]-1];
[self updateNetworkActivityIndicator];
if ([self requestDidFinishSelector]) {
[[self delegate] performSelector:[self requestDidFinishSelector] withObject:request];
}
if ([self requestsCount] == 0) {
if ([self queueDidFinishSelector]) {
[[self delegate] performSelector:[self queueDidFinishSelector] withObject:self];
}
}
}
- (void)setUploadBufferSize:(unsigned long long)bytes
{
if (![self uploadProgressDelegate]) {
return;
}
[self setTotalBytesToUpload:[self totalBytesToUpload] - bytes];
[self incrementUploadProgressBy:0];
}
- (void)incrementUploadSizeBy:(unsigned long long)bytes
{
if (![self uploadProgressDelegate]) {
return;
}
[self setTotalBytesToUpload:[self totalBytesToUpload] + bytes];
[self incrementUploadProgressBy:0];
}
- (void)decrementUploadProgressBy:(unsigned long long)bytes
{
if (![self uploadProgressDelegate] || [self totalBytesToUpload] == 0) {
return;
}
[self setBytesUploadedSoFar:[self bytesUploadedSoFar] - bytes];
double progress;
//Workaround for an issue with converting a long to a double on iPhone OS 2.2.1 with a base SDK >= 3.0
if ([ASIHTTPRequest isiPhoneOS2]) {
progress = [[NSNumber numberWithUnsignedLongLong:[self bytesUploadedSoFar]] doubleValue]/[[NSNumber numberWithUnsignedLongLong:[self totalBytesToUpload]] doubleValue];
} else {
progress = ([self bytesUploadedSoFar]*1.0)/([self totalBytesToUpload]*1.0);
}
[ASIHTTPRequest setProgress:progress forProgressIndicator:[self uploadProgressDelegate]];
}
- (void)incrementUploadProgressBy:(unsigned long long)bytes
{
if (![self uploadProgressDelegate] || [self totalBytesToUpload] == 0) {
return;
}
[self setBytesUploadedSoFar:[self bytesUploadedSoFar] + bytes];
double progress;
//Workaround for an issue with converting a long to a double on iPhone OS 2.2.1 with a base SDK >= 3.0
if ([ASIHTTPRequest isiPhoneOS2]) {
progress = [[NSNumber numberWithUnsignedLongLong:[self bytesUploadedSoFar]] doubleValue]/[[NSNumber numberWithUnsignedLongLong:[self totalBytesToUpload]] doubleValue];
} else {
progress = ([self bytesUploadedSoFar]*1.0)/([self totalBytesToUpload]*1.0);
}
[ASIHTTPRequest setProgress:progress forProgressIndicator:[self uploadProgressDelegate]];
}
- (void)incrementDownloadSizeBy:(unsigned long long)bytes
{
if (![self downloadProgressDelegate]) {
return;
}
[self setTotalBytesToDownload:[self totalBytesToDownload] + bytes];
[self incrementDownloadProgressBy:0];
}
- (void)incrementDownloadProgressBy:(unsigned long long)bytes
{
if (![self downloadProgressDelegate] || [self totalBytesToDownload] == 0) {
return;
}
[self setBytesDownloadedSoFar:[self bytesDownloadedSoFar] + bytes];
double progress;
//Workaround for an issue with converting a long to a double on iPhone OS 2.2.1 with a base SDK >= 3.0
if ([ASIHTTPRequest isiPhoneOS2]) {
progress = [[NSNumber numberWithUnsignedLongLong:[self bytesDownloadedSoFar]] doubleValue]/[[NSNumber numberWithUnsignedLongLong:[self totalBytesToDownload]] doubleValue];
} else {
progress = ([self bytesDownloadedSoFar]*1.0)/([self totalBytesToDownload]*1.0);
}
[ASIHTTPRequest setProgress:progress forProgressIndicator:[self downloadProgressDelegate]];
}
// Since this queue takes over as the delegate for all requests it contains, it should forward authorisation requests to its own delegate
- (void)authenticationNeededForRequest:(ASIHTTPRequest *)request
{
if ([[self delegate] respondsToSelector:@selector(authenticationNeededForRequest:)]) {
[[self delegate] performSelector:@selector(authenticationNeededForRequest:) withObject:request];
}
}
- (void)proxyAuthenticationNeededForRequest:(ASIHTTPRequest *)request
{
if ([[self delegate] respondsToSelector:@selector(proxyAuthenticationNeededForRequest:)]) {
[[self delegate] performSelector:@selector(proxyAuthenticationNeededForRequest:) withObject:request];
}
}
- (BOOL)respondsToSelector:(SEL)selector
{
if (selector == @selector(authenticationNeededForRequest:)) {
if ([[self delegate] respondsToSelector:@selector(authenticationNeededForRequest:)]) {
return YES;
}
return NO;
} else if (selector == @selector(proxyAuthenticationNeededForRequest:)) {
if ([[self delegate] respondsToSelector:@selector(proxyAuthenticationNeededForRequest:)]) {
return YES;
}
return NO;
}
return [super respondsToSelector:selector];
}
#pragma mark NSCopying
- (id)copyWithZone:(NSZone *)zone
{
ASINetworkQueue *newQueue = [[[self class] alloc] init];
[newQueue setDelegate:[self delegate]];
[newQueue setRequestDidStartSelector:[self requestDidStartSelector]];
[newQueue setRequestDidFinishSelector:[self requestDidFinishSelector]];
[newQueue setRequestDidFailSelector:[self requestDidFailSelector]];
[newQueue setQueueDidFinishSelector:[self queueDidFinishSelector]];
[newQueue setUploadProgressDelegate:[self uploadProgressDelegate]];
[newQueue setDownloadProgressDelegate:[self downloadProgressDelegate]];
[newQueue setShouldCancelAllRequestsOnFailure:[self shouldCancelAllRequestsOnFailure]];
[newQueue setShowAccurateProgress:[self showAccurateProgress]];
[newQueue setUserInfo:[[[self userInfo] copyWithZone:zone] autorelease]];
return newQueue;
}
@synthesize requestsCount;
@synthesize bytesUploadedSoFar;
@synthesize totalBytesToUpload;
@synthesize bytesDownloadedSoFar;
@synthesize totalBytesToDownload;
@synthesize shouldCancelAllRequestsOnFailure;
@synthesize uploadProgressDelegate;
@synthesize downloadProgressDelegate;
@synthesize requestDidStartSelector;
@synthesize requestDidFinishSelector;
@synthesize requestDidFailSelector;
@synthesize queueDidFinishSelector;
@synthesize delegate;
@synthesize showAccurateProgress;
@synthesize userInfo;
@end