Ben Copsey

Fix a problem where both finished and failed delegate methods could in some circ…

…umstances be called for the same request
Thanks to Mike DeSaro for catching this nasty bug!
@@ -263,6 +263,10 @@ static NSError *ASITooMuchRedirectionError; @@ -263,6 +263,10 @@ static NSError *ASITooMuchRedirectionError;
263 263
264 - (void)cancel 264 - (void)cancel
265 { 265 {
  266 + // Request may already be complete
  267 + if ([self complete] || [self isCancelled]) {
  268 + return;
  269 + }
266 [self failWithError:ASIRequestCancelledError]; 270 [self failWithError:ASIRequestCancelledError];
267 [super cancel]; 271 [super cancel];
268 [self cancelLoad]; 272 [self cancelLoad];
@@ -897,7 +901,7 @@ static NSError *ASITooMuchRedirectionError; @@ -897,7 +901,7 @@ static NSError *ASITooMuchRedirectionError;
897 // If you do this, don't forget to call [super requestFinished] to let the queue / delegate know we're done 901 // If you do this, don't forget to call [super requestFinished] to let the queue / delegate know we're done
898 - (void)requestFinished 902 - (void)requestFinished
899 { 903 {
900 - if ([self isCancelled] || [self mainRequest]) { 904 + if ([self error] || [self mainRequest]) {
901 return; 905 return;
902 } 906 }
903 // Let the queue know we are done 907 // Let the queue know we are done
@@ -917,36 +921,32 @@ static NSError *ASITooMuchRedirectionError; @@ -917,36 +921,32 @@ static NSError *ASITooMuchRedirectionError;
917 { 921 {
918 [self setComplete:YES]; 922 [self setComplete:YES];
919 923
920 - if ([self isCancelled]) { 924 + if ([self isCancelled] || [self error]) {
921 return; 925 return;
922 } 926 }
923 927
924 - if (!error) { 928 + // If this is a HEAD request created by an ASINetworkQueue or compatible queue delegate, make the main request fail
  929 + if ([self mainRequest]) {
  930 + ASIHTTPRequest *mRequest = [self mainRequest];
  931 + [mRequest setError:theError];
  932 +
  933 + // Let the queue know something went wrong
  934 + if ([queue respondsToSelector:@selector(requestDidFail:)]) {
  935 + [queue performSelectorOnMainThread:@selector(requestDidFail:) withObject:mRequest waitUntilDone:[NSThread isMainThread]];
  936 + }
  937 +
  938 + } else {
  939 + [self setError:theError];
925 940
926 - // If this is a HEAD request created by an ASINetworkQueue or compatible queue delegate, make the main request fail 941 + // Let the queue know something went wrong
927 - if ([self mainRequest]) { 942 + if ([queue respondsToSelector:@selector(requestDidFail:)]) {
928 - ASIHTTPRequest *mRequest = [self mainRequest]; 943 + [queue performSelectorOnMainThread:@selector(requestDidFail:) withObject:self waitUntilDone:[NSThread isMainThread]];
929 - [mRequest setError:theError]; 944 + }
930 -  
931 - // Let the queue know something went wrong  
932 - if ([queue respondsToSelector:@selector(requestDidFail:)]) {  
933 - [queue performSelectorOnMainThread:@selector(requestDidFail:) withObject:mRequest waitUntilDone:[NSThread isMainThread]];  
934 - }  
935 945
936 - } else { 946 + // Let the delegate know something went wrong
937 - [self setError:theError]; 947 + if (didFailSelector && [delegate respondsToSelector:didFailSelector]) {
938 - 948 + [delegate performSelectorOnMainThread:didFailSelector withObject:self waitUntilDone:[NSThread isMainThread]];
939 - // Let the queue know something went wrong  
940 - if ([queue respondsToSelector:@selector(requestDidFail:)]) {  
941 - [queue performSelectorOnMainThread:@selector(requestDidFail:) withObject:self waitUntilDone:[NSThread isMainThread]];  
942 - }  
943 -  
944 - // Let the delegate know something went wrong  
945 - if (didFailSelector && [delegate respondsToSelector:didFailSelector]) {  
946 - [delegate performSelectorOnMainThread:didFailSelector withObject:self waitUntilDone:[NSThread isMainThread]];  
947 - }  
948 } 949 }
949 -  
950 } 950 }
951 } 951 }
952 952
@@ -37,4 +37,5 @@ @@ -37,4 +37,5 @@
37 - (void)testTooMuchRedirection; 37 - (void)testTooMuchRedirection;
38 - (void)testRedirectToNewDomain; 38 - (void)testRedirectToNewDomain;
39 - (void)test303Redirect; 39 - (void)test303Redirect;
  40 +
40 @end 41 @end
@@ -680,4 +680,6 @@ @@ -680,4 +680,6 @@
680 GHAssertTrue(success,@"Failed to use GET on new URL"); 680 GHAssertTrue(success,@"Failed to use GET on new URL");
681 } 681 }
682 682
  683 +
  684 +
683 @end 685 @end
@@ -13,15 +13,17 @@ @@ -13,15 +13,17 @@
13 #endif 13 #endif
14 14
15 @class ASIHTTPRequest; 15 @class ASIHTTPRequest;
16 -@class ASINetworkQueue;  
17 16
18 @interface ASINetworkQueueTests : GHTestCase { 17 @interface ASINetworkQueueTests : GHTestCase {
19 ASIHTTPRequest *requestThatShouldFail; 18 ASIHTTPRequest *requestThatShouldFail;
20 - ASINetworkQueue *networkQueue;  
21 BOOL complete; 19 BOOL complete;
22 BOOL request_didfail; 20 BOOL request_didfail;
23 BOOL request_succeeded; 21 BOOL request_succeeded;
24 float progress; 22 float progress;
  23 +
  24 + NSOperationQueue *immediateCancelQueue;
  25 + NSMutableArray *failedRequests;
  26 + NSMutableArray *finishedRequests;
25 } 27 }
26 28
27 - (void)testFailure; 29 - (void)testFailure;
@@ -31,7 +33,11 @@ @@ -31,7 +33,11 @@
31 - (void)testProgressWithAuthentication; 33 - (void)testProgressWithAuthentication;
32 - (void)testWithNoListener; 34 - (void)testWithNoListener;
33 - (void)testPartialResume; 35 - (void)testPartialResume;
  36 +- (void)testImmediateCancel;
34 37
35 - (void)setProgress:(float)newProgress; 38 - (void)setProgress:(float)newProgress;
36 39
  40 +@property (retain) NSOperationQueue *immediateCancelQueue;
  41 +@property (retain) NSMutableArray *failedRequests;
  42 +@property (retain) NSMutableArray *finishedRequests;
37 @end 43 @end
@@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
19 complete = NO; 19 complete = NO;
20 progress = 0; 20 progress = 0;
21 21
22 - networkQueue = [[ASINetworkQueue queue] retain]; 22 + ASINetworkQueue *networkQueue = [ASINetworkQueue queue];
23 [networkQueue setDownloadProgressDelegate:self]; 23 [networkQueue setDownloadProgressDelegate:self];
24 [networkQueue setDelegate:self]; 24 [networkQueue setDelegate:self];
25 [networkQueue setShowAccurateProgress:NO]; 25 [networkQueue setShowAccurateProgress:NO];
@@ -73,9 +73,6 @@ @@ -73,9 +73,6 @@
73 success = (progress > 0.95); 73 success = (progress > 0.95);
74 GHAssertTrue(success,@"Failed to increment progress properly"); 74 GHAssertTrue(success,@"Failed to increment progress properly");
75 75
76 -  
77 - [networkQueue release];  
78 -  
79 } 76 }
80 77
81 - (void)testUploadProgress 78 - (void)testUploadProgress
@@ -83,7 +80,7 @@ @@ -83,7 +80,7 @@
83 complete = NO; 80 complete = NO;
84 progress = 0; 81 progress = 0;
85 82
86 - networkQueue = [[ASINetworkQueue alloc] init]; 83 + ASINetworkQueue *networkQueue = [[[ASINetworkQueue alloc] init] autorelease];
87 [networkQueue setUploadProgressDelegate:self]; 84 [networkQueue setUploadProgressDelegate:self];
88 [networkQueue setDelegate:self]; 85 [networkQueue setDelegate:self];
89 [networkQueue setShowAccurateProgress:NO]; 86 [networkQueue setShowAccurateProgress:NO];
@@ -135,8 +132,6 @@ @@ -135,8 +132,6 @@
135 success = (progress > 0.95); 132 success = (progress > 0.95);
136 GHAssertTrue(success,@"Failed to increment progress properly"); 133 GHAssertTrue(success,@"Failed to increment progress properly");
137 134
138 - [networkQueue release];  
139 -  
140 } 135 }
141 136
142 137
@@ -153,7 +148,7 @@ @@ -153,7 +148,7 @@
153 { 148 {
154 complete = NO; 149 complete = NO;
155 150
156 - networkQueue = [[ASINetworkQueue alloc] init]; 151 + ASINetworkQueue *networkQueue = [ASINetworkQueue queue];
157 [networkQueue setDelegate:self]; 152 [networkQueue setDelegate:self];
158 [networkQueue setRequestDidFailSelector:@selector(requestFailed:)]; 153 [networkQueue setRequestDidFailSelector:@selector(requestFailed:)];
159 [networkQueue setQueueDidFinishSelector:@selector(queueFinished:)]; 154 [networkQueue setQueueDidFinishSelector:@selector(queueFinished:)];
@@ -224,7 +219,7 @@ @@ -224,7 +219,7 @@
224 { 219 {
225 complete = NO; 220 complete = NO;
226 221
227 - networkQueue = [[ASINetworkQueue alloc] init]; 222 + ASINetworkQueue *networkQueue = [ASINetworkQueue queue];
228 [networkQueue setDelegate:self]; 223 [networkQueue setDelegate:self];
229 [networkQueue setRequestDidFailSelector:@selector(requestFailedCancellingOthers:)]; 224 [networkQueue setRequestDidFailSelector:@selector(requestFailedCancellingOthers:)];
230 [networkQueue setQueueDidFinishSelector:@selector(queueFinished:)]; 225 [networkQueue setQueueDidFinishSelector:@selector(queueFinished:)];
@@ -278,7 +273,7 @@ @@ -278,7 +273,7 @@
278 complete = NO; 273 complete = NO;
279 progress = 0; 274 progress = 0;
280 275
281 - networkQueue = [[ASINetworkQueue alloc] init]; 276 + ASINetworkQueue *networkQueue = [ASINetworkQueue queue];
282 [networkQueue setDownloadProgressDelegate:self]; 277 [networkQueue setDownloadProgressDelegate:self];
283 [networkQueue setDelegate:self]; 278 [networkQueue setDelegate:self];
284 [networkQueue setShowAccurateProgress:YES]; 279 [networkQueue setShowAccurateProgress:YES];
@@ -299,11 +294,10 @@ @@ -299,11 +294,10 @@
299 294
300 NSError *error = [request error]; 295 NSError *error = [request error];
301 GHAssertNotNil(error,@"The HEAD request failed, but it didn't tell the main request to fail"); 296 GHAssertNotNil(error,@"The HEAD request failed, but it didn't tell the main request to fail");
302 - [networkQueue release];  
303 297
304 complete = NO; 298 complete = NO;
305 progress = 0; 299 progress = 0;
306 - networkQueue = [[ASINetworkQueue alloc] init]; 300 + networkQueue = [ASINetworkQueue queue];
307 [networkQueue setDownloadProgressDelegate:self]; 301 [networkQueue setDownloadProgressDelegate:self];
308 [networkQueue setDelegate:self]; 302 [networkQueue setDelegate:self];
309 [networkQueue setShowAccurateProgress:YES]; 303 [networkQueue setShowAccurateProgress:YES];
@@ -322,7 +316,6 @@ @@ -322,7 +316,6 @@
322 316
323 error = [request error]; 317 error = [request error];
324 GHAssertNil(error,@"Failed to use authentication in a queue"); 318 GHAssertNil(error,@"Failed to use authentication in a queue");
325 - [networkQueue release];  
326 319
327 } 320 }
328 321
@@ -345,7 +338,7 @@ @@ -345,7 +338,7 @@
345 { 338 {
346 request_succeeded = NO; 339 request_succeeded = NO;
347 request_didfail = NO; 340 request_didfail = NO;
348 - networkQueue = [[ASINetworkQueue alloc] init]; 341 + ASINetworkQueue *networkQueue = [ASINetworkQueue queue];
349 [networkQueue setDownloadProgressDelegate:self]; 342 [networkQueue setDownloadProgressDelegate:self];
350 [networkQueue setDelegate:self]; 343 [networkQueue setDelegate:self];
351 [networkQueue setShowAccurateProgress:YES]; 344 [networkQueue setShowAccurateProgress:YES];
@@ -364,7 +357,6 @@ @@ -364,7 +357,6 @@
364 // This test may fail if you are using a proxy and it returns a page when you try to connect to a bad port. 357 // This test may fail if you are using a proxy and it returns a page when you try to connect to a bad port.
365 GHAssertTrue(!request_succeeded && request_didfail,@"Request to resource without listener succeeded but should have failed"); 358 GHAssertTrue(!request_succeeded && request_didfail,@"Request to resource without listener succeeded but should have failed");
366 359
367 - [networkQueue release];  
368 } 360 }
369 361
370 - (void)testPartialResume 362 - (void)testPartialResume
@@ -383,7 +375,7 @@ @@ -383,7 +375,7 @@
383 } 375 }
384 376
385 NSURL *downloadURL = [NSURL URLWithString:@"http://trails-network.net/Downloads/MemexTrails_1.0b1.zip"]; 377 NSURL *downloadURL = [NSURL URLWithString:@"http://trails-network.net/Downloads/MemexTrails_1.0b1.zip"];
386 - networkQueue = [[ASINetworkQueue alloc] init]; 378 + ASINetworkQueue *networkQueue = [ASINetworkQueue queue];
387 379
388 ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:downloadURL] autorelease]; 380 ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:downloadURL] autorelease];
389 [request setDownloadDestinationPath:downloadPath]; 381 [request setDownloadDestinationPath:downloadPath];
@@ -402,8 +394,7 @@ @@ -402,8 +394,7 @@
402 // 5 seconds is up, let's tell the queue to stop 394 // 5 seconds is up, let's tell the queue to stop
403 [networkQueue cancelAllOperations]; 395 [networkQueue cancelAllOperations];
404 396
405 - [networkQueue release]; 397 + networkQueue = [ASINetworkQueue queue];
406 - networkQueue = [[ASINetworkQueue alloc] init];  
407 [networkQueue setDownloadProgressDelegate:self]; 398 [networkQueue setDownloadProgressDelegate:self];
408 [networkQueue setShowAccurateProgress:YES]; 399 [networkQueue setShowAccurateProgress:YES];
409 [networkQueue setDelegate:self]; 400 [networkQueue setDelegate:self];
@@ -435,13 +426,12 @@ @@ -435,13 +426,12 @@
435 success = (progress > 0.95); 426 success = (progress > 0.95);
436 GHAssertTrue(success,@"Failed to increment progress properly"); 427 GHAssertTrue(success,@"Failed to increment progress properly");
437 428
438 - [networkQueue release]; 429 +
439 -  
440 430
441 //Test the temporary file cleanup 431 //Test the temporary file cleanup
442 complete = NO; 432 complete = NO;
443 progress = 0; 433 progress = 0;
444 - networkQueue = [[ASINetworkQueue alloc] init]; 434 + networkQueue = [ASINetworkQueue queue];
445 [networkQueue setDownloadProgressDelegate:self]; 435 [networkQueue setDownloadProgressDelegate:self];
446 [networkQueue setShowAccurateProgress:YES]; 436 [networkQueue setShowAccurateProgress:YES];
447 [networkQueue setDelegate:self]; 437 [networkQueue setDelegate:self];
@@ -470,7 +460,6 @@ @@ -470,7 +460,6 @@
470 GHAssertTrue(success,@"Temporary download file should have been deleted"); 460 GHAssertTrue(success,@"Temporary download file should have been deleted");
471 461
472 timeoutTimer = nil; 462 timeoutTimer = nil;
473 - [networkQueue release];  
474 463
475 } 464 }
476 465
@@ -480,5 +469,55 @@ @@ -480,5 +469,55 @@
480 } 469 }
481 470
482 471
  472 +// Not strictly an ASINetworkQueue test, but queue related
  473 +// As soon as one request finishes or fails, we'll cancel the others and ensure that no requests are both finished and failed
  474 +- (void)testImmediateCancel
  475 +{
  476 + [self setFailedRequests:[[[NSMutableArray alloc] init] autorelease]];
  477 + [self setFinishedRequests:[[[NSMutableArray alloc] init] autorelease]];
  478 + [self setImmediateCancelQueue:[[[NSOperationQueue alloc] init] autorelease]];
  479 + int i;
  480 + for (i=0; i<100; i++) {
  481 + ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://asi"]];
  482 + [request setDelegate:self];
  483 + [request setDidFailSelector:@selector(immediateCancelFail:)];
  484 + [request setDidFinishSelector:@selector(immediateCancelFinish:)];
  485 + [[self immediateCancelQueue] addOperation:request];
  486 + }
  487 +
  488 +}
  489 +
  490 +- (void)immediateCancelFail:(ASIHTTPRequest *)request
  491 +{
  492 + [[self immediateCancelQueue] cancelAllOperations];
  493 + if ([[self failedRequests] containsObject:request]) {
  494 + GHFail(@"A request called its fail delegate method twice");
  495 + }
  496 + if ([[self finishedRequests] containsObject:request]) {
  497 + GHFail(@"A request that had already finished called its fail delegate method");
  498 + }
  499 + [[self failedRequests] addObject:request];
  500 + if ([[self failedRequests] count]+[[self finishedRequests] count] > 100) {
  501 + GHFail(@"We got more than 100 delegate fail/finish calls - this shouldn't happen!");
  502 + }
  503 +}
  504 +
  505 +- (void)immediateCancelFinish:(ASIHTTPRequest *)request
  506 +{
  507 + [[self immediateCancelQueue] cancelAllOperations];
  508 + if ([[self finishedRequests] containsObject:request]) {
  509 + GHFail(@"A request called its finish delegate method twice");
  510 + }
  511 + if ([[self failedRequests] containsObject:request]) {
  512 + GHFail(@"A request that had already failed called its finish delegate method");
  513 + }
  514 + [[self finishedRequests] addObject:request];
  515 + if ([[self failedRequests] count]+[[self finishedRequests] count] > 100) {
  516 + GHFail(@"We got more than 100 delegate fail/finish calls - this shouldn't happen!");
  517 + }
  518 +}
483 519
  520 +@synthesize immediateCancelQueue;
  521 +@synthesize failedRequests;
  522 +@synthesize finishedRequests;
484 @end 523 @end