Ben Copsey

Refactor to fix lots of bugs, especially locking issues

@@ -50,6 +50,11 @@ @@ -50,6 +50,11 @@
50 50
51 - (void)buildPostBody 51 - (void)buildPostBody
52 { 52 {
  53 + if (!postData && ! fileData) {
  54 + [super buildPostBody];
  55 + return;
  56 + }
  57 +
53 NSMutableData *body = [[[NSMutableData alloc] init] autorelease]; 58 NSMutableData *body = [[[NSMutableData alloc] init] autorelease];
54 59
55 // Set your own boundary string only if really obsessive. We don't bother to check if post data contains the boundary, since it's pretty unlikely that it does. 60 // Set your own boundary string only if really obsessive. We don't bother to check if post data contains the boundary, since it's pretty unlikely that it does.
@@ -10,6 +10,7 @@ @@ -10,6 +10,7 @@
10 // Portions are based on the ImageClient example from Apple: 10 // Portions are based on the ImageClient example from Apple:
11 // See: http://developer.apple.com/samplecode/ImageClient/listing37.html 11 // See: http://developer.apple.com/samplecode/ImageClient/listing37.html
12 12
  13 +#import <Cocoa/Cocoa.h>
13 14
14 @interface ASIHTTPRequest : NSOperation { 15 @interface ASIHTTPRequest : NSOperation {
15 16
@@ -93,18 +94,18 @@ @@ -93,18 +94,18 @@
93 int responseStatusCode; 94 int responseStatusCode;
94 95
95 //Size of the response 96 //Size of the response
96 - unsigned int contentLength; 97 + unsigned long long contentLength;
97 98
98 //Size of the POST payload 99 //Size of the POST payload
99 - unsigned int postLength; 100 + unsigned long long postLength;
100 101
101 //The total amount of downloaded data 102 //The total amount of downloaded data
102 - unsigned int totalBytesRead; 103 + unsigned long long totalBytesRead;
103 104
104 //Last amount of data read (used for incrementing progress) 105 //Last amount of data read (used for incrementing progress)
105 - unsigned int lastBytesRead; 106 + unsigned long long lastBytesRead;
106 //Last amount of data sent (used for incrementing progress) 107 //Last amount of data sent (used for incrementing progress)
107 - unsigned int lastBytesSent; 108 + unsigned long long lastBytesSent;
108 109
109 //Realm for authentication when credentials are required 110 //Realm for authentication when credentials are required
110 NSString *authenticationRealm; 111 NSString *authenticationRealm;
@@ -112,6 +113,9 @@ @@ -112,6 +113,9 @@
112 //This lock will block the request until the delegate supplies authentication info 113 //This lock will block the request until the delegate supplies authentication info
113 NSConditionLock *authenticationLock; 114 NSConditionLock *authenticationLock;
114 115
  116 + //This lock prevents the operation from being cancelled at an inopportune moment
  117 + NSLock *cancelledLock;
  118 +
115 //Called on the delegate when the request completes successfully 119 //Called on the delegate when the request completes successfully
116 SEL didFinishSelector; 120 SEL didFinishSelector;
117 121
@@ -128,7 +132,7 @@ @@ -128,7 +132,7 @@
128 NSAutoreleasePool *pool; 132 NSAutoreleasePool *pool;
129 133
130 // Will be YES when a HEAD request will handle the content-length before this request starts 134 // Will be YES when a HEAD request will handle the content-length before this request starts
131 - BOOL useCachedContentLength; 135 + BOOL shouldResetProgressIndicators;
132 136
133 // Used by HEAD requests when showAccurateProgress is YES to preset the content-length for this request 137 // Used by HEAD requests when showAccurateProgress is YES to preset the content-length for this request
134 ASIHTTPRequest *mainRequest; 138 ASIHTTPRequest *mainRequest;
@@ -177,9 +181,9 @@ @@ -177,9 +181,9 @@
177 181
178 // Called on main thread to update progress delegates 182 // Called on main thread to update progress delegates
179 - (void)updateProgressIndicators; 183 - (void)updateProgressIndicators;
180 -- (void)resetUploadProgress:(NSNumber *)max; 184 +- (void)resetUploadProgress:(unsigned long long)value;
181 - (void)updateUploadProgress; 185 - (void)updateUploadProgress;
182 -- (void)resetDownloadProgress:(NSNumber *)max; 186 +- (void)resetDownloadProgress:(unsigned long long)value;
183 - (void)updateDownloadProgress; 187 - (void)updateDownloadProgress;
184 188
185 // Called when authorisation is needed, as we only find out we don't have permission to something when the upload is complete 189 // Called when authorisation is needed, as we only find out we don't have permission to something when the upload is complete
@@ -279,10 +283,10 @@ @@ -279,10 +283,10 @@
279 @property (assign) NSTimeInterval timeOutSeconds; 283 @property (assign) NSTimeInterval timeOutSeconds;
280 @property (retain) NSString *requestMethod; 284 @property (retain) NSString *requestMethod;
281 @property (retain,setter=setPostBody:) NSData *postBody; 285 @property (retain,setter=setPostBody:) NSData *postBody;
282 -@property (assign) unsigned int contentLength; 286 +@property (assign) unsigned long long contentLength;
283 -@property (assign) unsigned int postLength; 287 +@property (assign) unsigned long long postLength;
284 -@property (assign) BOOL useCachedContentLength; 288 +@property (assign) BOOL shouldResetProgressIndicators;
285 @property (retain) ASIHTTPRequest *mainRequest; 289 @property (retain) ASIHTTPRequest *mainRequest;
286 @property (assign) BOOL showAccurateProgress; 290 @property (assign) BOOL showAccurateProgress;
287 -@property (assign,readonly) unsigned int totalBytesRead; 291 +@property (assign,readonly) unsigned long long totalBytesRead;
288 @end 292 @end
@@ -33,7 +33,7 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy @@ -33,7 +33,7 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
33 } 33 }
34 34
35 // This lock prevents the operation from being cancelled while it is trying to update the progress, and vice versa 35 // This lock prevents the operation from being cancelled while it is trying to update the progress, and vice versa
36 -static NSLock *progressLock; 36 +static NSRecursiveLock *progressLock;
37 37
38 @implementation ASIHTTPRequest 38 @implementation ASIHTTPRequest
39 39
@@ -41,9 +41,9 @@ static NSLock *progressLock; @@ -41,9 +41,9 @@ static NSLock *progressLock;
41 41
42 #pragma mark init / dealloc 42 #pragma mark init / dealloc
43 43
44 -- (void)initialize 44 ++ (void)initialize
45 { 45 {
46 - progressLock = [[NSLock alloc] init]; 46 + progressLock = [[NSRecursiveLock alloc] init];
47 } 47 }
48 48
49 - (id)initWithURL:(NSURL *)newURL 49 - (id)initWithURL:(NSURL *)newURL
@@ -52,7 +52,7 @@ static NSLock *progressLock; @@ -52,7 +52,7 @@ static NSLock *progressLock;
52 [self setRequestMethod:@"GET"]; 52 [self setRequestMethod:@"GET"];
53 lastBytesSent = 0; 53 lastBytesSent = 0;
54 showAccurateProgress = YES; 54 showAccurateProgress = YES;
55 - useCachedContentLength = NO; 55 + shouldResetProgressIndicators = YES;
56 updatedProgress = NO; 56 updatedProgress = NO;
57 mainRequest = nil; 57 mainRequest = nil;
58 username = nil; 58 username = nil;
@@ -74,6 +74,7 @@ static NSLock *progressLock; @@ -74,6 +74,7 @@ static NSLock *progressLock;
74 didFailSelector = @selector(requestFailed:); 74 didFailSelector = @selector(requestFailed:);
75 delegate = nil; 75 delegate = nil;
76 url = [newURL retain]; 76 url = [newURL retain];
  77 + cancelledLock = [[NSLock alloc] init];
77 return self; 78 return self;
78 } 79 }
79 80
@@ -105,6 +106,7 @@ static NSLock *progressLock; @@ -105,6 +106,7 @@ static NSLock *progressLock;
105 [receivedData release]; 106 [receivedData release];
106 [responseHeaders release]; 107 [responseHeaders release];
107 [requestMethod release]; 108 [requestMethod release];
  109 + [cancelledLock release];
108 [super dealloc]; 110 [super dealloc];
109 } 111 }
110 112
@@ -143,17 +145,15 @@ static NSLock *progressLock; @@ -143,17 +145,15 @@ static NSLock *progressLock;
143 return complete; 145 return complete;
144 } 146 }
145 147
  148 +
146 - (void)cancel 149 - (void)cancel
147 { 150 {
148 - [progressLock lock]; 151 + [self failWithProblem:@"The request was cancelled"];
  152 + [self cancelLoad];
  153 + complete = YES;
149 [super cancel]; 154 [super cancel];
150 - [progressLock unlock];  
151 } 155 }
152 156
153 -- (int)totalBytesRead  
154 -{  
155 - return totalBytesRead;  
156 -}  
157 157
158 // Call this method to get the recieved data as an NSString. Don't use for Binary data! 158 // Call this method to get the recieved data as an NSString. Don't use for Binary data!
159 - (NSString *)dataString 159 - (NSString *)dataString
@@ -226,8 +226,8 @@ static NSLock *progressLock; @@ -226,8 +226,8 @@ static NSLock *progressLock;
226 CFHTTPMessageSetHeaderFieldValue(request, (CFStringRef)header, (CFStringRef)[requestHeaders objectForKey:header]); 226 CFHTTPMessageSetHeaderFieldValue(request, (CFStringRef)header, (CFStringRef)[requestHeaders objectForKey:header]);
227 } 227 }
228 228
229 - 229 + //NSData *d = (NSData *)CFHTTPMessageCopySerializedMessage(request);
230 - 230 + //NSLog(@"%@",[[[NSString alloc] initWithBytes:[d bytes] length:[d length] encoding:NSUTF8StringEncoding] autorelease]);
231 231
232 232
233 // If this is a post request and we have data to send, add it to the request 233 // If this is a post request and we have data to send, add it to the request
@@ -243,6 +243,13 @@ static NSLock *progressLock; @@ -243,6 +243,13 @@ static NSLock *progressLock;
243 // Start the request 243 // Start the request
244 - (void)loadRequest 244 - (void)loadRequest
245 { 245 {
  246 + [cancelledLock lock];
  247 +
  248 + if ([self isCancelled]) {
  249 + [cancelledLock unlock];
  250 + return;
  251 + }
  252 +
246 CFRunLoopAddCommonMode(CFRunLoopGetCurrent(),ASIHTTPRequestRunMode); 253 CFRunLoopAddCommonMode(CFRunLoopGetCurrent(),ASIHTTPRequestRunMode);
247 254
248 [authenticationLock release]; 255 [authenticationLock release];
@@ -258,7 +265,7 @@ static NSLock *progressLock; @@ -258,7 +265,7 @@ static NSLock *progressLock;
258 } 265 }
259 266
260 lastBytesSent = 0; 267 lastBytesSent = 0;
261 - if (!useCachedContentLength) { 268 + if (shouldResetProgressIndicators) {
262 contentLength = 0; 269 contentLength = 0;
263 } 270 }
264 [self setResponseHeaders:nil]; 271 [self setResponseHeaders:nil];
@@ -267,6 +274,7 @@ static NSLock *progressLock; @@ -267,6 +274,7 @@ static NSLock *progressLock;
267 // Create the stream for the request. 274 // Create the stream for the request.
268 readStream = CFReadStreamCreateForStreamedHTTPRequest(kCFAllocatorDefault, request,readStream); 275 readStream = CFReadStreamCreateForStreamedHTTPRequest(kCFAllocatorDefault, request,readStream);
269 if (!readStream) { 276 if (!readStream) {
  277 + [cancelledLock unlock];
270 [self failWithProblem:@"Unable to create read stream"]; 278 [self failWithProblem:@"Unable to create read stream"];
271 return; 279 return;
272 } 280 }
@@ -276,6 +284,7 @@ static NSLock *progressLock; @@ -276,6 +284,7 @@ static NSLock *progressLock;
276 if (!CFReadStreamSetClient(readStream, kNetworkEvents, ReadStreamClientCallBack, &ctxt)) { 284 if (!CFReadStreamSetClient(readStream, kNetworkEvents, ReadStreamClientCallBack, &ctxt)) {
277 CFRelease(readStream); 285 CFRelease(readStream);
278 readStream = NULL; 286 readStream = NULL;
  287 + [cancelledLock unlock];
279 [self failWithProblem:@"Unable to setup read stream"]; 288 [self failWithProblem:@"Unable to setup read stream"];
280 return; 289 return;
281 } 290 }
@@ -289,14 +298,23 @@ static NSLock *progressLock; @@ -289,14 +298,23 @@ static NSLock *progressLock;
289 CFReadStreamUnscheduleFromRunLoop(readStream, CFRunLoopGetCurrent(), ASIHTTPRequestRunMode); 298 CFReadStreamUnscheduleFromRunLoop(readStream, CFRunLoopGetCurrent(), ASIHTTPRequestRunMode);
290 CFRelease(readStream); 299 CFRelease(readStream);
291 readStream = NULL; 300 readStream = NULL;
  301 + [cancelledLock unlock];
292 [self failWithProblem:@"Unable to start http connection"]; 302 [self failWithProblem:@"Unable to start http connection"];
293 return; 303 return;
294 } 304 }
  305 + [cancelledLock unlock];
295 306
296 - if (uploadProgressDelegate) { 307 +
297 - [self performSelectorOnMainThread:@selector(resetUploadProgress:) withObject:[NSNumber numberWithDouble:postLength] waitUntilDone:YES]; 308 + if (uploadProgressDelegate && shouldResetProgressIndicators) {
  309 + double amount = 1;
  310 + if (showAccurateProgress) {
  311 + amount = postLength;
  312 + }
  313 + [self resetUploadProgress:amount];
298 } 314 }
299 315
  316 +
  317 +
300 // Record when the request started, so we can timeout if nothing happens 318 // Record when the request started, so we can timeout if nothing happens
301 [self setLastActivityTime:[NSDate date]]; 319 [self setLastActivityTime:[NSDate date]];
302 320
@@ -307,9 +325,11 @@ static NSLock *progressLock; @@ -307,9 +325,11 @@ static NSLock *progressLock;
307 [pool release]; 325 [pool release];
308 pool = [[NSAutoreleasePool alloc] init]; 326 pool = [[NSAutoreleasePool alloc] init];
309 327
  328 + NSDate *now = [NSDate new];
  329 +
310 // See if we need to timeout 330 // See if we need to timeout
311 if (lastActivityTime && timeOutSeconds > 0) { 331 if (lastActivityTime && timeOutSeconds > 0) {
312 - if ([[NSDate date] timeIntervalSinceDate:lastActivityTime] > timeOutSeconds) { 332 + if ([now timeIntervalSinceDate:lastActivityTime] > timeOutSeconds) {
313 [self failWithProblem:@"Request timed out"]; 333 [self failWithProblem:@"Request timed out"];
314 [self cancelLoad]; 334 [self cancelLoad];
315 complete = YES; 335 complete = YES;
@@ -324,10 +344,12 @@ static NSLock *progressLock; @@ -324,10 +344,12 @@ static NSLock *progressLock;
324 complete = YES; 344 complete = YES;
325 break; 345 break;
326 } 346 }
327 - [self performSelectorOnMainThread:@selector(updateProgressIndicators) withObject:nil waitUntilDone:YES]; 347 +
  348 + [self updateProgressIndicators];
328 349
329 //This thread should wait for 1/4 second for the stream to do something. We'll stop early if it does. 350 //This thread should wait for 1/4 second for the stream to do something. We'll stop early if it does.
330 CFRunLoopRunInMode(ASIHTTPRequestRunMode,0.25,YES); 351 CFRunLoopRunInMode(ASIHTTPRequestRunMode,0.25,YES);
  352 + [now release];
331 } 353 }
332 354
333 [pool release]; 355 [pool release];
@@ -337,6 +359,7 @@ static NSLock *progressLock; @@ -337,6 +359,7 @@ static NSLock *progressLock;
337 // Cancel loading and clean up 359 // Cancel loading and clean up
338 - (void)cancelLoad 360 - (void)cancelLoad
339 { 361 {
  362 + [cancelledLock lock];
340 if (readStream) { 363 if (readStream) {
341 CFReadStreamClose(readStream); 364 CFReadStreamClose(readStream);
342 CFReadStreamSetClient(readStream, kCFStreamEventNone, NULL, NULL); 365 CFReadStreamSetClient(readStream, kCFStreamEventNone, NULL, NULL);
@@ -355,6 +378,7 @@ static NSLock *progressLock; @@ -355,6 +378,7 @@ static NSLock *progressLock;
355 } 378 }
356 379
357 [self setResponseHeaders:nil]; 380 [self setResponseHeaders:nil];
  381 + [cancelledLock unlock];
358 } 382 }
359 383
360 384
@@ -364,9 +388,9 @@ static NSLock *progressLock; @@ -364,9 +388,9 @@ static NSLock *progressLock;
364 388
365 - (void)updateProgressIndicators 389 - (void)updateProgressIndicators
366 { 390 {
  391 +
367 //Only update progress if this isn't a HEAD request used to preset the content-length 392 //Only update progress if this isn't a HEAD request used to preset the content-length
368 if (!mainRequest) { 393 if (!mainRequest) {
369 -  
370 if (showAccurateProgress || (complete && !updatedProgress)) { 394 if (showAccurateProgress || (complete && !updatedProgress)) {
371 [self updateUploadProgress]; 395 [self updateUploadProgress];
372 [self updateDownloadProgress]; 396 [self updateDownloadProgress];
@@ -411,17 +435,11 @@ static NSLock *progressLock; @@ -411,17 +435,11 @@ static NSLock *progressLock;
411 } 435 }
412 436
413 437
414 -- (void)resetUploadProgress:(NSNumber *)max 438 +- (void)resetUploadProgress:(unsigned long long)value
415 { 439 {
416 [progressLock lock]; 440 [progressLock lock];
417 - if ([self isCancelled]) {  
418 - [progressLock unlock];  
419 - return;  
420 - }  
421 -  
422 //We're using a progress queue or compatible controller to handle progress 441 //We're using a progress queue or compatible controller to handle progress
423 if ([uploadProgressDelegate respondsToSelector:@selector(incrementUploadSizeBy:)]) { 442 if ([uploadProgressDelegate respondsToSelector:@selector(incrementUploadSizeBy:)]) {
424 - int value = [max intValue];  
425 SEL selector = @selector(incrementUploadSizeBy:); 443 SEL selector = @selector(incrementUploadSizeBy:);
426 NSMethodSignature *signature = [[uploadProgressDelegate class] instanceMethodSignatureForSelector:selector]; 444 NSMethodSignature *signature = [[uploadProgressDelegate class] instanceMethodSignatureForSelector:selector];
427 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; 445 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
@@ -437,20 +455,21 @@ static NSLock *progressLock; @@ -437,20 +455,21 @@ static NSLock *progressLock;
437 455
438 - (void)updateUploadProgress 456 - (void)updateUploadProgress
439 { 457 {
440 - [progressLock lock]; 458 + [cancelledLock lock];
441 if ([self isCancelled]) { 459 if ([self isCancelled]) {
442 - [progressLock unlock];  
443 return; 460 return;
444 } 461 }
445 - 462 + unsigned long long byteCount = [[(NSNumber *)CFReadStreamCopyProperty (readStream, kCFStreamPropertyHTTPRequestBytesWrittenCount) autorelease] unsignedLongLongValue];
  463 + [cancelledLock unlock];
  464 + if (byteCount > lastBytesSent) {
446 [self setLastActivityTime:[NSDate date]]; 465 [self setLastActivityTime:[NSDate date]];
  466 + }
447 467
448 - unsigned int byteCount = [[(NSNumber *)CFReadStreamCopyProperty (readStream, kCFStreamPropertyHTTPRequestBytesWrittenCount) autorelease] unsignedIntValue];  
449 if (uploadProgressDelegate) { 468 if (uploadProgressDelegate) {
450 469
451 //We're using a progress queue or compatible controller to handle progress 470 //We're using a progress queue or compatible controller to handle progress
452 if ([uploadProgressDelegate respondsToSelector:@selector(incrementUploadProgressBy:)]) { 471 if ([uploadProgressDelegate respondsToSelector:@selector(incrementUploadProgressBy:)]) {
453 - int value = 0; 472 + unsigned long long value = 0;
454 if (showAccurateProgress) { 473 if (showAccurateProgress) {
455 value = byteCount-lastBytesSent; 474 value = byteCount-lastBytesSent;
456 } else { 475 } else {
@@ -473,22 +492,15 @@ static NSLock *progressLock; @@ -473,22 +492,15 @@ static NSLock *progressLock;
473 492
474 } 493 }
475 lastBytesSent = byteCount; 494 lastBytesSent = byteCount;
476 - [progressLock unlock]; 495 +
477 } 496 }
478 497
479 498
480 -- (void)resetDownloadProgress:(NSNumber *)max 499 +- (void)resetDownloadProgress:(unsigned long long)value
481 { 500 {
482 [progressLock lock]; 501 [progressLock lock];
483 - if ([self isCancelled]) {  
484 - [progressLock unlock];  
485 - return;  
486 - }  
487 -  
488 -  
489 //We're using a progress queue or compatible controller to handle progress 502 //We're using a progress queue or compatible controller to handle progress
490 if ([downloadProgressDelegate respondsToSelector:@selector(incrementDownloadSizeBy:)]) { 503 if ([downloadProgressDelegate respondsToSelector:@selector(incrementDownloadSizeBy:)]) {
491 - int value = [max intValue];  
492 SEL selector = @selector(incrementDownloadSizeBy:); 504 SEL selector = @selector(incrementDownloadSizeBy:);
493 NSMethodSignature *signature = [[downloadProgressDelegate class] instanceMethodSignatureForSelector:selector]; 505 NSMethodSignature *signature = [[downloadProgressDelegate class] instanceMethodSignatureForSelector:selector];
494 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; 506 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
@@ -496,6 +508,7 @@ static NSLock *progressLock; @@ -496,6 +508,7 @@ static NSLock *progressLock;
496 [invocation setSelector:selector]; 508 [invocation setSelector:selector];
497 [invocation setArgument:&value atIndex:2]; 509 [invocation setArgument:&value atIndex:2];
498 [invocation invoke]; 510 [invocation invoke];
  511 +
499 } else { 512 } else {
500 [ASIHTTPRequest setProgress:0 forProgressIndicator:downloadProgressDelegate]; 513 [ASIHTTPRequest setProgress:0 forProgressIndicator:downloadProgressDelegate];
501 } 514 }
@@ -504,30 +517,33 @@ static NSLock *progressLock; @@ -504,30 +517,33 @@ static NSLock *progressLock;
504 517
505 - (void)updateDownloadProgress 518 - (void)updateDownloadProgress
506 { 519 {
507 - [progressLock lock]; 520 + unsigned long long bytesReadSoFar = totalBytesRead;
508 - if ([self isCancelled]) { 521 +
509 - [progressLock unlock]; 522 + //We won't update download progress until we've examined the headers, since we might need to authenticate
510 - return; 523 + if (responseHeaders) {
511 - }  
512 524
  525 + if (bytesReadSoFar > lastBytesRead) {
513 [self setLastActivityTime:[NSDate date]]; 526 [self setLastActivityTime:[NSDate date]];
  527 + }
  528 +
  529 + if (downloadProgressDelegate) {
514 530
515 - //We won't update downlaod progress until we've examined the headers, since we might need to authenticate  
516 - if (downloadProgressDelegate && responseHeaders) {  
517 531
518 //We're using a progress queue or compatible controller to handle progress 532 //We're using a progress queue or compatible controller to handle progress
519 if ([downloadProgressDelegate respondsToSelector:@selector(incrementDownloadProgressBy:)]) { 533 if ([downloadProgressDelegate respondsToSelector:@selector(incrementDownloadProgressBy:)]) {
520 534
521 NSAutoreleasePool *thePool = [[NSAutoreleasePool alloc] init]; 535 NSAutoreleasePool *thePool = [[NSAutoreleasePool alloc] init];
522 536
523 - int value = 0; 537 + unsigned long long value = 0;
524 if (showAccurateProgress) { 538 if (showAccurateProgress) {
525 - value = totalBytesRead-lastBytesRead; 539 + value = bytesReadSoFar-lastBytesRead;
526 } else { 540 } else {
527 value = 1; 541 value = 1;
528 updatedProgress = YES; 542 updatedProgress = YES;
529 } 543 }
530 544
  545 +
  546 +
531 SEL selector = @selector(incrementDownloadProgressBy:); 547 SEL selector = @selector(incrementDownloadProgressBy:);
532 NSMethodSignature *signature = [[downloadProgressDelegate class] instanceMethodSignatureForSelector:selector]; 548 NSMethodSignature *signature = [[downloadProgressDelegate class] instanceMethodSignatureForSelector:selector];
533 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; 549 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
@@ -540,26 +556,22 @@ static NSLock *progressLock; @@ -540,26 +556,22 @@ static NSLock *progressLock;
540 556
541 //We aren't using a queue, we should just set progress of the indicator to 0 557 //We aren't using a queue, we should just set progress of the indicator to 0
542 } else if (contentLength > 0) { 558 } else if (contentLength > 0) {
543 - [ASIHTTPRequest setProgress:(double)(totalBytesRead/contentLength) forProgressIndicator:downloadProgressDelegate]; 559 + [ASIHTTPRequest setProgress:(double)(bytesReadSoFar/contentLength) forProgressIndicator:downloadProgressDelegate];
  560 + }
544 } 561 }
545 562
546 - lastBytesRead = totalBytesRead; 563 + lastBytesRead = bytesReadSoFar;
547 } 564 }
548 - [progressLock unlock]; 565 +
549 } 566 }
550 567
551 -(void)removeUploadProgressSoFar 568 -(void)removeUploadProgressSoFar
552 { 569 {
553 - [progressLock lock];  
554 - if ([self isCancelled]) {  
555 - [progressLock unlock];  
556 - return;  
557 - }  
558 570
559 //We're using a progress queue or compatible controller to handle progress 571 //We're using a progress queue or compatible controller to handle progress
560 - if ([uploadProgressDelegate respondsToSelector:@selector(incrementUploadProgressBy:)]) { 572 + if ([uploadProgressDelegate respondsToSelector:@selector(decrementUploadProgressBy:)]) {
561 - int value = 0-lastBytesSent; 573 + unsigned long long value = 0-lastBytesSent;
562 - SEL selector = @selector(incrementUploadProgressBy:); 574 + SEL selector = @selector(decrementUploadProgressBy:);
563 NSMethodSignature *signature = [[uploadProgressDelegate class] instanceMethodSignatureForSelector:selector]; 575 NSMethodSignature *signature = [[uploadProgressDelegate class] instanceMethodSignatureForSelector:selector];
564 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; 576 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
565 [invocation setTarget:uploadProgressDelegate]; 577 [invocation setTarget:uploadProgressDelegate];
@@ -571,7 +583,6 @@ static NSLock *progressLock; @@ -571,7 +583,6 @@ static NSLock *progressLock;
571 } else { 583 } else {
572 [ASIHTTPRequest setProgress:0 forProgressIndicator:uploadProgressDelegate]; 584 [ASIHTTPRequest setProgress:0 forProgressIndicator:uploadProgressDelegate];
573 } 585 }
574 - [progressLock unlock];  
575 } 586 }
576 587
577 588
@@ -579,8 +590,9 @@ static NSLock *progressLock; @@ -579,8 +590,9 @@ static NSLock *progressLock;
579 { 590 {
580 591
581 SEL selector; 592 SEL selector;
  593 + [progressLock lock];
582 594
583 - //Cocoa Touch: UIProgressView 595 + // Cocoa Touch: UIProgressView
584 if ([indicator respondsToSelector:@selector(setProgress:)]) { 596 if ([indicator respondsToSelector:@selector(setProgress:)]) {
585 selector = @selector(setProgress:); 597 selector = @selector(setProgress:);
586 NSMethodSignature *signature = [[indicator class] instanceMethodSignatureForSelector:selector]; 598 NSMethodSignature *signature = [[indicator class] instanceMethodSignatureForSelector:selector];
@@ -590,26 +602,28 @@ static NSLock *progressLock; @@ -590,26 +602,28 @@ static NSLock *progressLock;
590 [invocation setArgument:&progressFloat atIndex:2]; 602 [invocation setArgument:&progressFloat atIndex:2];
591 [invocation invokeWithTarget:indicator]; 603 [invocation invokeWithTarget:indicator];
592 604
  605 + //If we're running in the main thread, update the progress straight away. Otherwise, it's not that urgent
  606 + [invocation performSelectorOnMainThread:@selector(invokeWithTarget:) withObject:indicator waitUntilDone:[NSThread isMainThread]];
593 607
594 - //Cocoa: NSProgressIndicator 608 +
  609 + // Cocoa: NSProgressIndicator
595 } else if ([indicator respondsToSelector:@selector(setDoubleValue:)]) { 610 } else if ([indicator respondsToSelector:@selector(setDoubleValue:)]) {
596 selector = @selector(setDoubleValue:); 611 selector = @selector(setDoubleValue:);
597 NSMethodSignature *signature = [[indicator class] instanceMethodSignatureForSelector:selector]; 612 NSMethodSignature *signature = [[indicator class] instanceMethodSignatureForSelector:selector];
598 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; 613 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
599 [invocation setSelector:selector]; 614 [invocation setSelector:selector];
600 [invocation setArgument:&progress atIndex:2]; 615 [invocation setArgument:&progress atIndex:2];
601 - [invocation invokeWithTarget:indicator];  
602 616
603 - //Progress indicator is some other thing that we can't handle 617 + //If we're running in the main thread, update the progress straight away. Otherwise, it's not that urgent
604 - } else { 618 + [invocation performSelectorOnMainThread:@selector(invokeWithTarget:) withObject:indicator waitUntilDone:[NSThread isMainThread]];
605 - return; 619 +
606 } 620 }
  621 + [progressLock unlock];
607 } 622 }
608 623
609 624
610 #pragma mark handling request complete / failure 625 #pragma mark handling request complete / failure
611 626
612 -  
613 // Subclasses can override this method to process the result in the same thread 627 // Subclasses can override this method to process the result in the same thread
614 // If not overidden, it will call the didFinishSelector on the delegate, if one has been setup 628 // If not overidden, it will call the didFinishSelector on the delegate, if one has been setup
615 - (void)requestFinished 629 - (void)requestFinished
@@ -656,18 +670,15 @@ static NSLock *progressLock; @@ -656,18 +670,15 @@ static NSLock *progressLock;
656 //We won't reset the download progress delegate if we got an authentication challenge 670 //We won't reset the download progress delegate if we got an authentication challenge
657 if (!isAuthenticationChallenge) { 671 if (!isAuthenticationChallenge) {
658 672
659 - //Only check the content length if we haven't already got one (may have been set by an ASINetworkQueue using a previous HEAD request)  
660 - if (!useCachedContentLength) {  
661 //See if we got a Content-length header 673 //See if we got a Content-length header
662 NSString *cLength = [responseHeaders valueForKey:@"Content-Length"]; 674 NSString *cLength = [responseHeaders valueForKey:@"Content-Length"];
663 if (cLength) { 675 if (cLength) {
664 - contentLength = CFStringGetDoubleValue((CFStringRef)cLength); 676 + contentLength = CFStringGetIntValue((CFStringRef)cLength);
665 if (mainRequest) { 677 if (mainRequest) {
666 [mainRequest setContentLength:contentLength]; 678 [mainRequest setContentLength:contentLength];
667 } 679 }
668 - if (downloadProgressDelegate && showAccurateProgress) { 680 + if (downloadProgressDelegate && showAccurateProgress && shouldResetProgressIndicators) {
669 - [self performSelectorOnMainThread:@selector(resetDownloadProgress:) withObject:[NSNumber numberWithDouble:contentLength] waitUntilDone:YES]; 681 + [self resetDownloadProgress:contentLength];
670 - }  
671 } 682 }
672 } 683 }
673 684
@@ -940,12 +951,15 @@ static NSLock *progressLock; @@ -940,12 +951,15 @@ static NSLock *progressLock;
940 [receivedData appendBytes:buffer length:bytesRead]; 951 [receivedData appendBytes:buffer length:bytesRead];
941 } 952 }
942 } 953 }
  954 +
  955 +
943 } 956 }
944 957
945 958
946 - (void)handleStreamComplete 959 - (void)handleStreamComplete
947 { 960 {
948 961
  962 +
949 //Try to read the headers (if this is a HEAD request handleBytesAvailable available may not be called) 963 //Try to read the headers (if this is a HEAD request handleBytesAvailable available may not be called)
950 if (!responseHeaders) { 964 if (!responseHeaders) {
951 if ([self readResponseHeadersReturningAuthenticationFailure]) { 965 if ([self readResponseHeadersReturningAuthenticationFailure]) {
@@ -953,10 +967,10 @@ static NSLock *progressLock; @@ -953,10 +967,10 @@ static NSLock *progressLock;
953 return; 967 return;
954 } 968 }
955 } 969 }
956 - 970 + [progressLock lock];
957 -  
958 complete = YES; 971 complete = YES;
959 [self updateProgressIndicators]; 972 [self updateProgressIndicators];
  973 +
960 if (readStream) { 974 if (readStream) {
961 CFReadStreamClose(readStream); 975 CFReadStreamClose(readStream);
962 CFReadStreamSetClient(readStream, kCFStreamEventNone, NULL, NULL); 976 CFReadStreamSetClient(readStream, kCFStreamEventNone, NULL, NULL);
@@ -969,8 +983,9 @@ static NSLock *progressLock; @@ -969,8 +983,9 @@ static NSLock *progressLock;
969 if (downloadDestinationPath) { 983 if (downloadDestinationPath) {
970 [outputStream close]; 984 [outputStream close];
971 } 985 }
972 - 986 + [progressLock unlock];
973 [self requestFinished]; 987 [self requestFinished];
  988 +
974 } 989 }
975 990
976 991
@@ -1101,7 +1116,9 @@ static NSLock *progressLock; @@ -1101,7 +1116,9 @@ static NSLock *progressLock;
1101 @synthesize postBody; 1116 @synthesize postBody;
1102 @synthesize contentLength; 1117 @synthesize contentLength;
1103 @synthesize postLength; 1118 @synthesize postLength;
1104 -@synthesize useCachedContentLength; 1119 +@synthesize shouldResetProgressIndicators;
1105 @synthesize mainRequest; 1120 @synthesize mainRequest;
  1121 +@synthesize totalBytesRead;
1106 @synthesize showAccurateProgress; 1122 @synthesize showAccurateProgress;
  1123 +@synthesize totalBytesRead;
1107 @end 1124 @end
@@ -109,8 +109,9 @@ @@ -109,8 +109,9 @@
109 ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; 109 ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
110 [request setDownloadProgressDelegate:self]; 110 [request setDownloadProgressDelegate:self];
111 [request start]; 111 [request start];
  112 +
112 BOOL success = (progress == 1); 113 BOOL success = (progress == 1);
113 - STAssertTrue(success,@"Failed to properly increment download progress"); 114 + STAssertTrue(success,@"Failed to properly increment download progress %f != 1.0",progress);
114 } 115 }
115 116
116 117
@@ -121,8 +122,9 @@ @@ -121,8 +122,9 @@
121 [request setPostBody:[NSMutableData dataWithLength:1024*32]]; 122 [request setPostBody:[NSMutableData dataWithLength:1024*32]];
122 [request setUploadProgressDelegate:self]; 123 [request setUploadProgressDelegate:self];
123 [request start]; 124 [request start];
  125 +
124 BOOL success = (progress == 1); 126 BOOL success = (progress == 1);
125 - STAssertTrue(success,@"Failed to properly increment upload progress"); 127 + STAssertTrue(success,@"Failed to properly increment upload progress %f != 1.0",progress);
126 } 128 }
127 129
128 130
@@ -6,7 +6,7 @@ @@ -6,7 +6,7 @@
6 // Copyright 2008 All-Seeing Interactive. All rights reserved. 6 // Copyright 2008 All-Seeing Interactive. All rights reserved.
7 // 7 //
8 8
9 - 9 +#import <Cocoa/Cocoa.h>
10 10
11 @interface ASINetworkQueue : NSOperationQueue { 11 @interface ASINetworkQueue : NSOperationQueue {
12 12
@@ -26,19 +26,19 @@ @@ -26,19 +26,19 @@
26 id uploadProgressDelegate; 26 id uploadProgressDelegate;
27 27
28 // Total amount uploaded so far for all requests in this queue 28 // Total amount uploaded so far for all requests in this queue
29 - unsigned int uploadProgressBytes; 29 + unsigned long long uploadProgressBytes;
30 30
31 // Total amount to be uploaded for all requests in this queue - requests add to this figure as they work out how much data they have to transmit 31 // Total amount to be uploaded for all requests in this queue - requests add to this figure as they work out how much data they have to transmit
32 - unsigned int uploadProgressTotalBytes; 32 + unsigned long long uploadProgressTotalBytes;
33 33
34 // Download progress indicator, probably an NSProgressIndicator or UIProgressView 34 // Download progress indicator, probably an NSProgressIndicator or UIProgressView
35 id downloadProgressDelegate; 35 id downloadProgressDelegate;
36 36
37 // Total amount downloaded so far for all requests in this queue 37 // Total amount downloaded so far for all requests in this queue
38 - unsigned int downloadProgressBytes; 38 + unsigned long long downloadProgressBytes;
39 39
40 // Total amount to be downloaded for all requests in this queue - requests add to this figure as they receive Content-Length headers 40 // Total amount to be downloaded for all requests in this queue - requests add to this figure as they receive Content-Length headers
41 - unsigned int downloadProgressTotalBytes; 41 + unsigned long long downloadProgressTotalBytes;
42 42
43 // When YES, the queue will cancel all requests when a request fails. Default is YES 43 // When YES, the queue will cancel all requests when a request fails. Default is YES
44 BOOL shouldCancelAllRequestsOnFailure; 44 BOOL shouldCancelAllRequestsOnFailure;
@@ -61,16 +61,19 @@ @@ -61,16 +61,19 @@
61 - (void)addHEADOperation:(NSOperation *)operation; 61 - (void)addHEADOperation:(NSOperation *)operation;
62 62
63 // Called at the start of a request to add on the size of this upload to the total 63 // Called at the start of a request to add on the size of this upload to the total
64 -- (void)incrementUploadSizeBy:(int)bytes; 64 +- (void)incrementUploadSizeBy:(unsigned long long)bytes;
65 65
66 // Called during a request when data is written to the upload stream to increment the progress indicator 66 // Called during a request when data is written to the upload stream to increment the progress indicator
67 -- (void)incrementUploadProgressBy:(int)bytes; 67 +- (void)incrementUploadProgressBy:(unsigned long long)bytes;
68 68
69 // Called at the start of a request to add on the size of this download to the total 69 // Called at the start of a request to add on the size of this download to the total
70 -- (void)incrementDownloadSizeBy:(int)bytes; 70 +- (void)incrementDownloadSizeBy:(unsigned long long)bytes;
71 71
72 // Called during a request when data is received to increment the progress indicator 72 // Called during a request when data is received to increment the progress indicator
73 -- (void)incrementDownloadProgressBy:(int)bytes; 73 +- (void)incrementDownloadProgressBy:(unsigned long long)bytes;
  74 +
  75 +// Called during a request when authorisation fails to cancel any progress so far
  76 +- (void)decrementUploadProgressBy:(unsigned long long)bytes;
74 77
75 // All ASINetworkQueues are paused when created so that total size can be calculated before the queue starts 78 // All ASINetworkQueues are paused when created so that total size can be calculated before the queue starts
76 // This method will start the queue 79 // This method will start the queue
@@ -34,6 +34,7 @@ @@ -34,6 +34,7 @@
34 34
35 showAccurateProgress = NO; 35 showAccurateProgress = NO;
36 36
  37 + [self setMaxConcurrentOperationCount:4];
37 [self setSuspended:YES]; 38 [self setSuspended:YES];
38 39
39 return self; 40 return self;
@@ -59,9 +60,6 @@ @@ -59,9 +60,6 @@
59 uploadProgressTotalBytes = 0; 60 uploadProgressTotalBytes = 0;
60 downloadProgressBytes = 0; 61 downloadProgressBytes = 0;
61 downloadProgressTotalBytes = 0; 62 downloadProgressTotalBytes = 0;
62 - [self setUploadProgressDelegate:nil];  
63 - [self setDownloadProgressDelegate:nil];  
64 - [self setDelegate:nil];  
65 [super cancelAllOperations]; 63 [super cancelAllOperations];
66 } 64 }
67 65
@@ -103,6 +101,8 @@ @@ -103,6 +101,8 @@
103 if ([operation isKindOfClass:[ASIHTTPRequest class]]) { 101 if ([operation isKindOfClass:[ASIHTTPRequest class]]) {
104 102
105 ASIHTTPRequest *request = (ASIHTTPRequest *)operation; 103 ASIHTTPRequest *request = (ASIHTTPRequest *)operation;
  104 + [request setRequestMethod:@"HEAD"];
  105 + [request setQueuePriority:10];
106 [request setShowAccurateProgress:YES]; 106 [request setShowAccurateProgress:YES];
107 if (uploadProgressDelegate) { 107 if (uploadProgressDelegate) {
108 [request setUploadProgressDelegate:self]; 108 [request setUploadProgressDelegate:self];
@@ -133,12 +133,11 @@ @@ -133,12 +133,11 @@
133 //If this is a GET request and we want accurate progress, perform a HEAD request first to get the content-length 133 //If this is a GET request and we want accurate progress, perform a HEAD request first to get the content-length
134 if ([[request requestMethod] isEqualToString:@"GET"]) { 134 if ([[request requestMethod] isEqualToString:@"GET"]) {
135 ASIHTTPRequest *HEADRequest = [[[ASIHTTPRequest alloc] initWithURL:[request url]] autorelease]; 135 ASIHTTPRequest *HEADRequest = [[[ASIHTTPRequest alloc] initWithURL:[request url]] autorelease];
136 - [HEADRequest setRequestMethod:@"HEAD"];  
137 - [HEADRequest setQueuePriority:10];  
138 [HEADRequest setMainRequest:request]; 136 [HEADRequest setMainRequest:request];
139 [self addHEADOperation:HEADRequest]; 137 [self addHEADOperation:HEADRequest];
140 138
141 - [request setUseCachedContentLength:YES]; 139 + //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
  140 + [request setShouldResetProgressIndicators:NO];
142 [request addDependency:HEADRequest]; 141 [request addDependency:HEADRequest];
143 142
144 //If we want to track uploading for this request accurately, we need to add the size of the post content to the total 143 //If we want to track uploading for this request accurately, we need to add the size of the post content to the total
@@ -150,6 +149,9 @@ @@ -150,6 +149,9 @@
150 [request setShowAccurateProgress:showAccurateProgress]; 149 [request setShowAccurateProgress:showAccurateProgress];
151 150
152 if (uploadProgressDelegate) { 151 if (uploadProgressDelegate) {
  152 +
  153 + //For uploads requests, we always work out the total upload size before the queue starts, so we tell the request not to reset the progress indicator when starting each request
  154 + [request setShouldResetProgressIndicators:NO];
153 [request setUploadProgressDelegate:self]; 155 [request setUploadProgressDelegate:self];
154 } else { 156 } else {
155 [request setUploadProgressDelegate:NULL]; 157 [request setUploadProgressDelegate:NULL];
@@ -173,7 +175,7 @@ @@ -173,7 +175,7 @@
173 if (requestDidFailSelector) { 175 if (requestDidFailSelector) {
174 [delegate performSelector:requestDidFailSelector withObject:request]; 176 [delegate performSelector:requestDidFailSelector withObject:request];
175 } 177 }
176 - if (shouldCancelAllRequestsOnFailure) { 178 + if (shouldCancelAllRequestsOnFailure && requestsCount > 0) {
177 [self cancelAllOperations]; 179 [self cancelAllOperations];
178 } 180 }
179 } 181 }
@@ -191,7 +193,7 @@ @@ -191,7 +193,7 @@
191 } 193 }
192 } 194 }
193 195
194 -- (void)incrementUploadSizeBy:(int)bytes 196 +- (void)incrementUploadSizeBy:(unsigned long long)bytes
195 { 197 {
196 if (!uploadProgressDelegate) { 198 if (!uploadProgressDelegate) {
197 return; 199 return;
@@ -200,7 +202,19 @@ @@ -200,7 +202,19 @@
200 [self incrementUploadProgressBy:0]; 202 [self incrementUploadProgressBy:0];
201 } 203 }
202 204
203 -- (void)incrementUploadProgressBy:(int)bytes 205 +- (void)decrementUploadProgressBy:(unsigned long long)bytes
  206 +{
  207 + if (!uploadProgressDelegate || uploadProgressTotalBytes == 0) {
  208 + return;
  209 + }
  210 + uploadProgressBytes -= bytes;
  211 +
  212 + double progress = (uploadProgressBytes*1.0)/(uploadProgressTotalBytes*1.0);
  213 + [ASIHTTPRequest setProgress:progress forProgressIndicator:uploadProgressDelegate];
  214 +}
  215 +
  216 +
  217 +- (void)incrementUploadProgressBy:(unsigned long long)bytes
204 { 218 {
205 if (!uploadProgressDelegate || uploadProgressTotalBytes == 0) { 219 if (!uploadProgressDelegate || uploadProgressTotalBytes == 0) {
206 return; 220 return;
@@ -209,24 +223,26 @@ @@ -209,24 +223,26 @@
209 223
210 double progress = (uploadProgressBytes*1.0)/(uploadProgressTotalBytes*1.0); 224 double progress = (uploadProgressBytes*1.0)/(uploadProgressTotalBytes*1.0);
211 [ASIHTTPRequest setProgress:progress forProgressIndicator:uploadProgressDelegate]; 225 [ASIHTTPRequest setProgress:progress forProgressIndicator:uploadProgressDelegate];
  226 +
212 } 227 }
213 228
214 -- (void)incrementDownloadSizeBy:(int)bytes 229 +- (void)incrementDownloadSizeBy:(unsigned long long)bytes
215 { 230 {
216 if (!downloadProgressDelegate) { 231 if (!downloadProgressDelegate) {
217 return; 232 return;
218 } 233 }
219 downloadProgressTotalBytes += bytes; 234 downloadProgressTotalBytes += bytes;
220 [self incrementDownloadProgressBy:0]; 235 [self incrementDownloadProgressBy:0];
  236 + NSLog(@"download size is now: %qu",downloadProgressTotalBytes);
221 } 237 }
222 238
223 -- (void)incrementDownloadProgressBy:(int)bytes 239 +- (void)incrementDownloadProgressBy:(unsigned long long)bytes
224 { 240 {
225 if (!downloadProgressDelegate || downloadProgressTotalBytes == 0) { 241 if (!downloadProgressDelegate || downloadProgressTotalBytes == 0) {
226 return; 242 return;
227 } 243 }
228 downloadProgressBytes += bytes; 244 downloadProgressBytes += bytes;
229 - //NSLog(@"%hu/%hu",downloadProgressBytes,downloadProgressTotalBytes); 245 + //NSLog(@"%qu/%qu",downloadProgressBytes,downloadProgressTotalBytes);
230 double progress = (downloadProgressBytes*1.0)/(downloadProgressTotalBytes*1.0); 246 double progress = (downloadProgressBytes*1.0)/(downloadProgressTotalBytes*1.0);
231 [ASIHTTPRequest setProgress:progress forProgressIndicator:downloadProgressDelegate]; 247 [ASIHTTPRequest setProgress:progress forProgressIndicator:downloadProgressDelegate];
232 } 248 }
@@ -201,8 +201,6 @@ @@ -201,8 +201,6 @@
201 201
202 - (void)requestFailedCancellingOthers:(ASIHTTPRequest *)request 202 - (void)requestFailedCancellingOthers:(ASIHTTPRequest *)request
203 { 203 {
204 - BOOL success = (request == requestThatShouldFail);  
205 - STAssertTrue(success,@"Wrong request failed");  
206 complete = YES; 204 complete = YES;
207 } 205 }
208 206