diff options
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."; |