Ben Copsey

Lots of changes to fix regressions

Redirect / authentication required requests download the full response before trying again, so they can re-use the connection
Invalidate the connection when a request fails to prevent subsequent requests getting stuck on a bad line
@@ -25,7 +25,6 @@ extern NSString *ASIHTTPRequestVersion; @@ -25,7 +25,6 @@ extern NSString *ASIHTTPRequestVersion;
25 #define __IPHONE_3_0 30000 25 #define __IPHONE_3_0 30000
26 #endif 26 #endif
27 27
28 -  
29 typedef enum _ASINetworkErrorType { 28 typedef enum _ASINetworkErrorType {
30 ASIConnectionFailureErrorType = 1, 29 ASIConnectionFailureErrorType = 1,
31 ASIRequestTimedOutErrorType = 2, 30 ASIRequestTimedOutErrorType = 2,
@@ -339,6 +338,10 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount; @@ -339,6 +338,10 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount;
339 338
340 // Set to yes when an appropriate keep-alive header is found 339 // Set to yes when an appropriate keep-alive header is found
341 BOOL canUsePersistentConnection; 340 BOOL canUsePersistentConnection;
  341 +
  342 + NSTimeInterval closeStreamTime;
  343 +
  344 + int usedStreamNumber;
342 } 345 }
343 346
344 #pragma mark init / dealloc 347 #pragma mark init / dealloc
@@ -431,8 +434,8 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount; @@ -431,8 +434,8 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount;
431 434
432 // Reads the response headers to find the content length, encoding, cookies for the session 435 // Reads the response headers to find the content length, encoding, cookies for the session
433 // Also initiates request redirection when shouldRedirect is true 436 // Also initiates request redirection when shouldRedirect is true
434 -// Returns true if the request needs a username and password (or if those supplied were incorrect) 437 +// And works out if HTTP auth is required
435 -- (BOOL)readResponseHeadersReturningAuthenticationFailure; 438 +- (void)readResponseHeaders;
436 439
437 #pragma mark http authentication stuff 440 #pragma mark http authentication stuff
438 441
@@ -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-54 2009-12-19"; 24 +NSString *ASIHTTPRequestVersion = @"v1.2-55 2009-12-20";
25 25
26 NSString* const NetworkRequestErrorDomain = @"ASIHTTPRequestErrorDomain"; 26 NSString* const NetworkRequestErrorDomain = @"ASIHTTPRequestErrorDomain";
27 27
@@ -58,6 +58,7 @@ static unsigned long averageBandwidthUsedPerSecond = 0; @@ -58,6 +58,7 @@ static unsigned long averageBandwidthUsedPerSecond = 0;
58 58
59 // These are used for queuing persistent connections on the same connection 59 // These are used for queuing persistent connections on the same connection
60 static unsigned char streamNumber = 0; 60 static unsigned char streamNumber = 0;
  61 +static unsigned char nextStreamNumberToCreate = 0;
61 static void *streamIDs[4]; 62 static void *streamIDs[4];
62 63
63 // Records how much bandwidth all requests combined have used in the last second 64 // Records how much bandwidth all requests combined have used in the last second
@@ -121,6 +122,7 @@ static BOOL isiPhoneOS2; @@ -121,6 +122,7 @@ static BOOL isiPhoneOS2;
121 - (void)startRequest; 122 - (void)startRequest;
122 123
123 - (void)markAsFinished; 124 - (void)markAsFinished;
  125 +- (void)redirectIfNeeded;
124 126
125 #if TARGET_OS_IPHONE 127 #if TARGET_OS_IPHONE
126 + (void)registerForNetworkReachabilityNotifications; 128 + (void)registerForNetworkReachabilityNotifications;
@@ -183,10 +185,9 @@ static BOOL isiPhoneOS2; @@ -183,10 +185,9 @@ static BOOL isiPhoneOS2;
183 ASIUnableToCreateRequestError = [[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIUnableToCreateRequestErrorType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Unable to create request (bad url?)",NSLocalizedDescriptionKey,nil]] retain]; 185 ASIUnableToCreateRequestError = [[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIUnableToCreateRequestErrorType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Unable to create request (bad url?)",NSLocalizedDescriptionKey,nil]] retain];
184 ASITooMuchRedirectionError = [[NSError errorWithDomain:NetworkRequestErrorDomain code:ASITooMuchRedirectionErrorType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"The request failed because it redirected too many times",NSLocalizedDescriptionKey,nil]] retain]; 186 ASITooMuchRedirectionError = [[NSError errorWithDomain:NetworkRequestErrorDomain code:ASITooMuchRedirectionErrorType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"The request failed because it redirected too many times",NSLocalizedDescriptionKey,nil]] retain];
185 187
186 - // IDs that will be used for the four streams we'll create (see 188 + // IDs that will be used for the four streams we'll create (see startRequest)
187 - char i; 189 + for (nextStreamNumberToCreate=0; nextStreamNumberToCreate<4; nextStreamNumberToCreate++) {
188 - for (i=0; i<4; i++) { 190 + streamIDs[nextStreamNumberToCreate] = (void *)CFNumberCreate(kCFAllocatorDefault, kCFNumberCharType, &nextStreamNumberToCreate);
189 - streamIDs[i] = (void *)CFNumberCreate(kCFAllocatorDefault, kCFNumberCharType, &i);  
190 } 191 }
191 #if TARGET_OS_IPHONE 192 #if TARGET_OS_IPHONE
192 isiPhoneOS2 = ((floorf([[[UIDevice currentDevice] systemVersion] floatValue]) == 2.0) ? YES : NO); 193 isiPhoneOS2 = ((floorf([[[UIDevice currentDevice] systemVersion] floatValue]) == 2.0) ? YES : NO);
@@ -232,6 +233,10 @@ static BOOL isiPhoneOS2; @@ -232,6 +233,10 @@ static BOOL isiPhoneOS2;
232 233
233 - (void)dealloc 234 - (void)dealloc
234 { 235 {
  236 + if (readStream && closeStreamTime) {
  237 + [NSTimer scheduledTimerWithTimeInterval:closeStreamTime target:[self class] selector:@selector(closePersistentConnection:) userInfo:(id)readStream repeats:NO];
  238 + }
  239 + [self destroyReadStream];
235 [self setAuthenticationChallengeInProgress:NO]; 240 [self setAuthenticationChallengeInProgress:NO];
236 if (requestAuthentication) { 241 if (requestAuthentication) {
237 CFRelease(requestAuthentication); 242 CFRelease(requestAuthentication);
@@ -622,11 +627,7 @@ static BOOL isiPhoneOS2; @@ -622,11 +627,7 @@ static BOOL isiPhoneOS2;
622 } 627 }
623 628
624 [self startRequest]; 629 [self startRequest];
625 - if ([self isSynchronous]) { 630 +
626 - [self loadSynchronous];  
627 - } else {  
628 - [self loadAsynchronous];  
629 - }  
630 } 631 }
631 632
632 - (void)applyAuthorizationHeader 633 - (void)applyAuthorizationHeader
@@ -788,6 +789,12 @@ static BOOL isiPhoneOS2; @@ -788,6 +789,12 @@ static BOOL isiPhoneOS2;
788 // Create the stream for the request 789 // Create the stream for the request
789 readStreamIsScheduled = NO; 790 readStreamIsScheduled = NO;
790 791
  792 + CFReadStreamRef oldStream = NULL;
  793 + if (readStream) {
  794 + oldStream = readStream;
  795 + readStream = NULL;
  796 + }
  797 +
791 // Do we need to stream the request body from disk 798 // Do we need to stream the request body from disk
792 if ([self shouldStreamPostDataFromDisk] && [self postBodyFilePath] && [[NSFileManager defaultManager] fileExistsAtPath:[self postBodyFilePath]]) { 799 if ([self shouldStreamPostDataFromDisk] && [self postBodyFilePath] && [[NSFileManager defaultManager] fileExistsAtPath:[self postBodyFilePath]]) {
793 800
@@ -830,17 +837,18 @@ static BOOL isiPhoneOS2; @@ -830,17 +837,18 @@ static BOOL isiPhoneOS2;
830 CFReadStreamSetProperty(readStream, kCFStreamPropertyHTTPAttemptPersistentConnection, kCFBooleanTrue); 837 CFReadStreamSetProperty(readStream, kCFStreamPropertyHTTPAttemptPersistentConnection, kCFBooleanTrue);
831 838
832 // Based on http://lists.apple.com/archives/macnetworkprog/2008/Dec/msg00001.html 839 // Based on http://lists.apple.com/archives/macnetworkprog/2008/Dec/msg00001.html
833 - // Basically, we aim to open a maximum of 4 connections (each one with a different id), and then subsequent requests will try to re-use the same stream, assuming we're connecting to the same server 840 + // Basically, we aim to open a maximum of 4 connections (each one with a different id), and then subsequent requests will try to re-use the same connection, assuming we're connecting to the same server
834 // I'm guessing this will perform less well when you're connecting to several different servers at once 841 // I'm guessing this will perform less well when you're connecting to several different servers at once
835 // But if you aren't, this appears to be the magic bullet for matching NSURLConnection's performance 842 // But if you aren't, this appears to be the magic bullet for matching NSURLConnection's performance
836 843
837 - // We will re-use the previous ID for a synchronous request, since that probably gives us a greater chance of maximimising connection re-use 844 + // We will re-use the previous ID for a synchronous request or when reconnecting/redirecting, since that probably gives us a greater chance of maximimising connection re-use
838 - if (![self isSynchronous]) { 845 + if (![self isSynchronous] && !oldStream) {
839 streamNumber++; 846 streamNumber++;
840 if (streamNumber == 4) { 847 if (streamNumber == 4) {
841 streamNumber = 0; 848 streamNumber = 0;
842 } 849 }
843 } 850 }
  851 + usedStreamNumber = streamNumber;
844 CFReadStreamSetProperty(readStream, CFSTR("ASIStreamID"), streamIDs[streamNumber]); 852 CFReadStreamSetProperty(readStream, CFSTR("ASIStreamID"), streamIDs[streamNumber]);
845 } 853 }
846 854
@@ -900,7 +908,6 @@ static BOOL isiPhoneOS2; @@ -900,7 +908,6 @@ static BOOL isiPhoneOS2;
900 NSMutableDictionary *proxyToUse = [NSMutableDictionary dictionaryWithObjectsAndKeys:[self proxyHost],kCFStreamPropertyHTTPProxyHost,[NSNumber numberWithInt:[self proxyPort]],kCFStreamPropertyHTTPProxyPort,nil]; 908 NSMutableDictionary *proxyToUse = [NSMutableDictionary dictionaryWithObjectsAndKeys:[self proxyHost],kCFStreamPropertyHTTPProxyHost,[NSNumber numberWithInt:[self proxyPort]],kCFStreamPropertyHTTPProxyPort,nil];
901 CFReadStreamSetProperty(readStream, kCFStreamPropertyHTTPProxy, proxyToUse); 909 CFReadStreamSetProperty(readStream, kCFStreamPropertyHTTPProxy, proxyToUse);
902 } 910 }
903 -  
904 // Set the client 911 // Set the client
905 CFStreamClientContext ctxt = {0, self, NULL, NULL, NULL}; 912 CFStreamClientContext ctxt = {0, self, NULL, NULL, NULL};
906 if (!CFReadStreamSetClient(readStream, kNetworkEvents, ReadStreamClientCallBack, &ctxt)) { 913 if (!CFReadStreamSetClient(readStream, kNetworkEvents, ReadStreamClientCallBack, &ctxt)) {
@@ -909,8 +916,8 @@ static BOOL isiPhoneOS2; @@ -909,8 +916,8 @@ static BOOL isiPhoneOS2;
909 [[self cancelledLock] unlock]; 916 [[self cancelledLock] unlock];
910 [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileBuildingRequestType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Unable to setup read stream",NSLocalizedDescriptionKey,nil]]]; 917 [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileBuildingRequestType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Unable to setup read stream",NSLocalizedDescriptionKey,nil]]];
911 return; 918 return;
912 - } 919 + }
913 - 920 +
914 // Start the HTTP connection 921 // Start the HTTP connection
915 if (!CFReadStreamOpen(readStream)) { 922 if (!CFReadStreamOpen(readStream)) {
916 [self destroyReadStream]; 923 [self destroyReadStream];
@@ -918,6 +925,17 @@ static BOOL isiPhoneOS2; @@ -918,6 +925,17 @@ static BOOL isiPhoneOS2;
918 [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileBuildingRequestType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Unable to start HTTP connection",NSLocalizedDescriptionKey,nil]]]; 925 [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileBuildingRequestType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Unable to start HTTP connection",NSLocalizedDescriptionKey,nil]]];
919 return; 926 return;
920 } 927 }
  928 + // Schedule the stream
  929 + if (!readStreamIsScheduled && (!throttleWakeUpTime || [throttleWakeUpTime timeIntervalSinceDate:[NSDate date]] < 0)) {
  930 + [self scheduleReadStream];
  931 + }
  932 +
  933 + if (oldStream) {
  934 + CFReadStreamClose(oldStream);
  935 + CFRelease(oldStream);
  936 + }
  937 +
  938 +
921 939
922 940
923 [[self cancelledLock] unlock]; 941 [[self cancelledLock] unlock];
@@ -938,16 +956,16 @@ static BOOL isiPhoneOS2; @@ -938,16 +956,16 @@ static BOOL isiPhoneOS2;
938 // Record when the request started, so we can timeout if nothing happens 956 // Record when the request started, so we can timeout if nothing happens
939 [self setLastActivityTime:[NSDate date]]; 957 [self setLastActivityTime:[NSDate date]];
940 958
  959 + if ([self isSynchronous]) {
  960 + [self loadSynchronous];
  961 + } else {
  962 + [self loadAsynchronous];
  963 + }
941 964
942 } 965 }
943 966
944 - (void)loadAsynchronous 967 - (void)loadAsynchronous
945 { 968 {
946 - // Schedule the stream  
947 - if (!readStreamIsScheduled && (!throttleWakeUpTime || [throttleWakeUpTime timeIntervalSinceDate:[NSDate date]] < 0)) {  
948 - [self scheduleReadStream];  
949 - }  
950 -  
951 [NSTimer scheduledTimerWithTimeInterval:0.25 target:self selector:@selector(updateStatus:) userInfo:nil repeats:YES]; 969 [NSTimer scheduledTimerWithTimeInterval:0.25 target:self selector:@selector(updateStatus:) userInfo:nil repeats:YES];
952 970
953 // If we're running asynchronously on the main thread, the runloop will already be running and we can return control 971 // If we're running asynchronously on the main thread, the runloop will already be running and we can return control
@@ -961,25 +979,45 @@ static BOOL isiPhoneOS2; @@ -961,25 +979,45 @@ static BOOL isiPhoneOS2;
961 // This is the main loop for synchronous requests. 979 // This is the main loop for synchronous requests.
962 - (void)loadSynchronous 980 - (void)loadSynchronous
963 { 981 {
964 - // Schedule the stream  
965 - if (!readStreamIsScheduled && (!throttleWakeUpTime || [throttleWakeUpTime timeIntervalSinceDate:[NSDate date]] < 0)) {  
966 - [self scheduleReadStream];  
967 - }  
968 -  
969 // If we don't need to track progress or throttle bandwidth, we won't bother to check up the status of the request (faster) 982 // If we don't need to track progress or throttle bandwidth, we won't bother to check up the status of the request (faster)
970 - if (downloadProgressDelegate || uploadProgressDelegate || queue || [[self class] isBandwidthThrottled]) { 983 + //if (downloadProgressDelegate || uploadProgressDelegate || queue || [[self class] isBandwidthThrottled]) {
971 - [NSTimer scheduledTimerWithTimeInterval:0.25 target:self selector:@selector(updateStatus:) userInfo:nil repeats:YES]; 984 + [NSTimer scheduledTimerWithTimeInterval:0.25 target:self selector:@selector(updateStatus:) userInfo:nil repeats:YES];
972 - } 985 +
973 - while (!complete) { 986 + //}
  987 + while (!complete && !needsRedirect) {
974 CFRunLoopRun(); 988 CFRunLoopRun();
975 } 989 }
  990 + if ([self authenticationChallengeInProgress]) {
  991 + [self attemptToApplyCredentialsAndResume];
  992 + return;
  993 + }
  994 + [self redirectIfNeeded];
  995 +}
  996 +
  997 +- (void)redirectIfNeeded
  998 +{
  999 + [[self cancelledLock] lock];
  1000 + // Do we need to redirect?
  1001 + if ([self needsRedirect]) {
976 1002
  1003 + [self setComplete:YES];
  1004 + [self setNeedsRedirect:NO];
  1005 + [self setRedirectCount:[self redirectCount]+1];
  1006 + if ([self redirectCount] > RedirectionLimit) {
  1007 + // Some naughty / badly coded website is trying to force us into a redirection loop. This is not cool.
  1008 + [self failWithError:ASITooMuchRedirectionError];
  1009 + [self setComplete:YES];
  1010 + } else {
  1011 + // Go all the way back to the beginning and build the request again, so that we can apply any new cookies
  1012 + [self main];
  1013 + }
  1014 + }
  1015 + [[self cancelledLock] unlock];
977 } 1016 }
978 1017
979 // This gets fired every 1/4 of a second in asynchronous requests to update the progress and work out if we need to timeout 1018 // This gets fired every 1/4 of a second in asynchronous requests to update the progress and work out if we need to timeout
980 - (void)updateStatus:(NSTimer*)timer 1019 - (void)updateStatus:(NSTimer*)timer
981 { 1020 {
982 - //NSLog(@"foo");  
983 [self checkRequestStatus]; 1021 [self checkRequestStatus];
984 if ([self complete] || [self error]) { 1022 if ([self complete] || [self error]) {
985 [timer invalidate]; 1023 [timer invalidate];
@@ -1026,24 +1064,6 @@ static BOOL isiPhoneOS2; @@ -1026,24 +1064,6 @@ static BOOL isiPhoneOS2;
1026 } 1064 }
1027 } 1065 }
1028 1066
1029 - // Do we need to redirect?  
1030 - if ([self needsRedirect]) {  
1031 - [self cancelLoad];  
1032 - [self setNeedsRedirect:NO];  
1033 - [self setRedirectCount:[self redirectCount]+1];  
1034 - if ([self redirectCount] > RedirectionLimit) {  
1035 - // Some naughty / badly coded website is trying to force us into a redirection loop. This is not cool.  
1036 - [self failWithError:ASITooMuchRedirectionError];  
1037 - [self setComplete:YES];  
1038 - [[self cancelledLock] unlock];  
1039 - } else {  
1040 - [[self cancelledLock] unlock];  
1041 - // Go all the way back to the beginning and build the request again, so that we can apply any new cookies  
1042 - [self main];  
1043 - }  
1044 - return;  
1045 - }  
1046 -  
1047 // readStream will be null if we aren't currently running (perhaps we're waiting for a delegate to supply credentials) 1067 // readStream will be null if we aren't currently running (perhaps we're waiting for a delegate to supply credentials)
1048 if (readStream) { 1068 if (readStream) {
1049 1069
@@ -1463,6 +1483,12 @@ static BOOL isiPhoneOS2; @@ -1463,6 +1483,12 @@ static BOOL isiPhoneOS2;
1463 #endif 1483 #endif
1464 [self setComplete:YES]; 1484 [self setComplete:YES];
1465 1485
  1486 + // Invalidate the current connection so subsequent requests don't attempt to reuse it
  1487 + if (theError && [theError code] != ASIAuthenticationErrorType && [theError code] != ASITooMuchRedirectionErrorType) {
  1488 + streamIDs[usedStreamNumber] = (void *)CFNumberCreate(kCFAllocatorDefault, kCFNumberCharType, &nextStreamNumberToCreate);
  1489 + nextStreamNumberToCreate++;
  1490 + }
  1491 +
1466 if ([self isCancelled] || [self error]) { 1492 if ([self isCancelled] || [self error]) {
1467 return; 1493 return;
1468 } 1494 }
@@ -1495,11 +1521,10 @@ static BOOL isiPhoneOS2; @@ -1495,11 +1521,10 @@ static BOOL isiPhoneOS2;
1495 1521
1496 #pragma mark parsing HTTP response headers 1522 #pragma mark parsing HTTP response headers
1497 1523
1498 -- (BOOL)readResponseHeadersReturningAuthenticationFailure 1524 +- (void)readResponseHeaders
1499 { 1525 {
1500 [self setAuthenticationChallengeInProgress:NO]; 1526 [self setAuthenticationChallengeInProgress:NO];
1501 [self setNeedsProxyAuthentication:NO]; 1527 [self setNeedsProxyAuthentication:NO];
1502 - BOOL isAuthenticationChallenge = NO;  
1503 CFHTTPMessageRef headers = (CFHTTPMessageRef)CFReadStreamCopyProperty(readStream, kCFStreamPropertyHTTPResponseHeader); 1528 CFHTTPMessageRef headers = (CFHTTPMessageRef)CFReadStreamCopyProperty(readStream, kCFStreamPropertyHTTPResponseHeader);
1504 if (CFHTTPMessageIsHeaderComplete(headers)) { 1529 if (CFHTTPMessageIsHeaderComplete(headers)) {
1505 1530
@@ -1511,16 +1536,16 @@ static BOOL isiPhoneOS2; @@ -1511,16 +1536,16 @@ static BOOL isiPhoneOS2;
1511 [self setResponseStatusMessage:[(NSString *)CFHTTPMessageCopyResponseStatusLine(headers) autorelease]]; 1536 [self setResponseStatusMessage:[(NSString *)CFHTTPMessageCopyResponseStatusLine(headers) autorelease]];
1512 1537
1513 // Is the server response a challenge for credentials? 1538 // Is the server response a challenge for credentials?
1514 - isAuthenticationChallenge = ([self responseStatusCode] == 401); 1539 + if ([self responseStatusCode] == 401) {
1515 - if ([self responseStatusCode] == 407) { 1540 + [self setAuthenticationChallengeInProgress:YES];
1516 - isAuthenticationChallenge = YES; 1541 + } else if ([self responseStatusCode] == 407) {
1517 [self setNeedsProxyAuthentication:YES]; 1542 [self setNeedsProxyAuthentication:YES];
  1543 + [self setAuthenticationChallengeInProgress:YES];
1518 } 1544 }
1519 - [self setAuthenticationChallengeInProgress:isAuthenticationChallenge];  
1520 1545
1521 // Authentication succeeded, or no authentication was required 1546 // Authentication succeeded, or no authentication was required
1522 - if (!isAuthenticationChallenge) { 1547 + if (!authenticationChallengeInProgress) {
1523 - 1548 +
1524 // Did we get here without an authentication challenge? (which can happen when shouldPresentCredentialsBeforeChallenge is YES and basic auth was successful) 1549 // Did we get here without an authentication challenge? (which can happen when shouldPresentCredentialsBeforeChallenge is YES and basic auth was successful)
1525 if (!requestAuthentication && [self username] && [self password] && [self useSessionPersistance]) { 1550 if (!requestAuthentication && [self username] && [self password] && [self useSessionPersistance]) {
1526 1551
@@ -1631,6 +1656,8 @@ static BOOL isiPhoneOS2; @@ -1631,6 +1656,8 @@ static BOOL isiPhoneOS2;
1631 #if DEBUG_REQUEST_STATUS 1656 #if DEBUG_REQUEST_STATUS
1632 NSLog(@"Request will redirect (code: %hi): %@",self,[self responseStatusCode]); 1657 NSLog(@"Request will redirect (code: %hi): %@",self,[self responseStatusCode]);
1633 #endif 1658 #endif
  1659 +
  1660 + //CFRunLoopStop(CFRunLoopGetCurrent());
1634 } 1661 }
1635 } 1662 }
1636 1663
@@ -1651,7 +1678,7 @@ static BOOL isiPhoneOS2; @@ -1651,7 +1678,7 @@ static BOOL isiPhoneOS2;
1651 if (max > 5) { 1678 if (max > 5) {
1652 [self setCanUsePersistentConnection:YES]; 1679 [self setCanUsePersistentConnection:YES];
1653 CFRetain(readStream); 1680 CFRetain(readStream);
1654 - [NSTimer scheduledTimerWithTimeInterval:max target:[self class] selector:@selector(closePersistentConnection:) userInfo:(id)readStream repeats:NO]; 1681 + closeStreamTime = max;
1655 } 1682 }
1656 } 1683 }
1657 } 1684 }
@@ -1660,7 +1687,6 @@ static BOOL isiPhoneOS2; @@ -1660,7 +1687,6 @@ static BOOL isiPhoneOS2;
1660 1687
1661 1688
1662 CFRelease(headers); 1689 CFRelease(headers);
1663 - return isAuthenticationChallenge;  
1664 } 1690 }
1665 1691
1666 #pragma mark http authentication 1692 #pragma mark http authentication
@@ -2277,14 +2303,9 @@ static BOOL isiPhoneOS2; @@ -2277,14 +2303,9 @@ static BOOL isiPhoneOS2;
2277 - (void)handleBytesAvailable 2303 - (void)handleBytesAvailable
2278 { 2304 {
2279 if (![self responseHeaders]) { 2305 if (![self responseHeaders]) {
2280 - if ([self readResponseHeadersReturningAuthenticationFailure]) { 2306 + [self readResponseHeaders];
2281 - [self attemptToApplyCredentialsAndResume];  
2282 - return;  
2283 - }  
2284 - }  
2285 - if ([self needsRedirect]) {  
2286 - return;  
2287 } 2307 }
  2308 +
2288 long long bufferSize = 16384; 2309 long long bufferSize = 16384;
2289 if (contentLength > 262144) { 2310 if (contentLength > 262144) {
2290 bufferSize = 262144; 2311 bufferSize = 262144;
@@ -2358,21 +2379,21 @@ static BOOL isiPhoneOS2; @@ -2358,21 +2379,21 @@ static BOOL isiPhoneOS2;
2358 { 2379 {
2359 //Try to read the headers (if this is a HEAD request handleBytesAvailable may not be called) 2380 //Try to read the headers (if this is a HEAD request handleBytesAvailable may not be called)
2360 if (![self responseHeaders]) { 2381 if (![self responseHeaders]) {
2361 - if ([self readResponseHeadersReturningAuthenticationFailure]) { 2382 + [self readResponseHeaders];
2362 - [self attemptToApplyCredentialsAndResume];  
2363 - return;  
2364 - }  
2365 - }  
2366 - if ([self needsRedirect]) {  
2367 - return;  
2368 } 2383 }
  2384 +
  2385 +
  2386 +
  2387 +
2369 [progressLock lock]; 2388 [progressLock lock];
2370 // Find out how much data we've uploaded so far 2389 // Find out how much data we've uploaded so far
2371 [self setTotalBytesSent:[[(NSNumber *)CFReadStreamCopyProperty(readStream, kCFStreamPropertyHTTPRequestBytesWrittenCount) autorelease] unsignedLongLongValue]]; 2390 [self setTotalBytesSent:[[(NSNumber *)CFReadStreamCopyProperty(readStream, kCFStreamPropertyHTTPRequestBytesWrittenCount) autorelease] unsignedLongLongValue]];
2372 [self setComplete:YES]; 2391 [self setComplete:YES];
2373 [self updateProgressIndicators]; 2392 [self updateProgressIndicators];
2374 2393
2375 - [self destroyReadStream]; 2394 + if (![self authenticationChallengeInProgress]) {
  2395 + [self destroyReadStream];
  2396 + }
2376 2397
2377 [[self postBodyReadStream] close]; 2398 [[self postBodyReadStream] close];
2378 2399
@@ -2417,12 +2438,18 @@ static BOOL isiPhoneOS2; @@ -2417,12 +2438,18 @@ static BOOL isiPhoneOS2;
2417 } 2438 }
2418 } 2439 }
2419 [progressLock unlock]; 2440 [progressLock unlock];
  2441 +
  2442 + if ([self needsRedirect] || [self authenticationChallengeInProgress]) {
  2443 + CFRunLoopStop(CFRunLoopGetCurrent());
  2444 + return;
  2445 + }
2420 2446
2421 if (fileError) { 2447 if (fileError) {
2422 [self failWithError:fileError]; 2448 [self failWithError:fileError];
2423 } else { 2449 } else {
2424 [self requestFinished]; 2450 [self requestFinished];
2425 } 2451 }
  2452 +
2426 [self markAsFinished]; 2453 [self markAsFinished];
2427 } 2454 }
2428 2455
@@ -2465,14 +2492,16 @@ static BOOL isiPhoneOS2; @@ -2465,14 +2492,16 @@ static BOOL isiPhoneOS2;
2465 { 2492 {
2466 if (readStream) { 2493 if (readStream) {
2467 CFReadStreamSetClient(readStream, kCFStreamEventNone, NULL, NULL); 2494 CFReadStreamSetClient(readStream, kCFStreamEventNone, NULL, NULL);
  2495 +
2468 if (readStreamIsScheduled) { 2496 if (readStreamIsScheduled) {
2469 CFReadStreamUnscheduleFromRunLoop(readStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); 2497 CFReadStreamUnscheduleFromRunLoop(readStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
2470 } 2498 }
2471 if (!canUsePersistentConnection) { 2499 if (!canUsePersistentConnection) {
2472 CFReadStreamClose(readStream); 2500 CFReadStreamClose(readStream);
  2501 + CFRelease(readStream);
  2502 + readStream = NULL;
2473 } 2503 }
2474 - CFRelease(readStream); 2504 +
2475 - readStream = NULL;  
2476 } 2505 }
2477 } 2506 }
2478 2507
@@ -320,7 +320,7 @@ @@ -320,7 +320,7 @@
320 320
321 request2 = [ASIFormDataRequest requestWithURL:url]; 321 request2 = [ASIFormDataRequest requestWithURL:url];
322 [request2 setPostValue:@"foo" forKey:@"eep"]; 322 [request2 setPostValue:@"foo" forKey:@"eep"];
323 - [request2 start]; 323 + [request2 startSynchronous];
324 324
325 NSString *method = @"GET"; 325 NSString *method = @"GET";
326 if (i>304) { 326 if (i>304) {