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,194 +1647,196 @@ static BOOL isiPhoneOS2; @@ -1647,194 +1647,196 @@ static BOOL isiPhoneOS2;
1647 if (!message) { 1647 if (!message) {
1648 return; 1648 return;
1649 } 1649 }
1650 - if (CFHTTPMessageIsHeaderComplete(message)) { 1650 +
  1651 + // Make sure we've received all the headers
  1652 + if (!CFHTTPMessageIsHeaderComplete(message)) {
  1653 + CFRelease(message);
  1654 + return;
  1655 + }
1651 1656
1652 -#if DEBUG_REQUEST_STATUS 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];
1660 1665
1661 - CFRelease(headerFields); 1666 + CFRelease(headerFields);
1662 - [self setResponseStatusCode:CFHTTPMessageGetResponseStatusCode(message)]; 1667 + [self setResponseStatusCode:CFHTTPMessageGetResponseStatusCode(message)];
1663 - [self setResponseStatusMessage:[(NSString *)CFHTTPMessageCopyResponseStatusLine(message) autorelease]]; 1668 + [self setResponseStatusMessage:[(NSString *)CFHTTPMessageCopyResponseStatusLine(message) autorelease]];
1664 1669
1665 - // Is the server response a challenge for credentials? 1670 + // Is the server response a challenge for credentials?
1666 - if ([self responseStatusCode] == 401) { 1671 + if ([self responseStatusCode] == 401) {
1667 - [self setAuthenticationNeeded:ASIHTTPAuthenticationNeeded]; 1672 + [self setAuthenticationNeeded:ASIHTTPAuthenticationNeeded];
1668 - } else if ([self responseStatusCode] == 407) { 1673 + } else if ([self responseStatusCode] == 407) {
1669 - [self setAuthenticationNeeded:ASIProxyAuthenticationNeeded]; 1674 + [self setAuthenticationNeeded:ASIProxyAuthenticationNeeded];
1670 - } 1675 + }
1671 1676
1672 - // Authentication succeeded, or no authentication was required 1677 + // Authentication succeeded, or no authentication was required
1673 - if (![self authenticationNeeded]) { 1678 + if (![self authenticationNeeded]) {
1674 1679
1675 - // Did we get here without an authentication challenge? (which can happen when shouldPresentCredentialsBeforeChallenge is YES and basic auth was successful) 1680 + // Did we get here without an authentication challenge? (which can happen when shouldPresentCredentialsBeforeChallenge is YES and basic auth was successful)
1676 - if (!requestAuthentication && [self username] && [self password] && [self useSessionPersistence]) { 1681 + if (!requestAuthentication && [self username] && [self password] && [self useSessionPersistence]) {
1677 -  
1678 - NSMutableDictionary *newCredentials = [NSMutableDictionary dictionaryWithCapacity:2];  
1679 - [newCredentials setObject:[self username] forKey:(NSString *)kCFHTTPAuthenticationUsername];  
1680 - [newCredentials setObject:[self password] forKey:(NSString *)kCFHTTPAuthenticationPassword];  
1681 -  
1682 - // Store the credentials in the session  
1683 - NSMutableDictionary *sessionCredentials = [NSMutableDictionary dictionary];  
1684 - [sessionCredentials setObject:newCredentials forKey:@"Credentials"];  
1685 - [sessionCredentials setObject:[self url] forKey:@"URL"];  
1686 - [sessionCredentials setObject:(NSString *)kCFHTTPAuthenticationSchemeBasic forKey:@"AuthenticationScheme"];  
1687 - [[self class] storeAuthenticationCredentialsInSessionStore:sessionCredentials];  
1688 - }  
1689 1682
  1683 + NSMutableDictionary *newCredentials = [NSMutableDictionary dictionaryWithCapacity:2];
  1684 + [newCredentials setObject:[self username] forKey:(NSString *)kCFHTTPAuthenticationUsername];
  1685 + [newCredentials setObject:[self password] forKey:(NSString *)kCFHTTPAuthenticationPassword];
1690 1686
1691 - // See if we got a Content-length header 1687 + // Store the credentials in the session
1692 - NSString *cLength = [responseHeaders valueForKey:@"Content-Length"]; 1688 + NSMutableDictionary *sessionCredentials = [NSMutableDictionary dictionary];
1693 - if (cLength) { 1689 + [sessionCredentials setObject:newCredentials forKey:@"Credentials"];
1694 - SInt32 length = CFStringGetIntValue((CFStringRef)cLength); 1690 + [sessionCredentials setObject:[self url] forKey:@"URL"];
1695 - 1691 + [sessionCredentials setObject:(NSString *)kCFHTTPAuthenticationSchemeBasic forKey:@"AuthenticationScheme"];
1696 - // Workaround for Apache HEAD requests for dynamically generated content returning the wrong Content-Length when using gzip 1692 + [[self class] storeAuthenticationCredentialsInSessionStore:sessionCredentials];
1697 - if ([self mainRequest] && [self allowCompressedResponse] && length == 20 && [self showAccurateProgress] && [self shouldResetProgressIndicators]) { 1693 + }
1698 - [[self mainRequest] setShowAccurateProgress:NO]; 1694 + }
1699 - [self resetDownloadProgress:1]; 1695 +
1700 - 1696 + // See if we got a Content-length header
1701 - } else { 1697 + NSString *cLength = [responseHeaders valueForKey:@"Content-Length"];
1702 - [self setContentLength:length]; 1698 + if (cLength) {
1703 - if ([self mainRequest]) { 1699 + SInt32 length = CFStringGetIntValue((CFStringRef)cLength);
1704 - [[self mainRequest] setContentLength:length]; 1700 +
1705 - } 1701 + // Workaround for Apache HEAD requests for dynamically generated content returning the wrong Content-Length when using gzip
  1702 + if ([self mainRequest] && [self allowCompressedResponse] && length == 20 && [self showAccurateProgress] && [self shouldResetProgressIndicators]) {
  1703 + [[self mainRequest] setShowAccurateProgress:NO];
  1704 + [self resetDownloadProgress:1];
  1705 +
  1706 + } else {
  1707 + [self setContentLength:length];
  1708 + if ([self mainRequest]) {
  1709 + [[self mainRequest] setContentLength:length];
  1710 + }
1706 1711
1707 - if ([self showAccurateProgress] && [self shouldResetProgressIndicators]) { 1712 + if ([self showAccurateProgress] && [self shouldResetProgressIndicators]) {
1708 - [self resetDownloadProgress:[self contentLength]+[self partialDownloadSize]]; 1713 + [self resetDownloadProgress:[self contentLength]+[self partialDownloadSize]];
1709 - }  
1710 - }  
1711 -  
1712 - } else if ([self showAccurateProgress] && [self shouldResetProgressIndicators]) {  
1713 - [[self mainRequest] setShowAccurateProgress:NO];  
1714 - [self resetDownloadProgress:1];  
1715 } 1714 }
1716 - 1715 + }
1717 - // Handle response text encoding 1716 +
1718 - // If the Content-Type header specified an encoding, we'll use that, otherwise we use defaultStringEncoding (which defaults to NSISOLatin1StringEncoding) 1717 + } else if ([self showAccurateProgress] && [self shouldResetProgressIndicators]) {
1719 - NSString *contentType = [[self responseHeaders] objectForKey:@"Content-Type"]; 1718 + [[self mainRequest] setShowAccurateProgress:NO];
1720 - NSStringEncoding encoding = [self defaultResponseEncoding]; 1719 + [self resetDownloadProgress:1];
1721 - if (contentType) { 1720 + }
1722 -  
1723 - NSString *charsetSeparator = @"charset=";  
1724 - NSScanner *charsetScanner = [NSScanner scannerWithString: contentType];  
1725 - NSString *IANAEncoding = nil;  
1726 -  
1727 - if ([charsetScanner scanUpToString: charsetSeparator intoString: NULL] && [charsetScanner scanLocation] < [contentType length])  
1728 - {  
1729 - [charsetScanner setScanLocation: [charsetScanner scanLocation] + [charsetSeparator length]];  
1730 - [charsetScanner scanUpToString: @";" intoString: &IANAEncoding];  
1731 - }  
1732 1721
1733 - if (IANAEncoding) { 1722 + // Handle response text encoding
1734 - CFStringEncoding cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)IANAEncoding); 1723 + // If the Content-Type header specified an encoding, we'll use that, otherwise we use defaultStringEncoding (which defaults to NSISOLatin1StringEncoding)
1735 - if (cfEncoding != kCFStringEncodingInvalidId) { 1724 + NSString *contentType = [[self responseHeaders] objectForKey:@"Content-Type"];
1736 - encoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding); 1725 + NSStringEncoding encoding = [self defaultResponseEncoding];
1737 - } 1726 + if (contentType) {
1738 - } 1727 +
  1728 + NSString *charsetSeparator = @"charset=";
  1729 + NSScanner *charsetScanner = [NSScanner scannerWithString: contentType];
  1730 + NSString *IANAEncoding = nil;
  1731 +
  1732 + if ([charsetScanner scanUpToString: charsetSeparator intoString: NULL] && [charsetScanner scanLocation] < [contentType length])
  1733 + {
  1734 + [charsetScanner setScanLocation: [charsetScanner scanLocation] + [charsetSeparator length]];
  1735 + [charsetScanner scanUpToString: @";" intoString: &IANAEncoding];
  1736 + }
  1737 +
  1738 + if (IANAEncoding) {
  1739 + CFStringEncoding cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)IANAEncoding);
  1740 + if (cfEncoding != kCFStringEncodingInvalidId) {
  1741 + encoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding);
1739 } 1742 }
1740 - [self setResponseEncoding:encoding]; 1743 + }
  1744 + }
  1745 + [self setResponseEncoding:encoding];
  1746 +
  1747 + // Handle cookies
  1748 + NSArray *newCookies = [NSHTTPCookie cookiesWithResponseHeaderFields:responseHeaders forURL:url];
  1749 + [self setResponseCookies:newCookies];
  1750 +
  1751 + if ([self useCookiePersistence]) {
  1752 +
  1753 + // Store cookies in global persistent store
  1754 + [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookies:newCookies forURL:url mainDocumentURL:nil];
  1755 +
  1756 + // We also keep any cookies in the sessionCookies array, so that we have a reference to them if we need to remove them later
  1757 + NSHTTPCookie *cookie;
  1758 + for (cookie in newCookies) {
  1759 + [ASIHTTPRequest addSessionCookie:cookie];
  1760 + }
  1761 + }
  1762 +
  1763 + // Do we need to redirect?
  1764 + // Note that ASIHTTPRequest does not currently support 305 Use Proxy
  1765 + if ([self shouldRedirect] && [responseHeaders valueForKey:@"Location"]) {
  1766 + if (([self responseStatusCode] > 300 && [self responseStatusCode] < 304) || [self responseStatusCode] == 307) {
1741 1767
1742 - // Handle cookies 1768 + // By default, we redirect 301 and 302 response codes as GET requests
1743 - NSArray *newCookies = [NSHTTPCookie cookiesWithResponseHeaderFields:responseHeaders forURL:url]; 1769 + // According to RFC 2616 this is wrong, but this is what most browsers do, so it's probably what you're expecting to happen
1744 - [self setResponseCookies:newCookies]; 1770 + // See also:
  1771 + // http://allseeing-i.lighthouseapp.com/projects/27881/tickets/27-302-redirection-issue
  1772 +
  1773 + if ([self responseStatusCode] != 307 && (![self shouldUseRFC2616RedirectBehaviour] || [self responseStatusCode] == 303)) {
  1774 + [self setRequestMethod:@"GET"];
  1775 + [self setPostBody:nil];
  1776 + [self setPostLength:0];
  1777 + [self setRequestHeaders:nil];
  1778 + [self setHaveBuiltRequestHeaders:NO];
  1779 + } else {
1745 1780
1746 - if ([self useCookiePersistence]) { 1781 + // Force rebuild the cookie header incase we got some new cookies from this request
1747 - 1782 + // All other request headers will remain as they are for 301 / 302 redirects
1748 - // Store cookies in global persistent store 1783 + [self applyCookieHeader];
1749 - [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookies:newCookies forURL:url mainDocumentURL:nil];  
1750 -  
1751 - // We also keep any cookies in the sessionCookies array, so that we have a reference to them if we need to remove them later  
1752 - NSHTTPCookie *cookie;  
1753 - for (cookie in newCookies) {  
1754 - [ASIHTTPRequest addSessionCookie:cookie];  
1755 - }  
1756 } 1784 }
1757 - // Do we need to redirect?  
1758 - // Note that ASIHTTPRequest does not currently support 305 Use Proxy  
1759 - if ([self shouldRedirect] && [responseHeaders valueForKey:@"Location"]) {  
1760 - if (([self responseStatusCode] > 300 && [self responseStatusCode] < 304) || [self responseStatusCode] == 307) {  
1761 -  
1762 - // By default, we redirect 301 and 302 response codes as GET requests  
1763 - // According to RFC 2616 this is wrong, but this is what most browsers do, so it's probably what you're expecting to happen  
1764 - // See also:  
1765 - // http://allseeing-i.lighthouseapp.com/projects/27881/tickets/27-302-redirection-issue  
1766 -  
1767 - if ([self responseStatusCode] != 307 && (![self shouldUseRFC2616RedirectBehaviour] || [self responseStatusCode] == 303)) {  
1768 - [self setRequestMethod:@"GET"];  
1769 - [self setPostBody:nil];  
1770 - [self setPostLength:0];  
1771 - [self setRequestHeaders:nil];  
1772 - [self setHaveBuiltRequestHeaders:NO];  
1773 - } else {  
1774 -  
1775 - // Force rebuild the cookie header incase we got some new cookies from this request  
1776 - // All other request headers will remain as they are for 301 / 302 redirects  
1777 - [self applyCookieHeader];  
1778 - }  
1779 1785
1780 - // Force the redirected request to rebuild the request headers (if not a 303, it will re-use old ones, and add any new ones) 1786 + // Force the redirected request to rebuild the request headers (if not a 303, it will re-use old ones, and add any new ones)
1781 -  
1782 - [self setURL:[[NSURL URLWithString:[responseHeaders valueForKey:@"Location"] relativeToURL:[self url]] absoluteURL]];  
1783 - [self setNeedsRedirect:YES];  
1784 -  
1785 - // Clear the request cookies  
1786 - // This means manually added cookies will not be added to the redirect request - only those stored in the global persistent store  
1787 - // But, this is probably the safest option - we might be redirecting to a different domain  
1788 - [self setRequestCookies:[NSMutableArray array]];  
1789 -  
1790 - #if DEBUG_REQUEST_STATUS  
1791 - NSLog(@"Request will redirect (code: %hi): %@",[self responseStatusCode],self);  
1792 - #endif  
1793 -  
1794 - }  
1795 - }  
1796 1787
1797 - } 1788 + [self setURL:[[NSURL URLWithString:[responseHeaders valueForKey:@"Location"] relativeToURL:[self url]] absoluteURL]];
1798 - 1789 + [self setNeedsRedirect:YES];
1799 - // Handle connection persistence 1790 +
1800 - if ([self shouldAttemptPersistentConnection]) { 1791 + // Clear the request cookies
  1792 + // This means manually added cookies will not be added to the redirect request - only those stored in the global persistent store
  1793 + // But, this is probably the safest option - we might be redirecting to a different domain
  1794 + [self setRequestCookies:[NSMutableArray array]];
1801 1795
1802 - NSString *connectionHeader = [[[self responseHeaders] objectForKey:@"Connection"] lowercaseString]; 1796 + #if DEBUG_REQUEST_STATUS
1803 - NSString *httpVersion = [(NSString *)CFHTTPMessageCopyVersion(message) autorelease]; 1797 + NSLog(@"Request will redirect (code: %hi): %@",[self responseStatusCode],self);
  1798 + #endif
1804 1799
1805 - // Don't re-use the connection if the server is HTTP 1.0 and didn't send Connection: Keep-Alive 1800 + }
1806 - if (![httpVersion isEqualToString:(NSString *)kCFHTTPVersion1_0] || [connectionHeader isEqualToString:@"keep-alive"]) { 1801 + }
  1802 + // Handle connection persistence
  1803 + if ([self shouldAttemptPersistentConnection]) {
  1804 +
  1805 + NSString *connectionHeader = [[[self responseHeaders] objectForKey:@"Connection"] lowercaseString];
  1806 + NSString *httpVersion = [(NSString *)CFHTTPMessageCopyVersion(message) autorelease];
  1807 +
  1808 + // Don't re-use the connection if the server is HTTP 1.0 and didn't send Connection: Keep-Alive
  1809 + if (![httpVersion isEqualToString:(NSString *)kCFHTTPVersion1_0] || [connectionHeader isEqualToString:@"keep-alive"]) {
1807 1810
1808 - // See if server explicitly told us to close the connection 1811 + // See if server explicitly told us to close the connection
1809 - if (![connectionHeader isEqualToString:@"close"]) { 1812 + if (![connectionHeader isEqualToString:@"close"]) {
1810 - 1813 +
1811 - NSString *keepAliveHeader = [[self responseHeaders] objectForKey:@"Keep-Alive"]; 1814 + NSString *keepAliveHeader = [[self responseHeaders] objectForKey:@"Keep-Alive"];
1812 - 1815 +
1813 - // If we got a keep alive header, we'll reuse the connection for as long as the server tells us 1816 + // If we got a keep alive header, we'll reuse the connection for as long as the server tells us
1814 - if (keepAliveHeader) { 1817 + if (keepAliveHeader) {
1815 - int timeout = 0; 1818 + int timeout = 0;
1816 - int max = 0; 1819 + int max = 0;
1817 - NSScanner *scanner = [NSScanner scannerWithString:keepAliveHeader]; 1820 + NSScanner *scanner = [NSScanner scannerWithString:keepAliveHeader];
1818 - [scanner scanString:@"timeout=" intoString:NULL]; 1821 + [scanner scanString:@"timeout=" intoString:NULL];
1819 - [scanner scanInt:&timeout]; 1822 + [scanner scanInt:&timeout];
1820 - [scanner scanUpToString:@"max=" intoString:NULL]; 1823 + [scanner scanUpToString:@"max=" intoString:NULL];
1821 - [scanner scanString:@"max=" intoString:NULL]; 1824 + [scanner scanString:@"max=" intoString:NULL];
1822 - [scanner scanInt:&max]; 1825 + [scanner scanInt:&max];
1823 - if (max > 5) { 1826 + if (max > 5) {
1824 - [self setConnectionCanBeReused:YES];  
1825 - [self setPersistentConnectionTimeoutSeconds:timeout];  
1826 - #if DEBUG_PERSISTENT_CONNECTIONS  
1827 - NSLog(@"Got a keep-alive header, will keep this connection open for %f seconds", [self persistentConnectionTimeoutSeconds]);  
1828 - #endif  
1829 - }  
1830 -  
1831 - // Otherwise, we'll assume we can keep this connection open  
1832 - } else {  
1833 [self setConnectionCanBeReused:YES]; 1827 [self setConnectionCanBeReused:YES];
  1828 + [self setPersistentConnectionTimeoutSeconds:timeout];
1834 #if DEBUG_PERSISTENT_CONNECTIONS 1829 #if DEBUG_PERSISTENT_CONNECTIONS
1835 - NSLog(@"Got no keep-alive header, will keep this connection open for %f seconds", [self persistentConnectionTimeoutSeconds]); 1830 + NSLog(@"Got a keep-alive header, will keep this connection open for %f seconds", [self persistentConnectionTimeoutSeconds]);
1836 - #endif 1831 + #endif
1837 } 1832 }
  1833 +
  1834 + // Otherwise, we'll assume we can keep this connection open
  1835 + } else {
  1836 + [self setConnectionCanBeReused:YES];
  1837 + #if DEBUG_PERSISTENT_CONNECTIONS
  1838 + NSLog(@"Got no keep-alive header, will keep this connection open for %f seconds", [self persistentConnectionTimeoutSeconds]);
  1839 + #endif
1838 } 1840 }
1839 } 1841 }
1840 } 1842 }
@@ -2687,15 +2689,13 @@ static BOOL isiPhoneOS2; @@ -2687,15 +2689,13 @@ 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) { 2697 + if ([self retryUsingNewConnection]) {
2696 - if ([self retryUsingNewConnection]) { 2698 + return;
2697 - return;  
2698 - }  
2699 } 2699 }
2700 } 2700 }
2701 2701
@@ -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