diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/base/net_util.cc | 46 | ||||
-rw-r--r-- | net/base/net_util.h | 13 | ||||
-rw-r--r-- | net/base/net_util_unittest.cc | 41 | ||||
-rw-r--r-- | net/build/net.vcproj | 8 | ||||
-rw-r--r-- | net/build/net_unittests.vcproj | 4 | ||||
-rw-r--r-- | net/http/http_network_transaction.cc | 23 | ||||
-rw-r--r-- | net/net.xcodeproj/project.pbxproj | 10 | ||||
-rw-r--r-- | net/net_lib.scons | 2 | ||||
-rw-r--r-- | net/net_unittests.scons | 1 | ||||
-rw-r--r-- | net/proxy/proxy_config_service_fixed.h | 2 | ||||
-rw-r--r-- | net/proxy/proxy_config_service_win.cc | 2 | ||||
-rw-r--r-- | net/proxy/proxy_resolver_mac.cc | 23 | ||||
-rw-r--r-- | net/proxy/proxy_resolver_winhttp.cc | 14 | ||||
-rw-r--r-- | net/proxy/proxy_server.cc | 215 | ||||
-rw-r--r-- | net/proxy/proxy_server.h | 118 | ||||
-rw-r--r-- | net/proxy/proxy_server_unittest.cc | 222 | ||||
-rw-r--r-- | net/proxy/proxy_service.cc | 90 | ||||
-rw-r--r-- | net/proxy/proxy_service.h | 76 | ||||
-rw-r--r-- | net/proxy/proxy_service_unittest.cc | 118 |
19 files changed, 912 insertions, 116 deletions
diff --git a/net/base/net_util.cc b/net/base/net_util.cc index 16adb8a..3158f46 100644 --- a/net/base/net_util.cc +++ b/net/base/net_util.cc @@ -958,4 +958,50 @@ bool FileURLToFilePath(const GURL& gurl, std::wstring* file_path) { return rv; } +bool GetHostAndPort(std::string::const_iterator host_and_port_begin, + std::string::const_iterator host_and_port_end, + std::string* host, + int* port) { + if (host_and_port_begin >= host_and_port_end) + return false; + + // TODO(eroman): support IPv6 literals. + std::string::const_iterator colon = + std::find(host_and_port_begin, host_and_port_end, ':'); + + if (colon == host_and_port_end) { + // No colon. + host->assign(host_and_port_begin, host_and_port_end); + *port = -1; + return true; + } + + if (colon == host_and_port_begin) + return false; // No host. + + if (colon == host_and_port_end - 1) + return false; // There is nothing past the colon. + + // Parse the port number to an integer. + std::string port_string(colon + 1, host_and_port_end); + int parsed_port_number = url_parse::ParsePort(port_string.data(), + url_parse::Component(0, port_string.size())); + + // If parsing failed, port_number will be either PORT_INVALID or + // PORT_UNSPECIFIED, both of which are negative. + if (parsed_port_number < 0) + return false; // Failed parsing the port number. + + // Else successfully parsed port number. + *port = parsed_port_number; + host->assign(host_and_port_begin, colon); + return true; +} + +bool GetHostAndPort(const std::string& host_and_port, + std::string* host, + int* port) { + return GetHostAndPort(host_and_port.begin(), host_and_port.end(), host, port); +} + } // namespace net diff --git a/net/base/net_util.h b/net/base/net_util.h index ddaa77b..e62e95a 100644 --- a/net/base/net_util.h +++ b/net/base/net_util.h @@ -40,6 +40,19 @@ bool FileURLToFilePath(const GURL& url, FilePath* file_path); // Deprecated temporary compatibility function. bool FileURLToFilePath(const GURL& url, std::wstring* file_path); +// Split an input of the form <host>[":"<port>] into its consitituent parts. +// Saves the result into |*host| and |*port|. If the input did not have +// the optional port, sets |*port| to -1. +// Returns true if the parsing was successful, false otherwise. +// TODO(eroman): support IPv6 literals. +bool GetHostAndPort(std::string::const_iterator host_and_port_begin, + std::string::const_iterator host_and_port_end, + std::string* host, + int* port); +bool GetHostAndPort(const std::string& host_and_port, + std::string* host, + int* port); + // Return the value of the HTTP response header with name 'name'. 'headers' // should be in the format that URLRequest::GetResponseHeaders() returns. // Returns the empty string if the header is not found. diff --git a/net/base/net_util_unittest.cc b/net/base/net_util_unittest.cc index 68ec2b3..d3f7b83 100644 --- a/net/base/net_util_unittest.cc +++ b/net/base/net_util_unittest.cc @@ -735,4 +735,45 @@ TEST(NetUtilTest, GetDirectoryListingEntry) { EXPECT_EQ(test_cases[i].expected, results); } } + +TEST(NetUtilTest, GetHostAndPort) { + const struct { + const char* input; + bool success; + const char* expected_host; + int expected_port; + } tests[] = { + // Valid inputs: + {"foo:10", true, "foo", 10}, + {"foo", true, "foo", -1}, + { + // TODO(eroman): support IPv6 literals. + "[1080:0:0:0:8:800:200C:4171]:11", + false, + "", + -1, + }, + // Invalid inputs: + {"foo:bar", false, "", -1}, + {"foo:", false, "", -1}, + {":", false, "", -1}, + {":80", false, "", -1}, + {"", false, "", -1}, + {"porttoolong:300000", false, "", -1}, + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { + std::string host; + int port; + bool ok = net::GetHostAndPort(tests[i].input, &host, &port); + + EXPECT_EQ(tests[i].success, ok); + + if (tests[i].success) { + EXPECT_EQ(tests[i].expected_host, host); + EXPECT_EQ(tests[i].expected_port, port); + } + } +} + #endif diff --git a/net/build/net.vcproj b/net/build/net.vcproj index dac85da..4afc6a9 100644 --- a/net/build/net.vcproj +++ b/net/build/net.vcproj @@ -1025,6 +1025,14 @@ > </File> <File + RelativePath="..\proxy\proxy_server.cc" + > + </File> + <File + RelativePath="..\proxy\proxy_server.h" + > + </File> + <File RelativePath="..\proxy\proxy_service.cc" > </File> diff --git a/net/build/net_unittests.vcproj b/net/build/net_unittests.vcproj index 2323492e..c25c546 100644 --- a/net/build/net_unittests.vcproj +++ b/net/build/net_unittests.vcproj @@ -415,6 +415,10 @@ > </File> <File + RelativePath="..\proxy\proxy_server_unittest.cc" + > + </File> + <File RelativePath="..\proxy\proxy_service_unittest.cc" > </File> diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc index 71666fc..eecb2f4 100644 --- a/net/http/http_network_transaction.cc +++ b/net/http/http_network_transaction.cc @@ -444,6 +444,12 @@ int HttpNetworkTransaction::DoResolveProxy() { int HttpNetworkTransaction::DoResolveProxyComplete(int result) { next_state_ = STATE_INIT_CONNECTION; + // Since we only support HTTP proxies or DIRECT connections, remove + // any other type of proxy from the list (i.e. SOCKS). + // Supporting SOCKS is issue http://crbug.com/469. + proxy_info_.RemoveProxiesWithoutScheme( + ProxyServer::SCHEME_DIRECT | ProxyServer::SCHEME_HTTP); + pac_request_ = NULL; if (result != OK) { @@ -465,7 +471,7 @@ int HttpNetworkTransaction::DoInitConnection() { // Build the string used to uniquely identify connections of this type. std::string connection_group; if (using_proxy_ || using_tunnel_) - connection_group = "proxy/" + proxy_info_.proxy_server() + "/"; + connection_group = "proxy/" + proxy_info_.proxy_server().ToURI() + "/"; if (!using_proxy_) connection_group.append(request_->url.GetOrigin().spec()); @@ -499,14 +505,9 @@ int HttpNetworkTransaction::DoResolveHost() { // Determine the host and port to connect to. if (using_proxy_ || using_tunnel_) { - const std::string& proxy = proxy_info_.proxy_server(); - StringTokenizer t(proxy, ":"); - // TODO(darin): Handle errors here. Perhaps HttpProxyInfo should do this - // before claiming a proxy server configuration. - t.GetNext(); - host = t.token(); - t.GetNext(); - port = StringToInt(t.token()); + ProxyServer proxy_server = proxy_info_.proxy_server(); + host = proxy_server.host(); + port = proxy_server.port(); } else { // Direct connection host = request_->url.host(); @@ -1192,7 +1193,7 @@ void HttpNetworkTransaction::ApplyAuth() { GURL HttpNetworkTransaction::AuthOrigin(HttpAuth::Target target) const { return target == HttpAuth::AUTH_PROXY ? - GURL("http://" + proxy_info_.proxy_server()) : + GURL("http://" + proxy_info_.proxy_server().host_and_port()) : request_->url.GetOrigin(); } @@ -1363,7 +1364,7 @@ void HttpNetworkTransaction::PopulateAuthChallenge(HttpAuth::Target target) { // TODO(eroman): decode realm according to RFC 2047. auth_info->realm = ASCIIToWide(auth_handler_[target]->realm()); if (target == HttpAuth::AUTH_PROXY) { - auth_info->host = ASCIIToWide(proxy_info_.proxy_server()); + auth_info->host = ASCIIToWide(proxy_info_.proxy_server().host_and_port()); } else { DCHECK(target == HttpAuth::AUTH_SERVER); auth_info->host = ASCIIToWide(request_->url.host()); diff --git a/net/net.xcodeproj/project.pbxproj b/net/net.xcodeproj/project.pbxproj index 553c9db..2016e45 100644 --- a/net/net.xcodeproj/project.pbxproj +++ b/net/net.xcodeproj/project.pbxproj @@ -135,6 +135,7 @@ 7BED34190E5A1A8600A747DB /* libnet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E4BA04540E25613300BE02C6 /* libnet.a */; }; 7BED34450E5A1A9600A747DB /* libbase.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7BED324A0E5A17C000A747DB /* libbase.a */; }; 7BED34520E5A1ABC00A747DB /* libgtest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7BED32530E5A17C300A747DB /* libgtest.a */; }; + 805696E7ABEB16C016A9DA47 /* proxy_server_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = B9C484F295FCC30AE23858CB /* proxy_server_unittest.cc */; }; 81C766A4F7089E3ED5BD33A4 /* addr.cc in Sources */ = {isa = PBXBuildFile; fileRef = D7745B2B51F48114C05EB14A /* addr.cc */; }; 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 */; }; @@ -189,6 +190,7 @@ E4CE9C260E8C027900D5378C /* http_network_layer_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7BED335C0E5A194700A747DB /* http_network_layer_unittest.cc */; }; E4CE9C2E0E8C02ED00D5378C /* http_transaction_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7BED334A0E5A194700A747DB /* http_transaction_unittest.cc */; }; E4CE9C380E8C035C00D5378C /* http_network_transaction_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7BED33590E5A194700A747DB /* http_network_transaction_unittest.cc */; }; + FEF52DBABC719A22288B2DBE /* proxy_server.cc in Sources */ = {isa = PBXBuildFile; fileRef = CCAE08104262ECEE01726162 /* proxy_server.cc */; }; E51639B07E0FC884891C5135 /* http_response_info.cc in Sources */ = {isa = PBXBuildFile; fileRef = AD46F9C0118A7D723526A361 /* http_response_info.cc */; }; /* End PBXBuildFile section */ @@ -446,6 +448,7 @@ 04E7BD560EC4ED020078FE58 /* http_auth_cache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = http_auth_cache.h; sourceTree = "<group>"; }; 0E81748E2B2E8B814DBB78EC /* ftp_auth_cache.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ftp_auth_cache.cc; path = ftp/ftp_auth_cache.cc; sourceTree = SOURCE_ROOT; }; 15C6370BF6FE62308A559648 /* ftp_auth_cache_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ftp_auth_cache_unittest.cc; path = ftp/ftp_auth_cache_unittest.cc; sourceTree = SOURCE_ROOT; }; + 3C5876D639CF4CE40AF8F45B /* proxy_server.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = proxy_server.h; path = proxy/proxy_server.h; sourceTree = SOURCE_ROOT; }; 2C481A2E21097E841607B968 /* client_socket.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = client_socket.cc; sourceTree = "<group>"; }; 4D4C5C050EF1B8C5002CA805 /* filter_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = filter_unittest.cc; sourceTree = "<group>"; }; 533102E60E5E3EBF00FF8E32 /* net_util_posix.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = net_util_posix.cc; sourceTree = "<group>"; }; @@ -696,7 +699,9 @@ ACAB6D5C0F43A727D039E138 /* ftp_auth_cache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ftp_auth_cache.h; path = ftp/ftp_auth_cache.h; sourceTree = SOURCE_ROOT; }; AD46F9C0118A7D723526A361 /* http_response_info.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = http_response_info.cc; sourceTree = "<group>"; }; B87DD6154A7A9ED60F28F016 /* cert_verifier.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cert_verifier.cc; sourceTree = "<group>"; }; + B9C484F295FCC30AE23858CB /* proxy_server_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = proxy_server_unittest.cc; path = proxy/proxy_server_unittest.cc; sourceTree = SOURCE_ROOT; }; C081BA94B2F59669BFAFD808 /* cert_status_flags.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cert_status_flags.cc; sourceTree = "<group>"; }; + CCAE08104262ECEE01726162 /* proxy_server.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = proxy_server.cc; path = proxy/proxy_server.cc; sourceTree = SOURCE_ROOT; }; D4726BC70CCE10F4FF2A5E12 /* connection_type_histograms.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = connection_type_histograms.h; sourceTree = "<group>"; }; D67E592A64772BE82718FD4C /* io_buffer.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = io_buffer.cc; sourceTree = "<group>"; }; D7745B2B51F48114C05EB14A /* addr.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = addr.cc; sourceTree = "<group>"; }; @@ -764,6 +769,9 @@ 0E81748E2B2E8B814DBB78EC /* ftp_auth_cache.cc */, ACAB6D5C0F43A727D039E138 /* ftp_auth_cache.h */, 15C6370BF6FE62308A559648 /* ftp_auth_cache_unittest.cc */, + CCAE08104262ECEE01726162 /* proxy_server.cc */, + 3C5876D639CF4CE40AF8F45B /* proxy_server.h */, + B9C484F295FCC30AE23858CB /* proxy_server_unittest.cc */, ); name = zlib; sourceTree = "<group>"; @@ -1543,6 +1551,7 @@ 533102E70E5E3EBF00FF8E32 /* net_util_posix.cc in Sources */, 7B85043C0E5B2E6400730B43 /* platform_mime_util_mac.cc in Sources */, 820701040EB6611F005CD9E7 /* proxy_resolver_mac.cc in Sources */, + FEF52DBABC719A22288B2DBE /* proxy_server.cc in Sources */, E49DD3370E8933A2003C7A87 /* proxy_service.cc in Sources */, 7B85043D0E5B2E6400730B43 /* rankings.cc in Sources */, 7B8B5B560E5CEADE002F9A97 /* registry_controlled_domain.cc in Sources */, @@ -1616,6 +1625,7 @@ 7B4DF9AC0E5C906A004D7619 /* mime_sniffer_unittest.cc in Sources */, 048268090E5B3B4800A30786 /* mime_util_unittest.cc in Sources */, BAA46E3B0E5CE99A00E77460 /* net_util_unittest.cc in Sources */, + 805696E7ABEB16C016A9DA47 /* proxy_server_unittest.cc in Sources */, 7BA361ED0E8C38D30023C8B9 /* proxy_service_unittest.cc in Sources */, 7B8B5B9E0E5D188E002F9A97 /* registry_controlled_domain_unittest.cc in Sources */, E4AFA6430E5241B400201347 /* run_all_unittests.cc in Sources */, diff --git a/net/net_lib.scons b/net/net_lib.scons index dfc5c71..efe3bbf 100644 --- a/net/net_lib.scons +++ b/net/net_lib.scons @@ -241,6 +241,8 @@ input_files = ChromeFileList([ 'proxy/proxy_resolver_winhttp.h', 'proxy/proxy_script_fetcher.cc', 'proxy/proxy_script_fetcher.h', + 'proxy/proxy_server.cc', + 'proxy/proxy_server.h', 'proxy/proxy_service.cc', 'proxy/proxy_service.h', ]), diff --git a/net/net_unittests.scons b/net/net_unittests.scons index 4f35414..c59b6f3 100644 --- a/net/net_unittests.scons +++ b/net/net_unittests.scons @@ -109,6 +109,7 @@ input_files = ChromeFileList([ ]), MSVSFilter('proxy', [ 'proxy/proxy_script_fetcher_unittest.cc', + 'proxy/proxy_server_unittest.cc', 'proxy/proxy_service_unittest.cc', ]), ]), diff --git a/net/proxy/proxy_config_service_fixed.h b/net/proxy/proxy_config_service_fixed.h index 633af95..ea0d8a1 100644 --- a/net/proxy/proxy_config_service_fixed.h +++ b/net/proxy/proxy_config_service_fixed.h @@ -16,7 +16,7 @@ class ProxyConfigServiceFixed : public ProxyConfigService { // ProxyConfigService methods: virtual int GetProxyConfig(ProxyConfig* config) { - config->proxy_server = pi_.proxy_server(); + config->proxy_rules = pi_.proxy_server().ToURI(); return OK; } diff --git a/net/proxy/proxy_config_service_win.cc b/net/proxy/proxy_config_service_win.cc index f356ac9..9779928 100644 --- a/net/proxy/proxy_config_service_win.cc +++ b/net/proxy/proxy_config_service_win.cc @@ -34,7 +34,7 @@ int ProxyConfigServiceWin::GetProxyConfig(ProxyConfig* config) { if (ie_config.fAutoDetect) config->auto_detect = true; if (ie_config.lpszProxy) - config->proxy_server = WideToASCII(ie_config.lpszProxy); + config->proxy_rules = WideToASCII(ie_config.lpszProxy); if (ie_config.lpszProxyBypass) { std::string proxy_bypass = WideToASCII(ie_config.lpszProxyBypass); diff --git a/net/proxy/proxy_resolver_mac.cc b/net/proxy/proxy_resolver_mac.cc index 4f021a4..c5b0139 100644 --- a/net/proxy/proxy_resolver_mac.cc +++ b/net/proxy/proxy_resolver_mac.cc @@ -154,8 +154,8 @@ int ProxyConfigServiceMac::GetProxyConfig(ProxyConfig* config) { kSCPropNetProxiesFTPProxy, kSCPropNetProxiesFTPPort); if (!host_port.empty()) { - config->proxy_server += "ftp="; - config->proxy_server += host_port; + config->proxy_rules += "ftp="; + config->proxy_rules += host_port; } } if (GetBoolFromDictionary(config_dict.get(), @@ -166,10 +166,10 @@ int ProxyConfigServiceMac::GetProxyConfig(ProxyConfig* config) { kSCPropNetProxiesHTTPProxy, kSCPropNetProxiesHTTPPort); if (!host_port.empty()) { - if (!config->proxy_server.empty()) - config->proxy_server += ";"; - config->proxy_server += "http="; - config->proxy_server += host_port; + if (!config->proxy_rules.empty()) + config->proxy_rules += ";"; + config->proxy_rules += "http="; + config->proxy_rules += host_port; } } if (GetBoolFromDictionary(config_dict.get(), @@ -180,10 +180,10 @@ int ProxyConfigServiceMac::GetProxyConfig(ProxyConfig* config) { kSCPropNetProxiesHTTPSProxy, kSCPropNetProxiesHTTPSPort); if (!host_port.empty()) { - if (!config->proxy_server.empty()) - config->proxy_server += ";"; - config->proxy_server += "https="; - config->proxy_server += host_port; + if (!config->proxy_rules.empty()) + config->proxy_rules += ";"; + config->proxy_rules += "https="; + config->proxy_rules += host_port; } } @@ -317,6 +317,9 @@ int ProxyResolverMac::GetProxyForURL(const GURL& query_url, if (CFEqual(proxy_type, kCFProxyTypeNone)) allow_direct = true; if (CFEqual(proxy_type, kCFProxyTypeNone) || + // TODO(eroman): Include the SOCKS proxies in the result list. + // While chromium does not yet support SOCKS, it is safe to + // include it in the list. CFEqual(proxy_type, kCFProxyTypeSOCKS) || CFEqual(proxy_type, kCFProxyTypeAutoConfigurationURL)) continue; diff --git a/net/proxy/proxy_resolver_winhttp.cc b/net/proxy/proxy_resolver_winhttp.cc index d4877ac..61a1d2f 100644 --- a/net/proxy/proxy_resolver_winhttp.cc +++ b/net/proxy/proxy_resolver_winhttp.cc @@ -106,6 +106,20 @@ int ProxyResolverWinHttp::GetProxyForURL(const GURL& query_url, results->UseDirect(); break; case WINHTTP_ACCESS_TYPE_NAMED_PROXY: + // According to MSDN: + // + // The proxy server list contains one or more of the following strings + // separated by semicolons or whitespace. + // + // ([<scheme>=][<scheme>"://"]<server>[":"<port>]) + // + // Based on this description, ProxyInfo::UseNamedProxy() isn't + // going to handle all the variations (in particular <scheme>=). + // + // However in practice, it seems that WinHTTP is simply returning + // things like "foopy1:80;foopy2:80". It strips out the non-HTTP + // proxy types, and stops the list when PAC encounters a "DIRECT". + // So UseNamedProxy() should work OK. results->UseNamedProxy(WideToASCII(info.lpszProxy)); break; default: diff --git a/net/proxy/proxy_server.cc b/net/proxy/proxy_server.cc new file mode 100644 index 0000000..d4f1f4e --- /dev/null +++ b/net/proxy/proxy_server.cc @@ -0,0 +1,215 @@ +// Copyright (c) 2009 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_server.h" + +#include "base/string_tokenizer.h" +#include "base/string_util.h" +#include "net/base/net_util.h" +#include "net/http/http_util.h" + +namespace net { + +namespace { + +// Returns the default port number for proxy server with the +// specified scheme. Returns -1 if unknown. +int GetDefaultPortForScheme(ProxyServer::Scheme scheme) { + switch (scheme) { + case ProxyServer::SCHEME_HTTP: + return 80; + case ProxyServer::SCHEME_SOCKS4: + case ProxyServer::SCHEME_SOCKS5: + return 1080; + default: + return -1; + } +} + +// Parse the proxy type from a PAC string, to a ProxyServer::Scheme. +// This mapping is case-insensitive. If no type could be matched +// returns SCHEME_INVALID. +ProxyServer::Scheme GetSchemeFromPacType(std::string::const_iterator begin, + std::string::const_iterator end) { + if (LowerCaseEqualsASCII(begin, end, "proxy")) + return ProxyServer::SCHEME_HTTP; + if (LowerCaseEqualsASCII(begin, end, "socks")) { + // Default to v4 for compatibility. This is because the SOCKS4 vs SOCKS5 + // notation didn't originally exist, so if a client returns SOCKS they + // really meant SOCKS4. + return ProxyServer::SCHEME_SOCKS4; + } + if (LowerCaseEqualsASCII(begin, end, "socks4")) + return ProxyServer::SCHEME_SOCKS4; + if (LowerCaseEqualsASCII(begin, end, "socks5")) + return ProxyServer::SCHEME_SOCKS5; + if (LowerCaseEqualsASCII(begin, end, "direct")) + return ProxyServer::SCHEME_DIRECT; + + return ProxyServer::SCHEME_INVALID; +} + +// Parse the proxy scheme from a URL-like representation, to a +// ProxyServer::Scheme. This corresponds with the values used in +// ProxyServer::ToURI(). If no type could be matched, returns SCHEME_INVALID. +ProxyServer::Scheme GetSchemeFromURI(std::string::const_iterator begin, + std::string::const_iterator end) { + if (LowerCaseEqualsASCII(begin, end, "http")) + return ProxyServer::SCHEME_HTTP; + if (LowerCaseEqualsASCII(begin, end, "socks4")) + return ProxyServer::SCHEME_SOCKS4; + if (LowerCaseEqualsASCII(begin, end, "socks5")) + return ProxyServer::SCHEME_SOCKS5; + if (LowerCaseEqualsASCII(begin, end, "direct")) + return ProxyServer::SCHEME_DIRECT; + return ProxyServer::SCHEME_INVALID; +} + +} // namespace + +const std::string& ProxyServer::host() const { + // Doesn't make sense to call this if the URI scheme doesn't + // have concept of a host. + DCHECK(is_valid() && !is_direct()); + return host_; +} + +int ProxyServer::port() const { + // Doesn't make sense to call this if the URI scheme doesn't + // have concept of a port. + DCHECK(is_valid() && !is_direct()); + return port_; +} + +std::string ProxyServer::host_and_port() const { + // Doesn't make sense to call this if the URI scheme doesn't + // have concept of a host. + DCHECK(is_valid() && !is_direct()); + return host_ + ":" + IntToString(port_); +} + +// static +ProxyServer ProxyServer::FromURI(const std::string& uri) { + return FromURI(uri.begin(), uri.end()); +} + +// static +ProxyServer ProxyServer::FromURI(std::string::const_iterator begin, + std::string::const_iterator end) { + // We will default to HTTP if no scheme specifier was given. + Scheme scheme = SCHEME_HTTP; + + // Trim the leading/trailing whitespace. + HttpUtil::TrimLWS(&begin, &end); + + // Check for [<scheme> "://"] + std::string::const_iterator colon = std::find(begin, end, ':'); + if (colon != end && + (end - colon) >= 3 && + *(colon + 1) == '/' && + *(colon + 2) == '/') { + scheme = GetSchemeFromURI(begin, colon); + begin = colon + 3; // Skip past the "://" + } + + // Now parse the <host>[":"<port>]. + return FromSchemeHostAndPort(scheme, begin, end); +} + +std::string ProxyServer::ToURI() const { + switch (scheme_) { + case SCHEME_DIRECT: + return "direct://"; + case SCHEME_HTTP: + // Leave off "http://" since it is our default scheme. + return host_and_port(); + case SCHEME_SOCKS4: + return std::string("socks4://") + host_and_port(); + case SCHEME_SOCKS5: + return std::string("socks5://") + host_and_port(); + default: + // Got called with an invalid scheme. + NOTREACHED(); + return std::string(); + } +} + +// static +ProxyServer ProxyServer::FromPacString(const std::string& pac_string) { + return FromPacString(pac_string.begin(), pac_string.end()); +} + +ProxyServer ProxyServer::FromPacString(std::string::const_iterator begin, + std::string::const_iterator end) { + // Trim the leading/trailing whitespace. + HttpUtil::TrimLWS(&begin, &end); + + // Input should match: + // "DIRECT" | ( <type> 1*(LWS) <host-and-port> ) + + // Start by finding the first space (if any). + std::string::const_iterator space; + for (space = begin; space != end; ++space) { + if (HttpUtil::IsLWS(*space)) { + break; + } + } + + // Everything to the left of the space is the scheme. + Scheme scheme = GetSchemeFromPacType(begin, space); + + // And everything to the right of the space is the + // <host>[":" <port>]. + return FromSchemeHostAndPort(scheme, space, end); +} + +std::string ProxyServer::ToPacString() const { + switch (scheme_) { + case SCHEME_DIRECT: + return "DIRECT"; + case SCHEME_HTTP: + return std::string("PROXY ") + host_and_port(); + case SCHEME_SOCKS4: + // For compatibility send SOCKS instead of SOCKS4. + return std::string("SOCKS ") + host_and_port(); + case SCHEME_SOCKS5: + return std::string("SOCKS5 ") + host_and_port(); + default: + // Got called with an invalid scheme. + NOTREACHED(); + return std::string(); + } +} + +// static +ProxyServer ProxyServer::FromSchemeHostAndPort( + Scheme scheme, + std::string::const_iterator begin, + std::string::const_iterator end) { + + // Trim leading/trailing space. + HttpUtil::TrimLWS(&begin, &end); + + if (scheme == SCHEME_DIRECT && begin != end) + return ProxyServer(); // Invalid -- DIRECT cannot have a host/port. + + std::string host; + int port = -1; + + if (scheme != SCHEME_INVALID && scheme != SCHEME_DIRECT) { + // If the scheme has a host/port, parse it. + bool ok = net::GetHostAndPort(begin, end, &host, &port); + if (!ok) + return ProxyServer(); // Invalid -- failed parsing <host>[":"<port>] + } + + // Choose a default port number if none was given. + if (port == -1) + port = GetDefaultPortForScheme(scheme); + + return ProxyServer(scheme, host, port); +} + +} // namespace net + diff --git a/net/proxy/proxy_server.h b/net/proxy/proxy_server.h new file mode 100644 index 0000000..59f0718 --- /dev/null +++ b/net/proxy/proxy_server.h @@ -0,0 +1,118 @@ +// Copyright (c) 2009 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_SERVER_H_ +#define NET_PROXY_SERVER_H_ + +#include <string> + +namespace net { + +// ProxyServer encodes the {type, host, port} of a proxy server. +// ProxyServer is immutable. +class ProxyServer { + public: + // The type of proxy. These are defined as bit flags so they can be ORed + // together to pass as the |scheme_bit_field| argument to + // ProxyService::RemoveProxiesWithoutScheme(). + enum Scheme { + SCHEME_INVALID = 1 << 0, + SCHEME_DIRECT = 1 << 1, + SCHEME_HTTP = 1 << 2, + SCHEME_SOCKS4 = 1 << 3, + SCHEME_SOCKS5 = 1 << 4, + }; + + // Default copy-constructor and assignment operator are OK! + + // Constructs an invalid ProxyServer. + ProxyServer() : scheme_(SCHEME_INVALID), port_(-1) {} + + ProxyServer(Scheme scheme, const std::string& host, int port) + : scheme_(scheme), host_(host), port_(port) {} + + bool is_valid() const { return scheme_ != SCHEME_INVALID; } + + // Gets the proxy's scheme (i.e. SOCKS4, SOCKS5, HTTP} + Scheme scheme() const { return scheme_; } + + // Returns true if this ProxyServer is actually just a DIRECT connection. + bool is_direct() const { return scheme_ == SCHEME_DIRECT; } + + // Returns true if this ProxyServer is an HTTP proxy. + bool is_http() const { return scheme_ == SCHEME_HTTP; } + + // Returns true if this ProxyServer is a SOCKS proxy. + bool is_socks() const { + return scheme_ == SCHEME_SOCKS4 || scheme_ == SCHEME_SOCKS5; + } + + // Gets the host portion of the proxy server. + const std::string& host() const; + + // Gets the port portion of the proxy server. + int port() const; + + // Returns the <host>":"<port> string for the proxy server. + std::string host_and_port() const; + + // Parse from an input with format: + // [<scheme>"://"]<server>[":"<port>] + // + // Both <scheme> and <port> are optional. If <scheme> is omitted, it will + // be assumed as "http". If <port> is omitted, it will be assumed as + // the default port for the chosen scheme (80 for "http", 1080 for "socks"). + // + // If parsing fails the instance will be set to invalid. + // + // Examples: + // "foopy" {scheme=HTTP, host="foopy", port=80} + // "socks4://foopy" {scheme=SOCKS4, host="foopy", port=1080} + // "socks5://foopy" {scheme=SOCKS5, host="foopy", port=1080} + // "http://foopy:17" {scheme=HTTP, host="foopy", port=17} + // "direct://" {scheme=DIRECT} + // "foopy:X" INVALID -- bad port. + static ProxyServer FromURI(const std::string& uri); + static ProxyServer FromURI(std::string::const_iterator uri_begin, + std::string::const_iterator uri_end); + + // Format as a URI string. This does the reverse of FromURI. + std::string ToURI() const; + + // Parses from a PAC string result. + // + // If <port> is omitted, it will be assumed as the default port for the + // chosen scheme (80 for "http", 1080 for "socks"). + // + // If parsing fails the instance will be set to invalid. + // + // Examples: + // "PROXY foopy:19" {scheme=HTTP, host="foopy", port=19} + // "DIRECT" {scheme=DIRECT} + // "SOCKS5 foopy" {scheme=SOCKS5, host="foopy", port=1080} + // "BLAH xxx:xx" INVALID + static ProxyServer FromPacString(const std::string& pac_string); + static ProxyServer FromPacString(std::string::const_iterator pac_string_begin, + std::string::const_iterator pac_string_end); + + // Format as a PAC result entry. This does the reverse of FromPacString(). + std::string ToPacString() const; + + private: + // Create a ProxyServer given a scheme, and host/port string. If parsing the + // host/port string fails, the returned instance will be invalid. + static ProxyServer FromSchemeHostAndPort( + Scheme scheme, + std::string::const_iterator host_and_port_begin, + std::string::const_iterator host_and_port_end); + + Scheme scheme_; + std::string host_; + int port_; +}; + +} // namespace net + +#endif // NET_PROXY_SERVER_H_ + diff --git a/net/proxy/proxy_server_unittest.cc b/net/proxy/proxy_server_unittest.cc new file mode 100644 index 0000000..7ebf529 --- /dev/null +++ b/net/proxy/proxy_server_unittest.cc @@ -0,0 +1,222 @@ +// Copyright (c) 2009 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 "base/basictypes.h" +#include "net/proxy/proxy_server.h" +#include "testing/gtest/include/gtest/gtest.h" + +// Test the creation of ProxyServer using ProxyServer::FromURI, which parses +// inputs of the form [<scheme>"://"]<host>[":"<port>]. Verify that each part +// was labelled correctly, and the accessors all give the right data. +TEST(ProxyServerTest, FromURI) { + const struct { + const char* input_uri; + const char* expected_uri; + net::ProxyServer::Scheme expected_scheme; + const char* expected_host; + int expected_port; + const char* expected_host_and_port; + const char* expected_pac_string; + } tests[] = { + // HTTP proxy URIs: + { + "foopy:10", // No scheme. + "foopy:10", + net::ProxyServer::SCHEME_HTTP, + "foopy", + 10, + "foopy:10", + "PROXY foopy:10" + }, + { + "http://foopy", // No port. + "foopy:80", + net::ProxyServer::SCHEME_HTTP, + "foopy", + 80, + "foopy:80", + "PROXY foopy:80" + }, + { + "http://foopy:10", + "foopy:10", + net::ProxyServer::SCHEME_HTTP, + "foopy", + 10, + "foopy:10", + "PROXY foopy:10" + }, + + // SOCKS4 proxy URIs: + { + "socks4://foopy", // No port. + "socks4://foopy:1080", + net::ProxyServer::SCHEME_SOCKS4, + "foopy", + 1080, + "foopy:1080", + "SOCKS foopy:1080" + }, + { + "socks4://foopy:10", + "socks4://foopy:10", + net::ProxyServer::SCHEME_SOCKS4, + "foopy", + 10, + "foopy:10", + "SOCKS foopy:10" + }, + + // SOCKS5 proxy URIs + { + "socks5://foopy", // No port. + "socks5://foopy:1080", + net::ProxyServer::SCHEME_SOCKS5, + "foopy", + 1080, + "foopy:1080", + "SOCKS5 foopy:1080" + }, + { + "socks5://foopy:10", + "socks5://foopy:10", + net::ProxyServer::SCHEME_SOCKS5, + "foopy", + 10, + "foopy:10", + "SOCKS5 foopy:10" + }, + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { + net::ProxyServer uri = net::ProxyServer::FromURI(tests[i].input_uri); + EXPECT_TRUE(uri.is_valid()); + EXPECT_FALSE(uri.is_direct()); + EXPECT_EQ(tests[i].expected_uri, uri.ToURI()); + EXPECT_EQ(tests[i].expected_scheme, uri.scheme()); + EXPECT_EQ(tests[i].expected_host, uri.host()); + EXPECT_EQ(tests[i].expected_port, uri.port()); + EXPECT_EQ(tests[i].expected_host_and_port, uri.host_and_port()); + EXPECT_EQ(tests[i].expected_pac_string, uri.ToPacString()); + } +} + +TEST(ProxyServerTest, DefaultConstructor) { + net::ProxyServer proxy_server; + EXPECT_FALSE(proxy_server.is_valid()); +} + +// Test parsing of the special URI form "direct://". Analagous to the "DIRECT" +// entry in a PAC result. +TEST(ProxyServerTest, Direct) { + net::ProxyServer uri = net::ProxyServer::FromURI("direct://"); + EXPECT_TRUE(uri.is_valid()); + EXPECT_TRUE(uri.is_direct()); + EXPECT_EQ("direct://", uri.ToURI()); + EXPECT_EQ("DIRECT", uri.ToPacString()); +} + +// Test parsing some invalid inputs. +TEST(ProxyServerTest, Invalid) { + const char* tests[] = { + "", + " ", + "dddf:", // not a valid port + "dddd:d", // not a valid port + "socks://foopy", // not a valid scheme (needs to be socks4 or sock5). + "http://", // not a valid host/port. + "direct://xyz", // direct is not allowed a host/port. + "http:/", // ambiguous, but will fail because of bad port. + "http:", // ambiguous, but will fail because of bad port. + "https://blah", // "https" is not a valid proxy scheme. + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { + net::ProxyServer uri = net::ProxyServer::FromURI(tests[i]); + EXPECT_FALSE(uri.is_valid()); + EXPECT_FALSE(uri.is_direct()); + EXPECT_FALSE(uri.is_http()); + EXPECT_FALSE(uri.is_socks()); + } +} + +// Test that LWS (SP | HT) is disregarded from the ends. +TEST(ProxyServerTest, Whitespace) { + const char* tests[] = { + " foopy:80", + "foopy:80 \t", + " \tfoopy:80 ", + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { + net::ProxyServer uri = net::ProxyServer::FromURI(tests[i]); + EXPECT_EQ("foopy:80", uri.ToURI()); + } +} + +// Test parsing a ProxyServer from a PAC representation. +TEST(ProxyServerTest, FromPACString) { + const struct { + const char* input_pac; + const char* expected_uri; + } tests[] = { + { + "PROXY foopy:10", + "foopy:10", + }, + { + " PROXY foopy:10 ", + "foopy:10", + }, + { + "pRoXy foopy:10", + "foopy:10", + }, + { + "PROXY foopy", // No port. + "foopy:80", + }, + { + "socks foopy", + "socks4://foopy:1080", + }, + { + "socks4 foopy", + "socks4://foopy:1080", + }, + { + "socks5 foopy", + "socks5://foopy:1080", + }, + { + "socks5 foopy:11", + "socks5://foopy:11", + }, + { + " direct ", + "direct://", + }, + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { + net::ProxyServer uri = net::ProxyServer::FromPacString(tests[i].input_pac); + EXPECT_TRUE(uri.is_valid()); + EXPECT_EQ(tests[i].expected_uri, uri.ToURI()); + } +} + +// Test parsing a ProxyServer from an invalid PAC representation. +TEST(ProxyServerTest, FromPACStringInvalid) { + const char* tests[] = { + "PROXY", // missing host/port. + "SOCKS", // missing host/port. + "DIRECT foopy:10", // direct cannot have host/port. + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { + net::ProxyServer uri = net::ProxyServer::FromPacString(tests[i]); + EXPECT_FALSE(uri.is_valid()); + } +} + diff --git a/net/proxy/proxy_service.cc b/net/proxy/proxy_service.cc index 8d4025b..b97c010 100644 --- a/net/proxy/proxy_service.cc +++ b/net/proxy/proxy_service.cc @@ -15,7 +15,6 @@ #include "base/logging.h" #include "base/message_loop.h" #include "base/string_tokenizer.h" -#include "base/string_util.h" #include "googleurl/src/gurl.h" #include "net/base/net_errors.h" #include "net/proxy/proxy_config_service_fixed.h" @@ -56,35 +55,31 @@ bool ProxyConfig::Equals(const ProxyConfig& other) const { // have the same settings. return auto_detect == other.auto_detect && pac_url == other.pac_url && - proxy_server == other.proxy_server && + proxy_rules == other.proxy_rules && proxy_bypass == other.proxy_bypass && proxy_bypass_local_names == other.proxy_bypass_local_names; } // ProxyList ------------------------------------------------------------------ -void ProxyList::SetVector(const std::vector<std::string>& proxies) { + +void ProxyList::Set(const std::string& proxy_uri_list) { proxies_.clear(); - std::vector<std::string>::const_iterator iter = proxies.begin(); - for (; iter != proxies.end(); ++iter) { - std::string proxy_sever; - TrimWhitespace(*iter, TRIM_ALL, &proxy_sever); - proxies_.push_back(proxy_sever); + StringTokenizer str_tok(proxy_uri_list, ";"); + while (str_tok.GetNext()) { + ProxyServer uri = ProxyServer::FromURI( + str_tok.token_begin(), str_tok.token_end()); + // Silently discard malformed inputs. + if (uri.is_valid()) + proxies_.push_back(uri); } } -void ProxyList::Set(const std::string& proxy_list) { - // Extract the different proxies from the list. - std::vector<std::string> proxies; - SplitString(proxy_list, L';', &proxies); - SetVector(proxies); -} - void ProxyList::RemoveBadProxies(const ProxyRetryInfoMap& proxy_retry_info) { - std::vector<std::string> new_proxy_list; - std::vector<std::string>::const_iterator iter = proxies_.begin(); + std::vector<ProxyServer> new_proxy_list; + std::vector<ProxyServer>::const_iterator iter = proxies_.begin(); for (; iter != proxies_.end(); ++iter) { ProxyRetryInfoMap::const_iterator bad_proxy = - proxy_retry_info.find(*iter); + proxy_retry_info.find(iter->ToURI()); if (bad_proxy != proxy_retry_info.end()) { // This proxy is bad. Check if it's time to retry. if (bad_proxy->second.bad_until >= TimeTicks::Now()) { @@ -98,25 +93,44 @@ void ProxyList::RemoveBadProxies(const ProxyRetryInfoMap& proxy_retry_info) { proxies_ = new_proxy_list; } -std::string ProxyList::Get() const { +void ProxyList::RemoveProxiesWithoutScheme(int scheme_bit_field) { + for (std::vector<ProxyServer>::iterator it = proxies_.begin(); + it != proxies_.end(); ) { + if (!(scheme_bit_field & it->scheme())) { + it = proxies_.erase(it); + continue; + } + ++it; + } +} + +ProxyServer ProxyList::Get() const { if (!proxies_.empty()) return proxies_[0]; - - return std::string(); + return ProxyServer(ProxyServer::SCHEME_DIRECT, std::string(), -1); } -std::string ProxyList::GetAnnotatedList() const { +std::string ProxyList::ToPacString() const { std::string proxy_list; - std::vector<std::string>::const_iterator iter = proxies_.begin(); + std::vector<ProxyServer>::const_iterator iter = proxies_.begin(); for (; iter != proxies_.end(); ++iter) { if (!proxy_list.empty()) proxy_list += ";"; - // Assume every proxy is an HTTP proxy, as that is all we currently support. - proxy_list += "PROXY "; - proxy_list += *iter; + proxy_list += iter->ToPacString(); } + return proxy_list.empty() ? "DIRECT" : proxy_list; +} - return proxy_list; +void ProxyList::SetFromPacString(const std::string& pac_string) { + StringTokenizer entry_tok(pac_string, ";"); + proxies_.clear(); + while (entry_tok.GetNext()) { + ProxyServer uri = ProxyServer::FromPacString( + entry_tok.token_begin(), entry_tok.token_end()); + // Silently discard malformed inputs. + if (uri.is_valid()) + proxies_.push_back(uri); + } } bool ProxyList::Fallback(ProxyRetryInfoMap* proxy_retry_info) { @@ -128,8 +142,10 @@ bool ProxyList::Fallback(ProxyRetryInfoMap* proxy_retry_info) { return false; } + std::string key = proxies_[0].ToURI(); + // Mark this proxy as bad. - ProxyRetryInfoMap::iterator iter = proxy_retry_info->find(proxies_[0]); + ProxyRetryInfoMap::iterator iter = proxy_retry_info->find(key); if (iter != proxy_retry_info->end()) { // TODO(nsylvain): This is not the first time we get this. We should // double the retry time. Bug 997660. @@ -138,7 +154,7 @@ bool ProxyList::Fallback(ProxyRetryInfoMap* proxy_retry_info) { ProxyRetryInfo retry_info; retry_info.current_delay = kProxyRetryDelay; retry_info.bad_until = TimeTicks().Now() + retry_info.current_delay; - (*proxy_retry_info)[proxies_[0]] = retry_info; + (*proxy_retry_info)[key] = retry_info; } // Remove this proxy from our list. @@ -162,12 +178,12 @@ void ProxyInfo::UseDirect() { proxy_list_.Set(std::string()); } -void ProxyInfo::UseNamedProxy(const std::string& proxy_server) { - proxy_list_.Set(proxy_server); +void ProxyInfo::UseNamedProxy(const std::string& proxy_uri_list) { + proxy_list_.Set(proxy_uri_list); } -std::string ProxyInfo::GetAnnotatedProxyList() { - return is_direct() ? "DIRECT" : proxy_list_.GetAnnotatedList(); +std::string ProxyInfo::ToPacString() { + return proxy_list_.ToPacString(); } // ProxyService::PacRequest --------------------------------------------------- @@ -312,7 +328,7 @@ int ProxyService::ResolveProxy(const GURL& url, ProxyInfo* result, // Remember that we are trying to use the current proxy configuration. result->config_was_tried_ = true; - if (!config_.proxy_server.empty()) { + if (!config_.proxy_rules.empty()) { if (ShouldBypassProxyForURL(url)) { result->UseDirect(); } else { @@ -321,7 +337,7 @@ int ProxyService::ResolveProxy(const GURL& url, ProxyInfo* result, // "scheme1=url:port;scheme2=url:port", etc. std::string url_scheme = url.scheme(); - StringTokenizer proxy_server_list(config_.proxy_server, ";"); + StringTokenizer proxy_server_list(config_.proxy_rules, ";"); while (proxy_server_list.GetNext()) { StringTokenizer proxy_server_for_scheme( proxy_server_list.token_begin(), proxy_server_list.token_end(), @@ -357,7 +373,7 @@ int ProxyService::ResolveProxy(const GURL& url, ProxyInfo* result, pac_thread_.reset(new base::Thread("pac-thread")); pac_thread_->Start(); } - + scoped_refptr<PacRequest> req = new PacRequest(this, config_.pac_url, callback); @@ -422,7 +438,7 @@ int ProxyService::ReconsiderProxyAfterError(const GURL& url, if (!was_direct && result->Fallback(&proxy_retry_info_)) return OK; - if (!config_.auto_detect && !config_.proxy_server.empty()) { + if (!config_.auto_detect && !config_.proxy_rules.empty()) { // If auto detect is on, then we should try a DIRECT connection // as the attempt to reach the proxy failed. return ERR_FAILED; diff --git a/net/proxy/proxy_service.h b/net/proxy/proxy_service.h index 2553124..d14cc3e 100644 --- a/net/proxy/proxy_service.h +++ b/net/proxy/proxy_service.h @@ -6,6 +6,7 @@ #define NET_PROXY_PROXY_SERVICE_H_ #include <map> +#include <string> #include <vector> #include "base/ref_counted.h" @@ -16,6 +17,7 @@ #include "base/waitable_event.h" #include "googleurl/src/gurl.h" #include "net/base/completion_callback.h" +#include "net/proxy/proxy_server.h" class GURL; @@ -45,10 +47,22 @@ class ProxyConfig { // If non-empty, indicates the URL of the proxy auto-config file to use. GURL 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 the proxy server to use, given by: + // + // proxy-uri = [<proxy-scheme>://]<proxy-host>[:"<proxy-port>] + // + // If the proxy to use depends on the scheme of the URL, can instead specify + // a semicolon separated list of: + // + // <url-scheme>"="<proxy-uri> + // + // For example: + // "http=foopy:80;ftp=foopy2" -- use HTTP proxy "foopy:80" for http URLs, + // and HTTP proxy "foopy2:80" for ftp URLs. + // "foopy:80" -- use HTTP proxy "foopy:80" for all URLs. + // "socks4://foopy" -- use SOCKS v4 proxy "foopy:1080" for all + // URLs. + std::string proxy_rules; // Indicates a list of hosts that should bypass any proxy configuration. For // these hosts, a direct connection should always be used. @@ -76,6 +90,7 @@ struct ProxyRetryInfo { }; // Map of proxy servers with the associated RetryInfo structures. +// The key is a proxy URI string [<scheme>"://"]<host>":"<port>. typedef std::map<std::string, ProxyRetryInfo> ProxyRetryInfoMap; // This class can be used to resolve the proxy server to use when loading a @@ -192,29 +207,32 @@ class ProxyService { // This class is used to hold a list of proxies returned by GetProxyForUrl or // manually configured. It handles proxy fallback if multiple servers are // specified. -// TODO(eroman): The proxy list should work for multiple proxy types. -// See http://crbug.com/469. class ProxyList { public: // Initializes the proxy list to a string containing one or more proxy servers // delimited by a semicolon. - void Set(const std::string& proxy_list); - - // Initializes the proxy list to a vector containing one or more proxy - // servers. - void SetVector(const std::vector<std::string>& proxy_list); + void Set(const std::string& proxy_uri_list); // Remove all proxies known to be bad from the proxy list. void RemoveBadProxies(const ProxyRetryInfoMap& proxy_retry_info); + // Delete any entry which doesn't have one of the specified proxy schemes. + // |scheme_bit_field| is a bunch of ProxyServer::Scheme bitwise ORed together. + void RemoveProxiesWithoutScheme(int scheme_bit_field); + // Returns the first valid proxy server in the list. - std::string Get() const; + ProxyServer Get() const; + + // Set the list by parsing the pac result |pac_string|. + // Some examples for |pac_string|: + // "DIRECT" + // "PROXY foopy1" + // "PROXY foopy1; SOCKS4 foopy2:1188" + void SetFromPacString(const std::string& pac_string); // Returns a PAC-style semicolon-separated list of valid proxy servers. // For example: "PROXY xxx.xxx.xxx.xxx:xx; SOCKS yyy.yyy.yyy:yy". - // Since ProxyList is currently just used for HTTP, this will return only - // entries of type "PROXY" or "DIRECT". - std::string GetAnnotatedList() const; + std::string ToPacString() const; // Marks the current proxy server as bad and deletes it from the list. The // list of known bad proxies is given by proxy_retry_info. Returns true if @@ -223,7 +241,7 @@ class ProxyList { private: // List of proxies. - std::vector<std::string> proxies_; + std::vector<ProxyServer> proxies_; }; // This object holds proxy information returned by ResolveProxy. @@ -238,18 +256,25 @@ class ProxyInfo { // Use a direct connection. void UseDirect(); - // Use a specific proxy server, of the form: <hostname> [":" <port>] - // This may optionally be a semi-colon delimited list of proxy servers. - void UseNamedProxy(const std::string& proxy_server); + // Use a specific proxy server, of the form: + // proxy-uri = [<scheme> "://"] <hostname> [":" <port>] + // This may optionally be a semi-colon delimited list of <proxy-uri>. + // It is OK to have LWS between entries. + void UseNamedProxy(const std::string& proxy_uri_list); + + // Parse from the given PAC result. + void UsePacString(const std::string& pac_string) { + proxy_list_.SetFromPacString(pac_string); + } // Returns true if this proxy info specifies a direct connection. - bool is_direct() const { return proxy_list_.Get().empty(); } + bool is_direct() const { return proxy_list_.Get().is_direct(); } // Returns the first valid proxy server. - std::string proxy_server() const { return proxy_list_.Get(); } + ProxyServer proxy_server() const { return proxy_list_.Get(); } - // See description in ProxyList::GetAnnotatedList(). - std::string GetAnnotatedProxyList(); + // See description in ProxyList::ToPacString(). + std::string ToPacString(); // Marks the current proxy as bad. Returns true if there is another proxy // available to try in proxy list_. @@ -262,6 +287,11 @@ class ProxyInfo { proxy_list_.RemoveBadProxies(proxy_retry_info); } + // Delete any entry which doesn't have one of the specified proxy schemes. + void RemoveProxiesWithoutScheme(int scheme_bit_field) { + proxy_list_.RemoveProxiesWithoutScheme(scheme_bit_field); + } + private: friend class ProxyService; diff --git a/net/proxy/proxy_service_unittest.cc b/net/proxy/proxy_service_unittest.cc index d90843d..35df20f 100644 --- a/net/proxy/proxy_service_unittest.cc +++ b/net/proxy/proxy_service_unittest.cc @@ -82,18 +82,70 @@ class SyncProxyService { } // namespace -// GetAnnotatedList() is used to generate a string for mozilla's GetProxyForUrl -// NPAPI extension. Check that it adheres to the expected format. -TEST(ProxyListTest, GetAnnotatedList) { - net::ProxyList proxy_list; - - std::vector<std::string> proxies; - proxies.push_back("www.first.com:80"); - proxies.push_back("www.second.com:80"); - proxy_list.SetVector(proxies); - - EXPECT_EQ(std::string("PROXY www.first.com:80;PROXY www.second.com:80"), - proxy_list.GetAnnotatedList()); +// Test parsing from a PAC string. +TEST(ProxyListTest, SetFromPacString) { + const struct { + const char* pac_input; + const char* pac_output; + } tests[] = { + // Valid inputs: + { "PROXY foopy:10", + "PROXY foopy:10", + }, + { " DIRECT", // leading space. + "DIRECT", + }, + { "PROXY foopy1 ; proxy foopy2;\t DIRECT", + "PROXY foopy1:80;PROXY foopy2:80;DIRECT", + }, + { "proxy foopy1 ; SOCKS foopy2", + "PROXY foopy1:80;SOCKS foopy2:1080", + }, + + // Invalid inputs (parts which aren't understood get + // silently discarded): + { "PROXY-foopy:10", + "DIRECT", + }, + { "PROXY", + "DIRECT", + }, + { "PROXY foopy1 ; JUNK ; JUNK ; SOCKS5 foopy2 ; ;", + "PROXY foopy1:80;SOCKS5 foopy2:1080", + }, + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { + net::ProxyList list; + list.SetFromPacString(tests[i].pac_input); + EXPECT_EQ(tests[i].pac_output, list.ToPacString()); + } +} + +TEST(ProxyListTest, RemoveProxiesWithoutScheme) { + const struct { + const char* pac_input; + int filter; + const char* filtered_pac_output; + } tests[] = { + { "PROXY foopy:10 ; SOCKS5 foopy2 ; SOCKS foopy11 ; PROXY foopy3 ; DIRECT", + // Remove anything that isn't HTTP or DIRECT. + net::ProxyServer::SCHEME_DIRECT | net::ProxyServer::SCHEME_HTTP, + "PROXY foopy:10;PROXY foopy3:80;DIRECT", + }, + { "PROXY foopy:10 | SOCKS5 foopy2", + // Remove anything that isn't HTTP or SOCKS5. + net::ProxyServer::SCHEME_DIRECT | net::ProxyServer::SCHEME_SOCKS4, + "DIRECT", + }, + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { + net::ProxyList list; + list.SetFromPacString(tests[i].pac_input); + list.RemoveProxiesWithoutScheme(tests[i].filter); + EXPECT_EQ(tests[i].filtered_pac_output, list.ToPacString()); + } } TEST(ProxyServiceTest, Direct) { @@ -124,7 +176,7 @@ TEST(ProxyServiceTest, PAC) { int rv = service.ResolveProxy(url, &info); EXPECT_EQ(rv, net::OK); EXPECT_FALSE(info.is_direct()); - EXPECT_EQ(info.proxy_server(), "foopy"); + EXPECT_EQ("foopy:80", info.proxy_server().ToURI()); } TEST(ProxyServiceTest, PAC_FailoverToDirect) { @@ -143,7 +195,7 @@ TEST(ProxyServiceTest, PAC_FailoverToDirect) { int rv = service.ResolveProxy(url, &info); EXPECT_EQ(rv, net::OK); EXPECT_FALSE(info.is_direct()); - EXPECT_EQ(info.proxy_server(), "foopy:8080"); + EXPECT_EQ("foopy:8080", info.proxy_server().ToURI()); // Now, imagine that connecting to foopy:8080 fails. rv = service.ReconsiderProxyAfterError(url, &info); @@ -184,7 +236,7 @@ TEST(ProxyServiceTest, PAC_FailsToDownload) { rv = service.ReconsiderProxyAfterError(url, &info); EXPECT_EQ(rv, net::OK); EXPECT_FALSE(info.is_direct()); - EXPECT_EQ(info.proxy_server(), "foopy_valid:8080"); + EXPECT_EQ("foopy_valid:8080", info.proxy_server().ToURI()); } TEST(ProxyServiceTest, ProxyFallback) { @@ -210,14 +262,14 @@ TEST(ProxyServiceTest, ProxyFallback) { EXPECT_FALSE(info.is_direct()); // The first item is valid. - EXPECT_EQ(info.proxy_server(), "foopy1:8080"); + EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI()); // Fake an error on the proxy. rv = service.ReconsiderProxyAfterError(url, &info); EXPECT_EQ(rv, net::OK); // The second proxy should be specified. - EXPECT_EQ(info.proxy_server(), "foopy2:9090"); + EXPECT_EQ("foopy2:9090", info.proxy_server().ToURI()); // Create a new resolver that returns 3 proxies. The second one is already // known to be bad. @@ -229,12 +281,12 @@ TEST(ProxyServiceTest, ProxyFallback) { rv = service.ResolveProxy(url, &info); EXPECT_EQ(rv, net::OK); EXPECT_FALSE(info.is_direct()); - EXPECT_EQ(info.proxy_server(), "foopy3:7070"); + EXPECT_EQ("foopy3:7070", info.proxy_server().ToURI()); // We fake another error. It should now try the third one. rv = service.ReconsiderProxyAfterError(url, &info); EXPECT_EQ(rv, net::OK); - EXPECT_EQ(info.proxy_server(), "foopy2:9090"); + EXPECT_EQ("foopy2:9090", info.proxy_server().ToURI()); // Fake another error, the last proxy is gone, the list should now be empty. rv = service.ReconsiderProxyAfterError(url, &info); @@ -270,7 +322,7 @@ TEST(ProxyServiceTest, ProxyFallback_NewSettings) { EXPECT_FALSE(info.is_direct()); // The first item is valid. - EXPECT_EQ(info.proxy_server(), "foopy1:8080"); + EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI()); // Fake an error on the proxy, and also a new configuration on the proxy. config_service->config = net::ProxyConfig(); @@ -280,12 +332,12 @@ TEST(ProxyServiceTest, ProxyFallback_NewSettings) { EXPECT_EQ(rv, net::OK); // The first proxy is still there since the configuration changed. - EXPECT_EQ(info.proxy_server(), "foopy1:8080"); + EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI()); // We fake another error. It should now ignore the first one. rv = service.ReconsiderProxyAfterError(url, &info); EXPECT_EQ(rv, net::OK); - EXPECT_EQ(info.proxy_server(), "foopy2:9090"); + EXPECT_EQ("foopy2:9090", info.proxy_server().ToURI()); // We simulate a new configuration. config_service->config = net::ProxyConfig(); @@ -294,7 +346,7 @@ TEST(ProxyServiceTest, ProxyFallback_NewSettings) { // We fake anothe error. It should go back to the first proxy. rv = service.ReconsiderProxyAfterError(url, &info); EXPECT_EQ(rv, net::OK); - EXPECT_EQ(info.proxy_server(), "foopy1:8080"); + EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI()); } TEST(ProxyServiceTest, ProxyFallback_BadConfig) { @@ -319,7 +371,7 @@ TEST(ProxyServiceTest, ProxyFallback_BadConfig) { EXPECT_FALSE(info.is_direct()); // The first item is valid. - EXPECT_EQ(info.proxy_server(), "foopy1:8080"); + EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI()); // Fake a proxy error. rv = service.ReconsiderProxyAfterError(url, &info); @@ -327,7 +379,7 @@ TEST(ProxyServiceTest, ProxyFallback_BadConfig) { // The first proxy is ignored, and the second one is selected. EXPECT_FALSE(info.is_direct()); - EXPECT_EQ(info.proxy_server(), "foopy2:9090"); + EXPECT_EQ("foopy2:9090", info.proxy_server().ToURI()); // Fake a PAC failure. net::ProxyInfo info2; @@ -357,14 +409,14 @@ TEST(ProxyServiceTest, ProxyFallback_BadConfig) { // The first proxy is still there since the list of bad proxies got cleared. EXPECT_FALSE(info3.is_direct()); - EXPECT_EQ(info3.proxy_server(), "foopy1:8080"); + EXPECT_EQ("foopy1:8080", info3.proxy_server().ToURI()); } TEST(ProxyServiceTest, ProxyBypassList) { // Test what happens when a proxy bypass list is specified. net::ProxyConfig config; - config.proxy_server = "foopy1:8080;foopy2:9090"; + config.proxy_rules = "foopy1:8080;foopy2:9090"; config.auto_detect = false; config.proxy_bypass_local_names = true; @@ -444,7 +496,7 @@ TEST(ProxyServiceTest, ProxyBypassList) { TEST(ProxyServiceTest, PerProtocolProxyTests) { net::ProxyConfig config; - config.proxy_server = "http=foopy1:8080;https=foopy2:8080"; + config.proxy_rules = "http=foopy1:8080;https=foopy2:8080"; config.auto_detect = false; SyncProxyService service1(new MockProxyConfigService(config), @@ -454,7 +506,7 @@ TEST(ProxyServiceTest, PerProtocolProxyTests) { int rv = service1.ResolveProxy(test_url1, &info1); EXPECT_EQ(rv, net::OK); EXPECT_FALSE(info1.is_direct()); - EXPECT_TRUE(info1.proxy_server() == "foopy1:8080"); + EXPECT_EQ("foopy1:8080", info1.proxy_server().ToURI()); SyncProxyService service2(new MockProxyConfigService(config), new MockProxyResolver); @@ -463,7 +515,7 @@ TEST(ProxyServiceTest, PerProtocolProxyTests) { rv = service2.ResolveProxy(test_url2, &info2); EXPECT_EQ(rv, net::OK); EXPECT_TRUE(info2.is_direct()); - EXPECT_TRUE(info2.proxy_server() == ""); + EXPECT_EQ("direct://", info2.proxy_server().ToURI()); SyncProxyService service3(new MockProxyConfigService(config), new MockProxyResolver); @@ -472,9 +524,9 @@ TEST(ProxyServiceTest, PerProtocolProxyTests) { rv = service3.ResolveProxy(test_url3, &info3); EXPECT_EQ(rv, net::OK); EXPECT_FALSE(info3.is_direct()); - EXPECT_TRUE(info3.proxy_server() == "foopy2:8080"); + EXPECT_EQ("foopy2:8080", info3.proxy_server().ToURI()); - config.proxy_server = "foopy1:8080"; + config.proxy_rules = "foopy1:8080"; SyncProxyService service4(new MockProxyConfigService(config), new MockProxyResolver); GURL test_url4("www.microsoft.com"); @@ -482,6 +534,6 @@ TEST(ProxyServiceTest, PerProtocolProxyTests) { rv = service4.ResolveProxy(test_url4, &info4); EXPECT_EQ(rv, net::OK); EXPECT_FALSE(info4.is_direct()); - EXPECT_TRUE(info4.proxy_server() == "foopy1:8080"); + EXPECT_EQ("foopy1:8080", info4.proxy_server().ToURI()); } |