Restructure authentication retries to make NTLM work
Moved stream setup into startRequest to help with above
Showing
6 changed files
with
85 additions
and
14 deletions
@@ -113,6 +113,8 @@ typedef enum _ASINetworkErrorType { | @@ -113,6 +113,8 @@ typedef enum _ASINetworkErrorType { | ||
113 | // Authentication currently being used for prompting and resuming | 113 | // Authentication currently being used for prompting and resuming |
114 | CFHTTPAuthenticationRef requestAuthentication; | 114 | CFHTTPAuthenticationRef requestAuthentication; |
115 | NSMutableDictionary *requestCredentials; | 115 | NSMutableDictionary *requestCredentials; |
116 | + int authenticationRetryCount; | ||
117 | + NSString *authenticationMethod; | ||
116 | 118 | ||
117 | // HTTP status code, eg: 200 = OK, 404 = Not found etc | 119 | // HTTP status code, eg: 200 = OK, 404 = Not found etc |
118 | int responseStatusCode; | 120 | int responseStatusCode; |
@@ -204,9 +206,12 @@ typedef enum _ASINetworkErrorType { | @@ -204,9 +206,12 @@ typedef enum _ASINetworkErrorType { | ||
204 | 206 | ||
205 | #pragma mark request logic | 207 | #pragma mark request logic |
206 | 208 | ||
207 | -// Start loading the request | 209 | +// Main request loop is in here |
208 | - (void)loadRequest; | 210 | - (void)loadRequest; |
209 | 211 | ||
212 | +// Start the read stream. Called by loadRequest, and again to restart the request when authentication is needed | ||
213 | +- (void)startRequest; | ||
214 | + | ||
210 | // Cancel loading and clean up | 215 | // Cancel loading and clean up |
211 | - (void)cancelLoad; | 216 | - (void)cancelLoad; |
212 | 217 |
@@ -61,6 +61,7 @@ static NSError *ASIUnableToCreateRequestError; | @@ -61,6 +61,7 @@ static NSError *ASIUnableToCreateRequestError; | ||
61 | self = [super init]; | 61 | self = [super init]; |
62 | [self setRequestMethod:@"GET"]; | 62 | [self setRequestMethod:@"GET"]; |
63 | lastBytesSent = 0; | 63 | lastBytesSent = 0; |
64 | + | ||
64 | showAccurateProgress = YES; | 65 | showAccurateProgress = YES; |
65 | shouldResetProgressIndicators = YES; | 66 | shouldResetProgressIndicators = YES; |
66 | updatedProgress = NO; | 67 | updatedProgress = NO; |
@@ -68,6 +69,8 @@ static NSError *ASIUnableToCreateRequestError; | @@ -68,6 +69,8 @@ static NSError *ASIUnableToCreateRequestError; | ||
68 | [self setPassword:nil]; | 69 | [self setPassword:nil]; |
69 | [self setUsername:nil]; | 70 | [self setUsername:nil]; |
70 | [self setRequestHeaders:nil]; | 71 | [self setRequestHeaders:nil]; |
72 | + authenticationRetryCount = 0; | ||
73 | + authenticationMethod = nil; | ||
71 | authenticationRealm = nil; | 74 | authenticationRealm = nil; |
72 | outputStream = nil; | 75 | outputStream = nil; |
73 | requestAuthentication = NULL; | 76 | requestAuthentication = NULL; |
@@ -120,6 +123,7 @@ static NSError *ASIUnableToCreateRequestError; | @@ -120,6 +123,7 @@ static NSError *ASIUnableToCreateRequestError; | ||
120 | [responseHeaders release]; | 123 | [responseHeaders release]; |
121 | [requestMethod release]; | 124 | [requestMethod release]; |
122 | [cancelledLock release]; | 125 | [cancelledLock release]; |
126 | + [authenticationMethod release]; | ||
123 | [super dealloc]; | 127 | [super dealloc]; |
124 | } | 128 | } |
125 | 129 | ||
@@ -291,11 +295,8 @@ static NSError *ASIUnableToCreateRequestError; | @@ -291,11 +295,8 @@ static NSError *ASIUnableToCreateRequestError; | ||
291 | 295 | ||
292 | } | 296 | } |
293 | 297 | ||
294 | - | 298 | +- (void)startRequest |
295 | -// Start the request | ||
296 | -- (void)loadRequest | ||
297 | { | 299 | { |
298 | - | ||
299 | [cancelledLock lock]; | 300 | [cancelledLock lock]; |
300 | 301 | ||
301 | if ([self isCancelled]) { | 302 | if ([self isCancelled]) { |
@@ -362,8 +363,15 @@ static NSError *ASIUnableToCreateRequestError; | @@ -362,8 +363,15 @@ static NSError *ASIUnableToCreateRequestError; | ||
362 | amount = postLength; | 363 | amount = postLength; |
363 | } | 364 | } |
364 | [self resetUploadProgress:amount]; | 365 | [self resetUploadProgress:amount]; |
365 | - } | 366 | + } |
366 | - | 367 | +} |
368 | + | ||
369 | +// Start the request | ||
370 | +- (void)loadRequest | ||
371 | +{ | ||
372 | + | ||
373 | + | ||
374 | + [self startRequest]; | ||
367 | 375 | ||
368 | 376 | ||
369 | // Record when the request started, so we can timeout if nothing happens | 377 | // Record when the request started, so we can timeout if nothing happens |
@@ -831,7 +839,8 @@ static NSError *ASIUnableToCreateRequestError; | @@ -831,7 +839,8 @@ static NSError *ASIUnableToCreateRequestError; | ||
831 | 839 | ||
832 | - (BOOL)applyCredentials:(NSMutableDictionary *)newCredentials | 840 | - (BOOL)applyCredentials:(NSMutableDictionary *)newCredentials |
833 | { | 841 | { |
834 | - | 842 | + authenticationRetryCount++; |
843 | + | ||
835 | if (newCredentials && requestAuthentication && request) { | 844 | if (newCredentials && requestAuthentication && request) { |
836 | // Apply whatever credentials we've built up to the old request | 845 | // Apply whatever credentials we've built up to the old request |
837 | if (CFHTTPMessageApplyCredentialDictionary(request, requestAuthentication, (CFMutableDictionaryRef)newCredentials, NULL)) { | 846 | if (CFHTTPMessageApplyCredentialDictionary(request, requestAuthentication, (CFMutableDictionaryRef)newCredentials, NULL)) { |
@@ -846,10 +855,10 @@ static NSError *ASIUnableToCreateRequestError; | @@ -846,10 +855,10 @@ static NSError *ASIUnableToCreateRequestError; | ||
846 | [ASIHTTPRequest setSessionCredentials:newCredentials]; | 855 | [ASIHTTPRequest setSessionCredentials:newCredentials]; |
847 | } | 856 | } |
848 | [self setRequestCredentials:newCredentials]; | 857 | [self setRequestCredentials:newCredentials]; |
849 | - return TRUE; | 858 | + return YES; |
850 | } | 859 | } |
851 | } | 860 | } |
852 | - return FALSE; | 861 | + return NO; |
853 | } | 862 | } |
854 | 863 | ||
855 | - (NSMutableDictionary *)findCredentials | 864 | - (NSMutableDictionary *)findCredentials |
@@ -858,6 +867,9 @@ static NSError *ASIUnableToCreateRequestError; | @@ -858,6 +867,9 @@ static NSError *ASIUnableToCreateRequestError; | ||
858 | 867 | ||
859 | // Is an account domain needed? (used currently for NTLM only) | 868 | // Is an account domain needed? (used currently for NTLM only) |
860 | if (CFHTTPAuthenticationRequiresAccountDomain(requestAuthentication)) { | 869 | if (CFHTTPAuthenticationRequiresAccountDomain(requestAuthentication)) { |
870 | + if (!domain) { | ||
871 | + [self setDomain:@""]; | ||
872 | + } | ||
861 | [newCredentials setObject:domain forKey:(NSString *)kCFHTTPAuthenticationAccountDomain]; | 873 | [newCredentials setObject:domain forKey:(NSString *)kCFHTTPAuthenticationAccountDomain]; |
862 | } | 874 | } |
863 | 875 | ||
@@ -925,7 +937,9 @@ static NSError *ASIUnableToCreateRequestError; | @@ -925,7 +937,9 @@ static NSError *ASIUnableToCreateRequestError; | ||
925 | CFHTTPMessageRef responseHeader = (CFHTTPMessageRef) CFReadStreamCopyProperty(readStream,kCFStreamPropertyHTTPResponseHeader); | 937 | CFHTTPMessageRef responseHeader = (CFHTTPMessageRef) CFReadStreamCopyProperty(readStream,kCFStreamPropertyHTTPResponseHeader); |
926 | requestAuthentication = CFHTTPAuthenticationCreateFromResponse(NULL, responseHeader); | 938 | requestAuthentication = CFHTTPAuthenticationCreateFromResponse(NULL, responseHeader); |
927 | CFRelease(responseHeader); | 939 | CFRelease(responseHeader); |
928 | - } | 940 | + authenticationMethod = (NSString *)CFHTTPAuthenticationCopyMethod(requestAuthentication); |
941 | + } | ||
942 | + | ||
929 | 943 | ||
930 | if (!requestAuthentication) { | 944 | if (!requestAuthentication) { |
931 | [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileApplyingCredentialsType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Failed to get authentication object from response headers",NSLocalizedDescriptionKey,nil]]]; | 945 | [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileApplyingCredentialsType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Failed to get authentication object from response headers",NSLocalizedDescriptionKey,nil]]]; |
@@ -963,8 +977,14 @@ static NSError *ASIUnableToCreateRequestError; | @@ -963,8 +977,14 @@ static NSError *ASIUnableToCreateRequestError; | ||
963 | [self cancelLoad]; | 977 | [self cancelLoad]; |
964 | 978 | ||
965 | if (requestCredentials) { | 979 | if (requestCredentials) { |
966 | - if ([self applyCredentials:requestCredentials]) { | 980 | + NSLog(@"%hi",authenticationRetryCount); |
967 | - [self loadRequest]; | 981 | + if (((authenticationMethod != (NSString *)kCFHTTPAuthenticationSchemeNTLM) || authenticationRetryCount < 2) && [self applyCredentials:requestCredentials]) { |
982 | + [self startRequest]; | ||
983 | + | ||
984 | + // We've failed NTLM authentication twice, we should assume our credentials are wrong | ||
985 | + } else if (authenticationMethod == (NSString *)kCFHTTPAuthenticationSchemeNTLM && authenticationRetryCount == 2) { | ||
986 | + [self failWithError:ASIAuthenticationError]; | ||
987 | + | ||
968 | } else { | 988 | } else { |
969 | [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileApplyingCredentialsType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Failed to apply credentials to request",NSLocalizedDescriptionKey,nil]]]; | 989 | [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileApplyingCredentialsType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Failed to apply credentials to request",NSLocalizedDescriptionKey,nil]]]; |
970 | } | 990 | } |
@@ -978,7 +998,7 @@ static NSError *ASIUnableToCreateRequestError; | @@ -978,7 +998,7 @@ static NSError *ASIUnableToCreateRequestError; | ||
978 | if (newCredentials) { | 998 | if (newCredentials) { |
979 | 999 | ||
980 | if ([self applyCredentials:newCredentials]) { | 1000 | if ([self applyCredentials:newCredentials]) { |
981 | - [self loadRequest]; | 1001 | + [self startRequest]; |
982 | } else { | 1002 | } else { |
983 | [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileApplyingCredentialsType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Failed to apply credentials to request",NSLocalizedDescriptionKey,nil]]]; | 1003 | [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileApplyingCredentialsType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Failed to apply credentials to request",NSLocalizedDescriptionKey,nil]]]; |
984 | } | 1004 | } |
@@ -23,7 +23,9 @@ | @@ -23,7 +23,9 @@ | ||
23 | - (void)testCookies; | 23 | - (void)testCookies; |
24 | - (void)testBasicAuthentication; | 24 | - (void)testBasicAuthentication; |
25 | - (void)testDigestAuthentication; | 25 | - (void)testDigestAuthentication; |
26 | +- (void)testNTLMAuthentication; | ||
26 | - (void)testCharacterEncoding; | 27 | - (void)testCharacterEncoding; |
27 | - (void)testCompressedResponse; | 28 | - (void)testCompressedResponse; |
28 | - (void)testCompressedResponseDownloadToFile; | 29 | - (void)testCompressedResponseDownloadToFile; |
30 | + | ||
29 | @end | 31 | @end |
@@ -439,6 +439,48 @@ | @@ -439,6 +439,48 @@ | ||
439 | GHAssertTrue(success,@"Failed to clear credentials"); | 439 | GHAssertTrue(success,@"Failed to clear credentials"); |
440 | } | 440 | } |
441 | 441 | ||
442 | + | ||
443 | +- (void)testNTLMAuthentication | ||
444 | +{ | ||
445 | + /* | ||
446 | + If you want to run this test, set your hostname, username, password and domain below. | ||
447 | + */ | ||
448 | + NSString *theURL = @""; | ||
449 | + NSString *username = @""; | ||
450 | + NSString *password = @""; | ||
451 | + NSString *domain = @""; | ||
452 | + | ||
453 | + if ([theURL isEqualToString:@""] || [username isEqualToString:@""] || [password isEqualToString:@""]) { | ||
454 | + GHAssertFalse(true,@"Skipping NTLM test because no server details were supplied"); | ||
455 | + } | ||
456 | + | ||
457 | + [ASIHTTPRequest clearSession]; | ||
458 | + | ||
459 | + NSURL *url = [[[NSURL alloc] initWithString:theURL] autorelease]; | ||
460 | + ASIHTTPRequest *request; | ||
461 | + BOOL success; | ||
462 | + NSError *err; | ||
463 | + | ||
464 | + request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; | ||
465 | + [request setUseKeychainPersistance:NO]; | ||
466 | + [request setUseSessionPersistance:NO]; | ||
467 | + [request start]; | ||
468 | + success = [[request error] code] == ASIAuthenticationErrorType; | ||
469 | + GHAssertTrue(success,@"Failed to generate permission denied error with no credentials"); | ||
470 | + | ||
471 | + | ||
472 | + request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; | ||
473 | + [request setUseSessionPersistance:YES]; | ||
474 | + [request setUseKeychainPersistance:NO]; | ||
475 | + [request setUsername:username]; | ||
476 | + [request setPassword:password]; | ||
477 | + [request setDomain:domain]; | ||
478 | + [request start]; | ||
479 | + err = [request error]; | ||
480 | + GHAssertNil(err,@"Got an error when correct credentials were supplied"); | ||
481 | + NSLog([request responseString]); | ||
482 | +} | ||
483 | + | ||
442 | - (void)testCompressedResponse | 484 | - (void)testCompressedResponse |
443 | { | 485 | { |
444 | // allseeing-i.com does not gzip png images | 486 | // allseeing-i.com does not gzip png images |
@@ -12,6 +12,7 @@ | @@ -12,6 +12,7 @@ | ||
12 | 12 | ||
13 | @implementation ASINetworkQueueTests | 13 | @implementation ASINetworkQueueTests |
14 | 14 | ||
15 | + | ||
15 | static CFStringRef ASIHTTPRequestTestsRunMode = CFSTR("ASIHTTPRequestTestsRunMode"); | 16 | static CFStringRef ASIHTTPRequestTestsRunMode = CFSTR("ASIHTTPRequestTestsRunMode"); |
16 | 17 | ||
17 | - (void)testProgress | 18 | - (void)testProgress |
-
Please register or login to post a comment