Ben Copsey

Added timeout support

@@ -122,7 +122,10 @@ @@ -122,7 +122,10 @@
122 //Called on the delegate when the request fails 122 //Called on the delegate when the request fails
123 SEL didFailSelector; 123 SEL didFailSelector;
124 124
  125 + //Used for recording when something last happened during the request, we will compare this value with the current date to time out requests when appropriate
  126 + NSDate *lastActivityTime;
125 127
  128 + NSTimeInterval timeOutSeconds;
126 } 129 }
127 130
128 #pragma mark init / dealloc 131 #pragma mark init / dealloc
@@ -254,5 +257,6 @@ @@ -254,5 +257,6 @@
254 @property (retain) NSDictionary *requestCredentials; 257 @property (retain) NSDictionary *requestCredentials;
255 @property (assign) int responseStatusCode; 258 @property (assign) int responseStatusCode;
256 @property (retain) NSMutableData *receivedData; 259 @property (retain) NSMutableData *receivedData;
257 - 260 +@property (retain) NSDate *lastActivityTime;
  261 +@property (assign) NSTimeInterval timeOutSeconds;
258 @end 262 @end
@@ -51,6 +51,7 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy @@ -51,6 +51,7 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
51 //credentials = NULL; 51 //credentials = NULL;
52 request = NULL; 52 request = NULL;
53 responseHeaders = nil; 53 responseHeaders = nil;
  54 + [self setTimeOutSeconds:30];
54 [self setUseKeychainPersistance:NO]; 55 [self setUseKeychainPersistance:NO];
55 [self setUseSessionPersistance:YES]; 56 [self setUseSessionPersistance:YES];
56 [self setUseCookiePersistance:YES]; 57 [self setUseCookiePersistance:YES];
@@ -83,6 +84,7 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy @@ -83,6 +84,7 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
83 [authenticationRealm release]; 84 [authenticationRealm release];
84 [url release]; 85 [url release];
85 [authenticationLock release]; 86 [authenticationLock release];
  87 + [lastActivityTime release];
86 [super dealloc]; 88 [super dealloc];
87 } 89 }
88 90
@@ -304,11 +306,23 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy @@ -304,11 +306,23 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
304 [self performSelectorOnMainThread:@selector(resetUploadProgress:) withObject:[NSNumber numberWithDouble:postLength] waitUntilDone:YES]; 306 [self performSelectorOnMainThread:@selector(resetUploadProgress:) withObject:[NSNumber numberWithDouble:postLength] waitUntilDone:YES];
305 } 307 }
306 308
  309 + //Record when the request started, so we can timeout if nothing happens
  310 + [self setLastActivityTime:[[NSDate new] autorelease]];
307 311
308 // Wait for the request to finish 312 // Wait for the request to finish
309 NSDate* endDate = [NSDate distantFuture]; 313 NSDate* endDate = [NSDate distantFuture];
310 while (!complete) { 314 while (!complete) {
311 315
  316 + //See if we need to timeout
  317 + if (lastActivityTime && timeOutSeconds > 0) {
  318 + if ([[[NSDate new] autorelease] timeIntervalSinceDate:lastActivityTime] > timeOutSeconds) {
  319 + [self failWithProblem:@"Request timed out"];
  320 + [self cancelLoad];
  321 + complete = YES;
  322 + break;
  323 + }
  324 + }
  325 +
312 // See if our NSOperationQueue told us to cancel 326 // See if our NSOperationQueue told us to cancel
313 if ([self isCancelled]) { 327 if ([self isCancelled]) {
314 [self failWithProblem:@"The request was cancelled"]; 328 [self failWithProblem:@"The request was cancelled"];
@@ -366,6 +380,8 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy @@ -366,6 +380,8 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
366 380
367 - (void)updateUploadProgress 381 - (void)updateUploadProgress
368 { 382 {
  383 + [self setLastActivityTime:[[NSDate new] autorelease]];
  384 +
369 double byteCount = [[(NSNumber *)CFReadStreamCopyProperty (readStream, kCFStreamPropertyHTTPRequestBytesWrittenCount) autorelease] doubleValue]; 385 double byteCount = [[(NSNumber *)CFReadStreamCopyProperty (readStream, kCFStreamPropertyHTTPRequestBytesWrittenCount) autorelease] doubleValue];
370 if (uploadProgressDelegate) { 386 if (uploadProgressDelegate) {
371 [uploadProgressDelegate incrementBy:byteCount-lastBytesSent]; 387 [uploadProgressDelegate incrementBy:byteCount-lastBytesSent];
@@ -385,6 +401,8 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy @@ -385,6 +401,8 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
385 401
386 - (void)updateDownloadProgress 402 - (void)updateDownloadProgress
387 { 403 {
  404 + [self setLastActivityTime:[[NSDate new] autorelease]];
  405 +
388 //We won't update downlaod progress until we've examined the headers, since we might need to authenticate 406 //We won't update downlaod progress until we've examined the headers, since we might need to authenticate
389 if (downloadProgressDelegate && responseHeaders) { 407 if (downloadProgressDelegate && responseHeaders) {
390 [downloadProgressDelegate incrementBy:totalBytesRead-lastBytesRead]; 408 [downloadProgressDelegate incrementBy:totalBytesRead-lastBytesRead];
@@ -586,6 +604,7 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy @@ -586,6 +604,7 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
586 // check for bad credentials, so we can give the delegate a chance to replace them 604 // check for bad credentials, so we can give the delegate a chance to replace them
587 if (err.domain == kCFStreamErrorDomainHTTP && (err.error == kCFStreamErrorHTTPAuthenticationBadUserName || err.error == kCFStreamErrorHTTPAuthenticationBadPassword)) { 605 if (err.domain == kCFStreamErrorDomainHTTP && (err.error == kCFStreamErrorHTTPAuthenticationBadUserName || err.error == kCFStreamErrorHTTPAuthenticationBadPassword)) {
588 ignoreError = YES; 606 ignoreError = YES;
  607 + [self setLastActivityTime:nil];
589 if ([delegate respondsToSelector:@selector(authorizationNeededForRequest:)]) { 608 if ([delegate respondsToSelector:@selector(authorizationNeededForRequest:)]) {
590 [delegate performSelectorOnMainThread:@selector(authorizationNeededForRequest:) withObject:self waitUntilDone:YES]; 609 [delegate performSelectorOnMainThread:@selector(authorizationNeededForRequest:) withObject:self waitUntilDone:YES];
591 [authenticationLock lockWhenCondition:2]; 610 [authenticationLock lockWhenCondition:2];
@@ -863,4 +882,6 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy @@ -863,4 +882,6 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
863 @synthesize requestCredentials; 882 @synthesize requestCredentials;
864 @synthesize responseStatusCode; 883 @synthesize responseStatusCode;
865 @synthesize receivedData; 884 @synthesize receivedData;
  885 +@synthesize lastActivityTime;
  886 +@synthesize timeOutSeconds;
866 @end 887 @end
@@ -13,6 +13,7 @@ @@ -13,6 +13,7 @@
13 } 13 }
14 14
15 - (void)testBasicDownload; 15 - (void)testBasicDownload;
  16 +- (void)testTimeOut;
16 - (void)testOperationQueue; 17 - (void)testOperationQueue;
17 - (void)testCookies; 18 - (void)testCookies;
18 @end 19 @end
@@ -58,6 +58,20 @@ More tests needed for: @@ -58,6 +58,20 @@ More tests needed for:
58 STAssertNotNil(error,@"Failed to generate an error for a bad host - this test may fail when your DNS server redirects you to another page when it can't find a domain name (eg OpenDNS)"); 58 STAssertNotNil(error,@"Failed to generate an error for a bad host - this test may fail when your DNS server redirects you to another page when it can't find a domain name (eg OpenDNS)");
59 } 59 }
60 60
  61 +- (void)testTimeOut
  62 +{
  63 + //Grab data
  64 + NSURL *url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com"] autorelease];
  65 + ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
  66 + [request setTimeOutSeconds:0.0001]; //It's pretty unlikely we will be able to grab the data this quickly, so the request should timeout
  67 + [request start];
  68 + NSError *error = [request error];
  69 + STAssertNotNil(error,@"Request didn't timeout");
  70 + BOOL success = [[[error userInfo] valueForKey:@"Description"] isEqualToString:@"Request timed out"];
  71 + STAssertTrue(success,@"Timeout didn't generate the correct error");
  72 +
  73 +}
  74 +
61 - (void)testOperationQueue 75 - (void)testOperationQueue
62 { 76 {
63 NSOperationQueue *queue = [[[NSOperationQueue alloc] init] autorelease]; 77 NSOperationQueue *queue = [[[NSOperationQueue alloc] init] autorelease];
@@ -40,4 +40,5 @@ @@ -40,4 +40,5 @@
40 - (IBAction)fetchTopSecretInformation:(id)sender; 40 - (IBAction)fetchTopSecretInformation:(id)sender;
41 41
42 - (IBAction)postWithProgress:(id)sender; 42 - (IBAction)postWithProgress:(id)sender;
  43 +
43 @end 44 @end
This diff is collapsed. Click to expand it.
This diff could not be displayed because it is too large.