diff options
author | sdoyon@chromium.org <sdoyon@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-20 16:50:56 +0000 |
---|---|---|
committer | sdoyon@chromium.org <sdoyon@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-20 16:50:56 +0000 |
commit | 861c6c6d56add9157e04aa2ac43bec8e33bf1cc1 (patch) | |
tree | 835dd6466912cd073e3ac16177252fe0c8865c93 /net | |
parent | bc1529bbafc42d96d9ac0839bcb0ba1ab9328445 (diff) | |
download | chromium_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.gyp | 14 | ||||
-rw-r--r-- | net/proxy/proxy_config.cc | 5 | ||||
-rw-r--r-- | net/proxy/proxy_config.h | 7 | ||||
-rw-r--r-- | net/proxy/proxy_config_service_common_unittest.cc | 51 | ||||
-rw-r--r-- | net/proxy/proxy_config_service_common_unittest.h | 38 | ||||
-rw-r--r-- | net/proxy/proxy_config_service_linux.cc | 556 | ||||
-rw-r--r-- | net/proxy/proxy_config_service_linux.h | 108 | ||||
-rw-r--r-- | net/proxy/proxy_config_service_linux_unittest.cc | 736 | ||||
-rw-r--r-- | net/proxy/proxy_config_service_win.h | 2 | ||||
-rw-r--r-- | net/proxy/proxy_config_service_win_unittest.cc | 43 | ||||
-rw-r--r-- | net/proxy/proxy_service.cc | 14 |
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 |