diff options
author | avi@google.com <avi@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-10-30 18:47:16 +0000 |
---|---|---|
committer | avi@google.com <avi@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-10-30 18:47:16 +0000 |
commit | 332ff119039b7dd3ba3d1994dde0b077f015bc03 (patch) | |
tree | 5d6ac0ad34e0a53d213fbefebf28e79668a67338 | |
parent | 38993f6f15fb7b12ee4c973910369dc82a1a5595 (diff) | |
download | chromium_src-332ff119039b7dd3ba3d1994dde0b077f015bc03.zip chromium_src-332ff119039b7dd3ba3d1994dde0b077f015bc03.tar.gz chromium_src-332ff119039b7dd3ba3d1994dde0b077f015bc03.tar.bz2 |
Initial Mac proxy implementation
Review URL: http://codereview.chromium.org/8671
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@4221 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | net/http/http_network_layer.cc | 4 | ||||
-rw-r--r-- | net/net.xcodeproj/project.pbxproj | 10 | ||||
-rw-r--r-- | net/net_lib.scons | 5 | ||||
-rw-r--r-- | net/proxy/proxy_resolver_mac.cc | 342 | ||||
-rw-r--r-- | net/proxy/proxy_resolver_mac.h | 25 | ||||
-rw-r--r-- | net/proxy/proxy_resolver_winhttp.cc | 15 | ||||
-rw-r--r-- | net/proxy/proxy_service.cc | 29 | ||||
-rw-r--r-- | net/proxy/proxy_service.h | 12 | ||||
-rw-r--r-- | net/proxy/proxy_service_unittest.cc | 23 |
9 files changed, 441 insertions, 24 deletions
diff --git a/net/http/http_network_layer.cc b/net/http/http_network_layer.cc index 1aaa226..9822998 100644 --- a/net/http/http_network_layer.cc +++ b/net/http/http_network_layer.cc @@ -13,6 +13,8 @@ #if defined(OS_WIN) #include "net/http/http_transaction_winhttp.h" #include "net/proxy/proxy_resolver_winhttp.h" +#elif defined(OS_MACOSX) +#include "net/proxy/proxy_resolver_mac.h" #endif namespace net { @@ -51,6 +53,8 @@ HttpNetworkLayer::HttpNetworkLayer(const ProxyInfo* pi) } else { #if defined(OS_WIN) proxy_resolver_.reset(new ProxyResolverWinHttp()); +#elif defined(OS_MACOSX) + proxy_resolver_.reset(new ProxyResolverMac()); #else NOTIMPLEMENTED(); proxy_resolver_.reset(new ProxyResolverNull()); diff --git a/net/net.xcodeproj/project.pbxproj b/net/net.xcodeproj/project.pbxproj index e454b48..bb4aded 100644 --- a/net/net.xcodeproj/project.pbxproj +++ b/net/net.xcodeproj/project.pbxproj @@ -127,6 +127,8 @@ 7BED34450E5A1A9600A747DB /* libbase.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7BED324A0E5A17C000A747DB /* libbase.a */; }; 7BED34520E5A1ABC00A747DB /* libgtest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7BED32530E5A17C300A747DB /* libgtest.a */; }; 8200F2030E5F741E005A3C44 /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8200F2020E5F741E005A3C44 /* CoreServices.framework */; }; + 820701040EB6611F005CD9E7 /* proxy_resolver_mac.cc in Sources */ = {isa = PBXBuildFile; fileRef = 820701020EB6611F005CD9E7 /* proxy_resolver_mac.cc */; }; + 820706940EB8D9D4005CD9E7 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 820706930EB8D9D4005CD9E7 /* SystemConfiguration.framework */; }; 82113A1D0E8434EE00E3848F /* x509_certificate_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 82113A1C0E8434EE00E3848F /* x509_certificate_unittest.cc */; }; 82113A280E84360200E3848F /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 82113A270E84360200E3848F /* Security.framework */; }; 82113BBD0E892E5800E3848F /* x509_certificate.cc in Sources */ = {isa = PBXBuildFile; fileRef = 82113BBC0E892E5800E3848F /* x509_certificate.cc */; }; @@ -655,6 +657,9 @@ 7BED33BB0E5A198600A747DB /* url_request_inet_job.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = url_request_inet_job.cc; sourceTree = "<group>"; }; 7BED33BC0E5A198600A747DB /* url_request_http_job.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = url_request_http_job.h; sourceTree = "<group>"; }; 8200F2020E5F741E005A3C44 /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = System/Library/Frameworks/CoreServices.framework; sourceTree = "<group>"; }; + 820701020EB6611F005CD9E7 /* proxy_resolver_mac.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = proxy_resolver_mac.cc; path = proxy/proxy_resolver_mac.cc; sourceTree = "<group>"; }; + 820701030EB6611F005CD9E7 /* proxy_resolver_mac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = proxy_resolver_mac.h; path = proxy/proxy_resolver_mac.h; sourceTree = "<group>"; }; + 820706930EB8D9D4005CD9E7 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = /System/Library/Frameworks/SystemConfiguration.framework; sourceTree = "<absolute>"; }; 82113A1C0E8434EE00E3848F /* x509_certificate_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = x509_certificate_unittest.cc; sourceTree = "<group>"; }; 82113A270E84360200E3848F /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = "<group>"; }; 82113BBC0E892E5800E3848F /* x509_certificate.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = x509_certificate.cc; sourceTree = "<group>"; }; @@ -705,6 +710,7 @@ 7BA362C40E8C3D4C0023C8B9 /* libsdch.a in Frameworks */, 7BA0169E0E5A1EAE00044150 /* libzlib.a in Frameworks */, 82113A280E84360200E3848F /* Security.framework in Frameworks */, + 820706940EB8D9D4005CD9E7 /* SystemConfiguration.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -816,6 +822,7 @@ 8200F2020E5F741E005A3C44 /* CoreServices.framework */, 7BA0157A0E5A1C9100044150 /* Foundation.framework */, 82113A270E84360200E3848F /* Security.framework */, + 820706930EB8D9D4005CD9E7 /* SystemConfiguration.framework */, ); name = Frameworks; sourceTree = SDKROOT; @@ -1137,6 +1144,8 @@ children = ( E49DD33A0E8933C0003C7A87 /* proxy_resolver_fixed.h */, E49DD33B0E8933C0003C7A87 /* proxy_resolver_fixed.cc */, + 820701030EB6611F005CD9E7 /* proxy_resolver_mac.h */, + 820701020EB6611F005CD9E7 /* proxy_resolver_mac.cc */, 04AC41720EA941AE0063A0AB /* proxy_resolver_null.h */, E49DD3350E8933A2003C7A87 /* proxy_service.h */, E49DD3360E8933A2003C7A87 /* proxy_service.cc */, @@ -1468,6 +1477,7 @@ 7B85043B0E5B2E6400730B43 /* os_file_posix.cc in Sources */, 7B85043C0E5B2E6400730B43 /* platform_mime_util_mac.mm in Sources */, E49DD33C0E8933C0003C7A87 /* proxy_resolver_fixed.cc in Sources */, + 820701040EB6611F005CD9E7 /* proxy_resolver_mac.cc in Sources */, E49DD3370E8933A2003C7A87 /* proxy_service.cc in Sources */, 7B85043D0E5B2E6400730B43 /* rankings.cc in Sources */, 7B8B5B560E5CEADE002F9A97 /* registry_controlled_domain.cc in Sources */, diff --git a/net/net_lib.scons b/net/net_lib.scons index 7db4aa7..82a82eb 100644 --- a/net/net_lib.scons +++ b/net/net_lib.scons @@ -89,7 +89,6 @@ input_files = [ 'http/http_vary_data.cc', 'http/winhttp_request_throttle.cc', 'proxy/proxy_resolver_fixed.cc', - 'proxy/proxy_resolver_winhttp.cc', 'proxy/proxy_service.cc', 'url_request/mime_sniffer_proxy.cc', 'url_request/url_request.cc', @@ -123,7 +122,6 @@ if env['PLATFORM'] in ('posix', 'darwin'): 'base/winsock_init.cc', 'http/http_transaction_winhttp.cc', 'http/winhttp_request_throttle.cc', - 'proxy/proxy_resolver_winhttp.cc', 'url_request/url_request_file_dir_job.cc', 'url_request/url_request_ftp_job.cc', 'url_request/url_request_inet_job.cc', @@ -142,12 +140,15 @@ if env['PLATFORM'] == 'win32': 'disk_cache/file_win.cc', 'disk_cache/mapped_file_win.cc', 'disk_cache/os_file_win.cc', + 'proxy/proxy_resolver_winhttp.cc', ]) if env['PLATFORM'] == 'darwin': input_files.extend([ 'base/platform_mime_util_mac.mm', + 'base/ssl_client_socket_mac.cc', 'base/x509_certificate_mac.cc', + 'proxy/proxy_resolver_mac.cc', ]) if env['PLATFORM'] == 'posix': diff --git a/net/proxy/proxy_resolver_mac.cc b/net/proxy/proxy_resolver_mac.cc new file mode 100644 index 0000000..0bf52b91 --- /dev/null +++ b/net/proxy/proxy_resolver_mac.cc @@ -0,0 +1,342 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/proxy/proxy_resolver_mac.h" + +#include <CoreFoundation/CoreFoundation.h> +#include <CoreServices/CoreServices.h> +#include <SystemConfiguration/SystemConfiguration.h> + +#include "base/scoped_cftyperef.h" +#include "base/string_util.h" +#include "base/sys_string_conversions.h" +#include "net/base/net_errors.h" + +namespace { + +// Utility function to pull out a value from a dictionary, check its type, and +// return it. Returns NULL if the key is not present or of the wrong type. +CFTypeRef GetValueFromDictionary(CFDictionaryRef dict, + CFStringRef key, + CFTypeID expected_type) { + CFTypeRef value = CFDictionaryGetValue(dict, key); + if (!value) + return value; + + if (CFGetTypeID(value) != expected_type) { + scoped_cftyperef<CFStringRef> expected_type_ref( + CFCopyTypeIDDescription(expected_type)); + scoped_cftyperef<CFStringRef> actual_type_ref( + CFCopyTypeIDDescription(CFGetTypeID(value))); + LOG(WARNING) << "Expected value for key " + << base::SysCFStringRefToUTF8(key) + << " to be " + << base::SysCFStringRefToUTF8(expected_type_ref) + << " but it was " + << base::SysCFStringRefToUTF8(actual_type_ref) + << " instead"; + return NULL; + } + + return value; +} + +// Utility function to pull out a boolean value from a dictionary and return it, +// returning a default value if the key is not present. +bool GetBoolFromDictionary(CFDictionaryRef dict, + CFStringRef key, + bool default_value) { + CFNumberRef number = (CFNumberRef)GetValueFromDictionary(dict, key, + CFNumberGetTypeID()); + if (!number) + return default_value; + + int int_value; + if (CFNumberGetValue(number, kCFNumberIntType, &int_value)) + return int_value; + else + return default_value; +} + +// Utility function to pull out a host/port pair from a dictionary and format +// them into a "<host>[:<port>]" style string. Pass in a dictionary that has a +// value for the host key and optionally a value for the port key. Returns a +// formatted string. In the error condition where the host value is especially +// malformed, returns an empty string. (You may still want to check for that +// result anyway.) +std::string GetHostPortFromDictionary(CFDictionaryRef dict, + CFStringRef host_key, + CFStringRef port_key) { + std::string result; + + CFStringRef host_ref = + (CFStringRef)GetValueFromDictionary(dict, host_key, + CFStringGetTypeID()); + if (!host_ref) { + LOG(WARNING) << "Could not find expected key " + << base::SysCFStringRefToUTF8(host_key) + << " in the proxy dictionary"; + return result; + } + result = base::SysCFStringRefToUTF8(host_ref); + + CFNumberRef port_ref = + (CFNumberRef)GetValueFromDictionary(dict, port_key, + CFNumberGetTypeID()); + if (port_ref) { + int port; + CFNumberGetValue(port_ref, kCFNumberIntType, &port); + result += ":"; + result += IntToString(port); + } + + return result; +} + +// Callback for CFNetworkExecuteProxyAutoConfigurationURL. |client| is a pointer +// to a CFTypeRef. This stashes either |error| or |proxies| in that location. +void ResultCallback(void* client, CFArrayRef proxies, CFErrorRef error) { + DCHECK((proxies != NULL) == (error == NULL)); + + CFTypeRef* result_ptr = (CFTypeRef*)client; + DCHECK(result_ptr != NULL); + DCHECK(*result_ptr == NULL); + + if (error != NULL) { + *result_ptr = CFRetain(error); + } else { + *result_ptr = CFRetain(proxies); + } + CFRunLoopStop(CFRunLoopGetCurrent()); +} + +} // namespace + +namespace net { + +int ProxyResolverMac::GetProxyConfig(ProxyConfig* config) { + scoped_cftyperef<CFDictionaryRef> config_dict( + SCDynamicStoreCopyProxies(NULL)); + DCHECK(config_dict); + + // auto-detect + + // There appears to be no UI for this configuration option, and we're not sure + // if Apple's proxy code even takes it into account. But the constant is in + // the header file so we'll use it. + config->auto_detect = + GetBoolFromDictionary(config_dict.get(), + kSCPropNetProxiesProxyAutoDiscoveryEnable, + false); + + // PAC file + + if (GetBoolFromDictionary(config_dict.get(), + kSCPropNetProxiesProxyAutoConfigEnable, + false)) { + CFStringRef pac_url_ref = + (CFStringRef)GetValueFromDictionary( + config_dict.get(), + kSCPropNetProxiesProxyAutoConfigURLString, + CFStringGetTypeID()); + if (pac_url_ref) + config->pac_url = base::SysCFStringRefToUTF8(pac_url_ref); + } + + // proxies (for now only ftp, http and https) + + if (GetBoolFromDictionary(config_dict.get(), + kSCPropNetProxiesFTPEnable, + false)) { + std::string host_port = + GetHostPortFromDictionary(config_dict.get(), + kSCPropNetProxiesFTPProxy, + kSCPropNetProxiesFTPPort); + if (!host_port.empty()) { + config->proxy_server += "ftp="; + config->proxy_server += host_port; + } + } + if (GetBoolFromDictionary(config_dict.get(), + kSCPropNetProxiesHTTPEnable, + false)) { + std::string host_port = + GetHostPortFromDictionary(config_dict.get(), + kSCPropNetProxiesHTTPProxy, + kSCPropNetProxiesHTTPPort); + if (!host_port.empty()) { + if (!config->proxy_server.empty()) + config->proxy_server += ";"; + config->proxy_server += "http="; + config->proxy_server += host_port; + } + } + if (GetBoolFromDictionary(config_dict.get(), + kSCPropNetProxiesHTTPSEnable, + false)) { + std::string host_port = + GetHostPortFromDictionary(config_dict.get(), + kSCPropNetProxiesHTTPSProxy, + kSCPropNetProxiesHTTPSPort); + if (!host_port.empty()) { + if (!config->proxy_server.empty()) + config->proxy_server += ";"; + config->proxy_server += "https="; + config->proxy_server += host_port; + } + } + + // proxy bypass list + + CFArrayRef bypass_array_ref = + (CFArrayRef)GetValueFromDictionary(config_dict.get(), + kSCPropNetProxiesExceptionsList, + CFArrayGetTypeID()); + if (bypass_array_ref) { + CFIndex bypass_array_count = CFArrayGetCount(bypass_array_ref); + for (CFIndex i = 0; i < bypass_array_count; ++i) { + CFStringRef bypass_item_ref = + (CFStringRef)CFArrayGetValueAtIndex(bypass_array_ref, i); + if (CFGetTypeID(bypass_item_ref) != CFStringGetTypeID()) { + LOG(WARNING) << "Expected value for item " << i + << " in the kSCPropNetProxiesExceptionsList" + " to be a CFStringRef but it was not"; + + } else { + config->proxy_bypass.push_back( + base::SysCFStringRefToUTF8(bypass_item_ref)); + } + } + } + + // proxy bypass boolean + + config->proxy_bypass_local_names = + GetBoolFromDictionary(config_dict.get(), + kSCPropNetProxiesExcludeSimpleHostnames, + false); + + return OK; +} + +// Gets the proxy information for a query URL from a PAC. Implementation +// inspired by http://developer.apple.com/samplecode/CFProxySupportTool/ +int ProxyResolverMac::GetProxyForURL(const std::string& query_url, + const std::string& pac_url, + ProxyInfo* results) { + scoped_cftyperef<CFStringRef> query_ref( + base::SysUTF8ToCFStringRef(query_url)); + scoped_cftyperef<CFStringRef> pac_ref( + base::SysUTF8ToCFStringRef(pac_url)); + scoped_cftyperef<CFURLRef> query_url_ref( + CFURLCreateWithString(kCFAllocatorDefault, + query_ref.get(), + NULL)); + scoped_cftyperef<CFURLRef> pac_url_ref( + CFURLCreateWithString(kCFAllocatorDefault, + pac_ref.get(), + NULL)); + + // Work around <rdar://problem/5530166>. This dummy call to + // CFNetworkCopyProxiesForURL initializes some state within CFNetwork that is + // required by CFNetworkExecuteProxyAutoConfigurationURL. + + CFArrayRef dummy_result = CFNetworkCopyProxiesForURL(query_url_ref.get(), + NULL); + if (dummy_result) + CFRelease(dummy_result); + + // We cheat here. We need to act as if we were synchronous, so we pump the + // runloop ourselves. Our caller moved us to a new thread anyway, so this is + // OK to do. (BTW, CFNetworkExecuteProxyAutoConfigurationURL returns a + // runloop source we need to release despite its name.) + + CFTypeRef result; + CFStreamClientContext context = { 0, &result, NULL, NULL, NULL }; + scoped_cftyperef<CFRunLoopSourceRef> runloop_source( + CFNetworkExecuteProxyAutoConfigurationURL(pac_url_ref.get(), + query_url_ref.get(), + ResultCallback, + &context)); + if (!runloop_source) + return ERR_FAILED; + + const CFStringRef private_runloop_mode = + CFSTR("org.chromium.ProxyResolverMac"); + + CFRunLoopAddSource(CFRunLoopGetCurrent(), runloop_source.get(), + private_runloop_mode); + CFRunLoopRunInMode(private_runloop_mode, DBL_MAX, false); + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), runloop_source.get(), + private_runloop_mode); + DCHECK(result != NULL); + + if (CFGetTypeID(result) == CFErrorGetTypeID()) { + // TODO(avi): do something better than this + CFRelease(result); + return ERR_FAILED; + } + DCHECK(CFGetTypeID(result) == CFArrayGetTypeID()); + scoped_cftyperef<CFArrayRef> proxy_array_ref((CFArrayRef)result); + + // Proxy information. If we're allowed to contact the site directly, we set + // allow_direct to true. If we've found proxies to use, apart from + // accumulating them in proxy_list, we set found_proxy to true. These + // bool results are orthogonal. + bool allow_direct = false; + std::string proxy_list; + bool found_proxy = false; + + CFIndex proxy_array_count = CFArrayGetCount(proxy_array_ref.get()); + for (CFIndex i = 0; i < proxy_array_count; ++i) { + CFDictionaryRef proxy_dictionary = + (CFDictionaryRef)CFArrayGetValueAtIndex(proxy_array_ref.get(), i); + DCHECK(CFGetTypeID(proxy_dictionary) == CFDictionaryGetTypeID()); + + // The dictionary may have the following keys: + // - kCFProxyTypeKey : The type of the proxy. We're assuming that it's of + // the type we asked for (e.g. kCFProxyTypeHTTP for + // http://... ) if it's not kCFProxyTypeNone. + // - kCFProxyHostNameKey + // - kCFProxyPortNumberKey : The meat we're after. + // - kCFProxyUsernameKey + // - kCFProxyPasswordKey : Despite the existence of these keys in the + // documentation, they're never populated. Even if a + // username/password were to be set in the network + // proxy system preferences, we'd need to fetch it + // from the Keychain ourselves. CFProxy is such a + // tease. + // - kCFProxyAutoConfigurationURLKey : If the PAC file specifies another + // PAC file, I'm going home. + + CFStringRef proxy_type = + (CFStringRef)GetValueFromDictionary(proxy_dictionary, + kCFProxyTypeKey, + CFStringGetTypeID()); + if (CFEqual(proxy_type, kCFProxyTypeNone)) + allow_direct = true; + if (CFEqual(proxy_type, kCFProxyTypeNone) || + CFEqual(proxy_type, kCFProxyTypeSOCKS) || + CFEqual(proxy_type, kCFProxyTypeAutoConfigurationURL)) + continue; + + if (found_proxy) + proxy_list += ";"; + else + found_proxy = true; + + proxy_list += GetHostPortFromDictionary(proxy_dictionary, + kCFProxyHostNameKey, + kCFProxyPortNumberKey); + } + + if (found_proxy) + results->UseNamedProxy(proxy_list); + if (allow_direct) + results->UseDirect(); + + return OK; +} + +} // namespace net diff --git a/net/proxy/proxy_resolver_mac.h b/net/proxy/proxy_resolver_mac.h new file mode 100644 index 0000000..3063866 --- /dev/null +++ b/net/proxy/proxy_resolver_mac.h @@ -0,0 +1,25 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_PROXY_PROXY_RESOLVER_MAC_H_ +#define NET_PROXY_PROXY_RESOLVER_MAC_H_ + +#include "net/proxy/proxy_service.h" + +namespace net { + +// Implementation of ProxyResolver that uses the Mac CFProxySupport to implement +// proxies. +class ProxyResolverMac : public ProxyResolver { + public: + // ProxyResolver methods: + virtual int GetProxyConfig(ProxyConfig* config); + virtual int GetProxyForURL(const std::string& query_url, + const std::string& pac_url, + ProxyInfo* results); +}; + +} // namespace net + +#endif // NET_PROXY_PROXY_RESOLVER_MAC_H_ diff --git a/net/proxy/proxy_resolver_winhttp.cc b/net/proxy/proxy_resolver_winhttp.cc index 5a16df5..19dd0b0 100644 --- a/net/proxy/proxy_resolver_winhttp.cc +++ b/net/proxy/proxy_resolver_winhttp.cc @@ -8,6 +8,7 @@ #include <winhttp.h> #include "base/histogram.h" +#include "base/string_tokenizer.h" #include "net/base/net_errors.h" #pragma comment(lib, "winhttp.lib") @@ -70,8 +71,18 @@ int ProxyResolverWinHttp::GetProxyConfig(ProxyConfig* config) { config->auto_detect = true; if (ie_config.lpszProxy) config->proxy_server = WideToASCII(ie_config.lpszProxy); - if (ie_config.lpszProxyBypass) - config->proxy_bypass = WideToASCII(ie_config.lpszProxyBypass); + if (ie_config.lpszProxyBypass) { + std::string proxy_bypass = WideToASCII(ie_config.lpszProxyBypass); + + StringTokenizer proxy_server_bypass_list(proxy_bypass, "; \t\n\r"); + while (proxy_server_bypass_list.GetNext()) { + std::string bypass_url_domain = proxy_server_bypass_list.token(); + if (bypass_url_domain == "<local>") + config->proxy_bypass_local_names = true; + else + config->proxy_bypass.push_back(bypass_url_domain); + } + } if (ie_config.lpszAutoConfigUrl) config->pac_url = WideToASCII(ie_config.lpszAutoConfigUrl); diff --git a/net/proxy/proxy_service.cc b/net/proxy/proxy_service.cc index 52d8491..394a97f 100644 --- a/net/proxy/proxy_service.cc +++ b/net/proxy/proxy_service.cc @@ -30,6 +30,7 @@ ProxyConfig::ID ProxyConfig::last_id_ = ProxyConfig::INVALID_ID; ProxyConfig::ProxyConfig() : auto_detect(false), + proxy_bypass_local_names(false), id_(++last_id_) { } @@ -39,7 +40,8 @@ bool ProxyConfig::Equals(const ProxyConfig& other) const { return auto_detect == other.auto_detect && pac_url == other.pac_url && proxy_server == other.proxy_server && - proxy_bypass == other.proxy_bypass; + proxy_bypass == other.proxy_bypass && + proxy_bypass_local_names == other.proxy_bypass_local_names; } // ProxyList ------------------------------------------------------------------ @@ -445,17 +447,18 @@ bool ProxyService::ShouldBypassProxyForURL(const GURL& url) { url_domain += "://"; url_domain += url.host(); + // This isn't superfluous; GURL case canonicalization doesn't hit the embedded + // percent-encoded characters. StringToLowerASCII(&url_domain); - StringTokenizer proxy_server_bypass_list(config_.proxy_bypass, ";"); - while (proxy_server_bypass_list.GetNext()) { - std::string bypass_url_domain = proxy_server_bypass_list.token(); - if (bypass_url_domain == "<local>") { - // Any name without a DOT (.) is considered to be local. - if (url.host().find('.') == std::string::npos) - return true; - continue; - } + if (config_.proxy_bypass_local_names) { + if (url.host().find('.') == std::string::npos) + return true; + } + + for(std::vector<std::string>::const_iterator i = config_.proxy_bypass.begin(); + i != config_.proxy_bypass.end(); ++i) { + std::string bypass_url_domain = *i; // The proxy server bypass list can contain entities with http/https // If no scheme is specified then it indicates that all schemes are @@ -473,6 +476,12 @@ bool ProxyService::ShouldBypassProxyForURL(const GURL& url) { if (MatchPattern(url_domain, bypass_url_domain)) return true; + + // Some systems (the Mac, for example) allow CIDR-style specification of + // proxy bypass for IP-specified hosts (e.g. "10.0.0.0/8"; see + // http://www.tcd.ie/iss/internet/osx_proxy.php for a real-world example). + // That's kinda cool so we'll provide that for everyone. + // TODO(avi): implement here } return false; diff --git a/net/proxy/proxy_service.h b/net/proxy/proxy_service.h index c596aab..8ea3653 100644 --- a/net/proxy/proxy_service.h +++ b/net/proxy/proxy_service.h @@ -47,12 +47,16 @@ class ProxyConfig { std::string pac_url; // If non-empty, indicates the proxy server to use (of the form host:port). + // If proxies depend on the scheme, a string of the format + // "scheme1=url[:port];scheme2=url[:port]" may be provided here. std::string proxy_server; - // If non-empty, indicates a comma-delimited list of hosts that should bypass - // any proxy configuration. For these hosts, a direct connection should - // always be used. - std::string proxy_bypass; + // Indicates a list of hosts that should bypass any proxy configuration. For + // these hosts, a direct connection should always be used. + std::vector<std::string> proxy_bypass; + + // Indicates whether local names (no dots) bypass proxies. + bool proxy_bypass_local_names; // Returns true if the given config is equivalent to this config. bool Equals(const ProxyConfig& other) const; diff --git a/net/proxy/proxy_service_unittest.cc b/net/proxy/proxy_service_unittest.cc index d33ac6d..8166a42 100644 --- a/net/proxy/proxy_service_unittest.cc +++ b/net/proxy/proxy_service_unittest.cc @@ -301,7 +301,7 @@ TEST(ProxyServiceTest, ProxyBypassList) { MockProxyResolver resolver; resolver.config->proxy_server = "foopy1:8080;foopy2:9090"; resolver.config->auto_detect = false; - resolver.config->proxy_bypass = "<local>"; + resolver.config->proxy_bypass_local_names = true; net::ProxyService service(&resolver); GURL url("http://www.google.com/"); @@ -318,7 +318,9 @@ TEST(ProxyServiceTest, ProxyBypassList) { EXPECT_EQ(rv, net::OK); EXPECT_TRUE(info1.is_direct()); - resolver.config->proxy_bypass = "<local>;*.org"; + resolver.config->proxy_bypass.clear(); + resolver.config->proxy_bypass.push_back("*.org"); + resolver.config->proxy_bypass_local_names = true; net::ProxyService service2(&resolver); GURL test_url2("http://www.webkit.org"); net::ProxyInfo info2; @@ -326,7 +328,10 @@ TEST(ProxyServiceTest, ProxyBypassList) { EXPECT_EQ(rv, net::OK); EXPECT_TRUE(info2.is_direct()); - resolver.config->proxy_bypass = "<local>;*.org;7*"; + resolver.config->proxy_bypass.clear(); + resolver.config->proxy_bypass.push_back("*.org"); + resolver.config->proxy_bypass.push_back("7*"); + resolver.config->proxy_bypass_local_names = true; net::ProxyService service3(&resolver); GURL test_url3("http://74.125.19.147"); net::ProxyInfo info3; @@ -334,7 +339,9 @@ TEST(ProxyServiceTest, ProxyBypassList) { EXPECT_EQ(rv, net::OK); EXPECT_TRUE(info3.is_direct()); - resolver.config->proxy_bypass = "<local>;*.org;"; + resolver.config->proxy_bypass.clear(); + resolver.config->proxy_bypass.push_back("*.org"); + resolver.config->proxy_bypass_local_names = true; net::ProxyService service4(&resolver); GURL test_url4("http://www.msn.com"); net::ProxyInfo info4; @@ -342,7 +349,9 @@ TEST(ProxyServiceTest, ProxyBypassList) { EXPECT_EQ(rv, net::OK); EXPECT_FALSE(info4.is_direct()); - resolver.config->proxy_bypass = "<local>;*.MSN.COM;"; + resolver.config->proxy_bypass.clear(); + resolver.config->proxy_bypass.push_back("*.MSN.COM"); + resolver.config->proxy_bypass_local_names = true; net::ProxyService service5(&resolver); GURL test_url5("http://www.msnbc.msn.com"); net::ProxyInfo info5; @@ -350,7 +359,9 @@ TEST(ProxyServiceTest, ProxyBypassList) { EXPECT_EQ(rv, net::OK); EXPECT_TRUE(info5.is_direct()); - resolver.config->proxy_bypass = "<local>;*.msn.com;"; + resolver.config->proxy_bypass.clear(); + resolver.config->proxy_bypass.push_back("*.msn.com"); + resolver.config->proxy_bypass_local_names = true; net::ProxyService service6(&resolver); GURL test_url6("HTTP://WWW.MSNBC.MSN.COM"); net::ProxyInfo info6; |