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 @@ @@ -12,7 +12,6 @@
12 @implementation ASIFormDataRequestTests 12 @implementation ASIFormDataRequestTests
13 13
14 14
15 -  
16 - (void)testPostWithFileUpload 15 - (void)testPostWithFileUpload
17 { 16 {
18 NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/post"]; 17 NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/post"];
@@ -280,6 +280,7 @@ typedef enum _ASINetworkErrorType { @@ -280,6 +280,7 @@ typedef enum _ASINetworkErrorType {
280 @property (retain) NSError *error; 280 @property (retain) NSError *error;
281 @property (assign,readonly) BOOL complete; 281 @property (assign,readonly) BOOL complete;
282 @property (retain) NSDictionary *responseHeaders; 282 @property (retain) NSDictionary *responseHeaders;
  283 +@property (retain) NSMutableDictionary *requestHeaders;
283 @property (retain) NSMutableArray *requestCookies; 284 @property (retain) NSMutableArray *requestCookies;
284 @property (retain) NSArray *responseCookies; 285 @property (retain) NSArray *responseCookies;
285 @property (assign) BOOL useCookiePersistance; 286 @property (assign) BOOL useCookiePersistance;
@@ -62,24 +62,24 @@ static NSError *ASIUnableToCreateRequestError; @@ -62,24 +62,24 @@ static NSError *ASIUnableToCreateRequestError;
62 showAccurateProgress = YES; 62 showAccurateProgress = YES;
63 shouldResetProgressIndicators = YES; 63 shouldResetProgressIndicators = YES;
64 updatedProgress = NO; 64 updatedProgress = NO;
65 - mainRequest = nil; 65 + [self setMainRequest:nil];
66 - username = nil; 66 + [self setPassword:nil];
67 - password = nil; 67 + [self setUsername:nil];
68 - requestHeaders = nil; 68 + [self setRequestHeaders:nil];
69 authenticationRealm = nil; 69 authenticationRealm = nil;
70 outputStream = nil; 70 outputStream = nil;
71 requestAuthentication = NULL; 71 requestAuthentication = NULL;
72 haveBuiltPostBody = NO; 72 haveBuiltPostBody = NO;
73 request = NULL; 73 request = NULL;
74 - responseHeaders = nil; 74 + [self setResponseHeaders:nil];
75 [self setTimeOutSeconds:10]; 75 [self setTimeOutSeconds:10];
76 [self setUseKeychainPersistance:NO]; 76 [self setUseKeychainPersistance:NO];
77 [self setUseSessionPersistance:YES]; 77 [self setUseSessionPersistance:YES];
78 [self setUseCookiePersistance:YES]; 78 [self setUseCookiePersistance:YES];
79 [self setRequestCookies:[[[NSMutableArray alloc] init] autorelease]]; 79 [self setRequestCookies:[[[NSMutableArray alloc] init] autorelease]];
80 - didFinishSelector = @selector(requestFinished:); 80 + [self setDidFinishSelector:@selector(requestFinished:)];
81 - didFailSelector = @selector(requestFailed:); 81 + [self setDidFailSelector:@selector(requestFailed:)];
82 - delegate = nil; 82 + [self setDelegate:nil];
83 url = [newURL retain]; 83 url = [newURL retain];
84 cancelledLock = [[NSLock alloc] init]; 84 cancelledLock = [[NSLock alloc] init];
85 return self; 85 return self;
@@ -123,7 +123,7 @@ static NSError *ASIUnableToCreateRequestError; @@ -123,7 +123,7 @@ static NSError *ASIUnableToCreateRequestError;
123 - (void)addRequestHeader:(NSString *)header value:(NSString *)value 123 - (void)addRequestHeader:(NSString *)header value:(NSString *)value
124 { 124 {
125 if (!requestHeaders) { 125 if (!requestHeaders) {
126 - requestHeaders = [[NSMutableDictionary alloc] init]; 126 + [self setRequestHeaders:[NSMutableDictionary dictionaryWithCapacity:1]];
127 } 127 }
128 [requestHeaders setObject:value forKey:header]; 128 [requestHeaders setObject:value forKey:header];
129 } 129 }
@@ -208,10 +208,16 @@ static NSError *ASIUnableToCreateRequestError; @@ -208,10 +208,16 @@ static NSError *ASIUnableToCreateRequestError;
208 } 208 }
209 209
210 // Apply request cookies 210 // Apply request cookies
211 - if ([requestCookies count] > 0) { 211 + NSArray *cookies;
  212 + if ([self mainRequest]) {
  213 + cookies = [[self mainRequest] requestCookies];
  214 + } else {
  215 + cookies = [self requestCookies];
  216 + }
  217 + if ([cookies count] > 0) {
212 NSHTTPCookie *cookie; 218 NSHTTPCookie *cookie;
213 NSString *cookieHeader = nil; 219 NSString *cookieHeader = nil;
214 - for (cookie in requestCookies) { 220 + for (cookie in cookies) {
215 if (!cookieHeader) { 221 if (!cookieHeader) {
216 cookieHeader = [NSString stringWithFormat: @"%@=%@",[cookie name],[cookie encodedValue]]; 222 cookieHeader = [NSString stringWithFormat: @"%@=%@",[cookie name],[cookie encodedValue]];
217 } else { 223 } else {
@@ -229,8 +235,16 @@ static NSError *ASIUnableToCreateRequestError; @@ -229,8 +235,16 @@ static NSError *ASIUnableToCreateRequestError;
229 } 235 }
230 236
231 // Add custom headers 237 // Add custom headers
  238 + NSDictionary *headers;
  239 +
  240 + //Add headers from the main request if this is a HEAD request generated by an ASINetwork Queue
  241 + if ([self mainRequest]) {
  242 + headers = [mainRequest requestHeaders];
  243 + } else {
  244 + headers = [self requestHeaders];
  245 + }
232 NSString *header; 246 NSString *header;
233 - for (header in requestHeaders) { 247 + for (header in headers) {
234 CFHTTPMessageSetHeaderFieldValue(request, (CFStringRef)header, (CFStringRef)[requestHeaders objectForKey:header]); 248 CFHTTPMessageSetHeaderFieldValue(request, (CFStringRef)header, (CFStringRef)[requestHeaders objectForKey:header]);
235 } 249 }
236 250
@@ -248,6 +262,7 @@ static NSError *ASIUnableToCreateRequestError; @@ -248,6 +262,7 @@ static NSError *ASIUnableToCreateRequestError;
248 // Start the request 262 // Start the request
249 - (void)loadRequest 263 - (void)loadRequest
250 { 264 {
  265 +
251 [cancelledLock lock]; 266 [cancelledLock lock];
252 267
253 if ([self isCancelled]) { 268 if ([self isCancelled]) {
@@ -643,12 +658,22 @@ static NSError *ASIUnableToCreateRequestError; @@ -643,12 +658,22 @@ static NSError *ASIUnableToCreateRequestError;
643 complete = YES; 658 complete = YES;
644 if (!error) { 659 if (!error) {
645 660
646 - [self setError:theError]; 661 + // If this is a HEAD request created by an ASINetworkQueue, make the main request fail
  662 + if ([self mainRequest]) {
  663 + ASIHTTPRequest *mRequest = [self mainRequest];
  664 + [mRequest setError:theError];
  665 + if ([mRequest didFailSelector] && ![self isCancelled] && [[mRequest delegate] respondsToSelector:[mRequest didFailSelector]]) {
  666 + [[mRequest delegate] performSelectorOnMainThread:[mRequest didFailSelector] withObject:mRequest waitUntilDone:[NSThread isMainThread]];
  667 + }
647 668
  669 + } else {
  670 + [self setError:theError];
648 if (didFailSelector && ![self isCancelled] && [delegate respondsToSelector:didFailSelector]) { 671 if (didFailSelector && ![self isCancelled] && [delegate respondsToSelector:didFailSelector]) {
649 [delegate performSelectorOnMainThread:didFailSelector withObject:self waitUntilDone:[NSThread isMainThread]]; 672 [delegate performSelectorOnMainThread:didFailSelector withObject:self waitUntilDone:[NSThread isMainThread]];
650 } 673 }
651 } 674 }
  675 +
  676 + }
652 } 677 }
653 678
654 679
@@ -761,12 +786,24 @@ static NSError *ASIUnableToCreateRequestError; @@ -761,12 +786,24 @@ static NSError *ASIUnableToCreateRequestError;
761 NSString *user = [url user]; 786 NSString *user = [url user];
762 NSString *pass = [url password]; 787 NSString *pass = [url password];
763 788
764 - // If the username and password weren't in the url, let's try to use the ones set in this object 789 + // If the username and password weren't in the url
765 - if ((!user || !pass) && username && password) { 790 + if (!user || !pass) {
  791 +
  792 + // If this is a HEAD request generated by an ASINetworkQueue, we'll try to use the details from the main request
  793 + if ([self mainRequest] && [[self mainRequest] username] && [[self mainRequest] password]) {
  794 + user = [[self mainRequest] username];
  795 + pass = [[self mainRequest] password];
  796 +
  797 + // Let's try to use the ones set in this object
  798 + } else if (username && password) {
766 user = username; 799 user = username;
767 pass = password; 800 pass = password;
768 } 801 }
769 802
  803 + }
  804 +
  805 +
  806 +
770 // Ok, that didn't work, let's try the keychain 807 // Ok, that didn't work, let's try the keychain
771 if ((!user || !pass) && useKeychainPersistance) { 808 if ((!user || !pass) && useKeychainPersistance) {
772 NSURLCredential *authenticationCredentials = [ASIHTTPRequest savedCredentialsForHost:[url host] port:443 protocol:[url scheme] realm:authenticationRealm]; 809 NSURLCredential *authenticationCredentials = [ASIHTTPRequest savedCredentialsForHost:[url host] port:443 protocol:[url scheme] realm:authenticationRealm];
@@ -833,8 +870,7 @@ static NSError *ASIUnableToCreateRequestError; @@ -833,8 +870,7 @@ static NSError *ASIUnableToCreateRequestError;
833 return; 870 return;
834 } 871 }
835 } 872 }
836 - [self setError:ASIAuthenticationError]; 873 + [self failWithError:ASIAuthenticationError];
837 - complete = YES;  
838 return; 874 return;
839 } 875 }
840 876
@@ -874,8 +910,7 @@ static NSError *ASIUnableToCreateRequestError; @@ -874,8 +910,7 @@ static NSError *ASIUnableToCreateRequestError;
874 } 910 }
875 911
876 // The delegate isn't interested, we'll have to give up 912 // The delegate isn't interested, we'll have to give up
877 - [self setError:ASIAuthenticationError]; 913 + [self failWithError:ASIAuthenticationError];
878 - complete = YES;  
879 return; 914 return;
880 } 915 }
881 916
@@ -982,7 +1017,6 @@ static NSError *ASIUnableToCreateRequestError; @@ -982,7 +1017,6 @@ static NSError *ASIUnableToCreateRequestError;
982 1017
983 - (void)handleStreamError 1018 - (void)handleStreamError
984 { 1019 {
985 - complete = YES;  
986 NSError *underlyingError = [(NSError *)CFReadStreamCopyError(readStream) autorelease]; 1020 NSError *underlyingError = [(NSError *)CFReadStreamCopyError(readStream) autorelease];
987 1021
988 [self cancelLoad]; 1022 [self cancelLoad];
@@ -1096,6 +1130,7 @@ static NSError *ASIUnableToCreateRequestError; @@ -1096,6 +1130,7 @@ static NSError *ASIUnableToCreateRequestError;
1096 @synthesize authenticationRealm; 1130 @synthesize authenticationRealm;
1097 @synthesize error; 1131 @synthesize error;
1098 @synthesize complete; 1132 @synthesize complete;
  1133 +@synthesize requestHeaders;
1099 @synthesize responseHeaders; 1134 @synthesize responseHeaders;
1100 @synthesize responseCookies; 1135 @synthesize responseCookies;
1101 @synthesize requestCookies; 1136 @synthesize requestCookies;
@@ -15,7 +15,6 @@ @@ -15,7 +15,6 @@
15 @implementation ASIHTTPRequestTests 15 @implementation ASIHTTPRequestTests
16 16
17 17
18 -  
19 - (void)testBasicDownload 18 - (void)testBasicDownload
20 { 19 {
21 NSURL *url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com"] autorelease]; 20 NSURL *url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com"] autorelease];
@@ -375,5 +374,4 @@ @@ -375,5 +374,4 @@
375 } 374 }
376 375
377 376
378 -  
379 @end 377 @end
@@ -254,6 +254,18 @@ @@ -254,6 +254,18 @@
254 } 254 }
255 255
256 256
  257 +- (BOOL)respondsToSelector:(SEL)selector
  258 +{
  259 + if (selector == @selector(authorizationNeededForRequest:)) {
  260 + if ([delegate respondsToSelector:@selector(authorizationNeededForRequest:)]) {
  261 + return YES;
  262 + }
  263 + return NO;
  264 + }
  265 + return [super respondsToSelector:selector];
  266 +}
  267 +
  268 +
257 269
258 @synthesize uploadProgressDelegate; 270 @synthesize uploadProgressDelegate;
259 @synthesize downloadProgressDelegate; 271 @synthesize downloadProgressDelegate;
@@ -21,6 +21,7 @@ @@ -21,6 +21,7 @@
21 - (void)testFailure; 21 - (void)testFailure;
22 - (void)testFailureCancelsOtherRequests; 22 - (void)testFailureCancelsOtherRequests;
23 - (void)testProgress; 23 - (void)testProgress;
  24 +- (void)testProgressWithAuthentication;
24 25
25 - (void)setProgress:(float)newProgress; 26 - (void)setProgress:(float)newProgress;
26 @end 27 @end
@@ -12,7 +12,6 @@ @@ -12,7 +12,6 @@
12 12
13 @implementation ASINetworkQueueTests 13 @implementation ASINetworkQueueTests
14 14
15 -  
16 - (void)testProgress 15 - (void)testProgress
17 { 16 {
18 complete = NO; 17 complete = NO;
@@ -78,12 +77,15 @@ @@ -78,12 +77,15 @@
78 77
79 } 78 }
80 79
  80 +
  81 +
81 - (void)setProgress:(float)newProgress 82 - (void)setProgress:(float)newProgress
82 { 83 {
83 progress = newProgress; 84 progress = newProgress;
84 } 85 }
85 86
86 87
  88 +
87 - (void)testFailure 89 - (void)testFailure
88 { 90 {
89 complete = NO; 91 complete = NO;
@@ -189,6 +191,8 @@ @@ -189,6 +191,8 @@
189 [requestThatShouldFail release]; 191 [requestThatShouldFail release];
190 } 192 }
191 193
  194 +
  195 +
192 - (void)requestFailedCancellingOthers:(ASIHTTPRequest *)request 196 - (void)requestFailedCancellingOthers:(ASIHTTPRequest *)request
193 { 197 {
194 complete = YES; 198 complete = YES;
@@ -205,6 +209,62 @@ @@ -205,6 +209,62 @@
205 complete = YES; 209 complete = YES;
206 } 210 }
207 211
  212 +- (void)testProgressWithAuthentication
  213 +{
  214 + complete = NO;
  215 + progress = 0;
  216 +
  217 + networkQueue = [[ASINetworkQueue alloc] init];
  218 + [networkQueue setDownloadProgressDelegate:self];
  219 + [networkQueue setDelegate:self];
  220 + [networkQueue setShowAccurateProgress:YES];
  221 + [networkQueue setQueueDidFinishSelector:@selector(queueFinished:)];
  222 + [networkQueue setRequestDidFailSelector:@selector(requestFailedCancellingOthers:)];
  223 +
  224 + NSURL *url;
  225 + url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/basic-authentication"] autorelease];
  226 + ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
  227 + [networkQueue addOperation:request];
  228 +
  229 + [networkQueue go];
  230 +
  231 + NSDate* endDate = [NSDate distantFuture];
  232 + while (!complete) {
  233 + [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:endDate];
  234 + }
  235 +
  236 + NSError *error = [request error];
  237 + STAssertNotNil(error,@"The HEAD request failed, but it didn't tell the main request to fail");
  238 + [networkQueue release];
  239 +
  240 +
  241 + networkQueue = [[ASINetworkQueue alloc] init];
  242 + [networkQueue setDownloadProgressDelegate:self];
  243 + [networkQueue setDelegate:self];
  244 + [networkQueue setShowAccurateProgress:YES];
  245 + [networkQueue setQueueDidFinishSelector:@selector(queueFinished:)];
  246 +
  247 + request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
  248 + [request setUsername:@"secret_username"];
  249 + [request setPassword:@"secret_password"];
  250 + [networkQueue addOperation:request];
  251 +
  252 + [networkQueue go];
  253 +
  254 + while (!complete) {
  255 + [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:endDate];
  256 + }
  257 +
  258 + error = [request error];
  259 + STAssertNil(error,@"Failed to use authentication in a queue");
  260 + [networkQueue release];
  261 +
  262 +
  263 +
  264 +
  265 +
  266 +}
  267 +
208 268
209 269
210 @end 270 @end