summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/base/net_util.cc46
-rw-r--r--net/base/net_util.h13
-rw-r--r--net/base/net_util_unittest.cc41
-rw-r--r--net/build/net.vcproj8
-rw-r--r--net/build/net_unittests.vcproj4
-rw-r--r--net/http/http_network_transaction.cc23
-rw-r--r--net/net.xcodeproj/project.pbxproj10
-rw-r--r--net/net_lib.scons2
-rw-r--r--net/net_unittests.scons1
-rw-r--r--net/proxy/proxy_config_service_fixed.h2
-rw-r--r--net/proxy/proxy_config_service_win.cc2
-rw-r--r--net/proxy/proxy_resolver_mac.cc23
-rw-r--r--net/proxy/proxy_resolver_winhttp.cc14
-rw-r--r--net/proxy/proxy_server.cc215
-rw-r--r--net/proxy/proxy_server.h118
-rw-r--r--net/proxy/proxy_server_unittest.cc222
-rw-r--r--net/proxy/proxy_service.cc90
-rw-r--r--net/proxy/proxy_service.h76
-rw-r--r--net/proxy/proxy_service_unittest.cc118
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());
}