diff options
author | mnissler@chromium.org <mnissler@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-11-24 14:44:02 +0000 |
---|---|---|
committer | mnissler@chromium.org <mnissler@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-11-24 14:44:02 +0000 |
commit | cc5bfd4de6d8b8c6f33ff1f4a9b28eecf562ec9d (patch) | |
tree | bc595667061dde2db1c835dde6e7505ae257a54d /chrome/browser | |
parent | 8e0e513103bb5dc6ff104c669432c3aba4af18d3 (diff) | |
download | chromium_src-cc5bfd4de6d8b8c6f33ff1f4a9b28eecf562ec9d.zip chromium_src-cc5bfd4de6d8b8c6f33ff1f4a9b28eecf562ec9d.tar.gz chromium_src-cc5bfd4de6d8b8c6f33ff1f4a9b28eecf562ec9d.tar.bz2 |
Dynamically refresh pref-configured proxies.
BUG=63175,48930
TEST=unit tests
Review URL: http://codereview.chromium.org/5005002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@67261 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r-- | chrome/browser/net/chrome_url_request_context.cc | 91 | ||||
-rw-r--r-- | chrome/browser/net/chrome_url_request_context.h | 5 | ||||
-rw-r--r-- | chrome/browser/net/chrome_url_request_context_unittest.cc | 181 | ||||
-rw-r--r-- | chrome/browser/net/pref_proxy_config_service.cc | 222 | ||||
-rw-r--r-- | chrome/browser/net/pref_proxy_config_service.h | 127 | ||||
-rw-r--r-- | chrome/browser/net/pref_proxy_config_service_unittest.cc | 384 | ||||
-rw-r--r-- | chrome/browser/profile.cc | 4 | ||||
-rw-r--r-- | chrome/browser/profile.h | 5 | ||||
-rw-r--r-- | chrome/browser/profile_impl.cc | 11 | ||||
-rw-r--r-- | chrome/browser/profile_impl.h | 4 |
10 files changed, 772 insertions, 262 deletions
diff --git a/chrome/browser/net/chrome_url_request_context.cc b/chrome/browser/net/chrome_url_request_context.cc index 4ec3939..bd3e9c6 100644 --- a/chrome/browser/net/chrome_url_request_context.cc +++ b/chrome/browser/net/chrome_url_request_context.cc @@ -18,6 +18,7 @@ #include "chrome/browser/net/chrome_net_log.h" #include "chrome/browser/net/sqlite_persistent_cookie_store.h" #include "chrome/browser/net/predictor_api.h" +#include "chrome/browser/net/pref_proxy_config_service.h" #include "chrome/browser/profile.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_switches.h" @@ -68,26 +69,25 @@ net::ProxyConfigService* CreateProxyConfigService(Profile* profile) { // from the UI thread. CheckCurrentlyOnMainThread(); - scoped_ptr<net::ProxyConfig> proxy_config(CreateProxyConfig( - profile->GetPrefs())); + // Create a baseline service that provides proxy configuration in case nothing + // is configured through prefs (Note: prefs include command line and + // configuration policy). + net::ProxyConfigService* base_service = NULL; - if (!proxy_config.get()) { - // Use system settings. - // TODO(port): the IO and FILE message loops are only used by Linux. Can - // that code be moved to chrome/browser instead of being in net, so that it - // can use BrowserThread instead of raw MessageLoop pointers? See bug 25354. + // TODO(port): the IO and FILE message loops are only used by Linux. Can + // that code be moved to chrome/browser instead of being in net, so that it + // can use BrowserThread instead of raw MessageLoop pointers? See bug 25354. #if defined(OS_CHROMEOS) - return new chromeos::ProxyConfigService( - profile->GetChromeOSProxyConfigServiceImpl()); + base_service = new chromeos::ProxyConfigService( + profile->GetChromeOSProxyConfigServiceImpl()); #else - return net::ProxyService::CreateSystemProxyConfigService( - g_browser_process->io_thread()->message_loop(), - g_browser_process->file_thread()->message_loop()); + base_service = net::ProxyService::CreateSystemProxyConfigService( + g_browser_process->io_thread()->message_loop(), + g_browser_process->file_thread()->message_loop()); #endif // defined(OS_CHROMEOS) - } - // Otherwise use the fixed settings from the command line. - return new net::ProxyConfigServiceFixed(*proxy_config.get()); + return new PrefProxyConfigService(profile->GetProxyConfigTracker(), + base_service); } // Create a proxy service according to the options on command line. @@ -982,64 +982,3 @@ void ChromeURLRequestContextFactory::ApplyProfileParametersToContext( context->set_browser_file_system_context(browser_file_system_context_); context->set_extension_info_map(extension_info_map_); } - -// ---------------------------------------------------------------------------- - -net::ProxyConfig* CreateProxyConfig(const PrefService* pref_service) { - // Scan for all "enable" type proxy switches. - static const char* proxy_prefs[] = { - prefs::kProxyPacUrl, - prefs::kProxyServer, - prefs::kProxyBypassList, - prefs::kProxyAutoDetect - }; - - // Check whether the preference system holds a valid proxy configuration. Note - // that preferences coming from a lower-priority source than the user settings - // are ignored. That's because chrome treats the system settings as the - // default values, which should apply if there's no explicit value forced by - // policy or the user. - bool found_enable_proxy_pref = false; - for (size_t i = 0; i < arraysize(proxy_prefs); i++) { - const PrefService::Preference* pref = - pref_service->FindPreference(proxy_prefs[i]); - DCHECK(pref); - if (pref && (!pref->IsUserModifiable() || pref->HasUserSetting())) { - found_enable_proxy_pref = true; - break; - } - } - - if (!found_enable_proxy_pref && - !pref_service->GetBoolean(prefs::kNoProxyServer)) { - return NULL; - } - - net::ProxyConfig* proxy_config = new net::ProxyConfig(); - if (pref_service->GetBoolean(prefs::kNoProxyServer)) { - // Ignore all the other proxy config preferences if the use of a proxy - // has been explicitly disabled. - return proxy_config; - } - - if (pref_service->HasPrefPath(prefs::kProxyServer)) { - std::string proxy_server = pref_service->GetString(prefs::kProxyServer); - proxy_config->proxy_rules().ParseFromString(proxy_server); - } - - if (pref_service->HasPrefPath(prefs::kProxyPacUrl)) { - std::string proxy_pac = pref_service->GetString(prefs::kProxyPacUrl); - proxy_config->set_pac_url(GURL(proxy_pac)); - } - - proxy_config->set_auto_detect(pref_service->GetBoolean( - prefs::kProxyAutoDetect)); - - if (pref_service->HasPrefPath(prefs::kProxyBypassList)) { - std::string proxy_bypass = - pref_service->GetString(prefs::kProxyBypassList); - proxy_config->proxy_rules().bypass_rules.ParseFromString(proxy_bypass); - } - - return proxy_config; -} diff --git a/chrome/browser/net/chrome_url_request_context.h b/chrome/browser/net/chrome_url_request_context.h index d7c08c0..8f2c852 100644 --- a/chrome/browser/net/chrome_url_request_context.h +++ b/chrome/browser/net/chrome_url_request_context.h @@ -388,9 +388,4 @@ class ChromeURLRequestContextFactory { DISALLOW_COPY_AND_ASSIGN(ChromeURLRequestContextFactory); }; -// Creates a proxy configuration from proxy-related preferences fetched -// from |pref_service|. The relevant preferences in |pref_service| are -// initialized from the process' command line or by applicable proxy policies. -net::ProxyConfig* CreateProxyConfig(const PrefService* pref_service); - #endif // CHROME_BROWSER_NET_CHROME_URL_REQUEST_CONTEXT_H_ diff --git a/chrome/browser/net/chrome_url_request_context_unittest.cc b/chrome/browser/net/chrome_url_request_context_unittest.cc deleted file mode 100644 index 5faaf44..0000000 --- a/chrome/browser/net/chrome_url_request_context_unittest.cc +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright (c) 2006-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 "chrome/browser/net/chrome_url_request_context.h" - -#include "base/command_line.h" -#include "base/format_macros.h" -#include "chrome/browser/policy/configuration_policy_pref_store.h" -#include "chrome/browser/prefs/command_line_pref_store.h" -#include "chrome/browser/prefs/default_pref_store.h" -#include "chrome/browser/prefs/pref_value_store.h" -#include "chrome/common/chrome_switches.h" -#include "chrome/test/testing_pref_service.h" -#include "net/proxy/proxy_config.h" -#include "net/proxy/proxy_config_service_common_unittest.h" -#include "testing/gtest/include/gtest/gtest.h" - -// Builds an identifier for each test in an array. -#define TEST_DESC(desc) StringPrintf("at line %d <%s>", __LINE__, desc) - -TEST(ChromeURLRequestContextTest, CreateProxyConfigTest) { - FilePath unused_path(FILE_PATH_LITERAL("foo.exe")); - // Build the input command lines here. - CommandLine empty(unused_path); - CommandLine no_proxy(unused_path); - no_proxy.AppendSwitch(switches::kNoProxyServer); - CommandLine no_proxy_extra_params(unused_path); - no_proxy_extra_params.AppendSwitch(switches::kNoProxyServer); - no_proxy_extra_params.AppendSwitchASCII(switches::kProxyServer, - "http://proxy:8888"); - CommandLine single_proxy(unused_path); - single_proxy.AppendSwitchASCII(switches::kProxyServer, "http://proxy:8888"); - CommandLine per_scheme_proxy(unused_path); - per_scheme_proxy.AppendSwitchASCII(switches::kProxyServer, - "http=httpproxy:8888;ftp=ftpproxy:8889"); - CommandLine per_scheme_proxy_bypass(unused_path); - per_scheme_proxy_bypass.AppendSwitchASCII( - switches::kProxyServer, - "http=httpproxy:8888;ftp=ftpproxy:8889"); - per_scheme_proxy_bypass.AppendSwitchASCII( - switches::kProxyBypassList, - ".google.com, foo.com:99, 1.2.3.4:22, 127.0.0.1/8"); - CommandLine with_pac_url(unused_path); - with_pac_url.AppendSwitchASCII(switches::kProxyPacUrl, "http://wpad/wpad.dat"); - with_pac_url.AppendSwitchASCII( - switches::kProxyBypassList, - ".google.com, foo.com:99, 1.2.3.4:22, 127.0.0.1/8"); - CommandLine with_auto_detect(unused_path); - with_auto_detect.AppendSwitch(switches::kProxyAutoDetect); - - // Inspired from proxy_config_service_win_unittest.cc. - const struct { - // Short description to identify the test - std::string description; - - // The command line to build a ProxyConfig from. - const CommandLine& command_line; - - // Expected outputs (fields of the ProxyConfig). - bool is_null; - bool auto_detect; - GURL pac_url; - net::ProxyRulesExpectation proxy_rules; - } tests[] = { - { - TEST_DESC("Empty command line"), - // Input - empty, - // Expected result - true, // is_null - false, // auto_detect - GURL(), // pac_url - net::ProxyRulesExpectation::Empty(), - }, - { - TEST_DESC("No proxy"), - // Input - no_proxy, - // Expected result - false, // is_null - false, // auto_detect - GURL(), // pac_url - net::ProxyRulesExpectation::Empty(), - }, - { - TEST_DESC("No proxy with extra parameters."), - // Input - no_proxy_extra_params, - // Expected result - false, // is_null - false, // auto_detect - GURL(), // pac_url - net::ProxyRulesExpectation::Empty(), - }, - { - TEST_DESC("Single proxy."), - // Input - single_proxy, - // Expected result - false, // is_null - false, // auto_detect - GURL(), // pac_url - net::ProxyRulesExpectation::Single( - "proxy:8888", // single proxy - ""), // bypass rules - }, - { - TEST_DESC("Per scheme proxy."), - // Input - per_scheme_proxy, - // Expected result - false, // is_null - false, // auto_detect - GURL(), // pac_url - net::ProxyRulesExpectation::PerScheme( - "httpproxy:8888", // http - "", // https - "ftpproxy:8889", // ftp - ""), // bypass rules - }, - { - TEST_DESC("Per scheme proxy with bypass URLs."), - // Input - per_scheme_proxy_bypass, - // Expected result - false, // is_null - false, // auto_detect - GURL(), // pac_url - net::ProxyRulesExpectation::PerScheme( - "httpproxy:8888", // http - "", // https - "ftpproxy:8889", // ftp - "*.google.com,foo.com:99,1.2.3.4:22,127.0.0.1/8"), - }, - { - TEST_DESC("Pac URL with proxy bypass URLs"), - // Input - with_pac_url, - // Expected result - false, // is_null - false, // auto_detect - GURL("http://wpad/wpad.dat"), // pac_url - net::ProxyRulesExpectation::EmptyWithBypass( - "*.google.com,foo.com:99,1.2.3.4:22,127.0.0.1/8"), - }, - { - TEST_DESC("Autodetect"), - // Input - with_auto_detect, - // Expected result - false, // is_null - true, // auto_detect - GURL(), // pac_url - net::ProxyRulesExpectation::Empty(), - } - }; - - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); i++) { - SCOPED_TRACE(StringPrintf("Test[%" PRIuS "] %s", i, - tests[i].description.c_str())); - CommandLine command_line(tests[i].command_line); - // Only configuration-policy and default prefs are needed. - PrefService prefs(new TestingPrefService::TestingPrefValueStore( - new policy::ConfigurationPolicyPrefStore(NULL), - new policy::ConfigurationPolicyPrefStore(NULL), NULL, - new CommandLinePrefStore(&command_line), NULL, NULL, - new DefaultPrefStore())); - ChromeURLRequestContextGetter::RegisterUserPrefs(&prefs); - scoped_ptr<net::ProxyConfig> config(CreateProxyConfig(&prefs)); - - if (tests[i].is_null) { - EXPECT_TRUE(config == NULL); - } else { - EXPECT_TRUE(config != NULL); - EXPECT_EQ(tests[i].auto_detect, config->auto_detect()); - EXPECT_EQ(tests[i].pac_url, config->pac_url()); - EXPECT_TRUE(tests[i].proxy_rules.Matches(config->proxy_rules())); - } - } -} diff --git a/chrome/browser/net/pref_proxy_config_service.cc b/chrome/browser/net/pref_proxy_config_service.cc new file mode 100644 index 0000000..5cd6213 --- /dev/null +++ b/chrome/browser/net/pref_proxy_config_service.cc @@ -0,0 +1,222 @@ +// Copyright (c) 2010 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 "chrome/browser/net/pref_proxy_config_service.h" + +#include "chrome/browser/browser_thread.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/prefs/pref_set_observer.h" +#include "chrome/common/notification_details.h" +#include "chrome/common/notification_source.h" +#include "chrome/common/notification_type.h" +#include "chrome/common/pref_names.h" + +PrefProxyConfigTracker::PrefProxyConfigTracker(PrefService* pref_service) + : pref_service_(pref_service) { + valid_ = ReadPrefConfig(&pref_config_); + proxy_prefs_observer_.reset( + PrefSetObserver::CreateProxyPrefSetObserver(pref_service_, this)); +} + +PrefProxyConfigTracker::~PrefProxyConfigTracker() { + DCHECK(pref_service_ == NULL); +} + +bool PrefProxyConfigTracker::GetProxyConfig(net::ProxyConfig* config) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + if (valid_) + *config = pref_config_; + return valid_; +} + +void PrefProxyConfigTracker::DetachFromPrefService() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + // Stop notifications. + proxy_prefs_observer_.reset(); + pref_service_ = NULL; +} + +void PrefProxyConfigTracker::AddObserver( + PrefProxyConfigTracker::ObserverInterface* observer) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + observers_.AddObserver(observer); +} + +void PrefProxyConfigTracker::RemoveObserver( + PrefProxyConfigTracker::ObserverInterface* observer) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + observers_.RemoveObserver(observer); +} + +void PrefProxyConfigTracker::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (type == NotificationType::PREF_CHANGED && + Source<PrefService>(source).ptr() == pref_service_) { + net::ProxyConfig new_config; + bool valid = ReadPrefConfig(&new_config); + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + NewRunnableMethod(this, + &PrefProxyConfigTracker::InstallProxyConfig, + new_config, valid)); + } else { + NOTREACHED() << "Unexpected notification of type " << type.value; + } +} + +void PrefProxyConfigTracker::InstallProxyConfig(const net::ProxyConfig& config, + bool valid) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + if (valid_ != valid || !pref_config_.Equals(config)) { + valid_ = valid; + if (valid_) + pref_config_ = config; + FOR_EACH_OBSERVER(ObserverInterface, observers_, + OnPrefProxyConfigChanged()); + } +} + +bool PrefProxyConfigTracker::ReadPrefConfig(net::ProxyConfig* config) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + // Clear the configuration. + *config = net::ProxyConfig(); + + // Scan for all "enable" type proxy switches. + static const char* proxy_prefs[] = { + prefs::kProxyPacUrl, + prefs::kProxyServer, + prefs::kProxyBypassList, + prefs::kProxyAutoDetect + }; + + // Check whether the preference system holds a valid proxy configuration. Note + // that preferences coming from a lower-priority source than the user settings + // are ignored. That's because chrome treats the system settings as the + // default values, which should apply if there's no explicit value forced by + // policy or the user. + bool found_enable_proxy_pref = false; + for (size_t i = 0; i < arraysize(proxy_prefs); i++) { + const PrefService::Preference* pref = + pref_service_->FindPreference(proxy_prefs[i]); + DCHECK(pref); + if (pref && (!pref->IsUserModifiable() || pref->HasUserSetting())) { + found_enable_proxy_pref = true; + break; + } + } + + if (!found_enable_proxy_pref && + !pref_service_->GetBoolean(prefs::kNoProxyServer)) { + return false; + } + + if (pref_service_->GetBoolean(prefs::kNoProxyServer)) { + // Ignore all the other proxy config preferences if the use of a proxy + // has been explicitly disabled. + return true; + } + + if (pref_service_->HasPrefPath(prefs::kProxyServer)) { + std::string proxy_server = pref_service_->GetString(prefs::kProxyServer); + config->proxy_rules().ParseFromString(proxy_server); + } + + if (pref_service_->HasPrefPath(prefs::kProxyPacUrl)) { + std::string proxy_pac = pref_service_->GetString(prefs::kProxyPacUrl); + config->set_pac_url(GURL(proxy_pac)); + } + + config->set_auto_detect(pref_service_->GetBoolean(prefs::kProxyAutoDetect)); + + if (pref_service_->HasPrefPath(prefs::kProxyBypassList)) { + std::string proxy_bypass = + pref_service_->GetString(prefs::kProxyBypassList); + config->proxy_rules().bypass_rules.ParseFromString(proxy_bypass); + } + + return true; +} + +PrefProxyConfigService::PrefProxyConfigService( + PrefProxyConfigTracker* tracker, + net::ProxyConfigService* base_service) + : base_service_(base_service), + pref_config_tracker_(tracker), + registered_observers_(false) { +} + +PrefProxyConfigService::~PrefProxyConfigService() { + if (registered_observers_) { + base_service_->RemoveObserver(this); + pref_config_tracker_->RemoveObserver(this); + } +} + +void PrefProxyConfigService::AddObserver( + net::ProxyConfigService::Observer* observer) { + RegisterObservers(); + observers_.AddObserver(observer); +} + +void PrefProxyConfigService::RemoveObserver( + net::ProxyConfigService::Observer* observer) { + observers_.RemoveObserver(observer); +} + +bool PrefProxyConfigService::GetLatestProxyConfig(net::ProxyConfig* config) { + RegisterObservers(); + const net::ProxyConfig pref_config; + if (pref_config_tracker_->GetProxyConfig(config)) + return true; + + return base_service_->GetLatestProxyConfig(config); +} + +void PrefProxyConfigService::OnLazyPoll() { + base_service_->OnLazyPoll(); +} + +void PrefProxyConfigService::OnProxyConfigChanged( + const net::ProxyConfig& config) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + // Check whether there is a proxy configuration defined by preferences. In + // this case that proxy configuration takes precedence and the change event + // from the delegate proxy service can be disregarded. + net::ProxyConfig pref_config; + if (!pref_config_tracker_->GetProxyConfig(&pref_config)) { + FOR_EACH_OBSERVER(net::ProxyConfigService::Observer, observers_, + OnProxyConfigChanged(config)); + } +} + +void PrefProxyConfigService::OnPrefProxyConfigChanged() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + // Evaluate the proxy configuration. If GetLatestProxyConfig returns false, + // we are using the system proxy service, but it doesn't have a valid + // configuration yet. Once it is ready, OnProxyConfigChanged() will be called + // and broadcast the proxy configuration. + // Note: If a switch between a preference proxy configuration and the system + // proxy configuration occurs an unnecessary notification might get send if + // the two configurations agree. This case should be rare however, so we don't + // handle that case specially. + net::ProxyConfig new_config; + if (GetLatestProxyConfig(&new_config)) { + FOR_EACH_OBSERVER(net::ProxyConfigService::Observer, observers_, + OnProxyConfigChanged(new_config)); + } +} + +void PrefProxyConfigService::RegisterObservers() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + if (!registered_observers_) { + base_service_->AddObserver(this); + pref_config_tracker_->AddObserver(this); + registered_observers_ = true; + } +} diff --git a/chrome/browser/net/pref_proxy_config_service.h b/chrome/browser/net/pref_proxy_config_service.h new file mode 100644 index 0000000..362b109 --- /dev/null +++ b/chrome/browser/net/pref_proxy_config_service.h @@ -0,0 +1,127 @@ +// Copyright (c) 2010 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 CHROME_BROWSER_NET_PREF_PROXY_CONFIG_SERVICE_H_ +#define CHROME_BROWSER_NET_PREF_PROXY_CONFIG_SERVICE_H_ +#pragma once + +#include "base/basictypes.h" +#include "base/observer_list.h" +#include "base/ref_counted.h" +#include "base/scoped_ptr.h" +#include "chrome/common/notification_observer.h" +#include "net/proxy/proxy_config.h" +#include "net/proxy/proxy_config_service.h" + +class PrefService; +class PrefSetObserver; + +// A helper class that tracks proxy preferences. It translates the configuration +// to net::ProxyConfig and proxies the result over to the IO thread for +// PrefProxyConfigService to use. +class PrefProxyConfigTracker + : public base::RefCountedThreadSafe<PrefProxyConfigTracker>, + public NotificationObserver { + public: + // Observer interface used to send out notifications on the IO thread about + // changes to the proxy configuration. + class ObserverInterface { + public: + virtual ~ObserverInterface() {} + virtual void OnPrefProxyConfigChanged() = 0; + }; + + explicit PrefProxyConfigTracker(PrefService* pref_service); + virtual ~PrefProxyConfigTracker(); + + // Observer manipulation is only valid on the IO thread. + void AddObserver(ObserverInterface* observer); + void RemoveObserver(ObserverInterface* observer); + + // Get the proxy configuration currently defined by preferences. Writes the + // configuration to |config| and returns true on success. |config| is not + // touched and false is returned if there is no configuration defined. This + // must be called on the IO thread. + bool GetProxyConfig(net::ProxyConfig* config); + + // Notifies the tracker that the pref service passed upon construction is + // about to go away. This must be called from the UI thread. + void DetachFromPrefService(); + + private: + // NotificationObserver implementation: + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + // Install a new configuration. This is invoked on the IO thread to update + // the internal state after handling a pref change on the UI thread. |valid| + // indicates whether there is a preference proxy configuration defined, in + // which case this configuration is given by |config|. |config| is ignored if + // |valid| is false. + void InstallProxyConfig(const net::ProxyConfig& config, bool valid); + + // Creates a proxy configuration from proxy-related preferences. Configuration + // is stored in |config| and the return value indicates whether the + // configuration is valid. + bool ReadPrefConfig(net::ProxyConfig* config); + + // Configuration as defined by prefs. Only to be accessed from the IO thread + // (expect for construction). + net::ProxyConfig pref_config_; + + // Whether |pref_config_| is valid. Only accessed from the IO thread. + bool valid_; + + // List of observers, accessed exclusively from the IO thread. + ObserverList<ObserverInterface, true> observers_; + + // Pref-related members that should only be accessed from the UI thread. + PrefService* pref_service_; + scoped_ptr<PrefSetObserver> proxy_prefs_observer_; + + DISALLOW_COPY_AND_ASSIGN(PrefProxyConfigTracker); +}; + +// A net::ProxyConfigService implementation that applies preference proxy +// settings as overrides to the proxy configuration determined by a baseline +// delegate ProxyConfigService. +class PrefProxyConfigService + : public net::ProxyConfigService, + public net::ProxyConfigService::Observer, + public PrefProxyConfigTracker::ObserverInterface { + public: + // Takes ownership of the passed |base_service|. + PrefProxyConfigService(PrefProxyConfigTracker* tracker, + net::ProxyConfigService* base_service); + virtual ~PrefProxyConfigService(); + + // ProxyConfigService implementation: + virtual void AddObserver(net::ProxyConfigService::Observer* observer); + virtual void RemoveObserver(net::ProxyConfigService::Observer* observer); + virtual bool GetLatestProxyConfig(net::ProxyConfig* config); + virtual void OnLazyPoll(); + + private: + // ProxyConfigService::Observer implementation: + virtual void OnProxyConfigChanged(const net::ProxyConfig& config); + + // PrefProxyConfigTracker::Observer implementation: + virtual void OnPrefProxyConfigChanged(); + + // Makes sure that the observer registrations with the base service and the + // tracker object are set up. + void RegisterObservers(); + + scoped_ptr<net::ProxyConfigService> base_service_; + ObserverList<net::ProxyConfigService::Observer, true> observers_; + scoped_refptr<PrefProxyConfigTracker> pref_config_tracker_; + + // Indicates whether the base service and tracker registrations are done. + bool registered_observers_; + + DISALLOW_COPY_AND_ASSIGN(PrefProxyConfigService); +}; + +#endif // CHROME_BROWSER_NET_PREF_PROXY_CONFIG_SERVICE_H_ diff --git a/chrome/browser/net/pref_proxy_config_service_unittest.cc b/chrome/browser/net/pref_proxy_config_service_unittest.cc new file mode 100644 index 0000000..e7917b1 --- /dev/null +++ b/chrome/browser/net/pref_proxy_config_service_unittest.cc @@ -0,0 +1,384 @@ +// Copyright (c) 2010 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 "chrome/browser/net/pref_proxy_config_service.h" + +#include "base/command_line.h" +#include "base/file_path.h" +#include "chrome/browser/net/chrome_url_request_context.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/pref_names.h" +#include "chrome/test/testing_pref_service.h" +#include "net/proxy/proxy_config_service_common_unittest.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; +using testing::Mock; + +namespace { + +const char kFixedPacUrl[] = "http://chromium.org/fixed_pac_url"; + +// Testing proxy config service that allows us to fire notifications at will. +class TestProxyConfigService : public net::ProxyConfigService { + public: + explicit TestProxyConfigService(const net::ProxyConfig& config) + : config_(config) { + } + + void SetProxyConfig(const net::ProxyConfig config) { + config_ = config; + FOR_EACH_OBSERVER(net::ProxyConfigService::Observer, observers_, + OnProxyConfigChanged(config_)); + } + + private: + virtual void AddObserver(net::ProxyConfigService::Observer* observer) { + observers_.AddObserver(observer); + } + + virtual void RemoveObserver(net::ProxyConfigService::Observer* observer) { + observers_.RemoveObserver(observer); + } + + virtual bool GetLatestProxyConfig(net::ProxyConfig* config) { + *config = config_; + return true; + } + + net::ProxyConfig config_; + ObserverList<net::ProxyConfigService::Observer, true> observers_; +}; + +// A mock observer for capturing callbacks. +class MockObserver : public net::ProxyConfigService::Observer { + public: + MOCK_METHOD1(OnProxyConfigChanged, void(const net::ProxyConfig&)); +}; + +template<typename TESTBASE> +class PrefProxyConfigServiceTestBase : public TESTBASE { + protected: + PrefProxyConfigServiceTestBase() + : ui_thread_(BrowserThread::UI, &loop_), + io_thread_(BrowserThread::IO, &loop_) {} + + virtual void SetUp() { + ASSERT_TRUE(pref_service_.get()); + ChromeURLRequestContextGetter::RegisterUserPrefs(pref_service_.get()); + fixed_config_.set_pac_url(GURL(kFixedPacUrl)); + delegate_service_ = new TestProxyConfigService(fixed_config_); + proxy_config_tracker_ = new PrefProxyConfigTracker(pref_service_.get()); + proxy_config_service_.reset( + new PrefProxyConfigService(proxy_config_tracker_.get(), + delegate_service_)); + } + + virtual void TearDown() { + proxy_config_tracker_->DetachFromPrefService(); + loop_.RunAllPending(); + proxy_config_service_.reset(); + pref_service_.reset(); + } + + MessageLoop loop_; + TestProxyConfigService* delegate_service_; // weak + scoped_ptr<TestingPrefService> pref_service_; + scoped_ptr<PrefProxyConfigService> proxy_config_service_; + net::ProxyConfig fixed_config_; + + private: + scoped_refptr<PrefProxyConfigTracker> proxy_config_tracker_; + BrowserThread ui_thread_; + BrowserThread io_thread_; +}; + +class PrefProxyConfigServiceTest + : public PrefProxyConfigServiceTestBase<testing::Test> { + protected: + virtual void SetUp() { + pref_service_.reset(new TestingPrefService); + PrefProxyConfigServiceTestBase<testing::Test>::SetUp(); + } +}; + +TEST_F(PrefProxyConfigServiceTest, BaseConfiguration) { + net::ProxyConfig actual_config; + proxy_config_service_->GetLatestProxyConfig(&actual_config); + EXPECT_EQ(GURL(kFixedPacUrl), actual_config.pac_url()); +} + +TEST_F(PrefProxyConfigServiceTest, DynamicPrefOverrides) { + pref_service_->SetManagedPref( + prefs::kProxyServer, Value::CreateStringValue("http://example.com:3128")); + loop_.RunAllPending(); + + net::ProxyConfig actual_config; + proxy_config_service_->GetLatestProxyConfig(&actual_config); + EXPECT_FALSE(actual_config.auto_detect()); + EXPECT_EQ(net::ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY, + actual_config.proxy_rules().type); + EXPECT_EQ(actual_config.proxy_rules().single_proxy, + net::ProxyServer::FromURI("http://example.com:3128", + net::ProxyServer::SCHEME_HTTP)); + + pref_service_->SetManagedPref( + prefs::kProxyAutoDetect, Value::CreateBooleanValue(true)); + loop_.RunAllPending(); + + proxy_config_service_->GetLatestProxyConfig(&actual_config); + EXPECT_TRUE(actual_config.auto_detect()); +} + +// Compares proxy configurations, but allows different identifiers. +MATCHER_P(ProxyConfigMatches, config, "") { + net::ProxyConfig reference(config); + reference.set_id(arg.id()); + return reference.Equals(arg); +} + +TEST_F(PrefProxyConfigServiceTest, Observers) { + MockObserver observer; + proxy_config_service_->AddObserver(&observer); + + // Firing the observers in the delegate should trigger a notification. + net::ProxyConfig config2; + config2.set_auto_detect(true); + EXPECT_CALL(observer, + OnProxyConfigChanged(ProxyConfigMatches(config2))).Times(1); + delegate_service_->SetProxyConfig(config2); + loop_.RunAllPending(); + Mock::VerifyAndClearExpectations(&observer); + + // Override configuration, this should trigger a notification. + net::ProxyConfig pref_config; + pref_config.set_pac_url(GURL(kFixedPacUrl)); + EXPECT_CALL(observer, + OnProxyConfigChanged(ProxyConfigMatches(pref_config))).Times(1); + pref_service_->SetManagedPref(prefs::kProxyPacUrl, + Value::CreateStringValue(kFixedPacUrl)); + loop_.RunAllPending(); + Mock::VerifyAndClearExpectations(&observer); + + // Since there are pref overrides, delegate changes should be ignored. + net::ProxyConfig config3; + config3.proxy_rules().ParseFromString("http=config3:80"); + EXPECT_CALL(observer, OnProxyConfigChanged(_)).Times(0); + fixed_config_.set_auto_detect(true); + delegate_service_->SetProxyConfig(config3); + loop_.RunAllPending(); + Mock::VerifyAndClearExpectations(&observer); + + // Clear the override should switch back to the fixed configuration. + EXPECT_CALL(observer, + OnProxyConfigChanged(ProxyConfigMatches(config3))).Times(1); + pref_service_->RemoveManagedPref(prefs::kProxyPacUrl); + loop_.RunAllPending(); + Mock::VerifyAndClearExpectations(&observer); + + // Delegate service notifications should show up again. + net::ProxyConfig config4; + config4.proxy_rules().ParseFromString("socks:config4"); + EXPECT_CALL(observer, + OnProxyConfigChanged(ProxyConfigMatches(config4))).Times(1); + delegate_service_->SetProxyConfig(config4); + loop_.RunAllPending(); + Mock::VerifyAndClearExpectations(&observer); + + proxy_config_service_->RemoveObserver(&observer); +} + +// Test parameter object for testing command line proxy configuration. +struct CommandLineTestParams { + // Explicit assignment operator, so testing::TestWithParam works with MSVC. + CommandLineTestParams& operator=(const CommandLineTestParams& other) { + description = other.description; + for (unsigned int i = 0; i < arraysize(switches); i++) + switches[i] = other.switches[i]; + is_null = other.is_null; + auto_detect = other.auto_detect; + pac_url = other.pac_url; + proxy_rules = other.proxy_rules; + return *this; + } + + // Short description to identify the test. + const char* description; + + // The command line to build a ProxyConfig from. + struct SwitchValue { + const char* name; + const char* value; + } switches[2]; + + // Expected outputs (fields of the ProxyConfig). + bool is_null; + bool auto_detect; + GURL pac_url; + net::ProxyRulesExpectation proxy_rules; +}; + +void PrintTo(const CommandLineTestParams& params, std::ostream* os) { + *os << params.description; +} + +class PrefProxyConfigServiceCommandLineTest + : public PrefProxyConfigServiceTestBase< + testing::TestWithParam<CommandLineTestParams> > { + protected: + PrefProxyConfigServiceCommandLineTest() + : command_line_(CommandLine::NO_PROGRAM) {} + + virtual void SetUp() { + for (size_t i = 0; i < arraysize(GetParam().switches); i++) { + const char* name = GetParam().switches[i].name; + const char* value = GetParam().switches[i].value; + if (name && value) + command_line_.AppendSwitchASCII(name, value); + else if (name) + command_line_.AppendSwitch(name); + } + pref_service_.reset(new TestingPrefService(NULL, NULL, &command_line_)); + PrefProxyConfigServiceTestBase< + testing::TestWithParam<CommandLineTestParams> >::SetUp(); + } + + private: + CommandLine command_line_; +}; + +TEST_P(PrefProxyConfigServiceCommandLineTest, CommandLine) { + net::ProxyConfig config; + proxy_config_service_->GetLatestProxyConfig(&config); + + if (GetParam().is_null) { + EXPECT_EQ(GURL(kFixedPacUrl), config.pac_url()); + } else { + EXPECT_NE(GURL(kFixedPacUrl), config.pac_url()); + EXPECT_EQ(GetParam().auto_detect, config.auto_detect()); + EXPECT_EQ(GetParam().pac_url, config.pac_url()); + EXPECT_TRUE(GetParam().proxy_rules.Matches(config.proxy_rules())); + } +} + +static const CommandLineTestParams kCommandLineTestParams[] = { + { + "Empty command line", + // Input + { }, + // Expected result + true, // is_null + false, // auto_detect + GURL(), // pac_url + net::ProxyRulesExpectation::Empty(), + }, + { + "No proxy", + // Input + { + { switches::kNoProxyServer, NULL }, + }, + // Expected result + false, // is_null + false, // auto_detect + GURL(), // pac_url + net::ProxyRulesExpectation::Empty(), + }, + { + "No proxy with extra parameters.", + // Input + { + { switches::kNoProxyServer, NULL }, + { switches::kProxyServer, "http://proxy:8888" }, + }, + // Expected result + false, // is_null + false, // auto_detect + GURL(), // pac_url + net::ProxyRulesExpectation::Empty(), + }, + { + "Single proxy.", + // Input + { + { switches::kProxyServer, "http://proxy:8888" }, + }, + // Expected result + false, // is_null + false, // auto_detect + GURL(), // pac_url + net::ProxyRulesExpectation::Single( + "proxy:8888", // single proxy + ""), // bypass rules + }, + { + "Per scheme proxy.", + // Input + { + { switches::kProxyServer, "http=httpproxy:8888;ftp=ftpproxy:8889" }, + }, + // Expected result + false, // is_null + false, // auto_detect + GURL(), // pac_url + net::ProxyRulesExpectation::PerScheme( + "httpproxy:8888", // http + "", // https + "ftpproxy:8889", // ftp + ""), // bypass rules + }, + { + "Per scheme proxy with bypass URLs.", + // Input + { + { switches::kProxyServer, "http=httpproxy:8888;ftp=ftpproxy:8889" }, + { switches::kProxyBypassList, + ".google.com, foo.com:99, 1.2.3.4:22, 127.0.0.1/8" }, + }, + // Expected result + false, // is_null + false, // auto_detect + GURL(), // pac_url + net::ProxyRulesExpectation::PerScheme( + "httpproxy:8888", // http + "", // https + "ftpproxy:8889", // ftp + "*.google.com,foo.com:99,1.2.3.4:22,127.0.0.1/8"), + }, + { + "Pac URL with proxy bypass URLs", + // Input + { + { switches::kProxyPacUrl, "http://wpad/wpad.dat" }, + { switches::kProxyBypassList, + ".google.com, foo.com:99, 1.2.3.4:22, 127.0.0.1/8" }, + }, + // Expected result + false, // is_null + false, // auto_detect + GURL("http://wpad/wpad.dat"), // pac_url + net::ProxyRulesExpectation::EmptyWithBypass( + "*.google.com,foo.com:99,1.2.3.4:22,127.0.0.1/8"), + }, + { + "Autodetect", + // Input + { + { switches::kProxyAutoDetect, NULL }, + }, + // Expected result + false, // is_null + true, // auto_detect + GURL(), // pac_url + net::ProxyRulesExpectation::Empty(), + }, +}; + +INSTANTIATE_TEST_CASE_P( + PrefProxyConfigServiceCommandLineTestInstance, + PrefProxyConfigServiceCommandLineTest, + testing::ValuesIn(kCommandLineTestParams)); + +} // namespace diff --git a/chrome/browser/profile.cc b/chrome/browser/profile.cc index 2b10639..9ccda97 100644 --- a/chrome/browser/profile.cc +++ b/chrome/browser/profile.cc @@ -590,6 +590,10 @@ class OffTheRecordProfileImpl : public Profile, return NULL; } + virtual PrefProxyConfigTracker* GetProxyConfigTracker() { + return profile_->GetProxyConfigTracker(); + } + private: NotificationRegistrar registrar_; diff --git a/chrome/browser/profile.h b/chrome/browser/profile.h index 57bb13d..5b5cdaf 100644 --- a/chrome/browser/profile.h +++ b/chrome/browser/profile.h @@ -72,6 +72,7 @@ class PersonalDataManager; class PinnedTabService; class PrefService; class ExtensionInfoMap; +class PrefProxyConfigTracker; class PromoCounter; class ProfileSyncService; class ProfileSyncFactory; @@ -485,6 +486,10 @@ class Profile { GetChromeOSProxyConfigServiceImpl() = 0; #endif // defined(OS_CHROMEOS) + // Returns the helper object that provides the proxy configuration service + // access to the the proxy configuration possibly defined by preferences. + virtual PrefProxyConfigTracker* GetProxyConfigTracker() = 0; + #ifdef UNIT_TEST // Use with caution. GetDefaultRequestContext may be called on any thread! static void set_default_request_context(URLRequestContextGetter* c) { diff --git a/chrome/browser/profile_impl.cc b/chrome/browser/profile_impl.cc index c66428d..8ee0b5d 100644 --- a/chrome/browser/profile_impl.cc +++ b/chrome/browser/profile_impl.cc @@ -51,6 +51,7 @@ #include "chrome/browser/net/chrome_url_request_context.h" #include "chrome/browser/net/gaia/token_service.h" #include "chrome/browser/net/net_pref_observer.h" +#include "chrome/browser/net/pref_proxy_config_service.h" #include "chrome/browser/net/ssl_config_service_manager.h" #include "chrome/browser/notifications/desktop_notification_service.h" #include "chrome/browser/password_manager/password_store_default.h" @@ -524,6 +525,9 @@ ProfileImpl::~ProfileImpl() { if (extensions_service_) extensions_service_->DestroyingProfile(); + if (pref_proxy_config_tracker_) + pref_proxy_config_tracker_->DetachFromPrefService(); + // This causes the Preferences file to be written to disk. MarkAsCleanShutdown(); } @@ -1318,3 +1322,10 @@ chromeos::ProxyConfigServiceImpl* return chromeos_proxy_config_service_impl_; } #endif // defined(OS_CHROMEOS) + +PrefProxyConfigTracker* ProfileImpl::GetProxyConfigTracker() { + if (!pref_proxy_config_tracker_) + pref_proxy_config_tracker_ = new PrefProxyConfigTracker(GetPrefs()); + + return pref_proxy_config_tracker_; +} diff --git a/chrome/browser/profile_impl.h b/chrome/browser/profile_impl.h index ea64bda..b0ea586 100644 --- a/chrome/browser/profile_impl.h +++ b/chrome/browser/profile_impl.h @@ -130,6 +130,8 @@ class ProfileImpl : public Profile, virtual chromeos::ProxyConfigServiceImpl* GetChromeOSProxyConfigServiceImpl(); #endif // defined(OS_CHROMEOS) + virtual PrefProxyConfigTracker* GetProxyConfigTracker(); + // NotificationObserver implementation. virtual void Observe(NotificationType type, const NotificationSource& source, @@ -280,6 +282,8 @@ class ProfileImpl : public Profile, chromeos_proxy_config_service_impl_; #endif + scoped_refptr<PrefProxyConfigTracker> pref_proxy_config_tracker_; + DISALLOW_COPY_AND_ASSIGN(ProfileImpl); }; |