summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorsdoyon@chromium.org <sdoyon@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-04-20 16:50:56 +0000
committersdoyon@chromium.org <sdoyon@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-04-20 16:50:56 +0000
commit861c6c6d56add9157e04aa2ac43bec8e33bf1cc1 (patch)
tree835dd6466912cd073e3ac16177252fe0c8865c93 /net
parentbc1529bbafc42d96d9ac0839bcb0ba1ab9328445 (diff)
downloadchromium_src-861c6c6d56add9157e04aa2ac43bec8e33bf1cc1.zip
chromium_src-861c6c6d56add9157e04aa2ac43bec8e33bf1cc1.tar.gz
chromium_src-861c6c6d56add9157e04aa2ac43bec8e33bf1cc1.tar.bz2
ProxyConfigService for Linux.
Establishes a ProxyConfig by reading settings from gconf or consulting environment variables. BUG=8143 Thanks to ermilov.maxim@gmail.com for his contribution: some ideas< and code snippets from his patch were folded into this one. (See http://codereview.chromium.org/49009) Review URL: http://codereview.chromium.org/60009 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@14034 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/net.gyp14
-rw-r--r--net/proxy/proxy_config.cc5
-rw-r--r--net/proxy/proxy_config.h7
-rw-r--r--net/proxy/proxy_config_service_common_unittest.cc51
-rw-r--r--net/proxy/proxy_config_service_common_unittest.h38
-rw-r--r--net/proxy/proxy_config_service_linux.cc556
-rw-r--r--net/proxy/proxy_config_service_linux.h108
-rw-r--r--net/proxy/proxy_config_service_linux_unittest.cc736
-rw-r--r--net/proxy/proxy_config_service_win.h2
-rw-r--r--net/proxy/proxy_config_service_win_unittest.cc43
-rw-r--r--net/proxy/proxy_service.cc14
11 files changed, 1522 insertions, 52 deletions
diff --git a/net/net.gyp b/net/net.gyp
index f1a4805..7759eb2 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -238,6 +238,8 @@
'proxy/proxy_config.h',
'proxy/proxy_config_service.h',
'proxy/proxy_config_service_fixed.h',
+ 'proxy/proxy_config_service_linux.cc',
+ 'proxy/proxy_config_service_linux.h',
'proxy/proxy_config_service_win.cc',
'proxy/proxy_config_service_win.h',
'proxy/proxy_info.cc',
@@ -307,6 +309,8 @@
'conditions': [
[ 'OS == "linux"', {
'dependencies': [
+ '../build/linux/system.gyp:gconf',
+ '../build/linux/system.gyp:gdk',
'../build/linux/system.gyp:nss',
],
}],
@@ -427,6 +431,9 @@
'http/http_transaction_unittest.h',
'http/http_util_unittest.cc',
'http/http_vary_data_unittest.cc',
+ 'proxy/proxy_config_service_common_unittest.cc',
+ 'proxy/proxy_config_service_common_unittest.h',
+ 'proxy/proxy_config_service_linux_unittest.cc',
'proxy/proxy_config_service_win_unittest.cc',
'proxy/proxy_config_unittest.cc',
'proxy/proxy_list_unittest.cc',
@@ -438,14 +445,18 @@
'url_request/url_request_unittest.h',
],
'conditions': [
+ [ 'OS == "win"', {
+ 'sources/': [ ['exclude', '_(mac|linux|posix)_unittest\\.cc$'] ],
+ },
+ ],
[ 'OS != "win"', {
'sources!': [
'base/wininet_util_unittest.cc',
- 'proxy/proxy_config_service_win_unittest.cc',
],
},
],
[ 'OS == "linux"', {
+ 'sources/': [ ['exclude', '_(mac|win)_unittest\\.cc$'] ],
'dependencies': [
'../build/linux/system.gyp:gtk',
],
@@ -456,6 +467,7 @@
},
],
[ 'OS == "mac"', {
+ 'sources/': [ ['exclude', '_(linux|win)_unittest\\.cc$'] ],
'sources!': [
'base/ssl_config_service_unittest.cc',
],
diff --git a/net/proxy/proxy_config.cc b/net/proxy/proxy_config.cc
index 009fcf0..20c0e3a 100644
--- a/net/proxy/proxy_config.cc
+++ b/net/proxy/proxy_config.cc
@@ -91,7 +91,8 @@ std::ostream& operator<<(std::ostream& out,
return out;
}
-// Helper to stringize a ProxyRules.
+} // namespace
+
std::ostream& operator<<(std::ostream& out,
const net::ProxyConfig::ProxyRules& rules) {
// Stringize the type enum.
@@ -119,8 +120,6 @@ std::ostream& operator<<(std::ostream& out,
<< " }";
}
-} // namespace
-
std::ostream& operator<<(std::ostream& out, const net::ProxyConfig& config) {
out << "{\n"
<< " auto_detect: " << config.auto_detect << "\n"
diff --git a/net/proxy/proxy_config.h b/net/proxy/proxy_config.h
index 4c3a731..ec6c1fc 100644
--- a/net/proxy/proxy_config.h
+++ b/net/proxy/proxy_config.h
@@ -107,8 +107,13 @@ class ProxyConfig {
} // namespace net
-// Dump a human-readable string representation of the configuration to |out|;
+// Dumps a human-readable string representation of the configuration to |out|;
// used when logging the configuration changes.
std::ostream& operator<<(std::ostream& out, const net::ProxyConfig& config);
+// Dumps a human-readable string representation of the |rules| to |out|;
+// used for logging and for better unittest failure output.
+std::ostream& operator<<(std::ostream& out,
+ const net::ProxyConfig::ProxyRules& rules);
+
#endif // NET_PROXY_PROXY_CONFIG_H_
diff --git a/net/proxy/proxy_config_service_common_unittest.cc b/net/proxy/proxy_config_service_common_unittest.cc
new file mode 100644
index 0000000..06f1c6a
--- /dev/null
+++ b/net/proxy/proxy_config_service_common_unittest.cc
@@ -0,0 +1,51 @@
+// 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_config_service_common_unittest.h"
+
+#include <string>
+#include <vector>
+
+#include "net/proxy/proxy_config.h"
+
+namespace net {
+
+ProxyConfig::ProxyRules MakeProxyRules(
+ ProxyConfig::ProxyRules::Type type,
+ const char* single_proxy,
+ const char* proxy_for_http,
+ const char* proxy_for_https,
+ const char* proxy_for_ftp) {
+ ProxyConfig::ProxyRules rules;
+ rules.type = type;
+ rules.single_proxy = ProxyServer::FromURI(single_proxy);
+ rules.proxy_for_http = ProxyServer::FromURI(proxy_for_http);
+ rules.proxy_for_https = ProxyServer::FromURI(proxy_for_https);
+ rules.proxy_for_ftp = ProxyServer::FromURI(proxy_for_ftp);
+ return rules;
+}
+
+ProxyConfig::ProxyRules MakeSingleProxyRules(const char* single_proxy) {
+ return MakeProxyRules(ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY,
+ single_proxy, "", "", "");
+}
+
+ProxyConfig::ProxyRules MakeProxyPerSchemeRules(
+ const char* proxy_http,
+ const char* proxy_https,
+ const char* proxy_ftp) {
+ return MakeProxyRules(ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME,
+ "", proxy_http, proxy_https, proxy_ftp);
+}
+
+std::string FlattenProxyBypass(const BypassList& proxy_bypass) {
+ std::string flattened_proxy_bypass;
+ for (BypassList::const_iterator it = proxy_bypass.begin();
+ it != proxy_bypass.end(); ++it) {
+ flattened_proxy_bypass += *it + "\n";
+ }
+ return flattened_proxy_bypass;
+}
+
+} // namespace net
diff --git a/net/proxy/proxy_config_service_common_unittest.h b/net/proxy/proxy_config_service_common_unittest.h
new file mode 100644
index 0000000..08487ec
--- /dev/null
+++ b/net/proxy/proxy_config_service_common_unittest.h
@@ -0,0 +1,38 @@
+// 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_PROXY_CONFIG_SERVICE_COMMON_UNITTEST_H_
+#define NET_PROXY_PROXY_CONFIG_SERVICE_COMMON_UNITTEST_H_
+
+#include <string>
+#include <vector>
+
+#include "net/proxy/proxy_config.h"
+
+// A few small helper functions common to the win and linux unittests.
+
+namespace net {
+
+ProxyConfig::ProxyRules MakeProxyRules(
+ ProxyConfig::ProxyRules::Type type,
+ const char* single_proxy,
+ const char* proxy_for_http,
+ const char* proxy_for_https,
+ const char* proxy_for_ftp);
+
+ProxyConfig::ProxyRules MakeSingleProxyRules(const char* single_proxy);
+
+ProxyConfig::ProxyRules MakeProxyPerSchemeRules(
+ const char* proxy_http,
+ const char* proxy_https,
+ const char* proxy_ftp);
+
+typedef std::vector<std::string> BypassList;
+
+// Joins the proxy bypass list using "\n" to make it into a single string.
+std::string FlattenProxyBypass(const BypassList& proxy_bypass);
+
+} // namespace net
+
+#endif // NET_PROXY_PROXY_CONFIG_SERVICE_COMMON_UNITTEST_H_
diff --git a/net/proxy/proxy_config_service_linux.cc b/net/proxy/proxy_config_service_linux.cc
new file mode 100644
index 0000000..f864ed2
--- /dev/null
+++ b/net/proxy/proxy_config_service_linux.cc
@@ -0,0 +1,556 @@
+// 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_config_service_linux.h"
+
+#include <gconf/gconf-client.h>
+#include <gdk/gdk.h>
+#include <stdlib.h>
+
+#include "base/logging.h"
+#include "base/string_tokenizer.h"
+#include "base/string_util.h"
+#include "googleurl/src/url_canon.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_util.h"
+#include "net/proxy/proxy_config.h"
+#include "net/proxy/proxy_server.h"
+
+namespace net {
+
+namespace {
+
+class EnvironmentVariableGetterImpl
+ : public ProxyConfigServiceLinux::EnvironmentVariableGetter {
+ public:
+ virtual bool Getenv(const char* variable_name, std::string* result) {
+ const char* env_value = ::getenv(variable_name);
+ if (env_value) {
+ // Note that the variable may be defined but empty.
+ *result = env_value;
+ return true;
+ }
+ // Some commonly used variable names are uppercase while others
+ // are lowercase, which is inconsistent. Let's try to be helpful
+ // and look for a variable name with the reverse case.
+ char first_char = variable_name[0];
+ std::string alternate_case_var;
+ if (first_char >= 'a' && first_char <= 'z')
+ alternate_case_var = StringToUpperASCII(std::string(variable_name));
+ else if (first_char >= 'A' && first_char <= 'Z')
+ alternate_case_var = StringToLowerASCII(std::string(variable_name));
+ else
+ return false;
+ env_value = ::getenv(alternate_case_var.c_str());
+ if (env_value) {
+ *result = env_value;
+ return true;
+ }
+ return false;
+ }
+};
+
+// Given a proxy hostname from a setting, returns that hostname with
+// an appropriate proxy server scheme prefix.
+// scheme indicates the desired proxy scheme: usually http, with
+// socks 4 or 5 as special cases.
+std::string FixupProxyHostScheme(ProxyServer::Scheme scheme,
+ std::string host) {
+ if (scheme == ProxyServer::SCHEME_SOCKS4 &&
+ StartsWithASCII(host, "socks5://", false)) {
+ // We default to socks 4, but if the user specifically set it to
+ // socks5://, then use that.
+ scheme = ProxyServer::SCHEME_SOCKS5;
+ }
+ // Strip the scheme if any.
+ std::string::size_type colon = host.find("://");
+ if (colon != std::string::npos)
+ host = host.substr(colon + 3);
+ // If a username and perhaps password are specified, give a warning.
+ std::string::size_type at_sign = host.find("@");
+ // Should this be supported?
+ if (at_sign != std::string::npos) {
+ LOG(ERROR) << "ProxyConfigServiceLinux: proxy authentication "
+ "not supported";
+ // Disregard the authentication parameters and continue with this hostname.
+ host = host.substr(at_sign + 1);
+ }
+ // If this is a socks proxy, prepend a scheme so as to tell
+ // ProxyServer. This also allows ProxyServer to choose the right
+ // default port.
+ if (scheme == ProxyServer::SCHEME_SOCKS4)
+ host = "socks4://" + host;
+ else if (scheme == ProxyServer::SCHEME_SOCKS5)
+ host = "socks5://" + host;
+ return host;
+}
+
+} // namespace
+
+bool ProxyConfigServiceLinux::GetProxyFromEnvVarForScheme(
+ const char* variable, ProxyServer::Scheme scheme,
+ ProxyServer* result_server) {
+ std::string env_value;
+ if (env_var_getter_->Getenv(variable, &env_value)) {
+ if (!env_value.empty()) {
+ env_value = FixupProxyHostScheme(scheme, env_value);
+ ProxyServer proxy_server = ProxyServer::FromURI(env_value);
+ if (proxy_server.is_valid() && !proxy_server.is_direct()) {
+ *result_server = proxy_server;
+ return true;
+ } else {
+ LOG(ERROR) << "ProxyConfigServiceLinux: failed to parse "
+ << "environment variable " << variable;
+ }
+ }
+ }
+ return false;
+}
+
+bool ProxyConfigServiceLinux::GetProxyFromEnvVar(
+ const char* variable, ProxyServer* result_server) {
+ return GetProxyFromEnvVarForScheme(variable, ProxyServer::SCHEME_HTTP,
+ result_server);
+}
+
+namespace {
+
+// Returns true if the given string represents an IP address.
+bool IsIPAddress(const std::string& domain) {
+ // From GURL::HostIsIPAddress()
+ url_canon::RawCanonOutputT<char, 128> ignored_output;
+ url_parse::Component ignored_component;
+ url_parse::Component domain_comp(0, domain.size());
+ return url_canon::CanonicalizeIPAddress(domain.c_str(), domain_comp,
+ &ignored_output,
+ &ignored_component);
+}
+
+} // namespace
+
+void ProxyConfigServiceLinux::ParseNoProxyList(const std::string& no_proxy,
+ ProxyConfig* config) {
+ if (no_proxy.empty())
+ return;
+ // Traditional semantics:
+ // A single "*" is specifically allowed and unproxies anything.
+ // "*" wildcards other than a single "*" entry are not universally
+ // supported. We will support them, as we get * wildcards for free
+ // (see MatchPattern() called from ProxyService::ShouldBypassProxyForURL()).
+ // no_proxy is a comma-separated list of <trailing_domain>[:<port>].
+ // If no port is specified then any port matches.
+ // The historical definition has trailing_domain match using a simple
+ // string "endswith" test, so that the match need not correspond to a
+ // "." boundary. For example: "google.com" matches "igoogle.com" too.
+ // Seems like that could be confusing, but we'll obey tradition.
+ // IP CIDR patterns are supposed to be supported too. We intend
+ // to do this in proxy_service.cc, but it's currently a TODO.
+ // See: http://crbug.com/9835.
+ StringTokenizer no_proxy_list(no_proxy, ",");
+ while (no_proxy_list.GetNext()) {
+ std::string bypass_entry = no_proxy_list.token();
+ TrimWhitespaceASCII(bypass_entry, TRIM_ALL, &bypass_entry);
+ if (bypass_entry.empty())
+ continue;
+ if (bypass_entry.at(0) != '*') {
+ // Insert a wildcard * to obtain an endsWith match, unless the
+ // entry looks like it might be an IP or CIDR.
+ // First look for either a :<port> or CIDR mask length suffix.
+ std::string::const_iterator begin = bypass_entry.begin();
+ std::string::const_iterator scan = bypass_entry.end() - 1;
+ while (scan > begin && IsAsciiDigit(*scan))
+ --scan;
+ std::string potential_ip;
+ if (*scan == '/' || *scan == ':')
+ potential_ip = std::string(begin, scan - 1);
+ else
+ potential_ip = bypass_entry;
+ if (!IsIPAddress(potential_ip)) {
+ // Do insert a wildcard.
+ bypass_entry.insert(0, "*");
+ }
+ // TODO(sdoyon): When CIDR matching is implemented in
+ // proxy_service.cc, consider making config->proxy_bypass more
+ // sophisticated to avoid parsing out the string on every
+ // request.
+ }
+ config->proxy_bypass.push_back(bypass_entry);
+ }
+}
+
+bool ProxyConfigServiceLinux::GetConfigFromEnv(ProxyConfig* config) {
+ // Check for automatic configuration first, in
+ // "auto_proxy". Possibly only the "environment_proxy" firefox
+ // extension has ever used this, but it still sounds like a good
+ // idea.
+ std::string auto_proxy;
+ if (env_var_getter_->Getenv("auto_proxy", &auto_proxy)) {
+ if (auto_proxy.empty()) {
+ // Defined and empty => autodetect
+ config->auto_detect = true;
+ } else {
+ // specified autoconfig URL
+ config->pac_url = GURL(auto_proxy);
+ }
+ return true;
+ }
+ // "all_proxy" is a shortcut to avoid defining {http,https,ftp}_proxy.
+ ProxyServer proxy_server;
+ if (GetProxyFromEnvVar("all_proxy", &proxy_server)) {
+ config->proxy_rules.type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
+ config->proxy_rules.single_proxy = proxy_server;
+ } else {
+ bool have_http = GetProxyFromEnvVar("http_proxy", &proxy_server);
+ if (have_http)
+ config->proxy_rules.proxy_for_http = proxy_server;
+ // It would be tempting to let http_proxy apply for all protocols
+ // if https_proxy and ftp_proxy are not defined. Googling turns up
+ // several documents that mention only http_proxy. But then the
+ // user really might not want to proxy https. And it doesn't seem
+ // like other apps do this. So we will refrain.
+ bool have_https = GetProxyFromEnvVar("https_proxy", &proxy_server);
+ if (have_https)
+ config->proxy_rules.proxy_for_https = proxy_server;
+ bool have_ftp = GetProxyFromEnvVar("ftp_proxy", &proxy_server);
+ if (have_ftp)
+ config->proxy_rules.proxy_for_ftp = proxy_server;
+ if (have_http || have_https || have_ftp) {
+ // mustn't change type unless some rules are actually set.
+ config->proxy_rules.type = ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
+ }
+ }
+ if (config->proxy_rules.empty()) {
+ // If the above were not defined, try for socks.
+ ProxyServer::Scheme scheme = ProxyServer::SCHEME_SOCKS4;
+ std::string env_version;
+ if (env_var_getter_->Getenv("SOCKS_VERSION", &env_version)
+ && env_version.compare("5") == 0)
+ scheme = ProxyServer::SCHEME_SOCKS5;
+ if (GetProxyFromEnvVarForScheme("SOCKS_SERVER", scheme, &proxy_server)) {
+ config->proxy_rules.type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
+ config->proxy_rules.single_proxy = proxy_server;
+ }
+ }
+ // Look for the proxy bypass list.
+ std::string no_proxy;
+ env_var_getter_->Getenv("no_proxy", &no_proxy);
+ if (config->proxy_rules.empty()) {
+ // Having only "no_proxy" set, presumably to "*", makes it
+ // explicit that env vars do specify a configuration: having no
+ // rules specified only means the user explicitly asks for direct
+ // connections.
+ return !no_proxy.empty();
+ }
+ ParseNoProxyList(no_proxy, config);
+ return true;
+}
+
+namespace {
+
+class GConfSettingGetterImpl
+ : public ProxyConfigServiceLinux::GConfSettingGetter {
+ public:
+ GConfSettingGetterImpl() : client_(NULL) {}
+ virtual ~GConfSettingGetterImpl() {
+ if (client_)
+ g_object_unref(client_);
+ }
+
+ virtual void Enter() {
+ gdk_threads_enter();
+ }
+ virtual void Leave() {
+ gdk_threads_leave();
+ }
+
+ virtual bool InitIfNeeded() {
+ if (!client_) {
+ Enter();
+ client_ = gconf_client_get_default();
+ Leave();
+ // It's not clear whether/when this can return NULL.
+ if (!client_)
+ LOG(ERROR) << "ProxyConfigServiceLinux: Unable to create "
+ "a gconf client";
+ }
+ return client_ != NULL;
+ }
+
+ virtual bool GetString(const char* key, std::string* result) {
+ CHECK(client_);
+ GError* error = NULL;
+ gchar* value = gconf_client_get_string(client_, key, &error);
+ if (HandleGError(error, key))
+ return false;
+ if (!value)
+ return false;
+ *result = value;
+ g_free(value);
+ return true;
+ }
+ virtual bool GetBoolean(const char* key, bool* result) {
+ CHECK(client_);
+ GError* error = NULL;
+ // We want to distinguish unset values from values defaulting to
+ // false. For that we need to use the type-generic
+ // gconf_client_get() rather than gconf_client_get_bool().
+ GConfValue* gconf_value = gconf_client_get(client_, key, &error);
+ if (HandleGError(error, key))
+ return false;
+ if (!gconf_value) {
+ // Unset.
+ return false;
+ }
+ if (gconf_value->type != GCONF_VALUE_BOOL) {
+ gconf_value_free(gconf_value);
+ return false;
+ }
+ gboolean bool_value = gconf_value_get_bool(gconf_value);
+ *result = static_cast<bool>(bool_value);
+ gconf_value_free(gconf_value);
+ return true;
+ }
+ virtual bool GetInt(const char* key, int* result) {
+ CHECK(client_);
+ GError* error = NULL;
+ int value = gconf_client_get_int(client_, key, &error);
+ if (HandleGError(error, key))
+ return false;
+ // We don't bother to distinguish an unset value because callers
+ // don't care. 0 is returned if unset.
+ *result = value;
+ return true;
+ }
+ virtual bool GetStringList(const char* key,
+ std::vector<std::string>* result) {
+ CHECK(client_);
+ GError* error = NULL;
+ GSList* list = gconf_client_get_list(client_, key,
+ GCONF_VALUE_STRING, &error);
+ if (HandleGError(error, key))
+ return false;
+ if (!list) {
+ // unset
+ return false;
+ }
+ for (GSList *it = list; it; it = it->next) {
+ result->push_back(static_cast<char*>(it->data));
+ g_free(it->data);
+ }
+ g_slist_free(list);
+ return true;
+ }
+
+ private:
+ // Logs and frees a glib error. Returns false if there was no error
+ // (error is NULL).
+ bool HandleGError(GError* error, const char* key) {
+ if (error != NULL) {
+ LOG(ERROR) << "ProxyConfigServiceLinux: error getting gconf value for "
+ << key << ": " << error->message;
+ g_error_free(error);
+ return true;
+ }
+ return false;
+ }
+
+ GConfClient* client_;
+
+ DISALLOW_COPY_AND_ASSIGN(GConfSettingGetterImpl);
+};
+
+} // namespace
+
+bool ProxyConfigServiceLinux::GetProxyFromGConf(
+ const char* key_prefix, bool is_socks, ProxyServer* result_server) {
+ std::string key(key_prefix);
+ std::string host;
+ if (!gconf_getter_->GetString((key + "host").c_str(), &host)
+ || host.empty()) {
+ // Unset or empty.
+ return false;
+ }
+ // Check for an optional port.
+ int port;
+ gconf_getter_->GetInt((key + "port").c_str(), &port);
+ if (port != 0) {
+ // If a port is set and non-zero:
+ host += ":" + IntToString(port);
+ }
+ host = FixupProxyHostScheme(
+ is_socks ? ProxyServer::SCHEME_SOCKS4 : ProxyServer::SCHEME_HTTP,
+ host);
+ ProxyServer proxy_server = ProxyServer::FromURI(host);
+ if (proxy_server.is_valid()) {
+ *result_server = proxy_server;
+ return true;
+ }
+ return false;
+}
+
+bool ProxyConfigServiceLinux::GetConfigFromGConf(ProxyConfig* config) {
+ std::string mode;
+ if (!gconf_getter_->GetString("/system/proxy/mode", &mode)) {
+ // We expect this to always be set, so if we don't see it then we
+ // probably have a gconf problem, and so we don't have a valid
+ // proxy config.
+ return false;
+ }
+ if (mode.compare("none") == 0)
+ // Specifically specifies no proxy.
+ return true;
+
+ if (mode.compare("auto") == 0) {
+ // automatic proxy config
+ std::string pac_url_str;
+ if (gconf_getter_->GetString("/system/proxy/autoconfig_url",
+ &pac_url_str)) {
+ if (!pac_url_str.empty()) {
+ GURL pac_url(pac_url_str);
+ if (!pac_url.is_valid())
+ return false;
+ config->pac_url = pac_url;
+ return true;
+ }
+ }
+ config->auto_detect = true;
+ return true;
+ }
+
+ if (mode.compare("manual") != 0) {
+ // Mode is unrecognized.
+ return false;
+ }
+ bool use_http_proxy;
+ if (gconf_getter_->GetBoolean("/system/http_proxy/use_http_proxy",
+ &use_http_proxy)
+ && !use_http_proxy) {
+ // Another master switch for some reason. If set to false, then no
+ // proxy. But we don't panic if the key doesn't exist.
+ return true;
+ }
+
+ bool same_proxy = false;
+ // Indicates to use the http proxy for all protocols. This one may
+ // not exist (presumably on older versions), assume false in that
+ // case.
+ gconf_getter_->GetBoolean("/system/http_proxy/use_same_proxy",
+ &same_proxy);
+
+ ProxyServer proxy_server;
+ if (!same_proxy) {
+ // Try socks.
+ if (GetProxyFromGConf("/system/proxy/socks_", true, &proxy_server)) {
+ // gconf settings do not appear to distinguish between socks
+ // version. We default to version 4.
+ config->proxy_rules.type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
+ config->proxy_rules.single_proxy = proxy_server;
+ }
+ }
+ if (config->proxy_rules.empty()) {
+ bool have_http = GetProxyFromGConf("/system/http_proxy/", false,
+ &proxy_server);
+ if (same_proxy) {
+ if (have_http) {
+ config->proxy_rules.type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
+ config->proxy_rules.single_proxy = proxy_server;
+ }
+ } else {
+ // Protocol specific settings.
+ if (have_http)
+ config->proxy_rules.proxy_for_http = proxy_server;
+ bool have_secure = GetProxyFromGConf("/system/proxy/secure_", false,
+ &proxy_server);
+ if (have_secure)
+ config->proxy_rules.proxy_for_https = proxy_server;
+ bool have_ftp = GetProxyFromGConf("/system/proxy/ftp_", false,
+ &proxy_server);
+ if (have_ftp)
+ config->proxy_rules.proxy_for_ftp = proxy_server;
+ if (have_http || have_secure || have_ftp)
+ config->proxy_rules.type =
+ ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
+ }
+ }
+
+ if (config->proxy_rules.empty()) {
+ // Manual mode but we couldn't parse any rules.
+ return false;
+ }
+
+ // Check for authentication, just so we can warn.
+ bool use_auth;
+ gconf_getter_->GetBoolean("/system/http_proxy/use_authentication",
+ &use_auth);
+ if (use_auth)
+ LOG(ERROR) << "ProxyConfigServiceLinux: proxy authentication "
+ "not supported";
+
+ // Now the bypass list.
+ gconf_getter_->GetStringList("/system/http_proxy/ignore_hosts",
+ &config->proxy_bypass);
+ // Note that there are no settings with semantics corresponding to
+ // config->proxy_bypass_local_names.
+
+ return true;
+}
+
+ProxyConfigServiceLinux::ProxyConfigServiceLinux(
+ EnvironmentVariableGetter* env_var_getter,
+ GConfSettingGetter* gconf_getter)
+ : env_var_getter_(env_var_getter), gconf_getter_(gconf_getter) {
+}
+
+ProxyConfigServiceLinux::ProxyConfigServiceLinux()
+ : env_var_getter_(new EnvironmentVariableGetterImpl()),
+ gconf_getter_(new GConfSettingGetterImpl()) {
+}
+
+int ProxyConfigServiceLinux::GetProxyConfig(ProxyConfig* config) {
+ // GNOME_DESKTOP_SESSION_ID being defined is a good indication that
+ // we are probably running under GNOME.
+ // Note: KDE_FULL_SESSION is a corresponding env var to recognize KDE.
+ std::string dummy, desktop_session;
+ bool ok = false;
+ if (env_var_getter_->Getenv("GNOME_DESKTOP_SESSION_ID", &dummy)
+ || (env_var_getter_->Getenv("DESKTOP_SESSION", &desktop_session)
+ && desktop_session.compare("gnome"))) {
+ // Get settings from gconf.
+ //
+ // I (sdoyon) would have liked to prioritize environment variables
+ // and only fallback to gconf if env vars were unset. But
+ // gnome-terminal "helpfully" sets http_proxy and no_proxy, and it
+ // does so even if the proxy mode is set to auto, which would
+ // mislead us.
+ //
+ // We could introduce a CHROME_PROXY_OBEY_ENV_VARS variable...??
+ if (gconf_getter_->InitIfNeeded()) {
+ gconf_getter_->Enter();
+ ok = GetConfigFromGConf(config);
+ gconf_getter_->Leave();
+ if (ok)
+ LOG(INFO) << "ProxyConfigServiceLinux: obtained proxy setting "
+ "from gconf";
+ // If gconf proxy mode is "none", meaning direct, then we take
+ // that to be a valid config and will not check environment variables.
+ // The alternative would have been to look for a proxy whereever
+ // we can find one.
+ //
+ // TODO(sdoyon): Consider wiring in the gconf notification
+ // system. Cache this result config to return on subsequent calls,
+ // and only call GetConfigFromGConf() when we know things have
+ // actually changed.
+ }
+ }
+ // An implementation for KDE settings would be welcome here.
+ if (!ok) {
+ ok = GetConfigFromEnv(config);
+ if (ok)
+ LOG(INFO) << "ProxyConfigServiceLinux: obtained proxy setting "
+ "from environment variables";
+ }
+ return ok ? OK : ERR_FAILED;
+}
+
+} // namespace net
diff --git a/net/proxy/proxy_config_service_linux.h b/net/proxy/proxy_config_service_linux.h
new file mode 100644
index 0000000..51436af
--- /dev/null
+++ b/net/proxy/proxy_config_service_linux.h
@@ -0,0 +1,108 @@
+// 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_PROXY_CONFIG_SERVICE_LINUX_H_
+#define NET_PROXY_PROXY_CONFIG_SERVICE_LINUX_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "net/proxy/proxy_config_service.h"
+#include "net/proxy/proxy_server.h"
+
+namespace net {
+
+// Implementation of ProxyConfigService that retrieves the system proxy
+// settings from environment variables or gconf.
+class ProxyConfigServiceLinux : public ProxyConfigService {
+ public:
+
+ // These are used to derive mocks for unittests.
+ class EnvironmentVariableGetter {
+ public:
+ virtual ~EnvironmentVariableGetter() {}
+ // Gets an environment variable's value and stores it in
+ // result. Returns false if the key is unset.
+ virtual bool Getenv(const char* variable_name, std::string* result) = 0;
+ };
+
+ class GConfSettingGetter {
+ public:
+ virtual ~GConfSettingGetter() {}
+
+ // GDK/GTK+ thread-safety: multi-threaded programs coordinate
+ // around a global lock. See
+ // http://library.gnome.org/devel/gdk/unstable/gdk-Threads.html
+ // and http://research.operationaldynamics.com/blogs/andrew/software/gnome-desktop/gtk-thread-awareness.html
+ // The following methods are used to grab said lock around any
+ // actual gconf usage: including calls to Init() and any of the
+ // Get* methods.
+ virtual void Enter() = 0;
+ virtual void Leave() = 0;
+
+ // Initializes the class: obtains a gconf client, in the concrete
+ // implementation. Returns true on success. Must be called before
+ // any of the Get* methods, and the Get* methods must not be used
+ // if this method returns false. This method may be called again,
+ // whether or not it succeeded before.
+ virtual bool InitIfNeeded() = 0;
+
+ // Gets a string type value from gconf and stores it in result. Returns
+ // false if the key is unset or on error.
+ virtual bool GetString(const char* key, std::string* result) = 0;
+ // Same thing for a bool typed value.
+ virtual bool GetBoolean(const char* key, bool* result) = 0;
+ // Same for an int typed value.
+ virtual bool GetInt(const char* key, int* result) = 0;
+ // And for a string list.
+ virtual bool GetStringList(const char* key,
+ std::vector<std::string>* result) = 0;
+ };
+
+ // Usual constructor
+ ProxyConfigServiceLinux();
+ // For testing: pass in alternate gconf and env var getter implementations.
+ // ProxyConfigServiceLinux takes ownership of the getter objects.
+ ProxyConfigServiceLinux(EnvironmentVariableGetter* env_var_getter,
+ GConfSettingGetter* gconf_getter);
+
+ // ProxyConfigService methods:
+ virtual int GetProxyConfig(ProxyConfig* config);
+
+ private:
+ // Obtains an environment variable's value. Parses a proxy server
+ // specification from it and puts it in result. Returns true if the
+ // requested variable is defined and the value valid.
+ bool GetProxyFromEnvVarForScheme(const char* variable,
+ ProxyServer::Scheme scheme,
+ ProxyServer* result_server);
+ // As above but with scheme set to HTTP, for convenience.
+ bool GetProxyFromEnvVar(const char* variable, ProxyServer* result_server);
+ // Parses entries from the value of the no_proxy env var, and stuffs
+ // them into config->proxy_bypass.
+ void ParseNoProxyList(const std::string& no_proxy, ProxyConfig* config);
+ // Fills proxy config from environment variables. Returns true if
+ // variables were found and the configuration is valid.
+ bool GetConfigFromEnv(ProxyConfig* config);
+
+ // Obtains host and port gconf settings and parses a proxy server
+ // specification from it and puts it in result. Returns true if the
+ // requested variable is defined and the value valid.
+ bool GetProxyFromGConf(const char* key_prefix, bool is_socks,
+ ProxyServer* result_server);
+ // Fills proxy config from gconf. Returns true if settings were found
+ // and the configuration is valid.
+ bool GetConfigFromGConf(ProxyConfig* config);
+
+ scoped_ptr<EnvironmentVariableGetter> env_var_getter_;
+ scoped_ptr<GConfSettingGetter> gconf_getter_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProxyConfigServiceLinux);
+};
+
+} // namespace net
+
+#endif // NET_PROXY_PROXY_CONFIG_SERVICE_LINUX_H_
diff --git a/net/proxy/proxy_config_service_linux_unittest.cc b/net/proxy/proxy_config_service_linux_unittest.cc
new file mode 100644
index 0000000..ae2b1f7
--- /dev/null
+++ b/net/proxy/proxy_config_service_linux_unittest.cc
@@ -0,0 +1,736 @@
+// 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 <map>
+#include <string>
+#include <vector>
+
+#include "net/proxy/proxy_config_service_linux.h"
+
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "net/proxy/proxy_config.h"
+#include "net/proxy/proxy_config_service_common_unittest.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+// Set of values for all environment variables that we might
+// query. NULL represents an unset variable.
+struct EnvVarValues {
+ // The strange capitalization is so that the field matches the
+ // environment variable name exactly.
+ const char *GNOME_DESKTOP_SESSION_ID, *DESKTOP_SESSION,
+ *auto_proxy, *all_proxy,
+ *http_proxy, *https_proxy, *ftp_proxy,
+ *SOCKS_SERVER, *SOCKS_VERSION,
+ *no_proxy;
+};
+
+// So as to distinguish between an unset gconf boolean variable and
+// one that is false.
+enum BoolSettingValue {
+ UNSET = 0, TRUE, FALSE
+};
+
+// Set of values for all gconf settings that we might query.
+struct GConfValues {
+ // strings
+ const char *mode, *autoconfig_url,
+ *http_host, *secure_host, *ftp_host, *socks_host;
+ // integers
+ int http_port, secure_port, ftp_port, socks_port;
+ // booleans
+ BoolSettingValue use_proxy, same_proxy, use_auth;
+ // string list
+ std::vector<std::string> ignore_hosts;
+};
+
+// Mapping from a setting name to the location of the corresponding
+// value (inside a EnvVarValues or GConfValues struct).
+template<typename value_type>
+struct SettingsTable {
+ typedef std::map<std::string, value_type*> map_type;
+
+ // Gets the value from its location
+ value_type Get(const std::string& key) {
+ typename map_type::const_iterator it = settings.find(key);
+ // In case there's a typo or the unittest becomes out of sync.
+ CHECK(it != settings.end()) << "key " << key << " not found";
+ value_type* value_ptr = it->second;
+ return *value_ptr;
+ }
+
+ map_type settings;
+};
+
+class MockEnvironmentVariableGetter
+ : public ProxyConfigServiceLinux::EnvironmentVariableGetter {
+ public:
+ MockEnvironmentVariableGetter() {
+#define ENTRY(x) table.settings[#x] = &values.x
+ ENTRY(GNOME_DESKTOP_SESSION_ID);
+ ENTRY(DESKTOP_SESSION);
+ ENTRY(auto_proxy);
+ ENTRY(all_proxy);
+ ENTRY(http_proxy);
+ ENTRY(https_proxy);
+ ENTRY(ftp_proxy);
+ ENTRY(no_proxy);
+ ENTRY(SOCKS_SERVER);
+ ENTRY(SOCKS_VERSION);
+#undef ENTRY
+ reset();
+ }
+
+ // Zeros all environment values.
+ void reset() {
+ EnvVarValues zero_values = { 0 };
+ values = zero_values;
+ }
+
+ virtual bool Getenv(const char* variable_name, std::string* result) {
+ const char* env_value = table.Get(variable_name);
+ if (env_value) {
+ // Note that the variable may be defined but empty.
+ *result = env_value;
+ return true;
+ }
+ return false;
+ }
+
+ // Intentionally public, for convenience when setting up a test.
+ EnvVarValues values;
+
+ private:
+ SettingsTable<const char*> table;
+};
+
+class MockGConfSettingGetter
+ : public ProxyConfigServiceLinux::GConfSettingGetter {
+ public:
+ MockGConfSettingGetter() {
+#define ENTRY(key, field) \
+ strings_table.settings["/system/" key] = &values.field
+ ENTRY("proxy/mode", mode);
+ ENTRY("proxy/autoconfig_url", autoconfig_url);
+ ENTRY("http_proxy/host", http_host);
+ ENTRY("proxy/secure_host", secure_host);
+ ENTRY("proxy/ftp_host", ftp_host);
+ ENTRY("proxy/socks_host", socks_host);
+#undef ENTRY
+#define ENTRY(key, field) \
+ ints_table.settings["/system/" key] = &values.field
+ ENTRY("http_proxy/port", http_port);
+ ENTRY("proxy/secure_port", secure_port);
+ ENTRY("proxy/ftp_port", ftp_port);
+ ENTRY("proxy/socks_port", socks_port);
+#undef ENTRY
+#define ENTRY(key, field) \
+ bools_table.settings["/system/" key] = &values.field
+ ENTRY("http_proxy/use_http_proxy", use_proxy);
+ ENTRY("http_proxy/use_same_proxy", same_proxy);
+ ENTRY("http_proxy/use_authentication", use_auth);
+#undef ENTRY
+ string_lists_table.settings["/system/http_proxy/ignore_hosts"] =
+ &values.ignore_hosts;
+ reset();
+ }
+
+ // Zeros all environment values.
+ void reset() {
+ GConfValues zero_values;
+ values = zero_values;
+ }
+
+ virtual void Enter() {}
+ virtual void Leave() {}
+
+ virtual bool InitIfNeeded() {
+ return true;
+ }
+
+ virtual bool GetString(const char* key, std::string* result) {
+ const char* value = strings_table.Get(key);
+ if (value) {
+ *result = value;
+ return true;
+ }
+ return false;
+ }
+
+ virtual bool GetInt(const char* key, int* result) {
+ // We don't bother to distinguish unset keys from 0 values.
+ *result = ints_table.Get(key);
+ return true;
+ }
+
+ virtual bool GetBoolean(const char* key, bool* result) {
+ BoolSettingValue value = bools_table.Get(key);
+ switch (value) {
+ case UNSET:
+ return false;
+ case TRUE:
+ *result = true;
+ break;
+ case FALSE:
+ *result = false;
+ }
+ return true;
+ }
+
+ virtual bool GetStringList(const char* key,
+ std::vector<std::string>* result) {
+ *result = string_lists_table.Get(key);
+ // We don't bother to distinguish unset keys from empty lists.
+ return !result->empty();
+ }
+
+ // Intentionally public, for convenience when setting up a test.
+ GConfValues values;
+
+ private:
+ SettingsTable<const char*> strings_table;
+ SettingsTable<int> ints_table;
+ SettingsTable<BoolSettingValue> bools_table;
+ SettingsTable<std::vector<std::string> > string_lists_table;
+};
+
+} // namespace
+
+// Builds an identifier for each test in an array.
+#define TEST_DESC(desc) StringPrintf("at line %d <%s>", __LINE__, desc)
+
+TEST(ProxyConfigServiceLinuxTest, BasicGConfTest) {
+ MockEnvironmentVariableGetter* env_getter =
+ new MockEnvironmentVariableGetter;
+ MockGConfSettingGetter* gconf_getter = new MockGConfSettingGetter;
+ ProxyConfigServiceLinux service(env_getter, gconf_getter);
+ // This env var indicates we are running Gnome and should consult gconf.
+ env_getter->values.GNOME_DESKTOP_SESSION_ID = "defined";
+
+ std::vector<std::string> empty_ignores;
+
+ std::vector<std::string> google_ignores;
+ google_ignores.push_back("*.google.com");
+
+ // Inspired from proxy_config_service_win_unittest.cc.
+ // Very neat, but harder to track down failures though.
+ const struct {
+ // Short description to identify the test
+ std::string description;
+
+ // Input.
+ GConfValues values;
+
+ // Expected outputs (fields of the ProxyConfig).
+ bool auto_detect;
+ GURL pac_url;
+ ProxyConfig::ProxyRules proxy_rules;
+ const char* proxy_bypass_list; // newline separated
+ bool bypass_local_names;
+ } tests[] = {
+ {
+ TEST_DESC("No proxying"),
+ { // Input.
+ "none", // mode
+ "", // autoconfig_url
+ "", "", "", "", // hosts
+ 0, 0, 0, 0, // ports
+ FALSE, FALSE, FALSE, // use, same, auth
+ empty_ignores, // ignore_hosts
+ },
+
+ // Expected result.
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyConfig::ProxyRules(), // proxy_rules
+ "", // proxy_bypass_list
+ false, // bypass_local_names
+ },
+
+ {
+ TEST_DESC("Auto detect"),
+ { // Input.
+ "auto", // mode
+ "", // autoconfig_url
+ "", "", "", "", // hosts
+ 0, 0, 0, 0, // ports
+ FALSE, FALSE, FALSE, // use, same, auth
+ empty_ignores, // ignore_hosts
+ },
+
+ // Expected result.
+ true, // auto_detect
+ GURL(), // pac_url
+ ProxyConfig::ProxyRules(), // proxy_rules
+ "", // proxy_bypass_list
+ false, // bypass_local_names
+ },
+
+ {
+ TEST_DESC("Valid PAC url"),
+ { // Input.
+ "auto", // mode
+ "http://wpad/wpad.dat", // autoconfig_url
+ "", "", "", "", // hosts
+ 0, 0, 0, 0, // ports
+ FALSE, FALSE, FALSE, // use, same, auth
+ empty_ignores, // ignore_hosts
+ },
+
+ // Expected result.
+ false, // auto_detect
+ GURL("http://wpad/wpad.dat"), // pac_url
+ ProxyConfig::ProxyRules(), // proxy_rules
+ "", // proxy_bypass_list
+ false, // bypass_local_names
+ },
+
+ {
+ TEST_DESC("Invalid PAC url"),
+ { // Input.
+ "auto", // mode
+ "wpad.dat", // autoconfig_url
+ "", "", "", "", // hosts
+ 0, 0, 0, 0, // ports
+ FALSE, FALSE, FALSE, // use, same, auth
+ empty_ignores, // ignore_hosts
+ },
+
+ // Expected result.
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyConfig::ProxyRules(), // proxy_rules
+ "", // proxy_bypass_list
+ false, // bypass_local_names
+ },
+
+ {
+ TEST_DESC("Single-host in proxy list"),
+ { // Input.
+ "manual", // mode
+ "", // autoconfig_url
+ "www.google.com", "", "", "", // hosts
+ 80, 0, 0, 0, // ports
+ TRUE, TRUE, FALSE, // use, same, auth
+ empty_ignores, // ignore_hosts
+ },
+
+ // Expected result.
+ false, // auto_detect
+ GURL(), // pac_url
+ MakeSingleProxyRules("www.google.com"), // proxy_rules
+ "", // proxy_bypass_list
+ false, // bypass_local_names
+ },
+
+ {
+ TEST_DESC("use_http_proxy is honored"),
+ { // Input.
+ "manual", // mode
+ "", // autoconfig_url
+ "www.google.com", "", "", "", // hosts
+ 80, 0, 0, 0, // ports
+ FALSE, TRUE, FALSE, // use, same, auth
+ empty_ignores, // ignore_hosts
+ },
+
+ // Expected result.
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyConfig::ProxyRules(), // proxy_rules
+ "", // proxy_bypass_list
+ false, // bypass_local_names
+ },
+
+ {
+ TEST_DESC("use_http_proxy and use_same_proxy are optional"),
+ { // Input.
+ "manual", // mode
+ "", // autoconfig_url
+ "www.google.com", "", "", "", // hosts
+ 80, 0, 0, 0, // ports
+ UNSET, UNSET, FALSE, // use, same, auth
+ empty_ignores, // ignore_hosts
+ },
+
+ // Expected result.
+ false, // auto_detect
+ GURL(), // pac_url
+ MakeProxyPerSchemeRules("www.google.com", // proxy_rules
+ "", ""),
+ "", // proxy_bypass_list
+ false, // bypass_local_names
+ },
+
+ {
+ TEST_DESC("Single-host, different port"),
+ { // Input.
+ "manual", // mode
+ "", // autoconfig_url
+ "www.google.com", "", "", "", // hosts
+ 88, 0, 0, 0, // ports
+ TRUE, TRUE, FALSE, // use, same, auth
+ empty_ignores, // ignore_hosts
+ },
+
+ // Expected result.
+ false, // auto_detect
+ GURL(), // pac_aurl
+ MakeSingleProxyRules("www.google.com:88"), // proxy_rules
+ "", // proxy_bypass_list
+ false, // bypass_local_names
+ },
+
+ {
+ TEST_DESC("Per-scheme proxy rules"),
+ { // Input.
+ "manual", // mode
+ "", // autoconfig_url
+ "www.google.com", // http_host
+ "www.foo.com", // secure_host
+ "ftpfoo.com", // ftp
+ "", // socks
+ 88, 110, 121, 0, // ports
+ TRUE, FALSE, FALSE, // use, same, auth
+ empty_ignores, // ignore_hosts
+ },
+
+ // Expected result.
+ false, // auto_detect
+ GURL(), // pac_url
+ MakeProxyPerSchemeRules("www.google.com:88", // proxy_rules
+ "www.foo.com:110",
+ "ftpfoo.com:121"),
+ "", // proxy_bypass_list
+ false, // bypass_local_names
+ },
+
+ {
+ TEST_DESC("socks"),
+ { // Input.
+ "manual", // mode
+ "", // autoconfig_url
+ "", "", "", "socks.com", // hosts
+ 0, 0, 0, 99, // ports
+ TRUE, FALSE, FALSE, // use, same, auth
+ empty_ignores, // ignore_hosts
+ },
+
+ // Expected result.
+ false, // auto_detect
+ GURL(), // pac_url
+ MakeSingleProxyRules("socks4://socks.com:99"), // proxy_rules
+ "", // proxy_bypass_list
+ false, // bypass_local_names
+ },
+
+ {
+ TEST_DESC("Bypass *.google.com"),
+ { // Input.
+ "manual", // mode
+ "", // autoconfig_url
+ "www.google.com", "", "", "", // hosts
+ 80, 0, 0, 0, // ports
+ TRUE, TRUE, FALSE, // use, same, auth
+ google_ignores, // ignore_hosts
+ },
+
+ false, // auto_detect
+ GURL(), // pac_url
+ MakeSingleProxyRules("www.google.com"), // proxy_rules
+ "*.google.com\n", // proxy_bypass_list
+ false, // bypass_local_names
+ },
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
+ SCOPED_TRACE(StringPrintf("Test[%d] %s", i, tests[i].description.c_str()));
+ ProxyConfig config;
+ gconf_getter->values = tests[i].values;
+ service.GetProxyConfig(&config);
+
+ // TODO(sdoyon): Add a description field to each test, and a
+ // corresponding message to the EXPECT statements, so that it is
+ // possible to identify which one of the tests failed.
+ EXPECT_EQ(tests[i].auto_detect, config.auto_detect);
+ EXPECT_EQ(tests[i].pac_url, config.pac_url);
+ EXPECT_EQ(tests[i].proxy_bypass_list,
+ FlattenProxyBypass(config.proxy_bypass));
+ EXPECT_EQ(tests[i].bypass_local_names, config.proxy_bypass_local_names);
+ EXPECT_EQ(tests[i].proxy_rules, config.proxy_rules);
+ }
+}
+
+TEST(ProxyConfigServiceLinuxTest, BasicEnvTest) {
+ MockEnvironmentVariableGetter* env_getter =
+ new MockEnvironmentVariableGetter;
+ MockGConfSettingGetter* gconf_getter = new MockGConfSettingGetter;
+ ProxyConfigServiceLinux service(env_getter, gconf_getter);
+
+ // Inspired from proxy_config_service_win_unittest.cc.
+ const struct {
+ // Short description to identify the test
+ std::string description;
+
+ // Input.
+ EnvVarValues values;
+
+ // Expected outputs (fields of the ProxyConfig).
+ bool auto_detect;
+ GURL pac_url;
+ ProxyConfig::ProxyRules proxy_rules;
+ const char* proxy_bypass_list; // newline separated
+ bool bypass_local_names;
+ } tests[] = {
+ {
+ TEST_DESC("No proxying"),
+ { // Input.
+ NULL, NULL, // *DESKTOP*
+ NULL, // auto_proxy
+ NULL, // all_proxy
+ NULL, NULL, NULL, // per-proto proxies
+ NULL, NULL, // SOCKS
+ "*", // no_proxy
+ },
+
+ // Expected result.
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyConfig::ProxyRules(), // proxy_rules
+ "", // proxy_bypass_list
+ false, // bypass_local_names
+ },
+
+ {
+ TEST_DESC("Auto detect"),
+ { // Input.
+ NULL, NULL, // *DESKTOP*
+ "", // auto_proxy
+ NULL, // all_proxy
+ NULL, NULL, NULL, // per-proto proxies
+ NULL, NULL, // SOCKS
+ NULL, // no_proxy
+ },
+
+ // Expected result.
+ true, // auto_detect
+ GURL(), // pac_url
+ ProxyConfig::ProxyRules(), // proxy_rules
+ "", // proxy_bypass_list
+ false, // bypass_local_names
+ },
+
+ {
+ TEST_DESC("Valid PAC url"),
+ { // Input.
+ NULL, NULL, // *DESKTOP*
+ "http://wpad/wpad.dat", // auto_proxy
+ NULL, // all_proxy
+ NULL, NULL, NULL, // per-proto proxies
+ NULL, NULL, // SOCKS
+ NULL, // no_proxy
+ },
+
+ // Expected result.
+ false, // auto_detect
+ GURL("http://wpad/wpad.dat"), // pac_url
+ ProxyConfig::ProxyRules(), // proxy_rules
+ "", // proxy_bypass_list
+ false, // bypass_local_names
+ },
+
+ {
+ TEST_DESC("Invalid PAC url"),
+ { // Input.
+ NULL, NULL, // *DESKTOP*
+ "wpad.dat", // auto_proxy
+ NULL, // all_proxy
+ NULL, NULL, NULL, // per-proto proxies
+ NULL, NULL, // SOCKS
+ NULL, // no_proxy
+ },
+
+ // Expected result.
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyConfig::ProxyRules(), // proxy_rules
+ "", // proxy_bypass_list
+ false, // bypass_local_names
+ },
+
+ {
+ TEST_DESC("Single-host in proxy list"),
+ { // Input.
+ NULL, NULL, // *DESKTOP*
+ NULL, // auto_proxy
+ "www.google.com", // all_proxy
+ NULL, NULL, NULL, // per-proto proxies
+ NULL, NULL, // SOCKS
+ NULL, // no_proxy
+ },
+
+ // Expected result.
+ false, // auto_detect
+ GURL(), // pac_url
+ MakeSingleProxyRules("www.google.com"), // proxy_rules
+ "", // proxy_bypass_list
+ false, // bypass_local_names
+ },
+
+ {
+ TEST_DESC("Single-host, different port"),
+ { // Input.
+ NULL, NULL, // *DESKTOP*
+ NULL, // auto_proxy
+ "www.google.com:99", // all_proxy
+ NULL, NULL, NULL, // per-proto proxies
+ NULL, NULL, // SOCKS
+ NULL, // no_proxy
+ },
+
+ // Expected result.
+ false, // auto_detect
+ GURL(), // pac_url
+ MakeSingleProxyRules("www.google.com:99"), // proxy_rules
+ "", // proxy_bypass_list
+ false, // bypass_local_names
+ },
+
+ {
+ TEST_DESC("Tolerate a scheme"),
+ { // Input.
+ NULL, NULL, // *DESKTOP*
+ NULL, // auto_proxy
+ "http://www.google.com:99", // all_proxy
+ NULL, NULL, NULL, // per-proto proxies
+ NULL, NULL, // SOCKS
+ NULL, // no_proxy
+ },
+
+ // Expected result.
+ false, // auto_detect
+ GURL(), // pac_url
+ MakeSingleProxyRules("www.google.com:99"), // proxy_rules
+ "", // proxy_bypass_list
+ false, // bypass_local_names
+ },
+
+ {
+ TEST_DESC("Per-scheme proxy rules"),
+ { // Input.
+ NULL, NULL, // *DESKTOP*
+ NULL, // auto_proxy
+ NULL, // all_proxy
+ "www.google.com:80", "www.foo.com:110", "ftpfoo.com:121", // per-proto
+ NULL, NULL, // SOCKS
+ NULL, // no_proxy
+ },
+
+ // Expected result.
+ false, // auto_detect
+ GURL(), // pac_url
+ MakeProxyPerSchemeRules("www.google.com", "www.foo.com:110",
+ "ftpfoo.com:121"),
+ "", // proxy_bypass_list
+ false, // bypass_local_names
+ },
+
+ {
+ TEST_DESC("socks"),
+ { // Input.
+ NULL, NULL, // *DESKTOP*
+ NULL, // auto_proxy
+ "", // all_proxy
+ NULL, NULL, NULL, // per-proto proxies
+ "socks.com:888", NULL, // SOCKS
+ NULL, // no_proxy
+ },
+
+ // Expected result.
+ false, // auto_detect
+ GURL(), // pac_url
+ MakeSingleProxyRules("socks4://socks.com:888"), // proxy_rules
+ "", // proxy_bypass_list
+ false, // bypass_local_names
+ },
+
+ {
+ TEST_DESC("socks5"),
+ { // Input.
+ NULL, NULL, // *DESKTOP*
+ NULL, // auto_proxy
+ "", // all_proxy
+ NULL, NULL, NULL, // per-proto proxies
+ "socks.com:888", "5", // SOCKS
+ NULL, // no_proxy
+ },
+
+ // Expected result.
+ false, // auto_detect
+ GURL(), // pac_url
+ MakeSingleProxyRules("socks5://socks.com:888"), // proxy_rules
+ "", // proxy_bypass_list
+ false, // bypass_local_names
+ },
+
+ {
+ TEST_DESC("socks default port"),
+ { // Input.
+ NULL, NULL, // *DESKTOP*
+ NULL, // auto_proxy
+ "", // all_proxy
+ NULL, NULL, NULL, // per-proto proxies
+ "socks.com", NULL, // SOCKS
+ NULL, // no_proxy
+ },
+
+ // Expected result.
+ false, // auto_detect
+ GURL(), // pac_url
+ MakeSingleProxyRules("socks4://socks.com"), // proxy_rules
+ "", // proxy_bypass_list
+ false, // bypass_local_names
+ },
+
+ {
+ TEST_DESC("bypass"),
+ { // Input.
+ NULL, NULL, // *DESKTOP*
+ NULL, // auto_proxy
+ "www.google.com", // all_proxy
+ NULL, NULL, NULL, // per-proto
+ NULL, NULL, // SOCKS
+ ".google.com, foo.com:99, 1.2.3.4:22, 127.0.0.1/8", // no_proxy
+ },
+
+ false, // auto_detect
+ GURL(), // pac_url
+ MakeSingleProxyRules("www.google.com"), // proxy_rules
+ // proxy_bypass_list
+ "*.google.com\n*foo.com:99\n1.2.3.4:22\n127.0.0.1/8\n",
+ false, // bypass_local_names
+ },
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
+ SCOPED_TRACE(StringPrintf("Test[%d] %s", i, tests[i].description.c_str()));
+ ProxyConfig config;
+ env_getter->values = tests[i].values;
+ service.GetProxyConfig(&config);
+
+ EXPECT_EQ(tests[i].auto_detect, config.auto_detect);
+ EXPECT_EQ(tests[i].pac_url, config.pac_url);
+ EXPECT_EQ(tests[i].proxy_bypass_list,
+ FlattenProxyBypass(config.proxy_bypass));
+ EXPECT_EQ(tests[i].bypass_local_names, config.proxy_bypass_local_names);
+ EXPECT_EQ(tests[i].proxy_rules, config.proxy_rules);
+ }
+}
+
+} // namespace net
diff --git a/net/proxy/proxy_config_service_win.h b/net/proxy/proxy_config_service_win.h
index 76f8ced..6217a9e 100644
--- a/net/proxy/proxy_config_service_win.h
+++ b/net/proxy/proxy_config_service_win.h
@@ -17,7 +17,7 @@ namespace net {
// settings.
class ProxyConfigServiceWin : public ProxyConfigService {
public:
- // ProxyConfigService methods.
+ // ProxyConfigService methods:
virtual int GetProxyConfig(ProxyConfig* config);
private:
diff --git a/net/proxy/proxy_config_service_win_unittest.cc b/net/proxy/proxy_config_service_win_unittest.cc
index f77ac16..c08b1a9 100644
--- a/net/proxy/proxy_config_service_win_unittest.cc
+++ b/net/proxy/proxy_config_service_win_unittest.cc
@@ -6,38 +6,11 @@
#include "net/base/net_errors.h"
#include "net/proxy/proxy_config.h"
+#include "net/proxy/proxy_config_service_common_unittest.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
-static ProxyConfig::ProxyRules MakeProxyRules(
- ProxyConfig::ProxyRules::Type type,
- const char* single_proxy,
- const char* proxy_for_http,
- const char* proxy_for_https,
- const char* proxy_for_ftp) {
- ProxyConfig::ProxyRules rules;
- rules.type = type;
- rules.single_proxy = ProxyServer::FromURI(single_proxy);
- rules.proxy_for_http = ProxyServer::FromURI(proxy_for_http);
- rules.proxy_for_https = ProxyServer::FromURI(proxy_for_https);
- rules.proxy_for_ftp = ProxyServer::FromURI(proxy_for_ftp);
- return rules;
-}
-
-static ProxyConfig::ProxyRules MakeSingleProxyRules(const char* single_proxy) {
- return MakeProxyRules(ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY,
- single_proxy, "", "", "");
-}
-
-static ProxyConfig::ProxyRules MakeProxyPerSchemeRules(
- const char* proxy_http,
- const char* proxy_https,
- const char* proxy_ftp) {
- return MakeProxyRules(ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME,
- "", proxy_http, proxy_https, proxy_ftp);
-}
-
TEST(ProxyConfigServiceWinTest, SetFromIEConfig) {
const struct {
// Input.
@@ -193,18 +166,10 @@ TEST(ProxyConfigServiceWinTest, SetFromIEConfig) {
EXPECT_EQ(tests[i].auto_detect, config.auto_detect);
EXPECT_EQ(tests[i].pac_url, config.pac_url);
-
- // Join the proxy bypass list using "\n" to make it into a single string.
- std::string flattened_proxy_bypass;
- typedef std::vector<std::string> BypassList;
- for (BypassList::const_iterator it = config.proxy_bypass.begin();
- it != config.proxy_bypass.end(); ++it) {
- flattened_proxy_bypass += *it + "\n";
- }
-
- EXPECT_EQ(tests[i].proxy_bypass_list, flattened_proxy_bypass);
+ EXPECT_EQ(tests[i].proxy_bypass_list,
+ FlattenProxyBypass(config.proxy_bypass));
EXPECT_EQ(tests[i].bypass_local_names, config.proxy_bypass_local_names);
- EXPECT_TRUE(tests[i].proxy_rules == config.proxy_rules);
+ EXPECT_EQ(tests[i].proxy_rules, config.proxy_rules);
}
}
diff --git a/net/proxy/proxy_service.cc b/net/proxy/proxy_service.cc
index c8e8a7a..bc4378e 100644
--- a/net/proxy/proxy_service.cc
+++ b/net/proxy/proxy_service.cc
@@ -18,6 +18,8 @@
#include "net/proxy/proxy_resolver_winhttp.h"
#elif defined(OS_MACOSX)
#include "net/proxy/proxy_resolver_mac.h"
+#elif defined(OS_LINUX)
+#include "net/proxy/proxy_config_service_linux.h"
#endif
#include "net/proxy/proxy_resolver.h"
#include "net/proxy/proxy_resolver_v8.h"
@@ -203,12 +205,10 @@ ProxyService* ProxyService::Create(const ProxyInfo* pi) {
#elif defined(OS_MACOSX)
return new ProxyService(new ProxyConfigServiceMac(),
new ProxyResolverMac());
+#elif defined(OS_LINUX)
+ // On Linux we use the V8Resolver, no fallback implementation.
+ return CreateNull();
#else
- // TODO(port): implement ProxyConfigServiceLinux as well as make use of
- // ProxyResolverV8 once it's implemented.
- // See:
- // - http://code.google.com/p/chromium/issues/detail?id=8143
- // - http://code.google.com/p/chromium/issues/detail?id=2764
return CreateNull();
#endif
}
@@ -228,9 +228,9 @@ ProxyService* ProxyService::CreateUsingV8Resolver(
config_service = new ProxyConfigServiceWin();
#elif defined(OS_MACOSX)
config_service = new ProxyConfigServiceMac();
+#elif defined(OS_LINUX)
+ config_service = new ProxyConfigServiceLinux();
#else
- // TODO(port): implement ProxyConfigServiceLinux.
- // See: http://code.google.com/p/chromium/issues/detail?id=8143
return CreateNull();
#endif