Ben Copsey

Fix bug where HEAD requests created by ASINeworkQueues didn't present the reques…

…t headers from the main request.
This meant they wouldn't present authentication information or cookies, as well as custom headers.
Errors thrown by the above HEAD requests now set the error on the main request and talk to its delegate when appropriate

Thanks to Alex Reynolds for noticing this issue!
... ... @@ -12,7 +12,6 @@
@implementation ASIFormDataRequestTests
- (void)testPostWithFileUpload
{
NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/post"];
... ...
... ... @@ -280,6 +280,7 @@ typedef enum _ASINetworkErrorType {
@property (retain) NSError *error;
@property (assign,readonly) BOOL complete;
@property (retain) NSDictionary *responseHeaders;
@property (retain) NSMutableDictionary *requestHeaders;
@property (retain) NSMutableArray *requestCookies;
@property (retain) NSArray *responseCookies;
@property (assign) BOOL useCookiePersistance;
... ...
... ... @@ -62,24 +62,24 @@ static NSError *ASIUnableToCreateRequestError;
showAccurateProgress = YES;
shouldResetProgressIndicators = YES;
updatedProgress = NO;
mainRequest = nil;
username = nil;
password = nil;
requestHeaders = nil;
[self setMainRequest:nil];
[self setPassword:nil];
[self setUsername:nil];
[self setRequestHeaders:nil];
authenticationRealm = nil;
outputStream = nil;
requestAuthentication = NULL;
haveBuiltPostBody = NO;
request = NULL;
responseHeaders = nil;
[self setResponseHeaders:nil];
[self setTimeOutSeconds:10];
[self setUseKeychainPersistance:NO];
[self setUseSessionPersistance:YES];
[self setUseCookiePersistance:YES];
[self setRequestCookies:[[[NSMutableArray alloc] init] autorelease]];
didFinishSelector = @selector(requestFinished:);
didFailSelector = @selector(requestFailed:);
delegate = nil;
[self setDidFinishSelector:@selector(requestFinished:)];
[self setDidFailSelector:@selector(requestFailed:)];
[self setDelegate:nil];
url = [newURL retain];
cancelledLock = [[NSLock alloc] init];
return self;
... ... @@ -123,7 +123,7 @@ static NSError *ASIUnableToCreateRequestError;
- (void)addRequestHeader:(NSString *)header value:(NSString *)value
{
if (!requestHeaders) {
requestHeaders = [[NSMutableDictionary alloc] init];
[self setRequestHeaders:[NSMutableDictionary dictionaryWithCapacity:1]];
}
[requestHeaders setObject:value forKey:header];
}
... ... @@ -208,10 +208,16 @@ static NSError *ASIUnableToCreateRequestError;
}
// Apply request cookies
if ([requestCookies count] > 0) {
NSArray *cookies;
if ([self mainRequest]) {
cookies = [[self mainRequest] requestCookies];
} else {
cookies = [self requestCookies];
}
if ([cookies count] > 0) {
NSHTTPCookie *cookie;
NSString *cookieHeader = nil;
for (cookie in requestCookies) {
for (cookie in cookies) {
if (!cookieHeader) {
cookieHeader = [NSString stringWithFormat: @"%@=%@",[cookie name],[cookie encodedValue]];
} else {
... ... @@ -229,8 +235,16 @@ static NSError *ASIUnableToCreateRequestError;
}
// Add custom headers
NSDictionary *headers;
//Add headers from the main request if this is a HEAD request generated by an ASINetwork Queue
if ([self mainRequest]) {
headers = [mainRequest requestHeaders];
} else {
headers = [self requestHeaders];
}
NSString *header;
for (header in requestHeaders) {
for (header in headers) {
CFHTTPMessageSetHeaderFieldValue(request, (CFStringRef)header, (CFStringRef)[requestHeaders objectForKey:header]);
}
... ... @@ -248,6 +262,7 @@ static NSError *ASIUnableToCreateRequestError;
// Start the request
- (void)loadRequest
{
[cancelledLock lock];
if ([self isCancelled]) {
... ... @@ -643,11 +658,21 @@ static NSError *ASIUnableToCreateRequestError;
complete = YES;
if (!error) {
[self setError:theError];
// If this is a HEAD request created by an ASINetworkQueue, make the main request fail
if ([self mainRequest]) {
ASIHTTPRequest *mRequest = [self mainRequest];
[mRequest setError:theError];
if ([mRequest didFailSelector] && ![self isCancelled] && [[mRequest delegate] respondsToSelector:[mRequest didFailSelector]]) {
[[mRequest delegate] performSelectorOnMainThread:[mRequest didFailSelector] withObject:mRequest waitUntilDone:[NSThread isMainThread]];
}
if (didFailSelector && ![self isCancelled] && [delegate respondsToSelector:didFailSelector]) {
[delegate performSelectorOnMainThread:didFailSelector withObject:self waitUntilDone:[NSThread isMainThread]];
} else {
[self setError:theError];
if (didFailSelector && ![self isCancelled] && [delegate respondsToSelector:didFailSelector]) {
[delegate performSelectorOnMainThread:didFailSelector withObject:self waitUntilDone:[NSThread isMainThread]];
}
}
}
}
... ... @@ -761,12 +786,24 @@ static NSError *ASIUnableToCreateRequestError;
NSString *user = [url user];
NSString *pass = [url password];
// If the username and password weren't in the url, let's try to use the ones set in this object
if ((!user || !pass) && username && password) {
user = username;
pass = password;
// If the username and password weren't in the url
if (!user || !pass) {
// If this is a HEAD request generated by an ASINetworkQueue, we'll try to use the details from the main request
if ([self mainRequest] && [[self mainRequest] username] && [[self mainRequest] password]) {
user = [[self mainRequest] username];
pass = [[self mainRequest] password];
// Let's try to use the ones set in this object
} else if (username && password) {
user = username;
pass = password;
}
}
// Ok, that didn't work, let's try the keychain
if ((!user || !pass) && useKeychainPersistance) {
NSURLCredential *authenticationCredentials = [ASIHTTPRequest savedCredentialsForHost:[url host] port:443 protocol:[url scheme] realm:authenticationRealm];
... ... @@ -833,8 +870,7 @@ static NSError *ASIUnableToCreateRequestError;
return;
}
}
[self setError:ASIAuthenticationError];
complete = YES;
[self failWithError:ASIAuthenticationError];
return;
}
... ... @@ -874,8 +910,7 @@ static NSError *ASIUnableToCreateRequestError;
}
// The delegate isn't interested, we'll have to give up
[self setError:ASIAuthenticationError];
complete = YES;
[self failWithError:ASIAuthenticationError];
return;
}
... ... @@ -982,7 +1017,6 @@ static NSError *ASIUnableToCreateRequestError;
- (void)handleStreamError
{
complete = YES;
NSError *underlyingError = [(NSError *)CFReadStreamCopyError(readStream) autorelease];
[self cancelLoad];
... ... @@ -1096,6 +1130,7 @@ static NSError *ASIUnableToCreateRequestError;
@synthesize authenticationRealm;
@synthesize error;
@synthesize complete;
@synthesize requestHeaders;
@synthesize responseHeaders;
@synthesize responseCookies;
@synthesize requestCookies;
... ...
... ... @@ -15,7 +15,6 @@
@implementation ASIHTTPRequestTests
- (void)testBasicDownload
{
NSURL *url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com"] autorelease];
... ... @@ -375,5 +374,4 @@
}
@end
... ...
... ... @@ -254,6 +254,18 @@
}
- (BOOL)respondsToSelector:(SEL)selector
{
if (selector == @selector(authorizationNeededForRequest:)) {
if ([delegate respondsToSelector:@selector(authorizationNeededForRequest:)]) {
return YES;
}
return NO;
}
return [super respondsToSelector:selector];
}
@synthesize uploadProgressDelegate;
@synthesize downloadProgressDelegate;
... ...
... ... @@ -21,6 +21,7 @@
- (void)testFailure;
- (void)testFailureCancelsOtherRequests;
- (void)testProgress;
- (void)testProgressWithAuthentication;
- (void)setProgress:(float)newProgress;
@end
... ...
... ... @@ -12,7 +12,6 @@
@implementation ASINetworkQueueTests
- (void)testProgress
{
complete = NO;
... ... @@ -78,12 +77,15 @@
}
- (void)setProgress:(float)newProgress
{
progress = newProgress;
}
- (void)testFailure
{
complete = NO;
... ... @@ -189,6 +191,8 @@
[requestThatShouldFail release];
}
- (void)requestFailedCancellingOthers:(ASIHTTPRequest *)request
{
complete = YES;
... ... @@ -205,6 +209,62 @@
complete = YES;
}
- (void)testProgressWithAuthentication
{
complete = NO;
progress = 0;
networkQueue = [[ASINetworkQueue alloc] init];
[networkQueue setDownloadProgressDelegate:self];
[networkQueue setDelegate:self];
[networkQueue setShowAccurateProgress:YES];
[networkQueue setQueueDidFinishSelector:@selector(queueFinished:)];
[networkQueue setRequestDidFailSelector:@selector(requestFailedCancellingOthers:)];
NSURL *url;
url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/basic-authentication"] autorelease];
ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
[networkQueue addOperation:request];
[networkQueue go];
NSDate* endDate = [NSDate distantFuture];
while (!complete) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:endDate];
}
NSError *error = [request error];
STAssertNotNil(error,@"The HEAD request failed, but it didn't tell the main request to fail");
[networkQueue release];
networkQueue = [[ASINetworkQueue alloc] init];
[networkQueue setDownloadProgressDelegate:self];
[networkQueue setDelegate:self];
[networkQueue setShowAccurateProgress:YES];
[networkQueue setQueueDidFinishSelector:@selector(queueFinished:)];
request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
[request setUsername:@"secret_username"];
[request setPassword:@"secret_password"];
[networkQueue addOperation:request];
[networkQueue go];
while (!complete) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:endDate];
}
error = [request error];
STAssertNil(error,@"Failed to use authentication in a queue");
[networkQueue release];
}
@end
... ...