Ben Copsey

Added automatic retry for timeouts

@@ -324,6 +324,13 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount; @@ -324,6 +324,13 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount;
324 // When set to yes, this request will create a thread to run itself in 324 // When set to yes, this request will create a thread to run itself in
325 // On Snow Leopard, requests using a queue will always run in a background thread, so this does nothing when using a queue on that platform 325 // On Snow Leopard, requests using a queue will always run in a background thread, so this does nothing when using a queue on that platform
326 BOOL shouldRunInBackgroundThread; 326 BOOL shouldRunInBackgroundThread;
  327 +
  328 + // Set to allow a request to automatically retry itself on timeout
  329 + // Default is zero - timeout will stop the request
  330 + int numberOfTimesToRetryOnTimeout;
  331 +
  332 + // The number of times this request has retried (when numberOfTimesToRetryOnTimeout > 0)
  333 + int retryCount;
327 } 334 }
328 335
329 #pragma mark init / dealloc 336 #pragma mark init / dealloc
@@ -374,9 +381,6 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount; @@ -374,9 +381,6 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount;
374 381
375 #pragma mark request logic 382 #pragma mark request logic
376 383
377 -// Start the read stream. Called by loadRequest, and again to restart the request when authentication is needed  
378 -- (void)startRequest;  
379 -  
380 // Call to delete the temporary file used during a file download (if it exists) 384 // Call to delete the temporary file used during a file download (if it exists)
381 // No need to call this if the request succeeds - it is removed automatically 385 // No need to call this if the request succeeds - it is removed automatically
382 - (void)removeTemporaryDownloadFile; 386 - (void)removeTemporaryDownloadFile;
@@ -660,4 +664,6 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount; @@ -660,4 +664,6 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount;
660 @property (assign, readonly) BOOL isSynchronous; 664 @property (assign, readonly) BOOL isSynchronous;
661 @property (assign, readonly) BOOL inProgress; 665 @property (assign, readonly) BOOL inProgress;
662 @property (assign) BOOL shouldRunInBackgroundThread; 666 @property (assign) BOOL shouldRunInBackgroundThread;
  667 +@property (assign) int numberOfTimesToRetryOnTimeout;
  668 +@property (assign, readonly) int retryCount;
663 @end 669 @end
@@ -21,7 +21,7 @@ @@ -21,7 +21,7 @@
21 #import "ASIInputStream.h" 21 #import "ASIInputStream.h"
22 22
23 // Automatically set on build 23 // Automatically set on build
24 -NSString *ASIHTTPRequestVersion = @"v1.2-45 2009-12-18"; 24 +NSString *ASIHTTPRequestVersion = @"v1.2-51 2009-12-19";
25 25
26 NSString* const NetworkRequestErrorDomain = @"ASIHTTPRequestErrorDomain"; 26 NSString* const NetworkRequestErrorDomain = @"ASIHTTPRequestErrorDomain";
27 27
@@ -106,13 +106,16 @@ static BOOL isiPhoneOS2; @@ -106,13 +106,16 @@ static BOOL isiPhoneOS2;
106 106
107 - (void)destroyReadStream; 107 - (void)destroyReadStream;
108 - (void)scheduleReadStream; 108 - (void)scheduleReadStream;
109 -- (void)scheduleReadStream; 109 +- (void)unscheduleReadStream;
110 110
111 - (BOOL)askDelegateForCredentials; 111 - (BOOL)askDelegateForCredentials;
112 - (BOOL)askDelegateForProxyCredentials; 112 - (BOOL)askDelegateForProxyCredentials;
113 + (void)measureBandwidthUsage; 113 + (void)measureBandwidthUsage;
114 + (void)recordBandwidthUsage; 114 + (void)recordBandwidthUsage;
115 115
  116 +// Start the read stream. Called by loadRequest, and again to restart the request when authentication is needed
  117 +- (void)startRequest;
  118 +
116 #if TARGET_OS_IPHONE 119 #if TARGET_OS_IPHONE
117 + (void)registerForNetworkReachabilityNotifications; 120 + (void)registerForNetworkReachabilityNotifications;
118 + (void)unsubscribeFromNetworkReachabilityNotifications; 121 + (void)unsubscribeFromNetworkReachabilityNotifications;
@@ -150,6 +153,7 @@ static BOOL isiPhoneOS2; @@ -150,6 +153,7 @@ static BOOL isiPhoneOS2;
150 @property (retain) NSString *responseStatusMessage; 153 @property (retain) NSString *responseStatusMessage;
151 @property (assign) BOOL isSynchronous; 154 @property (assign) BOOL isSynchronous;
152 @property (assign) BOOL inProgress; 155 @property (assign) BOOL inProgress;
  156 +@property (assign) int retryCount;
153 @end 157 @end
154 158
155 159
@@ -533,7 +537,6 @@ static BOOL isiPhoneOS2; @@ -533,7 +537,6 @@ static BOOL isiPhoneOS2;
533 [pool release]; 537 [pool release];
534 } 538 }
535 539
536 -  
537 #pragma mark concurrency 540 #pragma mark concurrency
538 541
539 - (BOOL)isConcurrent 542 - (BOOL)isConcurrent
@@ -751,7 +754,7 @@ static BOOL isiPhoneOS2; @@ -751,7 +754,7 @@ static BOOL isiPhoneOS2;
751 [self setTotalBytesRead:0]; 754 [self setTotalBytesRead:0];
752 [self setLastBytesRead:0]; 755 [self setLastBytesRead:0];
753 756
754 - // If we're retrying a request after an authentication failure, let's remove any progress we made 757 + // If we're retrying a request, let's remove any progress we made
755 if ([self lastBytesSent] > 0) { 758 if ([self lastBytesSent] > 0) {
756 [self removeUploadProgressSoFar]; 759 [self removeUploadProgressSoFar];
757 } 760 }
@@ -967,6 +970,15 @@ static BOOL isiPhoneOS2; @@ -967,6 +970,15 @@ static BOOL isiPhoneOS2;
967 // This is to workaround the fact that kCFStreamPropertyHTTPRequestBytesWrittenCount is the amount written to the buffer, not the amount actually sent 970 // This is to workaround the fact that kCFStreamPropertyHTTPRequestBytesWrittenCount is the amount written to the buffer, not the amount actually sent
968 // This workaround prevents erroneous timeouts in low bandwidth situations (eg iPhone) 971 // This workaround prevents erroneous timeouts in low bandwidth situations (eg iPhone)
969 if (totalBytesSent || postLength <= uploadBufferSize || (uploadBufferSize > 0 && totalBytesSent > uploadBufferSize)) { 972 if (totalBytesSent || postLength <= uploadBufferSize || (uploadBufferSize > 0 && totalBytesSent > uploadBufferSize)) {
  973 +
  974 + // Do we need to auto-retry this request?
  975 + if ([self numberOfTimesToRetryOnTimeout] > [self retryCount]) {
  976 + [self setRetryCount:[self retryCount]+1];
  977 + [self unscheduleReadStream];
  978 + [[self cancelledLock] unlock];
  979 + [self startRequest];
  980 + return;
  981 + }
970 [self failWithError:ASIRequestTimedOutError]; 982 [self failWithError:ASIRequestTimedOutError];
971 [self cancelLoad]; 983 [self cancelLoad];
972 [self setComplete:YES]; 984 [self setComplete:YES];
@@ -3356,6 +3368,8 @@ static BOOL isiPhoneOS2; @@ -3356,6 +3368,8 @@ static BOOL isiPhoneOS2;
3356 @synthesize isSynchronous; 3368 @synthesize isSynchronous;
3357 @synthesize inProgress; 3369 @synthesize inProgress;
3358 @synthesize shouldRunInBackgroundThread; 3370 @synthesize shouldRunInBackgroundThread;
  3371 +@synthesize numberOfTimesToRetryOnTimeout;
  3372 +@synthesize retryCount;
3359 @end 3373 @end
3360 3374
3361 3375
@@ -57,4 +57,5 @@ @@ -57,4 +57,5 @@
57 #if TARGET_OS_IPHONE 57 #if TARGET_OS_IPHONE
58 - (void)testReachability; 58 - (void)testReachability;
59 #endif 59 #endif
  60 +- (void)testAutomaticRetry;
60 @end 61 @end
@@ -1216,4 +1216,15 @@ @@ -1216,4 +1216,15 @@
1216 } 1216 }
1217 } 1217 }
1218 #endif 1218 #endif
  1219 +
  1220 +- (void)testAutomaticRetry
  1221 +{
  1222 + ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]];
  1223 + [request setTimeOutSeconds:0.001];
  1224 + [request setNumberOfTimesToRetryOnTimeout:5];
  1225 + [request startSynchronous];
  1226 + GHAssertNotNil([request error],@"Request failed to timeout, cannot proceed with test");
  1227 + BOOL success = ([request retryCount] == 5);
  1228 + GHAssertTrue(success,@"Request failed to retry on timeout");
  1229 +}
1219 @end 1230 @end