diff options
author | pliard@chromium.org <pliard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-06 12:57:44 +0000 |
---|---|---|
committer | pliard@chromium.org <pliard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-06 12:57:44 +0000 |
commit | 45ab7abc0b8ae4d3a78637c158d98cd06b0a8f7a (patch) | |
tree | f8fc5afc301734c421d361212329cc51ce2f7afc /net/proxy | |
parent | 6d478b1e77dba59521c5eac87bad1e1f6055219d (diff) | |
download | chromium_src-45ab7abc0b8ae4d3a78637c158d98cd06b0a8f7a.zip chromium_src-45ab7abc0b8ae4d3a78637c158d98cd06b0a8f7a.tar.gz chromium_src-45ab7abc0b8ae4d3a78637c158d98cd06b0a8f7a.tar.bz2 |
Upstream Android proxy config service.
This change adds in an Android-specific system proxy provider called
ProxyConfigServiceAndroid. This is created as required from C++. In
turn ProxyConfigServiceAndroid creates a java object which provides
the link to Android's proxy changed broadcast, as well as the
functionality to read the Java system properties where the current
proxy settings are located.
Review URL: https://chromiumcodereview.appspot.com/10206014
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@140748 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/proxy')
-rw-r--r-- | net/proxy/proxy_config_service_android.cc | 334 | ||||
-rw-r--r-- | net/proxy/proxy_config_service_android.h | 81 | ||||
-rw-r--r-- | net/proxy/proxy_config_service_android_unittest.cc | 353 | ||||
-rw-r--r-- | net/proxy/proxy_service.cc | 6 |
4 files changed, 774 insertions, 0 deletions
diff --git a/net/proxy/proxy_config_service_android.cc b/net/proxy/proxy_config_service_android.cc new file mode 100644 index 0000000..0bb75b8 --- /dev/null +++ b/net/proxy/proxy_config_service_android.cc @@ -0,0 +1,334 @@ +// Copyright (c) 2012 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_android.h" + +#include <sys/system_properties.h> + +#include "base/android/jni_string.h" +#include "base/basictypes.h" +#include "base/bind.h" +#include "base/callback.h" +#include "base/compiler_specific.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "base/observer_list.h" +#include "base/sequenced_task_runner.h" +#include "base/string_tokenizer.h" +#include "base/string_util.h" +#include "googleurl/src/url_parse.h" +#include "jni/proxy_change_listener_jni.h" +#include "net/base/host_port_pair.h" +#include "net/proxy/proxy_config.h" + +using base::android::AttachCurrentThread; +using base::android::ConvertUTF8ToJavaString; +using base::android::ConvertJavaStringToUTF8; +using base::android::CheckException; +using base::android::ClearException; +using base::android::GetMethodID; +using base::android::ScopedJavaGlobalRef; + +namespace net { + +namespace { + +typedef ProxyConfigServiceAndroid::GetPropertyCallback GetPropertyCallback; + +// Returns whether the provided string was successfully converted to a port. +bool ConvertStringToPort(const std::string& port, int* output) { + url_parse::Component component(0, port.size()); + int result = url_parse::ParsePort(port.c_str(), component); + if (result == url_parse::PORT_INVALID || + result == url_parse::PORT_UNSPECIFIED) + return false; + *output = result; + return true; +} + +ProxyServer ConstructProxyServer(ProxyServer::Scheme scheme, + const std::string& proxy_host, + const std::string& proxy_port) { + DCHECK(!proxy_host.empty()); + int port_as_int = 0; + if (proxy_port.empty()) + port_as_int = ProxyServer::GetDefaultPortForScheme(scheme); + else if (!ConvertStringToPort(proxy_port, &port_as_int)) + return ProxyServer(); + DCHECK(port_as_int > 0); + return ProxyServer( + scheme, + HostPortPair(proxy_host, static_cast<uint16>(port_as_int))); +} + +ProxyServer LookupProxy(const std::string& prefix, + const GetPropertyCallback& get_property, + ProxyServer::Scheme scheme) { + DCHECK(!prefix.empty()); + std::string proxy_host = get_property.Run(prefix + ".proxyHost"); + if (!proxy_host.empty()) { + std::string proxy_port = get_property.Run(prefix + ".proxyPort"); + return ConstructProxyServer(scheme, proxy_host, proxy_port); + } + // Fall back to default proxy, if any. + proxy_host = get_property.Run("proxyHost"); + if (!proxy_host.empty()) { + std::string proxy_port = get_property.Run("proxyPort"); + return ConstructProxyServer(scheme, proxy_host, proxy_port); + } + return ProxyServer(); +} + +ProxyServer LookupSocksProxy(const GetPropertyCallback& get_property) { + std::string proxy_host = get_property.Run("socksProxyHost"); + if (!proxy_host.empty()) { + std::string proxy_port = get_property.Run("socksProxyPort"); + return ConstructProxyServer(ProxyServer::SCHEME_SOCKS5, proxy_host, + proxy_port); + } + return ProxyServer(); +} + +void AddBypassRules(const std::string& scheme, + const GetPropertyCallback& get_property, + ProxyBypassRules* bypass_rules) { + // The format of a hostname pattern is a list of hostnames that are separated + // by | and that use * as a wildcard. For example, setting the + // http.nonProxyHosts property to *.android.com|*.kernel.org will cause + // requests to http://developer.android.com to be made without a proxy. + std::string non_proxy_hosts = + get_property.Run(scheme + ".nonProxyHosts"); + if (non_proxy_hosts.empty()) + return; + StringTokenizer tokenizer(non_proxy_hosts, "|"); + while (tokenizer.GetNext()) { + std::string token = tokenizer.token(); + std::string pattern; + TrimWhitespaceASCII(token, TRIM_ALL, &pattern); + if (pattern.empty()) + continue; + // '?' is not one of the specified pattern characters above. + DCHECK_EQ(std::string::npos, pattern.find('?')); + bypass_rules->AddRuleForHostname(scheme, pattern, -1); + } +} + +// Returns true if a valid proxy was found. +bool GetProxyRules(const GetPropertyCallback& get_property, + ProxyConfig::ProxyRules* rules) { + // See libcore/luni/src/main/java/java/net/ProxySelectorImpl.java for the + // equivalent Android implementation. + rules->type = ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME; + rules->proxy_for_http = LookupProxy("http", get_property, + ProxyServer::SCHEME_HTTP); + rules->proxy_for_https = LookupProxy("https", get_property, + ProxyServer::SCHEME_HTTPS); + rules->proxy_for_ftp = LookupProxy("ftp", get_property, + ProxyServer::SCHEME_HTTP); + rules->fallback_proxy = LookupSocksProxy(get_property); + rules->bypass_rules.Clear(); + AddBypassRules("ftp", get_property, &rules->bypass_rules); + AddBypassRules("http", get_property, &rules->bypass_rules); + AddBypassRules("https", get_property, &rules->bypass_rules); + return rules->proxy_for_http.is_valid() || + rules->proxy_for_https.is_valid() || + rules->proxy_for_ftp.is_valid() || + rules->fallback_proxy.is_valid(); +}; + +void GetLatestProxyConfigInternal(const GetPropertyCallback& get_property, + ProxyConfig* config) { + if (!GetProxyRules(get_property, &config->proxy_rules())) + *config = ProxyConfig::CreateDirect(); +} + +std::string GetJavaProperty(const std::string& property) { + // Use Java System.getProperty to get configuration information. + // TODO(pliard): Conversion to/from UTF8 ok here? + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jstring> str = ConvertUTF8ToJavaString(env, property); + ScopedJavaLocalRef<jstring> result = + Java_ProxyChangeListener_getProperty(env, str.obj()); + return result.is_null() ? + std::string() : ConvertJavaStringToUTF8(env, result.obj()); +} + +} // namespace + +class ProxyConfigServiceAndroid::Delegate + : public base::RefCountedThreadSafe<Delegate> { + public: + Delegate(base::SequencedTaskRunner* network_task_runner, + base::SequencedTaskRunner* jni_task_runner, + const GetPropertyCallback& get_property_callback) + : ALLOW_THIS_IN_INITIALIZER_LIST(jni_delegate_(this)), + network_task_runner_(network_task_runner), + jni_task_runner_(jni_task_runner), + get_property_callback_(get_property_callback) { + } + + void SetupJNI() { + DCHECK(OnJNIThread()); + JNIEnv* env = AttachCurrentThread(); + if (java_proxy_change_listener_.is_null()) { + java_proxy_change_listener_.Reset( + Java_ProxyChangeListener_create( + env, base::android::GetApplicationContext())); + CHECK(!java_proxy_change_listener_.is_null()); + } + Java_ProxyChangeListener_start( + env, + java_proxy_change_listener_.obj(), + reinterpret_cast<jint>(&jni_delegate_)); + } + + void FetchInitialConfig() { + DCHECK(OnJNIThread()); + ProxyConfig proxy_config; + GetLatestProxyConfigInternal(get_property_callback_, &proxy_config); + network_task_runner_->PostTask( + FROM_HERE, + base::Bind(&Delegate::SetNewConfigOnNetworkThread, this, proxy_config)); + } + + void Shutdown() { + if (OnJNIThread()) { + ShutdownOnJNIThread(); + } else { + jni_task_runner_->PostTask( + FROM_HERE, + base::Bind(&Delegate::ShutdownOnJNIThread, this)); + } + } + + // Called only on the network thread. + void AddObserver(Observer* observer) { + DCHECK(OnNetworkThread()); + observers_.AddObserver(observer); + } + + void RemoveObserver(Observer* observer) { + DCHECK(OnNetworkThread()); + observers_.RemoveObserver(observer); + } + + ConfigAvailability GetLatestProxyConfig(ProxyConfig* config) { + DCHECK(OnNetworkThread()); + if (!config) + return ProxyConfigService::CONFIG_UNSET; + *config = proxy_config_; + return ProxyConfigService::CONFIG_VALID; + } + + // Called on the JNI thread. + void ProxySettingsChanged() { + DCHECK(OnJNIThread()); + ProxyConfig proxy_config; + GetLatestProxyConfigInternal(get_property_callback_, &proxy_config); + network_task_runner_->PostTask( + FROM_HERE, + base::Bind( + &Delegate::SetNewConfigOnNetworkThread, this, proxy_config)); + } + + private: + friend class base::RefCountedThreadSafe<Delegate>; + + class JNIDelegateImpl : public ProxyConfigServiceAndroid::JNIDelegate { + public: + explicit JNIDelegateImpl(Delegate* delegate) : delegate_(delegate) {} + + // ProxyConfigServiceAndroid::JNIDelegate overrides. + virtual void ProxySettingsChanged(JNIEnv*, jobject) OVERRIDE { + delegate_->ProxySettingsChanged(); + } + + private: + Delegate* const delegate_; + }; + + virtual ~Delegate() {} + + void ShutdownOnJNIThread() { + if (java_proxy_change_listener_.is_null()) + return; + JNIEnv* env = AttachCurrentThread(); + Java_ProxyChangeListener_stop(env, java_proxy_change_listener_.obj()); + } + + // Called on the network thread. + void SetNewConfigOnNetworkThread(const ProxyConfig& proxy_config) { + DCHECK(OnNetworkThread()); + proxy_config_ = proxy_config; + FOR_EACH_OBSERVER(Observer, observers_, + OnProxyConfigChanged(proxy_config, + ProxyConfigService::CONFIG_VALID)); + } + + bool OnJNIThread() const { + return jni_task_runner_->RunsTasksOnCurrentThread(); + } + + bool OnNetworkThread() const { + return network_task_runner_->RunsTasksOnCurrentThread(); + } + + ScopedJavaGlobalRef<jobject> java_proxy_change_listener_; + + JNIDelegateImpl jni_delegate_; + ObserverList<Observer> observers_; + scoped_refptr<base::SequencedTaskRunner> network_task_runner_; + scoped_refptr<base::SequencedTaskRunner> jni_task_runner_; + GetPropertyCallback get_property_callback_; + ProxyConfig proxy_config_; + + DISALLOW_COPY_AND_ASSIGN(Delegate); +}; + +ProxyConfigServiceAndroid::ProxyConfigServiceAndroid( + base::SequencedTaskRunner* network_task_runner, + base::SequencedTaskRunner* jni_task_runner) + : delegate_(new Delegate( + network_task_runner, jni_task_runner, base::Bind(&GetJavaProperty))) { + delegate_->SetupJNI(); + delegate_->FetchInitialConfig(); +} + +ProxyConfigServiceAndroid::~ProxyConfigServiceAndroid() { + delegate_->Shutdown(); +} + +// static +bool ProxyConfigServiceAndroid::Register(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +void ProxyConfigServiceAndroid::AddObserver(Observer* observer) { + delegate_->AddObserver(observer); +} + +void ProxyConfigServiceAndroid::RemoveObserver(Observer* observer) { + delegate_->RemoveObserver(observer); +} + +ProxyConfigService::ConfigAvailability +ProxyConfigServiceAndroid::GetLatestProxyConfig(ProxyConfig* config) { + return delegate_->GetLatestProxyConfig(config); +} + +ProxyConfigServiceAndroid::ProxyConfigServiceAndroid( + base::SequencedTaskRunner* network_task_runner, + base::SequencedTaskRunner* jni_task_runner, + GetPropertyCallback get_property_callback) + : delegate_(new Delegate( + network_task_runner, jni_task_runner, get_property_callback)) { + delegate_->FetchInitialConfig(); +} + +void ProxyConfigServiceAndroid::ProxySettingsChanged() { + delegate_->ProxySettingsChanged(); +} + +} // namespace net diff --git a/net/proxy/proxy_config_service_android.h b/net/proxy/proxy_config_service_android.h new file mode 100644 index 0000000..30ebd11 --- /dev/null +++ b/net/proxy/proxy_config_service_android.h @@ -0,0 +1,81 @@ +// Copyright (c) 2012 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_ANDROID_H_ +#define NET_PROXY_PROXY_CONFIG_SERVICE_ANDROID_H_ +#pragma once + +#include <string> + +#include "base/android/jni_android.h" +#include "base/basictypes.h" +#include "base/callback_forward.h" +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "net/base/net_export.h" +#include "net/proxy/proxy_config_service.h" + +namespace base { +class SequencedTaskRunner; +} + +namespace net { + +class ProxyConfig; + +class NET_EXPORT ProxyConfigServiceAndroid : public ProxyConfigService { + public: + // Callback that returns the value of the property identified by the provided + // key. If it was not found, an empty string is returned. Note that this + // interface does not let you distinguish an empty property from a + // non-existing property. This callback is invoked on the JNI thread. + typedef base::Callback<std::string (const std::string& property)> + GetPropertyCallback; + + // Separate class whose instance is owned by the Delegate class implemented in + // the .cc file. + class JNIDelegate { + public: + virtual ~JNIDelegate() {} + + // Called from Java (on JNI thread) to signal that the proxy settings have + // changed. + virtual void ProxySettingsChanged(JNIEnv*, jobject) = 0; + }; + + ProxyConfigServiceAndroid(base::SequencedTaskRunner* network_task_runner, + base::SequencedTaskRunner* jni_task_runner); + + virtual ~ProxyConfigServiceAndroid(); + + // Register JNI bindings. + static bool Register(JNIEnv* env); + + // ProxyConfigService: + // Called only on the network thread. + virtual void AddObserver(Observer* observer) OVERRIDE; + virtual void RemoveObserver(Observer* observer) OVERRIDE; + virtual ConfigAvailability GetLatestProxyConfig(ProxyConfig* config) OVERRIDE; + + private: + friend class ProxyConfigServiceAndroidTestBase; + class Delegate; + + + // For tests. + ProxyConfigServiceAndroid(base::SequencedTaskRunner* network_task_runner, + base::SequencedTaskRunner* jni_task_runner, + GetPropertyCallback get_property_callback); + + // For tests. + void ProxySettingsChanged(); + + scoped_refptr<Delegate> delegate_; + + DISALLOW_COPY_AND_ASSIGN(ProxyConfigServiceAndroid); +}; + +} // namespace net + +#endif // NET_PROXY_PROXY_CONFIG_SERVICE_ANDROID_H_ diff --git a/net/proxy/proxy_config_service_android_unittest.cc b/net/proxy/proxy_config_service_android_unittest.cc new file mode 100644 index 0000000..84449f0 --- /dev/null +++ b/net/proxy/proxy_config_service_android_unittest.cc @@ -0,0 +1,353 @@ +// Copyright (c) 2012 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 "base/bind.h" +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" +#include "net/proxy/proxy_config.h" +#include "net/proxy/proxy_config_service_android.h" +#include "net/proxy/proxy_info.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { + +namespace { + +class TestObserver : public ProxyConfigService::Observer { + public: + TestObserver() : availability_(ProxyConfigService::CONFIG_UNSET) {} + + // ProxyConfigService::Observer: + virtual void OnProxyConfigChanged( + const ProxyConfig& config, + ProxyConfigService::ConfigAvailability availability) OVERRIDE { + config_ = config; + availability_ = availability; + } + + ProxyConfigService::ConfigAvailability availability() const { + return availability_; + } + + const ProxyConfig& config() const { + return config_; + } + + private: + ProxyConfig config_; + ProxyConfigService::ConfigAvailability availability_; +}; + +} // namespace + +typedef std::map<std::string, std::string> StringMap; + +class ProxyConfigServiceAndroidTestBase : public testing::Test { + protected: + // Note that the current thread's message loop is initialized by the test + // suite (see net/base/net_test_suite.cc). + ProxyConfigServiceAndroidTestBase(const StringMap& initial_configuration) + : configuration_(initial_configuration), + message_loop_(MessageLoop::current()), + service_( + message_loop_->message_loop_proxy(), + message_loop_->message_loop_proxy(), + base::Bind(&ProxyConfigServiceAndroidTestBase::GetProperty, + base::Unretained(this))) {} + + virtual ~ProxyConfigServiceAndroidTestBase() {} + + // testing::Test: + virtual void SetUp() OVERRIDE { + message_loop_->RunAllPending(); + service_.AddObserver(&observer_); + } + + virtual void TearDown() OVERRIDE { + service_.RemoveObserver(&observer_); + } + + void ClearConfiguration() { + configuration_.clear(); + } + + void AddProperty(const std::string& key, const std::string& value) { + configuration_[key] = value; + } + + std::string GetProperty(const std::string& key) { + StringMap::const_iterator it = configuration_.find(key); + if (it == configuration_.end()) + return std::string(); + return it->second; + } + + void ProxySettingsChanged() { + service_.ProxySettingsChanged(); + message_loop_->RunAllPending(); + } + + void TestMapping(const std::string& url, const std::string& expected) { + ProxyConfigService::ConfigAvailability availability; + ProxyConfig proxy_config; + availability = service_.GetLatestProxyConfig(&proxy_config); + EXPECT_EQ(ProxyConfigService::CONFIG_VALID, availability); + ProxyInfo proxy_info; + proxy_config.proxy_rules().Apply(GURL(url), &proxy_info); + EXPECT_EQ(expected, proxy_info.ToPacString()); + } + + StringMap configuration_; + TestObserver observer_; + MessageLoop* const message_loop_; + ProxyConfigServiceAndroid service_; +}; + +class ProxyConfigServiceAndroidTest : public ProxyConfigServiceAndroidTestBase { + public: + ProxyConfigServiceAndroidTest() + : ProxyConfigServiceAndroidTestBase(StringMap()) {} +}; + +class ProxyConfigServiceAndroidWithInitialConfigTest + : public ProxyConfigServiceAndroidTestBase { + public: + ProxyConfigServiceAndroidWithInitialConfigTest() + : ProxyConfigServiceAndroidTestBase(MakeInitialConfiguration()) {} + + private: + StringMap MakeInitialConfiguration() { + StringMap initial_configuration; + initial_configuration["http.proxyHost"] = "httpproxy.com"; + initial_configuration["http.proxyPort"] = "8080"; + return initial_configuration; + } +}; + +TEST_F(ProxyConfigServiceAndroidTest, TestChangePropertiesNotification) { + // Set up a non-empty configuration + AddProperty("http.proxyHost", "localhost"); + ProxySettingsChanged(); + EXPECT_EQ(ProxyConfigService::CONFIG_VALID, observer_.availability()); + EXPECT_FALSE(observer_.config().proxy_rules().empty()); + + // Set up an empty configuration + ClearConfiguration(); + ProxySettingsChanged(); + EXPECT_EQ(ProxyConfigService::CONFIG_VALID, observer_.availability()); + EXPECT_TRUE(observer_.config().proxy_rules().empty()); +} + +TEST_F(ProxyConfigServiceAndroidWithInitialConfigTest, TestInitialConfig) { + // Make sure that the initial config is set. + TestMapping("ftp://example.com/", "DIRECT"); + TestMapping("http://example.com/", "PROXY httpproxy.com:8080"); + + // Override the initial configuration. + ClearConfiguration(); + AddProperty("http.proxyHost", "httpproxy.com"); + ProxySettingsChanged(); + TestMapping("http://example.com/", "PROXY httpproxy.com:80"); +} + +// !! The following test cases are automatically generated from +// !! net/android/tools/proxy_test_cases.py. +// !! Please edit that file instead of editing the test cases below and +// !! update also the corresponding Java unit tests in +// !! AndroidProxySelectorTest.java + +TEST_F(ProxyConfigServiceAndroidTest, NoProxy) { + // Test direct mapping when no proxy defined. + ProxySettingsChanged(); + TestMapping("ftp://example.com/", "DIRECT"); + TestMapping("http://example.com/", "DIRECT"); + TestMapping("https://example.com/", "DIRECT"); +} + +TEST_F(ProxyConfigServiceAndroidTest, HttpProxyHostAndPort) { + // Test http.proxyHost and http.proxyPort works. + AddProperty("http.proxyHost", "httpproxy.com"); + AddProperty("http.proxyPort", "8080"); + ProxySettingsChanged(); + TestMapping("ftp://example.com/", "DIRECT"); + TestMapping("http://example.com/", "PROXY httpproxy.com:8080"); + TestMapping("https://example.com/", "DIRECT"); +} + +TEST_F(ProxyConfigServiceAndroidTest, HttpProxyHostOnly) { + // We should get the default port (80) for proxied hosts. + AddProperty("http.proxyHost", "httpproxy.com"); + ProxySettingsChanged(); + TestMapping("ftp://example.com/", "DIRECT"); + TestMapping("http://example.com/", "PROXY httpproxy.com:80"); + TestMapping("https://example.com/", "DIRECT"); +} + +TEST_F(ProxyConfigServiceAndroidTest, HttpProxyPortOnly) { + // http.proxyPort only should not result in any hosts being proxied. + AddProperty("http.proxyPort", "8080"); + ProxySettingsChanged(); + TestMapping("ftp://example.com/", "DIRECT"); + TestMapping("http://example.com/", "DIRECT"); + TestMapping("https://example.com/", "DIRECT"); +} + +TEST_F(ProxyConfigServiceAndroidTest, HttpNonProxyHosts1) { + // Test that HTTP non proxy hosts are mapped correctly + AddProperty("http.nonProxyHosts", "slashdot.org"); + AddProperty("http.proxyHost", "httpproxy.com"); + AddProperty("http.proxyPort", "8080"); + ProxySettingsChanged(); + TestMapping("http://example.com/", "PROXY httpproxy.com:8080"); + TestMapping("http://slashdot.org/", "DIRECT"); +} + +TEST_F(ProxyConfigServiceAndroidTest, HttpNonProxyHosts2) { + // Test that | pattern works. + AddProperty("http.nonProxyHosts", "slashdot.org|freecode.net"); + AddProperty("http.proxyHost", "httpproxy.com"); + AddProperty("http.proxyPort", "8080"); + ProxySettingsChanged(); + TestMapping("http://example.com/", "PROXY httpproxy.com:8080"); + TestMapping("http://freecode.net/", "DIRECT"); + TestMapping("http://slashdot.org/", "DIRECT"); +} + +TEST_F(ProxyConfigServiceAndroidTest, HttpNonProxyHosts3) { + // Test that * pattern works. + AddProperty("http.nonProxyHosts", "*example.com"); + AddProperty("http.proxyHost", "httpproxy.com"); + AddProperty("http.proxyPort", "8080"); + ProxySettingsChanged(); + TestMapping("http://example.com/", "DIRECT"); + TestMapping("http://slashdot.org/", "PROXY httpproxy.com:8080"); + TestMapping("http://www.example.com/", "DIRECT"); +} + +TEST_F(ProxyConfigServiceAndroidTest, FtpNonProxyHosts) { + // Test that FTP non proxy hosts are mapped correctly + AddProperty("ftp.nonProxyHosts", "slashdot.org"); + AddProperty("ftp.proxyHost", "httpproxy.com"); + AddProperty("ftp.proxyPort", "8080"); + ProxySettingsChanged(); + TestMapping("ftp://example.com/", "PROXY httpproxy.com:8080"); + TestMapping("http://example.com/", "DIRECT"); +} + +TEST_F(ProxyConfigServiceAndroidTest, FtpProxyHostAndPort) { + // Test ftp.proxyHost and ftp.proxyPort works. + AddProperty("ftp.proxyHost", "httpproxy.com"); + AddProperty("ftp.proxyPort", "8080"); + ProxySettingsChanged(); + TestMapping("ftp://example.com/", "PROXY httpproxy.com:8080"); + TestMapping("http://example.com/", "DIRECT"); + TestMapping("https://example.com/", "DIRECT"); +} + +TEST_F(ProxyConfigServiceAndroidTest, FtpProxyHostOnly) { + // Test ftp.proxyHost and default port. + AddProperty("ftp.proxyHost", "httpproxy.com"); + ProxySettingsChanged(); + TestMapping("ftp://example.com/", "PROXY httpproxy.com:80"); + TestMapping("http://example.com/", "DIRECT"); + TestMapping("https://example.com/", "DIRECT"); +} + +TEST_F(ProxyConfigServiceAndroidTest, HttpsProxyHostAndPort) { + // Test https.proxyHost and https.proxyPort works. + AddProperty("https.proxyHost", "httpproxy.com"); + AddProperty("https.proxyPort", "8080"); + ProxySettingsChanged(); + TestMapping("ftp://example.com/", "DIRECT"); + TestMapping("http://example.com/", "DIRECT"); + TestMapping("https://example.com/", "HTTPS httpproxy.com:8080"); +} + +TEST_F(ProxyConfigServiceAndroidTest, HttpsProxyHostOnly) { + // Test https.proxyHost and default port. + AddProperty("https.proxyHost", "httpproxy.com"); + ProxySettingsChanged(); + TestMapping("ftp://example.com/", "DIRECT"); + TestMapping("http://example.com/", "DIRECT"); + TestMapping("https://example.com/", "HTTPS httpproxy.com:443"); +} + +TEST_F(ProxyConfigServiceAndroidTest, HttpProxyHostIPv6) { + // Test IPv6 https.proxyHost and default port. + AddProperty("http.proxyHost", "a:b:c::d:1"); + ProxySettingsChanged(); + TestMapping("ftp://example.com/", "DIRECT"); + TestMapping("http://example.com/", "PROXY [a:b:c::d:1]:80"); +} + +TEST_F(ProxyConfigServiceAndroidTest, HttpProxyHostAndPortIPv6) { + // Test IPv6 http.proxyHost and http.proxyPort works. + AddProperty("http.proxyHost", "a:b:c::d:1"); + AddProperty("http.proxyPort", "8080"); + ProxySettingsChanged(); + TestMapping("ftp://example.com/", "DIRECT"); + TestMapping("http://example.com/", "PROXY [a:b:c::d:1]:8080"); +} + +TEST_F(ProxyConfigServiceAndroidTest, HttpProxyHostAndInvalidPort) { + // Test invalid http.proxyPort does not crash. + AddProperty("http.proxyHost", "a:b:c::d:1"); + AddProperty("http.proxyPort", "65536"); + ProxySettingsChanged(); + TestMapping("ftp://example.com/", "DIRECT"); + TestMapping("http://example.com/", "DIRECT"); +} + +TEST_F(ProxyConfigServiceAndroidTest, DefaultProxyExplictPort) { + // Default http proxy is used if a scheme-specific one is not found. + AddProperty("ftp.proxyHost", "httpproxy.com"); + AddProperty("ftp.proxyPort", "8080"); + AddProperty("proxyHost", "defaultproxy.com"); + AddProperty("proxyPort", "8080"); + ProxySettingsChanged(); + TestMapping("ftp://example.com/", "PROXY httpproxy.com:8080"); + TestMapping("http://example.com/", "PROXY defaultproxy.com:8080"); + TestMapping("https://example.com/", "HTTPS defaultproxy.com:8080"); +} + +TEST_F(ProxyConfigServiceAndroidTest, DefaultProxyDefaultPort) { + // Check that the default proxy port is as expected. + AddProperty("proxyHost", "defaultproxy.com"); + ProxySettingsChanged(); + TestMapping("http://example.com/", "PROXY defaultproxy.com:80"); + TestMapping("https://example.com/", "HTTPS defaultproxy.com:443"); +} + +TEST_F(ProxyConfigServiceAndroidTest, FallbackToSocks) { + // SOCKS proxy is used if scheme-specific one is not found. + AddProperty("http.proxyHost", "defaultproxy.com"); + AddProperty("socksProxyHost", "socksproxy.com"); + ProxySettingsChanged(); + TestMapping("ftp://example.com", "SOCKS5 socksproxy.com:1080"); + TestMapping("http://example.com/", "PROXY defaultproxy.com:80"); + TestMapping("https://example.com/", "SOCKS5 socksproxy.com:1080"); +} + +TEST_F(ProxyConfigServiceAndroidTest, SocksExplicitPort) { + // SOCKS proxy port is used if specified + AddProperty("socksProxyHost", "socksproxy.com"); + AddProperty("socksProxyPort", "9000"); + ProxySettingsChanged(); + TestMapping("http://example.com/", "SOCKS5 socksproxy.com:9000"); +} + +TEST_F(ProxyConfigServiceAndroidTest, HttpProxySupercedesSocks) { + // SOCKS proxy is ignored if default HTTP proxy defined. + AddProperty("proxyHost", "defaultproxy.com"); + AddProperty("socksProxyHost", "socksproxy.com"); + AddProperty("socksProxyPort", "9000"); + ProxySettingsChanged(); + TestMapping("http://example.com/", "PROXY defaultproxy.com:80"); +} + +} // namespace net diff --git a/net/proxy/proxy_service.cc b/net/proxy/proxy_service.cc index 3cd82a5..35ccfbd 100644 --- a/net/proxy/proxy_service.cc +++ b/net/proxy/proxy_service.cc @@ -41,6 +41,8 @@ #include "net/proxy/proxy_resolver_mac.h" #elif defined(OS_LINUX) && !defined(OS_CHROMEOS) #include "net/proxy/proxy_config_service_linux.h" +#elif defined(OS_ANDROID) +#include "net/proxy/proxy_config_service_android.h" #endif using base::TimeDelta; @@ -1453,6 +1455,10 @@ ProxyConfigService* ProxyService::CreateSystemProxyConfigService( static_cast<MessageLoopForIO*>(file_loop)); return linux_config_service; +#elif defined(OS_ANDROID) + return new ProxyConfigServiceAndroid( + io_thread_task_runner, + MessageLoopForUI::current()->message_loop_proxy()); #else LOG(WARNING) << "Failed to choose a system proxy settings fetcher " "for this platform."; |