Ben Copsey

Retry automatically on 'connection lost'

@@ -23,7 +23,7 @@ @@ -23,7 +23,7 @@
23 23
24 24
25 // Automatically set on build 25 // Automatically set on build
26 -NSString *ASIHTTPRequestVersion = @"v1.6-15 2010-03-18"; 26 +NSString *ASIHTTPRequestVersion = @"v1.6-16 2010-03-19";
27 27
28 NSString* const NetworkRequestErrorDomain = @"ASIHTTPRequestErrorDomain"; 28 NSString* const NetworkRequestErrorDomain = @"ASIHTTPRequestErrorDomain";
29 29
@@ -1647,13 +1647,18 @@ static BOOL isiPhoneOS2; @@ -1647,13 +1647,18 @@ static BOOL isiPhoneOS2;
1647 if (!message) { 1647 if (!message) {
1648 return; 1648 return;
1649 } 1649 }
1650 - if (CFHTTPMessageIsHeaderComplete(message)) {  
1651 1650
1652 -#if DEBUG_REQUEST_STATUS 1651 + // Make sure we've received all the headers
  1652 + if (!CFHTTPMessageIsHeaderComplete(message)) {
  1653 + CFRelease(message);
  1654 + return;
  1655 + }
  1656 +
  1657 + #if DEBUG_REQUEST_STATUS
1653 if ([self totalBytesSent] == [self postLength]) { 1658 if ([self totalBytesSent] == [self postLength]) {
1654 NSLog(@"Request %@ received response headers",self); 1659 NSLog(@"Request %@ received response headers",self);
1655 } 1660 }
1656 -#endif 1661 + #endif
1657 1662
1658 CFDictionaryRef headerFields = CFHTTPMessageCopyAllHeaderFields(message); 1663 CFDictionaryRef headerFields = CFHTTPMessageCopyAllHeaderFields(message);
1659 [self setResponseHeaders:(NSDictionary *)headerFields]; 1664 [self setResponseHeaders:(NSDictionary *)headerFields];
@@ -1686,7 +1691,7 @@ static BOOL isiPhoneOS2; @@ -1686,7 +1691,7 @@ static BOOL isiPhoneOS2;
1686 [sessionCredentials setObject:(NSString *)kCFHTTPAuthenticationSchemeBasic forKey:@"AuthenticationScheme"]; 1691 [sessionCredentials setObject:(NSString *)kCFHTTPAuthenticationSchemeBasic forKey:@"AuthenticationScheme"];
1687 [[self class] storeAuthenticationCredentialsInSessionStore:sessionCredentials]; 1692 [[self class] storeAuthenticationCredentialsInSessionStore:sessionCredentials];
1688 } 1693 }
1689 - 1694 + }
1690 1695
1691 // See if we got a Content-length header 1696 // See if we got a Content-length header
1692 NSString *cLength = [responseHeaders valueForKey:@"Content-Length"]; 1697 NSString *cLength = [responseHeaders valueForKey:@"Content-Length"];
@@ -1754,6 +1759,7 @@ static BOOL isiPhoneOS2; @@ -1754,6 +1759,7 @@ static BOOL isiPhoneOS2;
1754 [ASIHTTPRequest addSessionCookie:cookie]; 1759 [ASIHTTPRequest addSessionCookie:cookie];
1755 } 1760 }
1756 } 1761 }
  1762 +
1757 // Do we need to redirect? 1763 // Do we need to redirect?
1758 // Note that ASIHTTPRequest does not currently support 305 Use Proxy 1764 // Note that ASIHTTPRequest does not currently support 305 Use Proxy
1759 if ([self shouldRedirect] && [responseHeaders valueForKey:@"Location"]) { 1765 if ([self shouldRedirect] && [responseHeaders valueForKey:@"Location"]) {
@@ -1793,9 +1799,6 @@ static BOOL isiPhoneOS2; @@ -1793,9 +1799,6 @@ static BOOL isiPhoneOS2;
1793 1799
1794 } 1800 }
1795 } 1801 }
1796 -  
1797 - }  
1798 -  
1799 // Handle connection persistence 1802 // Handle connection persistence
1800 if ([self shouldAttemptPersistentConnection]) { 1803 if ([self shouldAttemptPersistentConnection]) {
1801 1804
@@ -1838,7 +1841,6 @@ static BOOL isiPhoneOS2; @@ -1838,7 +1841,6 @@ static BOOL isiPhoneOS2;
1838 } 1841 }
1839 } 1842 }
1840 } 1843 }
1841 - }  
1842 1844
1843 1845
1844 1846
@@ -2687,17 +2689,15 @@ static BOOL isiPhoneOS2; @@ -2687,17 +2689,15 @@ static BOOL isiPhoneOS2;
2687 2689
2688 if (![self error]) { // We may already have handled this error 2690 if (![self error]) { // We may already have handled this error
2689 2691
2690 - // First, check for a socket not connected error 2692 + // First, check for a 'socket not connected' or 'connection lost' error
2691 // This may occur when we've attempted to reuse a connection that should have been closed 2693 // This may occur when we've attempted to reuse a connection that should have been closed
2692 // If we get this, we need to retry the request 2694 // If we get this, we need to retry the request
2693 // We'll only do this once - if it happens again on retry, we'll give up 2695 // We'll only do this once - if it happens again on retry, we'll give up
2694 - if ([[underlyingError domain] isEqualToString:NSPOSIXErrorDomain]) { 2696 + if (([[underlyingError domain] isEqualToString:NSPOSIXErrorDomain] && [underlyingError code] == ENOTCONN) || ([[underlyingError domain] isEqualToString:(NSString *)kCFErrorDomainCFNetwork] && [underlyingError code] == kCFURLErrorNetworkConnectionLost)) {
2695 - if ([underlyingError code] == ENOTCONN) {  
2696 if ([self retryUsingNewConnection]) { 2697 if ([self retryUsingNewConnection]) {
2697 return; 2698 return;
2698 } 2699 }
2699 } 2700 }
2700 - }  
2701 2701
2702 NSString *reason = @"A connection failure occurred"; 2702 NSString *reason = @"A connection failure occurred";
2703 2703
@@ -2706,7 +2706,7 @@ static BOOL isiPhoneOS2; @@ -2706,7 +2706,7 @@ static BOOL isiPhoneOS2;
2706 // Also, iPhone seems to handle errors differently from Mac OS X - a self-signed certificate returns a different error code on each platform, so we'll just provide a general error 2706 // Also, iPhone seems to handle errors differently from Mac OS X - a self-signed certificate returns a different error code on each platform, so we'll just provide a general error
2707 if ([[underlyingError domain] isEqualToString:NSOSStatusErrorDomain]) { 2707 if ([[underlyingError domain] isEqualToString:NSOSStatusErrorDomain]) {
2708 if ([underlyingError code] <= -9800 && [underlyingError code] >= -9818) { 2708 if ([underlyingError code] <= -9800 && [underlyingError code] >= -9818) {
2709 - reason = [NSString stringWithFormat:@"%@: SSL problem (possibily a bad/expired/self-signed certificate)",reason]; 2709 + reason = [NSString stringWithFormat:@"%@: SSL problem (possibly a bad/expired/self-signed certificate)",reason];
2710 } 2710 }
2711 } 2711 }
2712 2712
@@ -1464,6 +1464,13 @@ @@ -1464,6 +1464,13 @@
1464 BOOL success = ![request connectionCanBeReused]; 1464 BOOL success = ![request connectionCanBeReused];
1465 GHAssertTrue(success,@"Should not be able to re-use a request sent with Connection:close"); 1465 GHAssertTrue(success,@"Should not be able to re-use a request sent with Connection:close");
1466 1466
  1467 + // Ensure we close the connection when authentication is needed
  1468 + request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://asi/ASIHTTPRequest/tests/close-connection-auth-needed"]];
  1469 + [request startSynchronous];
  1470 +
  1471 + success = ![request connectionCanBeReused];
  1472 + GHAssertTrue(success,@"Should not be able to re-use a request sent with Connection:close");
  1473 +
1467 } 1474 }
1468 1475
1469 - (void)testPersistentConnectionTimeout 1476 - (void)testPersistentConnectionTimeout