Skip to content
Browse files

Restructure authentication retries to make NTLM work

Moved stream setup into startRequest to help with above
  • Loading branch information...
1 parent 4c3db1b commit 78feef9cb4f116c5c2a2bb0b8d243babf97c818e @pokeb committed Mar 12, 2009
Showing with 85 additions and 14 deletions.
  1. +6 −1 ASIHTTPRequest.h
  2. +33 −13 ASIHTTPRequest.m
  3. +2 −0 ASIHTTPRequestTests.h
  4. +42 −0 ASIHTTPRequestTests.m
  5. +1 −0 ASINetworkQueueTests.h
  6. +1 −0 ASINetworkQueueTests.m
View
7 ASIHTTPRequest.h
@@ -113,6 +113,8 @@ typedef enum _ASINetworkErrorType {
// Authentication currently being used for prompting and resuming
CFHTTPAuthenticationRef requestAuthentication;
NSMutableDictionary *requestCredentials;
+ int authenticationRetryCount;
+ NSString *authenticationMethod;
// HTTP status code, eg: 200 = OK, 404 = Not found etc
int responseStatusCode;
@@ -204,9 +206,12 @@ typedef enum _ASINetworkErrorType {
#pragma mark request logic
-// Start loading the request
+// Main request loop is in here
- (void)loadRequest;
+// Start the read stream. Called by loadRequest, and again to restart the request when authentication is needed
+- (void)startRequest;
+
// Cancel loading and clean up
- (void)cancelLoad;
View
46 ASIHTTPRequest.m
@@ -61,13 +61,16 @@ - (id)initWithURL:(NSURL *)newURL
self = [super init];
[self setRequestMethod:@"GET"];
lastBytesSent = 0;
+
showAccurateProgress = YES;
shouldResetProgressIndicators = YES;
updatedProgress = NO;
[self setMainRequest:nil];
[self setPassword:nil];
[self setUsername:nil];
[self setRequestHeaders:nil];
+ authenticationRetryCount = 0;
+ authenticationMethod = nil;
authenticationRealm = nil;
outputStream = nil;
requestAuthentication = NULL;
@@ -120,6 +123,7 @@ - (void)dealloc
[responseHeaders release];
[requestMethod release];
[cancelledLock release];
+ [authenticationMethod release];
[super dealloc];
}
@@ -291,11 +295,8 @@ - (void)main
}
-
-// Start the request
-- (void)loadRequest
+- (void)startRequest
{
-
[cancelledLock lock];
if ([self isCancelled]) {
@@ -362,8 +363,15 @@ - (void)loadRequest
amount = postLength;
}
[self resetUploadProgress:amount];
- }
-
+ }
+}
+
+// Start the request
+- (void)loadRequest
+{
+
+
+ [self startRequest];
// Record when the request started, so we can timeout if nothing happens
@@ -831,7 +839,8 @@ - (void)saveCredentialsToKeychain:(NSMutableDictionary *)newCredentials
- (BOOL)applyCredentials:(NSMutableDictionary *)newCredentials
{
-
+ authenticationRetryCount++;
+
if (newCredentials && requestAuthentication && request) {
// Apply whatever credentials we've built up to the old request
if (CFHTTPMessageApplyCredentialDictionary(request, requestAuthentication, (CFMutableDictionaryRef)newCredentials, NULL)) {
@@ -846,10 +855,10 @@ - (BOOL)applyCredentials:(NSMutableDictionary *)newCredentials
[ASIHTTPRequest setSessionCredentials:newCredentials];
}
[self setRequestCredentials:newCredentials];
- return TRUE;
+ return YES;
}
}
- return FALSE;
+ return NO;
}
- (NSMutableDictionary *)findCredentials
@@ -858,6 +867,9 @@ - (NSMutableDictionary *)findCredentials
// Is an account domain needed? (used currently for NTLM only)
if (CFHTTPAuthenticationRequiresAccountDomain(requestAuthentication)) {
+ if (!domain) {
+ [self setDomain:@""];
+ }
[newCredentials setObject:domain forKey:(NSString *)kCFHTTPAuthenticationAccountDomain];
}
@@ -925,7 +937,9 @@ - (void)attemptToApplyCredentialsAndResume
CFHTTPMessageRef responseHeader = (CFHTTPMessageRef) CFReadStreamCopyProperty(readStream,kCFStreamPropertyHTTPResponseHeader);
requestAuthentication = CFHTTPAuthenticationCreateFromResponse(NULL, responseHeader);
CFRelease(responseHeader);
- }
+ authenticationMethod = (NSString *)CFHTTPAuthenticationCopyMethod(requestAuthentication);
+ }
+
if (!requestAuthentication) {
[self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileApplyingCredentialsType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Failed to get authentication object from response headers",NSLocalizedDescriptionKey,nil]]];
@@ -963,8 +977,14 @@ - (void)attemptToApplyCredentialsAndResume
[self cancelLoad];
if (requestCredentials) {
- if ([self applyCredentials:requestCredentials]) {
- [self loadRequest];
+ NSLog(@"%hi",authenticationRetryCount);
+ if (((authenticationMethod != (NSString *)kCFHTTPAuthenticationSchemeNTLM) || authenticationRetryCount < 2) && [self applyCredentials:requestCredentials]) {
+ [self startRequest];
+
+ // We've failed NTLM authentication twice, we should assume our credentials are wrong
+ } else if (authenticationMethod == (NSString *)kCFHTTPAuthenticationSchemeNTLM && authenticationRetryCount == 2) {
+ [self failWithError:ASIAuthenticationError];
+
} else {
[self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileApplyingCredentialsType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Failed to apply credentials to request",NSLocalizedDescriptionKey,nil]]];
}
@@ -978,7 +998,7 @@ - (void)attemptToApplyCredentialsAndResume
if (newCredentials) {
if ([self applyCredentials:newCredentials]) {
- [self loadRequest];
+ [self startRequest];
} else {
[self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileApplyingCredentialsType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Failed to apply credentials to request",NSLocalizedDescriptionKey,nil]]];
}
View
2 ASIHTTPRequestTests.h
@@ -23,7 +23,9 @@
- (void)testCookies;
- (void)testBasicAuthentication;
- (void)testDigestAuthentication;
+- (void)testNTLMAuthentication;
- (void)testCharacterEncoding;
- (void)testCompressedResponse;
- (void)testCompressedResponseDownloadToFile;
+
@end
View
42 ASIHTTPRequestTests.m
@@ -439,6 +439,48 @@ - (void)testDigestAuthentication
GHAssertTrue(success,@"Failed to clear credentials");
}
+
+- (void)testNTLMAuthentication
+{
+ /*
+ If you want to run this test, set your hostname, username, password and domain below.
+ */
+ NSString *theURL = @"";
+ NSString *username = @"";
+ NSString *password = @"";
+ NSString *domain = @"";
+
+ if ([theURL isEqualToString:@""] || [username isEqualToString:@""] || [password isEqualToString:@""]) {
+ GHAssertFalse(true,@"Skipping NTLM test because no server details were supplied");
+ }
+
+ [ASIHTTPRequest clearSession];
+
+ NSURL *url = [[[NSURL alloc] initWithString:theURL] autorelease];
+ ASIHTTPRequest *request;
+ BOOL success;
+ NSError *err;
+
+ request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
+ [request setUseKeychainPersistance:NO];
+ [request setUseSessionPersistance:NO];
+ [request start];
+ success = [[request error] code] == ASIAuthenticationErrorType;
+ GHAssertTrue(success,@"Failed to generate permission denied error with no credentials");
+
+
+ request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
+ [request setUseSessionPersistance:YES];
+ [request setUseKeychainPersistance:NO];
+ [request setUsername:username];
+ [request setPassword:password];
+ [request setDomain:domain];
+ [request start];
+ err = [request error];
+ GHAssertNil(err,@"Got an error when correct credentials were supplied");
+ NSLog([request responseString]);
+}
+
- (void)testCompressedResponse
{
// allseeing-i.com does not gzip png images
View
1 ASINetworkQueueTests.h
@@ -24,4 +24,5 @@
- (void)testProgressWithAuthentication;
- (void)setProgress:(float)newProgress;
+
@end
View
1 ASINetworkQueueTests.m
@@ -12,6 +12,7 @@
@implementation ASINetworkQueueTests
+
static CFStringRef ASIHTTPRequestTestsRunMode = CFSTR("ASIHTTPRequestTestsRunMode");
- (void)testProgress

0 comments on commit 78feef9

Please sign in to comment.
Something went wrong with that request. Please try again.