Skip to content

Commit

Permalink
Added basic unit tests
Browse files Browse the repository at this point in the history
Added responseStatusCode property
Various cleanups
  • Loading branch information
Ben Copsey committed Aug 1, 2008
1 parent abdaa62 commit fe503b0
Show file tree
Hide file tree
Showing 8 changed files with 1,927 additions and 1,083 deletions.
43 changes: 28 additions & 15 deletions ASIHTTPRequest.h
Expand Up @@ -26,9 +26,12 @@
//Files that will be POSTed to the url
NSMutableDictionary *fileData;

//Dictionary for custom request headers
//Dictionary for custom HTTP request headers
NSMutableDictionary *requestHeaders;

//Will be populate with HTTP response headers from the server
NSDictionary *responseHeaders;

//If useKeychainPersistance is true, network requests will attempt to read credentials from the keychain, and will save them in the keychain when they are successfully presented
BOOL useKeychainPersistance;

Expand Down Expand Up @@ -72,11 +75,15 @@
CFReadStreamRef readStream;

// Authentication currently being used for prompting and resuming
CFHTTPAuthenticationRef authentication;
CFHTTPAuthenticationRef requestAuthentication;
NSMutableDictionary *requestCredentials;

// Credentials associated with the authentication (reused until server says no)
//CFMutableDictionaryRef credentials;

// HTTP status code, eg: 200 = OK, 404 = Not found etc
int responseStatusCode;

//Size of the response
double contentLength;

Expand All @@ -103,8 +110,6 @@
//Called on the delegate when the request fails
SEL didFailSelector;

NSDictionary *responseHeaders;
NSMutableDictionary *requestCredentials;

}

Expand All @@ -113,9 +118,6 @@
// Should be an HTTP or HTTPS url, may include username and password if appropriate
- (id)initWithURL:(NSURL *)newURL;

#pragma mark delegate configuration


#pragma mark setup request

//Add a custom header to the request
Expand Down Expand Up @@ -159,17 +161,23 @@

#pragma mark handling request complete / failure

//Called when a request completes successfully - defaults to: @selector(requestFinished:)
// Called when a request completes successfully - defaults to: @selector(requestFinished:)
- (void)requestFinished;

//Called when a request fails - defaults to: @selector(requestFailed:)
// Called when a request fails - defaults to: @selector(requestFailed:)
- (void)failWithProblem:(NSString *)problem;

#pragma mark http authentication stuff

// Reads the response headers to find the content length, and returns true if the request needs a username and password (or if those supplied were incorrect)
- (BOOL)readResponseHeadersReturningAuthenticationFailure;

// Apply credentials to this request
- (BOOL)applyCredentials:(NSMutableDictionary *)newCredentials;

// Attempt to obtain credentials for this request from the URL, username and password or keychain
- (NSMutableDictionary *)findCredentials;

// Unlock (unpause) the request thread so it can resume the request
// Should be called by delegates when they have populated the authentication information after an authentication challenge
- (void)retryWithAuthentication;
Expand All @@ -188,16 +196,23 @@
- (void)handleStreamComplete;
- (void)handleStreamError;

#pragma mark managing the session

+ (void)setSessionCredentials:(NSMutableDictionary *)newCredentials;
+ (void)setSessionAuthentication:(CFHTTPAuthenticationRef)newAuthentication;

#pragma mark keychain storage

//Save credentials to the keychain
// Save credentials for this request to the keychain
- (void)saveCredentialsToKeychain:(NSMutableDictionary *)newCredentials;

// Save creddentials to the keychain
+ (void)saveCredentials:(NSURLCredential *)credentials forHost:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm;

//Return credentials from the keychain
// Return credentials from the keychain
+ (NSURLCredential *)savedCredentialsForHost:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm;

//Remove credentials from the keychain
// Remove credentials from the keychain
+ (void)removeCredentialsForHost:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm;


Expand All @@ -215,9 +230,7 @@
@property (assign,readonly) BOOL complete;
@property (retain) NSDictionary *responseHeaders;
@property (retain) NSDictionary *requestCredentials;

- (void)saveCredentialsToKeychain:(NSMutableDictionary *)newCredentials;
- (BOOL)applyCredentials:(NSMutableDictionary *)newCredentials;
@property (assign) int responseStatusCode;


@end
96 changes: 50 additions & 46 deletions ASIHTTPRequest.m
Expand Up @@ -33,12 +33,6 @@ @implementation ASIHTTPRequest

- (id)initWithURL:(NSURL *)newURL
{
[self init];
url = [newURL retain];
return self;
}

- (id)init {
[super init];
lastBytesSent = 0;
postData = nil;
Expand All @@ -48,7 +42,7 @@ - (id)init {
requestHeaders = nil;
authenticationRealm = nil;
outputStream = nil;
authentication = NULL;
requestAuthentication = NULL;
//credentials = NULL;
request = NULL;
responseHeaders = nil;
Expand All @@ -57,13 +51,14 @@ - (id)init {
didFinishSelector = @selector(requestFinished:);
didFailSelector = @selector(requestFailed:);
delegate = nil;
return self;
url = [newURL retain];
return self;
}

- (void)dealloc
{
if (authentication) {
CFRelease(authentication);
if (requestAuthentication) {
CFRelease(requestAuthentication);
}
if (request) {
CFRelease(request);
Expand Down Expand Up @@ -146,12 +141,6 @@ - (NSString *)dataString

#pragma mark request logic

+ (void)setSessionCredentials:(NSMutableDictionary *)newCredentials
{
[sessionCredentials release];
sessionCredentials = [newCredentials retain];
}

// Create the request
- (void)main
{
Expand All @@ -173,8 +162,7 @@ - (void)main
//If we've already talked to this server and have valid credentials, let's apply them to the request
if (useSessionPersistance && sessionCredentials && sessionAuthentication) {
if (!CFHTTPMessageApplyCredentialDictionary(request, sessionAuthentication, (CFMutableDictionaryRef)sessionCredentials, NULL)) {
CFRelease(sessionAuthentication);
sessionAuthentication = NULL;
[ASIHTTPRequest setSessionAuthentication:NULL];
[ASIHTTPRequest setSessionCredentials:nil];
}
}
Expand Down Expand Up @@ -415,9 +403,10 @@ - (BOOL)readResponseHeadersReturningAuthenticationFailure
CFHTTPMessageRef headers = (CFHTTPMessageRef)CFReadStreamCopyProperty(readStream, kCFStreamPropertyHTTPResponseHeader);
if (CFHTTPMessageIsHeaderComplete(headers)) {
responseHeaders = (NSDictionary *)CFHTTPMessageCopyAllHeaderFields(headers);

responseStatusCode = CFHTTPMessageGetResponseStatusCode(headers);

// Is the server response a challenge for credentials?
isAuthenticationChallenge = (CFHTTPMessageGetResponseStatusCode(headers) == 401);
isAuthenticationChallenge = (responseStatusCode == 401);

//We won't reset the download progress delegate if we got an authentication challenge
if (!isAuthenticationChallenge) {
Expand All @@ -438,13 +427,6 @@ - (BOOL)readResponseHeadersReturningAuthenticationFailure
}


// Called by delegate to resume loading once authentication info has been populated
- (void)retryWithAuthentication
{
[authenticationLock lockWhenCondition:1];
[authenticationLock unlockWithCondition:2];
}

- (void)saveCredentialsToKeychain:(NSMutableDictionary *)newCredentials
{
NSURLCredential *authenticationCredentials = [NSURLCredential credentialWithUser:[newCredentials objectForKey:(NSString *)kCFHTTPAuthenticationUsername]
Expand All @@ -458,20 +440,16 @@ - (void)saveCredentialsToKeychain:(NSMutableDictionary *)newCredentials
- (BOOL)applyCredentials:(NSMutableDictionary *)newCredentials
{

if (newCredentials && authentication && request) {
if (newCredentials && requestAuthentication && request) {
// Apply whatever credentials we've built up to the old request
if (CFHTTPMessageApplyCredentialDictionary(request, authentication, (CFMutableDictionaryRef)newCredentials, NULL)) {
if (CFHTTPMessageApplyCredentialDictionary(request, requestAuthentication, (CFMutableDictionaryRef)newCredentials, NULL)) {
//If we have credentials and they're ok, let's save them to the keychain
if (useKeychainPersistance) {
[self saveCredentialsToKeychain:newCredentials];
}
if (useSessionPersistance) {
if (sessionAuthentication) {
CFRelease(sessionAuthentication);
}
sessionAuthentication = authentication;
CFRetain(sessionAuthentication);

[ASIHTTPRequest setSessionAuthentication:requestAuthentication];
[ASIHTTPRequest setSessionCredentials:newCredentials];
}
[self setRequestCredentials:newCredentials];
Expand All @@ -481,15 +459,15 @@ - (BOOL)applyCredentials:(NSMutableDictionary *)newCredentials
return FALSE;
}

- (NSMutableDictionary *)getCredentials
- (NSMutableDictionary *)findCredentials
{
NSMutableDictionary *newCredentials = [[[NSMutableDictionary alloc] init] autorelease];

// Get the authentication realm
[authenticationRealm release];
authenticationRealm = nil;
if (!CFHTTPAuthenticationRequiresAccountDomain(authentication)) {
authenticationRealm = (NSString *)CFHTTPAuthenticationCopyRealm(authentication);
if (!CFHTTPAuthenticationRequiresAccountDomain(requestAuthentication)) {
authenticationRealm = (NSString *)CFHTTPAuthenticationCopyRealm(requestAuthentication);
}

//First, let's look at the url to see if the username and password were included
Expand Down Expand Up @@ -519,30 +497,37 @@ - (NSMutableDictionary *)getCredentials
[newCredentials setObject:pass forKey:(NSString *)kCFHTTPAuthenticationPassword];
return newCredentials;
}
return NULL;
return nil;
}

// Called by delegate to resume loading once authentication info has been populated
- (void)retryWithAuthentication
{
[authenticationLock lockWhenCondition:1];
[authenticationLock unlockWithCondition:2];
}

- (void)attemptToApplyCredentialsAndResume
{

//Read authentication data
if (!authentication) {
if (!requestAuthentication) {
CFHTTPMessageRef responseHeader = (CFHTTPMessageRef) CFReadStreamCopyProperty(readStream,kCFStreamPropertyHTTPResponseHeader);
authentication = CFHTTPAuthenticationCreateFromResponse(NULL, responseHeader);
requestAuthentication = CFHTTPAuthenticationCreateFromResponse(NULL, responseHeader);
CFRelease(responseHeader);
}

if (!authentication) {
if (!requestAuthentication) {
[self failWithProblem:@"Failed to get authentication object from response headers"];
return;
}

//See if authentication is valid
CFStreamError err;
if (!CFHTTPAuthenticationIsValid(authentication, &err)) {
if (!CFHTTPAuthenticationIsValid(requestAuthentication, &err)) {

CFRelease(authentication);
authentication = NULL;
CFRelease(requestAuthentication);
requestAuthentication = NULL;

// check for bad credentials, so we can give the delegate a chance to replace them
if (err.domain == kCFStreamErrorDomainHTTP && (err.error == kCFStreamErrorHTTPAuthenticationBadUserName || err.error == kCFStreamErrorHTTPAuthenticationBadPassword)) {
Expand Down Expand Up @@ -572,9 +557,9 @@ - (void)attemptToApplyCredentialsAndResume
}

// are a user name & password needed?
} else if (CFHTTPAuthenticationRequiresUserNameAndPassword(authentication)) {
} else if (CFHTTPAuthenticationRequiresUserNameAndPassword(requestAuthentication)) {

NSMutableDictionary *newCredentials = [self getCredentials];
NSMutableDictionary *newCredentials = [self findCredentials];

//If we have some credentials to use let's apply them to the request and continue
if (newCredentials) {
Expand Down Expand Up @@ -714,6 +699,24 @@ - (void)handleStreamError
}
}

#pragma mark managing the session

+ (void)setSessionCredentials:(NSMutableDictionary *)newCredentials
{
[sessionCredentials release];
sessionCredentials = [newCredentials retain];
}

+ (void)setSessionAuthentication:(CFHTTPAuthenticationRef)newAuthentication
{
if (sessionAuthentication) {
CFRelease(sessionAuthentication);
}
sessionAuthentication = newAuthentication;
if (newAuthentication) {
CFRetain(sessionAuthentication);
}
}

#pragma mark keychain storage

Expand Down Expand Up @@ -774,4 +777,5 @@ + (void)removeCredentialsForHost:(NSString *)host port:(int)port protocol:(NSStr
@synthesize complete;
@synthesize responseHeaders;
@synthesize requestCredentials;
@synthesize responseStatusCode;
@end
17 changes: 17 additions & 0 deletions ASIHTTPRequestTests.h
@@ -0,0 +1,17 @@
//
// ASIHTTPRequestTests.h
// asi-http-request
//
// Created by Ben Copsey on 01/08/2008.
// Copyright 2008 All-Seeing Interactive. All rights reserved.
//

#import <SenTestingKit/SenTestingKit.h>


@interface ASIHTTPRequestTests : SenTestCase {
}

- (void)testBasicDownload;

@end

0 comments on commit fe503b0

Please sign in to comment.