Ben Copsey

Added support for PAC scripts, plus two tests for same

@@ -275,6 +275,8 @@ extern NSString* const NetworkRequestErrorDomain; @@ -275,6 +275,8 @@ extern NSString* const NetworkRequestErrorDomain;
275 // Details on the proxy to use - you could set these yourself, but it's probably best to let ASIHTTPRequest detect the system proxy settings 275 // Details on the proxy to use - you could set these yourself, but it's probably best to let ASIHTTPRequest detect the system proxy settings
276 NSString *proxyHost; 276 NSString *proxyHost;
277 int proxyPort; 277 int proxyPort;
  278 +
  279 + NSURL *PACurl;
278 } 280 }
279 281
280 #pragma mark init / dealloc 282 #pragma mark init / dealloc
@@ -437,6 +439,11 @@ extern NSString* const NetworkRequestErrorDomain; @@ -437,6 +439,11 @@ extern NSString* const NetworkRequestErrorDomain;
437 // Is only used when you have specified a Bundle Display Name (CFDisplayBundleName) or Bundle Name (CFBundleName) in your plist 439 // Is only used when you have specified a Bundle Display Name (CFDisplayBundleName) or Bundle Name (CFBundleName) in your plist
438 + (NSString *)defaultUserAgentString; 440 + (NSString *)defaultUserAgentString;
439 441
  442 +#pragma mark proxy autoconfiguration
  443 +
  444 +// Returns an array of proxies to use for a particular url, given the url of a PAC script
  445 ++ (NSArray *)proxiesForURL:(NSURL *)theURL fromPAC:(NSURL *)pacScriptURL;
  446 +
440 @property (retain) NSString *username; 447 @property (retain) NSString *username;
441 @property (retain) NSString *password; 448 @property (retain) NSString *password;
442 @property (retain) NSString *domain; 449 @property (retain) NSString *domain;
@@ -495,4 +502,6 @@ extern NSString* const NetworkRequestErrorDomain; @@ -495,4 +502,6 @@ extern NSString* const NetworkRequestErrorDomain;
495 @property (assign) BOOL shouldRedirect; 502 @property (assign) BOOL shouldRedirect;
496 @property (assign) BOOL validatesSecureCertificate; 503 @property (assign) BOOL validatesSecureCertificate;
497 @property (assign) BOOL shouldCompressRequestBody; 504 @property (assign) BOOL shouldCompressRequestBody;
  505 +@property (assign) BOOL needsProxyAuthentication;
  506 +@property (retain) NSURL *PACurl;
498 @end 507 @end
@@ -76,7 +76,6 @@ static NSError *ASITooMuchRedirectionError; @@ -76,7 +76,6 @@ static NSError *ASITooMuchRedirectionError;
76 @property (retain, nonatomic) NSOutputStream *fileDownloadOutputStream; 76 @property (retain, nonatomic) NSOutputStream *fileDownloadOutputStream;
77 @property (assign, nonatomic) int authenticationRetryCount; 77 @property (assign, nonatomic) int authenticationRetryCount;
78 @property (assign, nonatomic) int proxyAuthenticationRetryCount; 78 @property (assign, nonatomic) int proxyAuthenticationRetryCount;
79 -@property (assign, nonatomic) BOOL needsProxyAuthentication;  
80 @property (assign, nonatomic) BOOL updatedProgress; 79 @property (assign, nonatomic) BOOL updatedProgress;
81 @property (assign, nonatomic) BOOL needsRedirect; 80 @property (assign, nonatomic) BOOL needsRedirect;
82 @property (assign, nonatomic) int redirectCount; 81 @property (assign, nonatomic) int redirectCount;
@@ -518,9 +517,23 @@ static NSError *ASITooMuchRedirectionError; @@ -518,9 +517,23 @@ static NSError *ASITooMuchRedirectionError;
518 } 517 }
519 518
520 519
  520 + // Handle proxy settings
  521 +
  522 + // Have details of the proxy been set on this request
521 if (![self proxyHost] && ![self proxyPort]) { 523 if (![self proxyHost] && ![self proxyPort]) {
522 524
  525 + // If not, we need to figure out what they'll be
  526 +
  527 + NSArray *proxies = nil;
  528 +
  529 + // Have we been given a proxy auto config file?
  530 + if ([self PACurl]) {
  531 +
  532 + proxies = [ASIHTTPRequest proxiesForURL:[self url] fromPAC:[self PACurl]];
  533 +
523 // Detect proxy settings and apply them 534 // Detect proxy settings and apply them
  535 + } else {
  536 +
524 #if TARGET_OS_IPHONE 537 #if TARGET_OS_IPHONE
525 #if !defined(TARGET_IPHONE_SIMULATOR) || __IPHONE_OS_VERSION_MIN_REQUIRED > __IPHONE_2_2 538 #if !defined(TARGET_IPHONE_SIMULATOR) || __IPHONE_OS_VERSION_MIN_REQUIRED > __IPHONE_2_2
526 NSDictionary *proxySettings = [(NSDictionary *)CFNetworkCopySystemProxySettings() autorelease]; 539 NSDictionary *proxySettings = [(NSDictionary *)CFNetworkCopySystemProxySettings() autorelease];
@@ -532,8 +545,16 @@ static NSError *ASITooMuchRedirectionError; @@ -532,8 +545,16 @@ static NSError *ASITooMuchRedirectionError;
532 NSDictionary *proxySettings = [(NSDictionary *)SCDynamicStoreCopyProxies(NULL) autorelease]; 545 NSDictionary *proxySettings = [(NSDictionary *)SCDynamicStoreCopyProxies(NULL) autorelease];
533 #endif 546 #endif
534 547
535 - NSArray *proxies = [(NSArray *)CFNetworkCopyProxiesForURL((CFURLRef)[self url], (CFDictionaryRef)proxySettings) autorelease]; 548 + proxies = [(NSArray *)CFNetworkCopyProxiesForURL((CFURLRef)[self url], (CFDictionaryRef)proxySettings) autorelease];
536 - if (proxies == NULL) { 549 +
  550 + // Now check to see if the proxy settings contained a PAC url, we need to run the script to get the real list of proxies if so
  551 + NSDictionary *settings = [proxies objectAtIndex:0];
  552 + if ([settings objectForKey:(NSString *)kCFProxyAutoConfigurationURLKey]) {
  553 + proxies = [ASIHTTPRequest proxiesForURL:[self url] fromPAC:[settings objectForKey:(NSString *)kCFProxyAutoConfigurationURLKey]];
  554 + }
  555 + }
  556 +
  557 + if (!proxies) {
537 CFRelease(readStream); 558 CFRelease(readStream);
538 readStream = NULL; 559 readStream = NULL;
539 [[self cancelledLock] unlock]; 560 [[self cancelledLock] unlock];
@@ -546,8 +567,6 @@ static NSError *ASITooMuchRedirectionError; @@ -546,8 +567,6 @@ static NSError *ASITooMuchRedirectionError;
546 NSDictionary *settings = [proxies objectAtIndex:0]; 567 NSDictionary *settings = [proxies objectAtIndex:0];
547 [self setProxyHost:[settings objectForKey:(NSString *)kCFProxyHostNameKey]]; 568 [self setProxyHost:[settings objectForKey:(NSString *)kCFProxyHostNameKey]];
548 [self setProxyPort:[[settings objectForKey:(NSString *)kCFProxyPortNumberKey] intValue]]; 569 [self setProxyPort:[[settings objectForKey:(NSString *)kCFProxyPortNumberKey] intValue]];
549 - //NSLog(@"%@",proxySettings);  
550 - //NSLog(@"%@",[proxies objectAtIndex:0]);  
551 } 570 }
552 } 571 }
553 if ([self proxyHost] && [self proxyPort]) { 572 if ([self proxyHost] && [self proxyPort]) {
@@ -2173,6 +2192,31 @@ static NSError *ASITooMuchRedirectionError; @@ -2173,6 +2192,31 @@ static NSError *ASITooMuchRedirectionError;
2173 return [NSString stringWithFormat:@"%@ %@ (%@; %@ %@; %@)", appName, appVersion, deviceName, OSName, OSVersion, locale]; 2192 return [NSString stringWithFormat:@"%@ %@ (%@; %@ %@; %@)", appName, appVersion, deviceName, OSName, OSVersion, locale];
2174 } 2193 }
2175 2194
  2195 +#pragma mark proxy autoconfiguration
  2196 +
  2197 +// Returns an array of proxies to use for a particular url, given the url of a PAC script
  2198 ++ (NSArray *)proxiesForURL:(NSURL *)theURL fromPAC:(NSURL *)pacScriptURL
  2199 +{
  2200 + // From: http://developer.apple.com/samplecode/CFProxySupportTool/listing1.html
  2201 + // Work around <rdar://problem/5530166>. This dummy call to
  2202 + // CFNetworkCopyProxiesForURL initialise some state within CFNetwork
  2203 + // that is required by CFNetworkCopyProxiesForAutoConfigurationScript.
  2204 + (void) CFNetworkCopyProxiesForURL((CFURLRef)theURL, NULL);
  2205 +
  2206 + NSStringEncoding encoding;
  2207 + NSError *err = nil;
  2208 + NSString *script = [NSString stringWithContentsOfURL:pacScriptURL usedEncoding:&encoding error:&err];
  2209 + if (err) {
  2210 + return nil;
  2211 + }
  2212 + CFErrorRef err2 = NULL;
  2213 + // Obtain the list of proxies by running the autoconfiguration script
  2214 + NSArray *proxies = [(NSArray *)CFNetworkCopyProxiesForAutoConfigurationScript((CFStringRef)script,(CFURLRef)theURL, &err2) autorelease];
  2215 + if (err2) {
  2216 + return nil;
  2217 + }
  2218 + return proxies;
  2219 +}
2176 2220
2177 2221
2178 @synthesize username; 2222 @synthesize username;
@@ -2249,4 +2293,5 @@ static NSError *ASITooMuchRedirectionError; @@ -2249,4 +2293,5 @@ static NSError *ASITooMuchRedirectionError;
2249 2293
2250 @synthesize proxyHost; 2294 @synthesize proxyHost;
2251 @synthesize proxyPort; 2295 @synthesize proxyPort;
  2296 +@synthesize PACurl;
2252 @end 2297 @end
@@ -18,6 +18,37 @@ static NSString *proxyPassword = @""; @@ -18,6 +18,37 @@ static NSString *proxyPassword = @"";
18 18
19 @implementation ProxyTests 19 @implementation ProxyTests
20 20
  21 +- (void)testAutoConfigureWithPAC
  22 +{
  23 + // To run this test, specify the location of the pac script that is available at http://developer.apple.com/samplecode/CFProxySupportTool/listing1.html
  24 + NSString *pacurl = @"file:///Users/ben/Desktop/test.pac";
  25 + ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]];
  26 + [request setPACurl:[NSURL URLWithString:pacurl]];
  27 + [request start];
  28 + NSLog(@"%@",[request proxyHost]);
  29 + BOOL success = [[request proxyHost] isEqualToString:@"proxy1.apple.com"];
  30 + GHAssertTrue(success,@"Failed to use the correct proxy");
  31 +
  32 + request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://www.apple.com"]];
  33 + [request setPACurl:[NSURL URLWithString:pacurl]];
  34 + [request start];
  35 + GHAssertNil([request proxyHost],@"Used a proxy when the script told us to go direct");
  36 +}
  37 +
  38 +- (void)testAutoConfigureWithSystemPAC
  39 +{
  40 + // To run this test, specify the pac script above in your network settings
  41 + ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]];
  42 + [request start];
  43 + NSLog(@"%@",[request proxyHost]);
  44 + BOOL success = [[request proxyHost] isEqualToString:@"proxy1.apple.com"];
  45 + GHAssertTrue(success,@"Failed to use the correct proxy");
  46 +
  47 + request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://www.apple.com"]];
  48 + [request start];
  49 + GHAssertNil([request proxyHost],@"Used a proxy when the script told us to go direct");
  50 +}
  51 +
21 - (void)testProxy 52 - (void)testProxy
22 { 53 {
23 BOOL success = (![proxyHost isEqualToString:@""] && proxyPort > 0); 54 BOOL success = (![proxyHost isEqualToString:@""] && proxyPort > 0);
@@ -217,8 +217,13 @@ @@ -217,8 +217,13 @@
217 - (void)authSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo { 217 - (void)authSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo {
218 ASIHTTPRequest *request = (ASIHTTPRequest *)contextInfo; 218 ASIHTTPRequest *request = (ASIHTTPRequest *)contextInfo;
219 if (returnCode == NSOKButton) { 219 if (returnCode == NSOKButton) {
  220 + if ([request needsProxyAuthentication]) {
  221 + [request setProxyUsername:[[[username stringValue] copy] autorelease]];
  222 + [request setProxyPassword:[[[password stringValue] copy] autorelease]];
  223 + } else {
220 [request setUsername:[[[username stringValue] copy] autorelease]]; 224 [request setUsername:[[[username stringValue] copy] autorelease]];
221 [request setPassword:[[[password stringValue] copy] autorelease]]; 225 [request setPassword:[[[password stringValue] copy] autorelease]];
  226 + }
222 [request retryWithAuthentication]; 227 [request retryWithAuthentication];
223 } else { 228 } else {
224 [request cancelLoad]; 229 [request cancelLoad];