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!
Showing
5 changed files
with
97 additions
and
49 deletions
| @@ -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 |
| @@ -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 |
-
Please register or login to post a comment