diff options
author | eroman@chromium.org <eroman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-23 06:02:40 +0000 |
---|---|---|
committer | eroman@chromium.org <eroman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-23 06:02:40 +0000 |
commit | 119655003d8f225282179043e990df879062e529 (patch) | |
tree | 4ee907ddfb8e308a00b5bb9b624e072b028623b6 | |
parent | dacc2c255ae3f823e4a39d975e97c067a76dacf9 (diff) | |
download | chromium_src-119655003d8f225282179043e990df879062e529.zip chromium_src-119655003d8f225282179043e990df879062e529.tar.gz chromium_src-119655003d8f225282179043e990df879062e529.tar.bz2 |
Change the ProxyConfigService interface to be asynchronous, and support observers.
The Windows implementation is still using a polling mechanism under the hood, however that polling has been moved to the worker pool so it won't block the IO thread in case WinHttpGetIEProxyConfigForCurrentUser is slow (crbug.com/12189).
BUG=12189
Review URL: http://codereview.chromium.org/3056011
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@53442 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/net/resolve_proxy_msg_helper_unittest.cc | 8 | ||||
-rw-r--r-- | net/base/net_log_event_type_list.h | 3 | ||||
-rw-r--r-- | net/net.gyp | 2 | ||||
-rw-r--r-- | net/proxy/polling_proxy_config_service.cc | 166 | ||||
-rw-r--r-- | net/proxy/polling_proxy_config_service.h | 49 | ||||
-rw-r--r-- | net/proxy/proxy_config_service.h | 37 | ||||
-rw-r--r-- | net/proxy/proxy_config_service_fixed.h | 6 | ||||
-rw-r--r-- | net/proxy/proxy_config_service_linux.cc | 27 | ||||
-rw-r--r-- | net/proxy/proxy_config_service_linux.h | 34 | ||||
-rw-r--r-- | net/proxy/proxy_config_service_linux_unittest.cc | 37 | ||||
-rw-r--r-- | net/proxy/proxy_config_service_mac.cc | 19 | ||||
-rw-r--r-- | net/proxy/proxy_config_service_mac.h | 9 | ||||
-rw-r--r-- | net/proxy/proxy_config_service_win.cc | 21 | ||||
-rw-r--r-- | net/proxy/proxy_config_service_win.h | 11 | ||||
-rw-r--r-- | net/proxy/proxy_service.cc | 224 | ||||
-rw-r--r-- | net/proxy/proxy_service.h | 51 | ||||
-rw-r--r-- | net/proxy/proxy_service_unittest.cc | 135 |
17 files changed, 534 insertions, 305 deletions
diff --git a/chrome/browser/net/resolve_proxy_msg_helper_unittest.cc b/chrome/browser/net/resolve_proxy_msg_helper_unittest.cc index c84ad41..78a16d7 100644 --- a/chrome/browser/net/resolve_proxy_msg_helper_unittest.cc +++ b/chrome/browser/net/resolve_proxy_msg_helper_unittest.cc @@ -13,9 +13,11 @@ // This ProxyConfigService always returns "http://pac" as the PAC url to use. class MockProxyConfigService : public net::ProxyConfigService { public: - virtual int GetProxyConfig(net::ProxyConfig* results) { - results->set_pac_url(GURL("http://pac")); - return net::OK; + virtual void AddObserver(Observer* observer) {} + virtual void RemoveObserver(Observer* observer) {} + virtual bool GetLatestProxyConfig(net::ProxyConfig* results) { + *results = net::ProxyConfig::CreateFromCustomPacURL(GURL("http://pac")); + return true; } }; diff --git a/net/base/net_log_event_type_list.h b/net/base/net_log_event_type_list.h index 2fcd6fa..d9225f8 100644 --- a/net/base/net_log_event_type_list.h +++ b/net/base/net_log_event_type_list.h @@ -79,9 +79,6 @@ EVENT_TYPE(PROXY_SERVICE) // are found from ProxyService::init_proxy_resolver_log(). EVENT_TYPE(PROXY_SERVICE_WAITING_FOR_INIT_PAC) -// The time taken to fetch the system proxy configuration. -EVENT_TYPE(PROXY_SERVICE_POLL_CONFIG_SERVICE_FOR_CHANGES) - // This event is emitted to show what the PAC script returned. It can contain // extra parameters that are either: // { diff --git a/net/net.gyp b/net/net.gyp index bb3149d..c89d8ed 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -413,6 +413,8 @@ 'proxy/init_proxy_resolver.h', 'proxy/multi_threaded_proxy_resolver.cc', 'proxy/multi_threaded_proxy_resolver.h', + 'proxy/polling_proxy_config_service.cc', + 'proxy/polling_proxy_config_service.h', 'proxy/proxy_bypass_rules.cc', 'proxy/proxy_bypass_rules.h', 'proxy/proxy_config.cc', diff --git a/net/proxy/polling_proxy_config_service.cc b/net/proxy/polling_proxy_config_service.cc new file mode 100644 index 0000000..ac42d9c --- /dev/null +++ b/net/proxy/polling_proxy_config_service.cc @@ -0,0 +1,166 @@ +// 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 "net/proxy/polling_proxy_config_service.h" + +#include "base/lock.h" +#include "base/message_loop_proxy.h" +#include "base/observer_list.h" +#include "base/scoped_ptr.h" +#include "base/worker_pool.h" +#include "net/proxy/proxy_config.h" + +namespace net { + +// Reference-counted wrapper that does all the work (needs to be +// reference-counted since we post tasks between threads; may outlive +// the parent PollingProxyConfigService). +class PollingProxyConfigService::Core + : public base::RefCountedThreadSafe<PollingProxyConfigService::Core> { + public: + Core(base::TimeDelta poll_interval, + GetConfigFunction get_config_func) + : get_config_func_(get_config_func), + has_config_(false), + poll_task_outstanding_(false), + poll_interval_(poll_interval), + have_initialized_origin_loop_(false) { + } + + // Called when the parent PollingProxyConfigService is destroyed + // (observers should not be called past this point). + void Orphan() { + AutoLock l(lock_); + origin_loop_proxy_ = NULL; + } + + bool GetLatestProxyConfig(ProxyConfig* config) { + LazyInitializeOriginLoop(); + DCHECK(origin_loop_proxy_->BelongsToCurrentThread()); + + OnLazyPoll(); + + // If we have already retrieved the proxy settings (on worker thread) + // then return what we last saw. + if (has_config_) { + *config = last_config_; + return true; + } + return false; + } + + void AddObserver(Observer* observer) { + LazyInitializeOriginLoop(); + DCHECK(origin_loop_proxy_->BelongsToCurrentThread()); + observers_.AddObserver(observer); + } + + void RemoveObserver(Observer* observer) { + DCHECK(origin_loop_proxy_->BelongsToCurrentThread()); + observers_.RemoveObserver(observer); + } + + // Check for a new configuration if enough time has elapsed. + void OnLazyPoll() { + LazyInitializeOriginLoop(); + DCHECK(origin_loop_proxy_->BelongsToCurrentThread()); + + if (poll_task_outstanding_) + return; // Still waiting for earlier test to finish. + + base::TimeTicks now = base::TimeTicks::Now(); + + if (last_poll_time_.is_null() || + (now - last_poll_time_) > poll_interval_) { + last_poll_time_ = now; + poll_task_outstanding_ = true; + WorkerPool::PostTask( + FROM_HERE, + NewRunnableMethod( + this, &Core::PollOnWorkerThread, get_config_func_), true); + } + } + + private: + void PollOnWorkerThread(GetConfigFunction func) { + ProxyConfig config; + func(&config); + + AutoLock l(lock_); + if (origin_loop_proxy_) { + origin_loop_proxy_->PostTask( + FROM_HERE, + NewRunnableMethod(this, &Core::GetConfigCompleted, config)); + } + } + + // Called after the worker thread has finished retrieving a configuration. + void GetConfigCompleted(const ProxyConfig& config) { + DCHECK(poll_task_outstanding_); + poll_task_outstanding_ = false; + + if (!origin_loop_proxy_) + return; // Was orphaned (parent has already been destroyed). + + DCHECK(origin_loop_proxy_->BelongsToCurrentThread()); + + if (!has_config_ || !last_config_.Equals(config)) { + // If the configuration has changed, notify the observers. + has_config_ = true; + last_config_ = config; + FOR_EACH_OBSERVER(Observer, observers_, OnProxyConfigChanged(config)); + } + } + + void LazyInitializeOriginLoop() { + // TODO(eroman): Really this should be done in the constructor, but right + // now chrome is constructing the ProxyConfigService on the + // UI thread so we can't cache the IO thread for the purpose + // of DCHECKs until the first call is made. + if (!have_initialized_origin_loop_) { + origin_loop_proxy_ = base::MessageLoopProxy::CreateForCurrentThread(); + have_initialized_origin_loop_ = true; + } + } + + GetConfigFunction get_config_func_; + ObserverList<Observer> observers_; + bool has_config_; + bool poll_task_outstanding_; + ProxyConfig last_config_; + base::TimeTicks last_poll_time_; + base::TimeDelta poll_interval_; + bool have_initialized_origin_loop_; + + Lock lock_; + scoped_refptr<base::MessageLoopProxy> origin_loop_proxy_; +}; + +PollingProxyConfigService::PollingProxyConfigService( + base::TimeDelta poll_interval, + GetConfigFunction get_config_func) + : core_(new Core(poll_interval, get_config_func)) { +} + +PollingProxyConfigService::~PollingProxyConfigService() { + core_->Orphan(); +} + +void PollingProxyConfigService::AddObserver(Observer* observer) { + core_->AddObserver(observer); +} + +void PollingProxyConfigService::RemoveObserver(Observer* observer) { + core_->RemoveObserver(observer); +} + +bool PollingProxyConfigService::GetLatestProxyConfig(ProxyConfig* config) { + return core_->GetLatestProxyConfig(config); +} + +void PollingProxyConfigService::OnLazyPoll() { + core_->OnLazyPoll(); +} + +} // namespace net diff --git a/net/proxy/polling_proxy_config_service.h b/net/proxy/polling_proxy_config_service.h new file mode 100644 index 0000000..1bf889b --- /dev/null +++ b/net/proxy/polling_proxy_config_service.h @@ -0,0 +1,49 @@ +// 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 NET_PROXY_POLLING_PROXY_CONFIG_SERVICE_H_ +#define NET_PROXY_POLLING_PROXY_CONFIG_SERVICE_H_ + +#include "base/time.h" +#include "base/ref_counted.h" +#include "net/proxy/proxy_config_service.h" + +namespace net { + +// PollingProxyConfigService is a base class for creating ProxyConfigService +// implementations that use polling to notice when settings have change. +// +// It runs code to get the current proxy settings on a background worker +// thread, and notifies registered observers when the value changes. +class PollingProxyConfigService : public ProxyConfigService { + public: + // ProxyConfigService implementation: + virtual void AddObserver(Observer* observer); + virtual void RemoveObserver(Observer* observer); + virtual bool GetLatestProxyConfig(ProxyConfig* config); + virtual void OnLazyPoll(); + + protected: + // Function for retrieving the current proxy configuration. + // Implementors must be threadsafe as the function will be invoked from + // worker threads. + typedef void (*GetConfigFunction)(ProxyConfig*); + + // Creates a polling-based ProxyConfigService which will test for new + // settings at most every |poll_interval| time by calling |get_config_func| + // on a worker thread. + PollingProxyConfigService( + base::TimeDelta poll_interval, + GetConfigFunction get_config_func); + + virtual ~PollingProxyConfigService(); + + private: + class Core; + scoped_refptr<Core> core_; +}; + +} // namespace net + +#endif // NET_PROXY_POLLING_PROXY_CONFIG_SERVICE_H_ diff --git a/net/proxy/proxy_config_service.h b/net/proxy/proxy_config_service.h index 9f6bb50..1c65e3a 100644 --- a/net/proxy/proxy_config_service.h +++ b/net/proxy/proxy_config_service.h @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// 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. @@ -9,16 +9,39 @@ namespace net { class ProxyConfig; -// Synchronously fetch the system's proxy configuration settings. Called on -// the IO Thread. +// Service for watching when the proxy settings have changed. class ProxyConfigService { public: + // Observer for being notified when the proxy settings have changed. + class Observer { + public: + virtual ~Observer() {} + virtual void OnProxyConfigChanged(const ProxyConfig& config) = 0; + }; + virtual ~ProxyConfigService() {} - // Get the proxy configuration. Returns OK if successful or an error code if - // otherwise. |config| should be in its initial state when this method is - // called. - virtual int GetProxyConfig(ProxyConfig* config) = 0; + // Adds/Removes an observer that will be called whenever the proxy + // configuration has changed. + virtual void AddObserver(Observer* observer) = 0; + virtual void RemoveObserver(Observer* observer) = 0; + + // Gets the most recent value of the proxy configuration. Returns false if + // it is not available yet. In the case where we returned false, it is + // guaranteed that subscribed observers will be notified of a change at + // some point in the future once the configuration is available. + // Note that to avoid re-entrancy problems, implementations should not + // dispatch any change notifications from within this function. + virtual bool GetLatestProxyConfig(ProxyConfig* config) = 0; + + // ProxyService will call this periodically during periods of activity. + // It can be used as a signal for polling-based implementations. + // + // Note that this is purely used as an optimization -- polling + // implementations could simply set a global timer that goes off every + // X seconds at which point they check for changes. However that has + // the disadvantage of doing continuous work even during idle periods. + virtual void OnLazyPoll() {} }; } // namespace net diff --git a/net/proxy/proxy_config_service_fixed.h b/net/proxy/proxy_config_service_fixed.h index b677eb4..c8641b8 100644 --- a/net/proxy/proxy_config_service_fixed.h +++ b/net/proxy/proxy_config_service_fixed.h @@ -17,9 +17,11 @@ class ProxyConfigServiceFixed : public ProxyConfigService { explicit ProxyConfigServiceFixed(const ProxyConfig& pc) : pc_(pc) {} // ProxyConfigService methods: - virtual int GetProxyConfig(ProxyConfig* config) { + virtual void AddObserver(Observer* observer) {} + virtual void RemoveObserver(Observer* observer) {} + virtual bool GetLatestProxyConfig(ProxyConfig* config) { *config = pc_; - return OK; + return true; } private: diff --git a/net/proxy/proxy_config_service_linux.cc b/net/proxy/proxy_config_service_linux.cc index bd863bd..e33a487 100644 --- a/net/proxy/proxy_config_service_linux.cc +++ b/net/proxy/proxy_config_service_linux.cc @@ -1085,7 +1085,7 @@ void ProxyConfigServiceLinux::Delegate::SetupAndFetchInitialConfig( LOG(INFO) << "Monitoring of proxy setting changes is disabled"; // Fetch and cache the current proxy config. The config is left in - // cached_config_, where GetProxyConfig() running on the IO thread + // cached_config_, where GetLatestProxyConfig() running on the IO thread // will expect to find it. This is safe to do because we return // before this ProxyConfigServiceLinux is passed on to // the ProxyService. @@ -1133,14 +1133,30 @@ void ProxyConfigServiceLinux::Delegate::SetupAndFetchInitialConfig( } } -int ProxyConfigServiceLinux::Delegate::GetProxyConfig(ProxyConfig* config) { +void ProxyConfigServiceLinux::Delegate::AddObserver(Observer* observer) { + observers_.AddObserver(observer); +} + +void ProxyConfigServiceLinux::Delegate::RemoveObserver(Observer* observer) { + observers_.RemoveObserver(observer); +} + +bool ProxyConfigServiceLinux::Delegate::GetLatestProxyConfig( + ProxyConfig* config) { // This is called from the IO thread. DCHECK(!io_loop_ || MessageLoop::current() == io_loop_); // Simply return the last proxy configuration that glib_default_loop // notified us of. - *config = cached_config_; - return cached_config_.is_valid() ? OK : ERR_FAILED; + *config = cached_config_.is_valid() ? + cached_config_ : ProxyConfig::CreateDirect(); + + // We return true to indicate that *config was filled in. It is always + // going to be available since we initialized eagerly on the UI thread. + // TODO(eroman): do lazy initialization instead, so we no longer need + // to construct ProxyConfigServiceLinux on the UI thread. + // In which case, we may return false here. + return true; } // Depending on the GConfSettingGetter in use, this method will be called @@ -1153,7 +1169,7 @@ void ProxyConfigServiceLinux::Delegate::OnCheckProxyConfigSettings() { if (valid) new_config.set_id(1); // mark it as valid - // See if it is different than what we had before. + // See if it is different from what we had before. if (new_config.is_valid() != reference_config_.is_valid() || !new_config.Equals(reference_config_)) { // Post a task to |io_loop| with the new configuration, so it can @@ -1174,6 +1190,7 @@ void ProxyConfigServiceLinux::Delegate::SetNewProxyConfig( DCHECK(MessageLoop::current() == io_loop_); LOG(INFO) << "Proxy configuration changed"; cached_config_ = new_config; + FOR_EACH_OBSERVER(Observer, observers_, OnProxyConfigChanged(new_config)); } void ProxyConfigServiceLinux::Delegate::PostDestroyTask() { diff --git a/net/proxy/proxy_config_service_linux.h b/net/proxy/proxy_config_service_linux.h index c6e9c90..600c5cc 100644 --- a/net/proxy/proxy_config_service_linux.h +++ b/net/proxy/proxy_config_service_linux.h @@ -11,6 +11,7 @@ #include "base/basictypes.h" #include "base/env_var.h" #include "base/message_loop.h" +#include "base/observer_list.h" #include "base/ref_counted.h" #include "base/scoped_ptr.h" #include "net/proxy/proxy_config.h" @@ -90,16 +91,18 @@ class ProxyConfigServiceLinux : public ProxyConfigService { // ProxyConfigServiceLinux is created on the UI thread, and // SetupAndFetchInitialConfig() is immediately called to - // synchronously fetch the original configuration and setup gconf + // synchronously fetch the original configuration and set up gconf // notifications on the UI thread. // - // Passed that point, it is accessed periodically through - // GetProxyConfig() from the IO thread. + // Past that point, it is accessed periodically through the + // ProxyConfigService interface (GetLatestProxyConfig, AddObserver, + // RemoveObserver) from the IO thread. // // gconf change notification callbacks can occur at any time and are // run on the UI thread. The new gconf settings are fetched on the // UI thread, and the new resulting proxy config is posted to the IO - // thread through Delegate::SetNewProxyConfig(). + // thread through Delegate::SetNewProxyConfig(). We then notify + // observers on the IO thread of the configuration change. // // ProxyConfigServiceLinux is deleted from the IO thread. // @@ -138,7 +141,9 @@ class ProxyConfigServiceLinux : public ProxyConfigService { void OnCheckProxyConfigSettings(); // Called from IO thread. - int GetProxyConfig(ProxyConfig* config); + void AddObserver(Observer* observer); + void RemoveObserver(Observer* observer); + bool GetLatestProxyConfig(ProxyConfig* config); // Posts a call to OnDestroy() to the UI thread. Called from // ProxyConfigServiceLinux's destructor. @@ -180,7 +185,7 @@ class ProxyConfigServiceLinux : public ProxyConfigService { scoped_ptr<GConfSettingGetter> gconf_getter_; // Cached proxy configuration, to be returned by - // GetProxyConfig. Initially populated from the UI thread, but + // GetLatestProxyConfig. Initially populated from the UI thread, but // afterwards only accessed from the IO thread. ProxyConfig cached_config_; @@ -199,10 +204,12 @@ class ProxyConfigServiceLinux : public ProxyConfigService { // this thread. Since gconf is not thread safe, any use of gconf // must be done on the thread running this loop. MessageLoop* glib_default_loop_; - // MessageLoop for the IO thread. GetProxyConfig() is called from + // MessageLoop for the IO thread. GetLatestProxyConfig() is called from // the thread running this loop. MessageLoop* io_loop_; + ObserverList<Observer> observers_; + DISALLOW_COPY_AND_ASSIGN(Delegate); }; @@ -231,8 +238,17 @@ class ProxyConfigServiceLinux : public ProxyConfigService { // ProxyConfigService methods: // Called from IO thread. - virtual int GetProxyConfig(ProxyConfig* config) { - return delegate_->GetProxyConfig(config); + + virtual void AddObserver(Observer* observer) { + delegate_->AddObserver(observer); + } + + virtual void RemoveObserver(Observer* observer) { + delegate_->RemoveObserver(observer); + } + + virtual bool GetLatestProxyConfig(ProxyConfig* config) { + return delegate_->GetLatestProxyConfig(config); } private: diff --git a/net/proxy/proxy_config_service_linux_unittest.cc b/net/proxy/proxy_config_service_linux_unittest.cc index 0a84548..c43e207 100644 --- a/net/proxy/proxy_config_service_linux_unittest.cc +++ b/net/proxy/proxy_config_service_linux_unittest.cc @@ -239,7 +239,7 @@ class MockGConfSettingGetter } // namespace } // namespace net -// This helper class runs ProxyConfigServiceLinux::GetProxyConfig() on +// This helper class runs ProxyConfigServiceLinux::GetLatestProxyConfig() on // the IO thread and synchronously waits for the result. // Some code duplicated from proxy_script_fetcher_unittest.cc. class SynchConfigGetter { @@ -282,12 +282,12 @@ class SynchConfigGetter { static_cast<MessageLoopForIO*>(file_loop)); } // Synchronously gets the proxy config. - int SyncGetProxyConfig(net::ProxyConfig* config) { + bool SyncGetLatestProxyConfig(net::ProxyConfig* config) { io_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod( - this, &SynchConfigGetter::GetConfigOnIOThread)); + this, &SynchConfigGetter::GetLatestConfigOnIOThread)); Wait(); *config = proxy_config_; - return get_config_result_; + return get_latest_config_result_; } private: @@ -296,10 +296,11 @@ class SynchConfigGetter { event_.Signal(); } - // Calls GetProxyConfig, running on |io_thread_|] Signals |event_| + // Calls GetLatestProxyConfig, running on |io_thread_| Signals |event_| // on completion. - void GetConfigOnIOThread() { - get_config_result_ = config_service_->GetProxyConfig(&proxy_config_); + void GetLatestConfigOnIOThread() { + get_latest_config_result_ = + config_service_->GetLatestProxyConfig(&proxy_config_); event_.Signal(); } @@ -322,7 +323,7 @@ class SynchConfigGetter { // The config obtained by |io_thread_| and read back by the main // thread. net::ProxyConfig proxy_config_; - int get_config_result_; // Return value from GetProxyConfig(). + bool get_latest_config_result_; // Return value from GetLatestProxyConfig(). }; DISABLE_RUNNABLE_METHOD_REFCOUNT(SynchConfigGetter); @@ -606,7 +607,7 @@ TEST_F(ProxyConfigServiceLinuxTest, BasicGConfTest) { ProxyConfig config; gconf_getter->values = tests[i].values; sync_config_getter.SetupAndInitialFetch(); - sync_config_getter.SyncGetProxyConfig(&config); + sync_config_getter.SyncGetLatestProxyConfig(&config); EXPECT_EQ(tests[i].auto_detect, config.auto_detect()); EXPECT_EQ(tests[i].pac_url, config.pac_url()); @@ -896,7 +897,7 @@ TEST_F(ProxyConfigServiceLinuxTest, BasicEnvTest) { ProxyConfig config; env_getter->values = tests[i].values; sync_config_getter.SetupAndInitialFetch(); - sync_config_getter.SyncGetProxyConfig(&config); + sync_config_getter.SyncGetLatestProxyConfig(&config); EXPECT_EQ(tests[i].auto_detect, config.auto_detect()); EXPECT_EQ(tests[i].pac_url, config.pac_url()); @@ -915,14 +916,14 @@ TEST_F(ProxyConfigServiceLinuxTest, GconfNotification) { // Start with no proxy. gconf_getter->values.mode = "none"; sync_config_getter.SetupAndInitialFetch(); - sync_config_getter.SyncGetProxyConfig(&config); + sync_config_getter.SyncGetLatestProxyConfig(&config); EXPECT_FALSE(config.auto_detect()); // Now set to auto-detect. gconf_getter->values.mode = "auto"; // Simulate gconf notification callback. service->OnCheckProxyConfigSettings(); - sync_config_getter.SyncGetProxyConfig(&config); + sync_config_getter.SyncGetLatestProxyConfig(&config); EXPECT_TRUE(config.auto_detect()); } @@ -1298,7 +1299,7 @@ TEST_F(ProxyConfigServiceLinuxTest, KDEConfigParser) { file_util::WriteFile(kioslaverc_, tests[i].kioslaverc.c_str(), tests[i].kioslaverc.length()); sync_config_getter.SetupAndInitialFetch(); - sync_config_getter.SyncGetProxyConfig(&config); + sync_config_getter.SyncGetLatestProxyConfig(&config); EXPECT_EQ(tests[i].auto_detect, config.auto_detect()); EXPECT_EQ(tests[i].pac_url, config.pac_url()); @@ -1329,7 +1330,7 @@ TEST_F(ProxyConfigServiceLinuxTest, KDEHomePicker) { new ProxyConfigServiceLinux(env_getter)); ProxyConfig config; sync_config_getter.SetupAndInitialFetch(); - sync_config_getter.SyncGetProxyConfig(&config); + sync_config_getter.SyncGetLatestProxyConfig(&config); EXPECT_TRUE(config.auto_detect()); EXPECT_EQ(GURL(), config.pac_url()); } @@ -1348,7 +1349,7 @@ TEST_F(ProxyConfigServiceLinuxTest, KDEHomePicker) { new ProxyConfigServiceLinux(env_getter)); ProxyConfig config; sync_config_getter.SetupAndInitialFetch(); - sync_config_getter.SyncGetProxyConfig(&config); + sync_config_getter.SyncGetLatestProxyConfig(&config); EXPECT_FALSE(config.auto_detect()); EXPECT_EQ(slaverc4_pac_url, config.pac_url()); } @@ -1361,7 +1362,7 @@ TEST_F(ProxyConfigServiceLinuxTest, KDEHomePicker) { new ProxyConfigServiceLinux(env_getter)); ProxyConfig config; sync_config_getter.SetupAndInitialFetch(); - sync_config_getter.SyncGetProxyConfig(&config); + sync_config_getter.SyncGetLatestProxyConfig(&config); EXPECT_TRUE(config.auto_detect()); EXPECT_EQ(GURL(), config.pac_url()); } @@ -1375,7 +1376,7 @@ TEST_F(ProxyConfigServiceLinuxTest, KDEHomePicker) { new ProxyConfigServiceLinux(env_getter)); ProxyConfig config; sync_config_getter.SetupAndInitialFetch(); - sync_config_getter.SyncGetProxyConfig(&config); + sync_config_getter.SyncGetLatestProxyConfig(&config); EXPECT_TRUE(config.auto_detect()); EXPECT_EQ(GURL(), config.pac_url()); } @@ -1392,7 +1393,7 @@ TEST_F(ProxyConfigServiceLinuxTest, KDEHomePicker) { new ProxyConfigServiceLinux(env_getter)); ProxyConfig config; sync_config_getter.SetupAndInitialFetch(); - sync_config_getter.SyncGetProxyConfig(&config); + sync_config_getter.SyncGetLatestProxyConfig(&config); EXPECT_TRUE(config.auto_detect()); EXPECT_EQ(GURL(), config.pac_url()); } diff --git a/net/proxy/proxy_config_service_mac.cc b/net/proxy/proxy_config_service_mac.cc index 1e04ff0..27a0426 100644 --- a/net/proxy/proxy_config_service_mac.cc +++ b/net/proxy/proxy_config_service_mac.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// 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. @@ -16,8 +16,12 @@ #include "net/proxy/proxy_info.h" #include "net/proxy/proxy_server.h" +namespace net { + namespace { +const int kPollIntervalSec = 5; + // Utility function to pull out a boolean value from a dictionary and return it, // returning a default value if the key is not present. bool GetBoolFromDictionary(CFDictionaryRef dict, @@ -35,11 +39,7 @@ bool GetBoolFromDictionary(CFDictionaryRef dict, return default_value; } -} // namespace - -namespace net { - -int ProxyConfigServiceMac::GetProxyConfig(ProxyConfig* config) { +void GetCurrentProxyConfig(ProxyConfig* config) { scoped_cftyperef<CFDictionaryRef> config_dict( SCDynamicStoreCopyProxies(NULL)); DCHECK(config_dict); @@ -157,8 +157,13 @@ int ProxyConfigServiceMac::GetProxyConfig(ProxyConfig* config) { false)) { config->proxy_rules().bypass_rules.AddRuleToBypassLocal(); } +} + +} // namespace - return OK; +ProxyConfigServiceMac::ProxyConfigServiceMac() + : PollingProxyConfigService(base::TimeDelta::FromSeconds(kPollIntervalSec), + &GetCurrentProxyConfig) { } } // namespace net diff --git a/net/proxy/proxy_config_service_mac.h b/net/proxy/proxy_config_service_mac.h index 6a93228..4a62fa4 100644 --- a/net/proxy/proxy_config_service_mac.h +++ b/net/proxy/proxy_config_service_mac.h @@ -5,14 +5,15 @@ #ifndef NET_PROXY_PROXY_CONFIG_SERVICE_MAC_H_ #define NET_PROXY_PROXY_CONFIG_SERVICE_MAC_H_ -#include "net/proxy/proxy_config_service.h" +#include "net/proxy/polling_proxy_config_service.h" namespace net { -class ProxyConfigServiceMac : public ProxyConfigService { +// TODO(eroman): Use notification-based system APIs instead of polling for +// changes. +class ProxyConfigServiceMac : public PollingProxyConfigService { public: - // ProxyConfigService methods: - virtual int GetProxyConfig(ProxyConfig* config); + ProxyConfigServiceMac(); }; } // namespace net diff --git a/net/proxy/proxy_config_service_win.cc b/net/proxy/proxy_config_service_win.cc index 38819de..403d149 100644 --- a/net/proxy/proxy_config_service_win.cc +++ b/net/proxy/proxy_config_service_win.cc @@ -17,7 +17,11 @@ namespace net { -static void FreeIEConfig(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG* ie_config) { +namespace { + +const int kPollIntervalSec = 5; + +void FreeIEConfig(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG* ie_config) { if (ie_config->lpszAutoConfigUrl) GlobalFree(ie_config->lpszAutoConfigUrl); if (ie_config->lpszProxy) @@ -26,16 +30,25 @@ static void FreeIEConfig(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG* ie_config) { GlobalFree(ie_config->lpszProxyBypass); } -int ProxyConfigServiceWin::GetProxyConfig(ProxyConfig* config) { +} // namespace + +ProxyConfigServiceWin::ProxyConfigServiceWin() + : PollingProxyConfigService( + base::TimeDelta::FromSeconds(kPollIntervalSec), + &ProxyConfigServiceWin::GetCurrentProxyConfig) { +} + +// static +void ProxyConfigServiceWin::GetCurrentProxyConfig(ProxyConfig* config) { WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ie_config = {0}; if (!WinHttpGetIEProxyConfigForCurrentUser(&ie_config)) { LOG(ERROR) << "WinHttpGetIEProxyConfigForCurrentUser failed: " << GetLastError(); - return ERR_FAILED; // TODO(darin): Bug 1189288: translate error code. + *config = ProxyConfig::CreateDirect(); + return; } SetFromIEConfig(config, ie_config); FreeIEConfig(&ie_config); - return OK; } // static diff --git a/net/proxy/proxy_config_service_win.h b/net/proxy/proxy_config_service_win.h index 6217a9e..fee68cc 100644 --- a/net/proxy/proxy_config_service_win.h +++ b/net/proxy/proxy_config_service_win.h @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// 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. @@ -8,21 +8,22 @@ #include <windows.h> #include <winhttp.h> -#include "net/proxy/proxy_config_service.h" +#include "net/proxy/polling_proxy_config_service.h" #include "testing/gtest/include/gtest/gtest_prod.h" namespace net { // Implementation of ProxyConfigService that retrieves the system proxy // settings. -class ProxyConfigServiceWin : public ProxyConfigService { +class ProxyConfigServiceWin : public PollingProxyConfigService { public: - // ProxyConfigService methods: - virtual int GetProxyConfig(ProxyConfig* config); + ProxyConfigServiceWin(); private: FRIEND_TEST(ProxyConfigServiceWinTest, SetFromIEConfig); + static void GetCurrentProxyConfig(ProxyConfig* config); + // Set |config| using the proxy configuration values of |ie_config|. static void SetFromIEConfig( ProxyConfig* config, diff --git a/net/proxy/proxy_service.cc b/net/proxy/proxy_service.cc index 8f9d28c..d13b1f1 100644 --- a/net/proxy/proxy_service.cc +++ b/net/proxy/proxy_service.cc @@ -43,12 +43,15 @@ namespace net { static const size_t kMaxNumNetLogEntries = 100; static const size_t kDefaultNumPacThreads = 4; -// Config getter that fails every time. -class ProxyConfigServiceNull : public ProxyConfigService { +// Config getter that always returns direct settings. +class ProxyConfigServiceDirect : public ProxyConfigService { public: // ProxyConfigService implementation: - virtual int GetProxyConfig(ProxyConfig* config) { - return ERR_NOT_IMPLEMENTED; + virtual void AddObserver(Observer* observer) {} + virtual void RemoveObserver(Observer* observer) {} + virtual bool GetLatestProxyConfig(ProxyConfig* config) { + *config = ProxyConfig::CreateDirect(); + return true; } }; @@ -163,6 +166,8 @@ class ProxyService::PacRequest DCHECK(!was_cancelled()); DCHECK(!is_started()); + DCHECK(service_->config_.is_valid()); + config_id_ = service_->config_.id(); return resolver()->GetProxyForURL( @@ -262,14 +267,15 @@ class ProxyService::PacRequest ProxyService::ProxyService(ProxyConfigService* config_service, ProxyResolver* resolver, NetLog* net_log) - : config_service_(config_service), - resolver_(resolver), + : resolver_(resolver), next_config_id_(1), should_use_proxy_resolver_(false), ALLOW_THIS_IN_INITIALIZER_LIST(init_proxy_resolver_callback_( this, &ProxyService::OnInitProxyResolverComplete)), + current_state_(STATE_NONE) , net_log_(net_log) { NetworkChangeNotifier::AddObserver(this); + ResetConfigService(config_service); } // static @@ -319,8 +325,8 @@ ProxyService* ProxyService::CreateFixed(const ProxyConfig& pc) { // static ProxyService* ProxyService::CreateNull() { - // Use a configuration fetcher and proxy resolver which always fail. - return new ProxyService(new ProxyConfigServiceNull, new ProxyResolverNull, + // Use direct connections. + return new ProxyService(new ProxyConfigServiceDirect, new ProxyResolverNull, NULL); } @@ -333,13 +339,16 @@ int ProxyService::ResolveProxy(const GURL& raw_url, net_log.BeginEvent(NetLog::TYPE_PROXY_SERVICE, NULL); + config_service_->OnLazyPoll(); + if (current_state_ == STATE_NONE) + ApplyProxyConfigIfAvailable(); + // Strip away any reference fragments and the username/password, as they // are not relevant to proxy resolution. GURL url = SimplifyUrlForRequest(raw_url); - // Check if the request can be completed right away. This is the case when - // using a direct connection, or when the config is bad. - UpdateConfigIfOld(net_log); + // Check if the request can be completed right away. (This is the case when + // using a direct connection for example). int rv = TryToCompleteSynchronously(url, result); if (rv != ERR_IO_PENDING) return DidFinishResolvingProxy(result, rv, net_log); @@ -347,9 +356,7 @@ int ProxyService::ResolveProxy(const GURL& raw_url, scoped_refptr<PacRequest> req = new PacRequest(this, url, result, callback, net_log); - bool resolver_is_ready = !IsInitializingProxyResolver(); - - if (resolver_is_ready) { + if (current_state_ == STATE_READY) { // Start the resolve request. rv = req->Start(); if (rv != ERR_IO_PENDING) @@ -372,22 +379,24 @@ int ProxyService::ResolveProxy(const GURL& raw_url, int ProxyService::TryToCompleteSynchronously(const GURL& url, ProxyInfo* result) { - result->config_id_ = config_.id(); + DCHECK_NE(STATE_NONE, current_state_); - DCHECK(config_.id() != ProxyConfig::INVALID_ID); + if (current_state_ != STATE_READY) + return ERR_IO_PENDING; // Still initializing. - if (should_use_proxy_resolver_ || IsInitializingProxyResolver()) { - // May need to go through ProxyResolver for this. - return ERR_IO_PENDING; - } + if (should_use_proxy_resolver_) + return ERR_IO_PENDING; // Must submit the request to the proxy resolver. // Use the manual proxy settings. + DCHECK(config_.id() != ProxyConfig::INVALID_ID); config_.proxy_rules().Apply(url, result); + result->config_id_ = config_.id(); return OK; } ProxyService::~ProxyService() { NetworkChangeNotifier::RemoveObserver(this); + config_service_->RemoveObserver(this); // Cancel any inprogress requests. for (PendingRequests::iterator it = pending_requests_.begin(); @@ -395,10 +404,6 @@ ProxyService::~ProxyService() { ++it) { (*it)->Cancel(); } - - // Make sure that InitProxyResolver gets destroyed BEFORE the - // CapturingNetLog it is using is deleted. - init_proxy_resolver_.reset(); } void ProxyService::SuspendAllPendingRequests() { @@ -415,8 +420,9 @@ void ProxyService::SuspendAllPendingRequests() { } } -void ProxyService::ResumeAllPendingRequests() { - DCHECK(!IsInitializingProxyResolver()); +void ProxyService::SetReady() { + DCHECK(!init_proxy_resolver_.get()); + current_state_ = STATE_READY; // Make a copy in case |this| is deleted during the synchronous completion // of one of the requests. If |this| is deleted then all of the PacRequest @@ -438,7 +444,24 @@ void ProxyService::ResumeAllPendingRequests() { } } +void ProxyService::ApplyProxyConfigIfAvailable() { + DCHECK_EQ(STATE_NONE, current_state_); + + current_state_ = STATE_WAITING_FOR_PROXY_CONFIG; + + config_service_->OnLazyPoll(); + + // Retrieve the current proxy configuration from the ProxyConfigService. + // If a configuration is not available yet, we will get called back later + // by our ProxyConfigService::Observer once it changes. + ProxyConfig config; + bool has_config = config_service_->GetLatestProxyConfig(&config); + if (has_config) + OnProxyConfigChanged(config); +} + void ProxyService::OnInitProxyResolverComplete(int result) { + DCHECK_EQ(STATE_WAITING_FOR_INIT_PROXY_RESOLVER, current_state_); DCHECK(init_proxy_resolver_.get()); DCHECK(config_.MayRequirePACResolver()); DCHECK(!should_use_proxy_resolver_); @@ -453,7 +476,7 @@ void ProxyService::OnInitProxyResolverComplete(int result) { // Resume any requests which we had to defer until the PAC script was // downloaded. - ResumeAllPendingRequests(); + SetReady(); } int ProxyService::ReconsiderProxyAfterError(const GURL& url, @@ -466,13 +489,7 @@ int ProxyService::ReconsiderProxyAfterError(const GURL& url, // direct connection failed and we never tried the current config. bool re_resolve = result->config_id_ != config_.id(); - if (!re_resolve) { - UpdateConfig(net_log); - if (result->config_id_ != config_.id()) { - // A new configuration! - re_resolve = true; - } - } + if (re_resolve) { // If we have a new config or the config was never tried, we delete the // list of bad proxies and we try again. @@ -542,29 +559,43 @@ int ProxyService::DidFinishResolvingProxy(ProxyInfo* result, void ProxyService::SetProxyScriptFetcher( ProxyScriptFetcher* proxy_script_fetcher) { - if (init_proxy_resolver_.get()) { - // We need to be careful to first cancel |init_proxy_resolver_|, since it - // holds a pointer to the old proxy script fetcher we are about to delete. - - DCHECK(IsInitializingProxyResolver()); - init_proxy_resolver_.reset(); - proxy_script_fetcher_.reset(proxy_script_fetcher); - - // Restart the initialization, using the new proxy script fetcher. - StartInitProxyResolver(); - } else { - proxy_script_fetcher_.reset(proxy_script_fetcher); - } + State previous_state = ResetProxyConfig(); + proxy_script_fetcher_.reset(proxy_script_fetcher); + if (previous_state != STATE_NONE) + ApplyProxyConfigIfAvailable(); } ProxyScriptFetcher* ProxyService::GetProxyScriptFetcher() const { return proxy_script_fetcher_.get(); } +ProxyService::State ProxyService::ResetProxyConfig() { + State previous_state = current_state_; + + proxy_retry_info_.clear(); + init_proxy_resolver_.reset(); + should_use_proxy_resolver_ = false; + SuspendAllPendingRequests(); + config_ = ProxyConfig(); + current_state_ = STATE_NONE; + + return previous_state; +} + void ProxyService::ResetConfigService( ProxyConfigService* new_proxy_config_service) { + State previous_state = ResetProxyConfig(); + + // Release the old configuration service. + if (config_service_.get()) + config_service_->RemoveObserver(this); + + // Set the new configuration service. config_service_.reset(new_proxy_config_service); - UpdateConfig(BoundNetLog()); + config_service_->AddObserver(this); + + if (previous_state != STATE_NONE) + ApplyProxyConfigIfAvailable(); } void ProxyService::PurgeMemory() { @@ -573,11 +604,8 @@ void ProxyService::PurgeMemory() { } void ProxyService::ForceReloadProxyConfig() { - // Mark the current configuration as being un-initialized, then force it to - // start updating (normally this would happen lazily during the next - // call to ResolveProxy()). - config_.set_id(ProxyConfig::INVALID_ID); - UpdateConfig(BoundNetLog()); + ResetProxyConfig(); + ApplyProxyConfigIfAvailable(); } // static @@ -614,74 +642,20 @@ ProxyConfigService* ProxyService::CreateSystemProxyConfigService( #endif } -void ProxyService::UpdateConfig(const BoundNetLog& net_log) { - bool is_first_update = !config_has_been_initialized(); - - ProxyConfig latest; - - // Fetch the proxy settings. - TimeTicks start_time = TimeTicks::Now(); - net_log.BeginEvent( - NetLog::TYPE_PROXY_SERVICE_POLL_CONFIG_SERVICE_FOR_CHANGES, NULL); - int rv = config_service_->GetProxyConfig(&latest); - net_log.EndEvent(NetLog::TYPE_PROXY_SERVICE_POLL_CONFIG_SERVICE_FOR_CHANGES, - NULL); - TimeTicks end_time = TimeTicks::Now(); - - // Record how long the call to config_service_->GetConfig() above took. - // On some setups of Windows, we have reports that querying the system - // proxy settings can take multiple seconds (http://crbug.com/12189). - UMA_HISTOGRAM_CUSTOM_TIMES("Net.ProxyPollConfigurationTime", - end_time - start_time, - TimeDelta::FromMilliseconds(1), - TimeDelta::FromSeconds(30), - 50); - - if (rv != OK) { - if (is_first_update) { - // Default to direct-connection if the first fetch fails. - LOG(INFO) << "Failed initial proxy configuration fetch."; - SetConfig(ProxyConfig()); - } - return; - } - config_last_update_time_ = TimeTicks::Now(); - - if (!is_first_update && latest.Equals(config_)) - return; - - SetConfig(latest); -} - -void ProxyService::SetConfig(const ProxyConfig& config) { - config_ = config; +void ProxyService::OnProxyConfigChanged(const ProxyConfig& config) { + ResetProxyConfig(); // Increment the ID to reflect that the config has changed. + config_ = config; config_.set_id(next_config_id_++); - // Reset state associated with latest config. - proxy_retry_info_.clear(); - - // Cancel any PAC fetching / ProxyResolver::SetPacScript() which was - // in progress for the previous configuration. - init_proxy_resolver_.reset(); - should_use_proxy_resolver_ = false; - - // Start downloading + testing the PAC scripts for this new configuration. - if (config_.MayRequirePACResolver()) { - // Since InitProxyResolver will be playing around with the proxy resolver - // as it tests the parsing of various PAC scripts, make sure there is - // nothing in-flight in |resolver_|. These paused requests are resumed by - // OnInitProxyResolverComplete(). - SuspendAllPendingRequests(); - - // Calls OnInitProxyResolverComplete() on completion. - StartInitProxyResolver(); + if (!config_.MayRequirePACResolver()) { + SetReady(); + return; } -} -void ProxyService::StartInitProxyResolver() { - DCHECK(!init_proxy_resolver_.get()); + // Start downloading + testing the PAC scripts for this new configuration. + current_state_ = STATE_WAITING_FOR_INIT_PROXY_RESOLVER; init_proxy_resolver_.reset( new InitProxyResolver(resolver_.get(), proxy_script_fetcher_.get(), @@ -694,24 +668,10 @@ void ProxyService::StartInitProxyResolver() { OnInitProxyResolverComplete(rv); } -void ProxyService::UpdateConfigIfOld(const BoundNetLog& net_log) { - // The overhead of calling ProxyConfigService::GetProxyConfig is very low. - const TimeDelta kProxyConfigMaxAge = TimeDelta::FromSeconds(5); - - // Periodically check for a new config. - if (!config_has_been_initialized() || - (TimeTicks::Now() - config_last_update_time_) > kProxyConfigMaxAge) - UpdateConfig(net_log); -} - - void ProxyService::OnIPAddressChanged() { - // Mark the current configuration as being un-initialized. - // - // This will force us to re-fetch the configuration (and re-run all of - // the initialization steps) on the next ResolveProxy() request, as part - // of UpdateConfigIfOld(). - config_.set_id(ProxyConfig::INVALID_ID); + State previous_state = ResetProxyConfig(); + if (previous_state != STATE_NONE) + ApplyProxyConfigIfAvailable(); } SyncProxyServiceHelper::SyncProxyServiceHelper(MessageLoop* io_message_loop, diff --git a/net/proxy/proxy_service.h b/net/proxy/proxy_service.h index a702b38..d3699cd 100644 --- a/net/proxy/proxy_service.h +++ b/net/proxy/proxy_service.h @@ -14,6 +14,7 @@ #include "net/base/completion_callback.h" #include "net/base/network_change_notifier.h" #include "net/base/net_log.h" +#include "net/proxy/proxy_config_service.h" #include "net/proxy/proxy_server.h" #include "net/proxy/proxy_info.h" #include "testing/gtest/include/gtest/gtest_prod.h" @@ -25,7 +26,6 @@ class URLRequestContext; namespace net { class InitProxyResolver; -class ProxyConfigService; class ProxyResolver; class ProxyScriptFetcher; @@ -33,7 +33,8 @@ class ProxyScriptFetcher; // HTTP(S) URL. It uses the given ProxyResolver to handle the actual proxy // resolution. See ProxyResolverV8 for example. class ProxyService : public base::RefCountedThreadSafe<ProxyService>, - public NetworkChangeNotifier::Observer { + public NetworkChangeNotifier::Observer, + public ProxyConfigService::Observer { public: // The instance takes ownership of |config_service| and |resolver|. // |net_log| is a possibly NULL destination to send log events to. It must @@ -196,31 +197,23 @@ class ProxyService : public base::RefCountedThreadSafe<ProxyService>, // which expects requests to finish in the order they were added. typedef std::vector<scoped_refptr<PacRequest> > PendingRequests; - ~ProxyService(); - - // Identifies the proxy configuration. - ProxyConfig::ID config_id() const { return config_.id(); } - - // Checks to see if the proxy configuration changed, and then updates config_ - // to reference the new configuration. - void UpdateConfig(const BoundNetLog& net_log); - - // Assign |config| as the current configuration. - void SetConfig(const ProxyConfig& config); + enum State { + STATE_NONE, + STATE_WAITING_FOR_PROXY_CONFIG, + STATE_WAITING_FOR_INIT_PROXY_RESOLVER, + STATE_READY, + }; - // Starts downloading and testing the various PAC choices. - // Calls OnInitProxyResolverComplete() when completed. - void StartInitProxyResolver(); + ~ProxyService(); - // Tries to update the configuration if it hasn't been checked in a while. - void UpdateConfigIfOld(const BoundNetLog& net_log); + // Resets all the variables associated with the current proxy configuration, + // and rewinds the current state to |STATE_NONE|. Returns the previous value + // of |current_state_|. + State ResetProxyConfig(); - // Returns true if the proxy resolver is being initialized for PAC - // (downloading PAC script(s) + testing). - // Resolve requests will be frozen until the initialization has completed. - bool IsInitializingProxyResolver() const { - return init_proxy_resolver_.get() != NULL; - } + // Retrieves the current proxy configuration from the ProxyConfigService, and + // starts initializing for it. + void ApplyProxyConfigIfAvailable(); // Callback for when the proxy resolver has been initialized with a // PAC script. @@ -235,8 +228,9 @@ class ProxyService : public base::RefCountedThreadSafe<ProxyService>, // restarted when calling ResumeAllPendingRequests(). void SuspendAllPendingRequests(); - // Sends all the unstarted pending requests off to the resolver. - void ResumeAllPendingRequests(); + // Advances the current state to |STATE_READY|, and resumes any pending + // requests which had been stalled waiting for initialization to complete. + void SetReady(); // Returns true if |pending_requests_| contains |req|. bool ContainsPendingRequest(PacRequest* req); @@ -255,6 +249,9 @@ class ProxyService : public base::RefCountedThreadSafe<ProxyService>, // When this is called, we re-fetch PAC scripts and re-run WPAD. virtual void OnIPAddressChanged(); + // ProxyConfigService::Observer + virtual void OnProxyConfigChanged(const ProxyConfig& config); + scoped_ptr<ProxyConfigService> config_service_; scoped_ptr<ProxyResolver> resolver_; @@ -291,6 +288,8 @@ class ProxyService : public base::RefCountedThreadSafe<ProxyService>, // |proxy_resolver_| must outlive |init_proxy_resolver_|. scoped_ptr<InitProxyResolver> init_proxy_resolver_; + State current_state_; + // This is the log where any events generated by |init_proxy_resolver_| are // sent to. NetLog* net_log_; diff --git a/net/proxy/proxy_service_unittest.cc b/net/proxy/proxy_service_unittest.cc index a69b66e..bc5f74e 100644 --- a/net/proxy/proxy_service_unittest.cc +++ b/net/proxy/proxy_service_unittest.cc @@ -27,18 +27,41 @@ namespace { class MockProxyConfigService: public ProxyConfigService { public: - MockProxyConfigService() {} // Direct connect. - explicit MockProxyConfigService(const ProxyConfig& pc) : config(pc) {} - explicit MockProxyConfigService(const std::string& pac_url) { - config.set_pac_url(GURL(pac_url)); + explicit MockProxyConfigService(const ProxyConfig& config) + : has_config_(true), config_(config) { } - virtual int GetProxyConfig(ProxyConfig* results) { - *results = config; - return OK; + explicit MockProxyConfigService(const std::string& pac_url) + : has_config_(true), + config_(ProxyConfig::CreateFromCustomPacURL(GURL(pac_url))) { } - ProxyConfig config; + virtual void AddObserver(Observer* observer) { + observers_.AddObserver(observer); + } + + virtual void RemoveObserver(Observer* observer) { + observers_.RemoveObserver(observer); + } + + virtual bool GetLatestProxyConfig(ProxyConfig* results) { + if (has_config_) { + *results = config_; + return true; + } + return false; + } + + void SetConfig(const ProxyConfig& config) { + has_config_ = true; + config_ = config; + FOR_EACH_OBSERVER(Observer, observers_, OnProxyConfigChanged(config)); + } + + private: + bool has_config_; + ProxyConfig config_; + ObserverList<Observer, true> observers_; }; } // namespace @@ -91,7 +114,8 @@ class MockProxyScriptFetcher : public ProxyScriptFetcher { TEST(ProxyServiceTest, Direct) { MockAsyncProxyResolver* resolver = new MockAsyncProxyResolver; scoped_refptr<ProxyService> service( - new ProxyService(new MockProxyConfigService, resolver, NULL)); + new ProxyService(new MockProxyConfigService( + ProxyConfig::CreateDirect()), resolver, NULL)); GURL url("http://www.google.com/"); @@ -105,11 +129,14 @@ TEST(ProxyServiceTest, Direct) { EXPECT_TRUE(info.is_direct()); // Check the NetLog was filled correctly. - EXPECT_EQ(5u, log.entries().size()); + EXPECT_EQ(3u, log.entries().size()); EXPECT_TRUE(LogContainsBeginEvent( log.entries(), 0, NetLog::TYPE_PROXY_SERVICE)); + EXPECT_TRUE(LogContainsEvent( + log.entries(), 1, NetLog::TYPE_PROXY_SERVICE_RESOLVED_PROXY_LIST, + NetLog::PHASE_NONE)); EXPECT_TRUE(LogContainsEndEvent( - log.entries(), 4, NetLog::TYPE_PROXY_SERVICE)); + log.entries(), 2, NetLog::TYPE_PROXY_SERVICE)); } TEST(ProxyServiceTest, PAC) { @@ -146,15 +173,15 @@ TEST(ProxyServiceTest, PAC) { EXPECT_EQ("foopy:80", info.proxy_server().ToURI()); // Check the NetLog was filled correctly. - EXPECT_EQ(7u, log.entries().size()); + EXPECT_EQ(5u, log.entries().size()); EXPECT_TRUE(LogContainsBeginEvent( log.entries(), 0, NetLog::TYPE_PROXY_SERVICE)); EXPECT_TRUE(LogContainsBeginEvent( - log.entries(), 3, NetLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC)); + log.entries(), 1, NetLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC)); EXPECT_TRUE(LogContainsEndEvent( - log.entries(), 4, NetLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC)); + log.entries(), 2, NetLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC)); EXPECT_TRUE(LogContainsEndEvent( - log.entries(), 6, NetLog::TYPE_PROXY_SERVICE)); + log.entries(), 4, NetLog::TYPE_PROXY_SERVICE)); } // Test that the proxy resolver does not see the URL's username/password @@ -552,8 +579,8 @@ TEST(ProxyServiceTest, ProxyFallback_NewSettings) { EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI()); // Fake an error on the proxy, and also a new configuration on the proxy. - config_service->config = ProxyConfig(); - config_service->config.set_pac_url(GURL("http://foopy-new/proxy.pac")); + config_service->SetConfig( + ProxyConfig::CreateFromCustomPacURL(GURL("http://foopy-new/proxy.pac"))); TestCompletionCallback callback2; rv = service->ReconsiderProxyAfterError(url, &info, &callback2, NULL, @@ -583,8 +610,9 @@ TEST(ProxyServiceTest, ProxyFallback_NewSettings) { EXPECT_EQ("foopy2:9090", info.proxy_server().ToURI()); // We simulate a new configuration. - config_service->config = ProxyConfig(); - config_service->config.set_pac_url(GURL("http://foopy-new2/proxy.pac")); + config_service->SetConfig( + ProxyConfig::CreateFromCustomPacURL( + GURL("http://foopy-new2/proxy.pac"))); // We fake another error. It should go back to the first proxy. TestCompletionCallback callback4; @@ -1121,17 +1149,17 @@ TEST(ProxyServiceTest, CancelWhilePACFetching) { EXPECT_FALSE(callback2.have_result()); // Cancelled. // Check the NetLog for request 1 (which was cancelled) got filled properly. - EXPECT_EQ(6u, log1.entries().size()); + EXPECT_EQ(4u, log1.entries().size()); EXPECT_TRUE(LogContainsBeginEvent( log1.entries(), 0, NetLog::TYPE_PROXY_SERVICE)); EXPECT_TRUE(LogContainsBeginEvent( - log1.entries(), 3, NetLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC)); + log1.entries(), 1, NetLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC)); // Note that TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC is never completed before // the cancellation occured. EXPECT_TRUE(LogContainsEvent( - log1.entries(), 4, NetLog::TYPE_CANCELLED, NetLog::PHASE_NONE)); + log1.entries(), 2, NetLog::TYPE_CANCELLED, NetLog::PHASE_NONE)); EXPECT_TRUE(LogContainsEndEvent( - log1.entries(), 5, NetLog::TYPE_PROXY_SERVICE)); + log1.entries(), 3, NetLog::TYPE_PROXY_SERVICE)); } // Test that if auto-detect fails, we fall-back to the custom pac. @@ -1411,8 +1439,8 @@ TEST(ProxyServiceTest, BypassDoesntApplyToPac) { // have any memory errors (used to be that the ProxyScriptFetcher was // being deleted prior to the InitProxyResolver). TEST(ProxyServiceTest, DeleteWhileInitProxyResolverHasOutstandingFetch) { - ProxyConfig config; - config.set_pac_url(GURL("http://foopy/proxy.pac")); + ProxyConfig config = + ProxyConfig::CreateFromCustomPacURL(GURL("http://foopy/proxy.pac")); MockProxyConfigService* config_service = new MockProxyConfigService(config); MockAsyncProxyResolverExpectsBytes* resolver = @@ -1496,61 +1524,10 @@ TEST(ProxyServiceTest, ResetProxyConfigService) { EXPECT_EQ("foopy2:8080", info.proxy_server().ToURI()); } -// Check that after we have done the auto-detect test, and the configuration -// is updated (with no change), we don't re-try the autodetect test. -// Regression test for http://crbug.com/18526 -- the configuration was being -// mutated to cancel out the automatic settings, which meant UpdateConfig() -// thought it had received a new configuration. -TEST(ProxyServiceTest, UpdateConfigAfterFailedAutodetect) { - ProxyConfig config; - config.set_auto_detect(true); - - MockProxyConfigService* config_service = new MockProxyConfigService(config); - MockAsyncProxyResolver* resolver = new MockAsyncProxyResolver; - scoped_refptr<ProxyService> service( - new ProxyService(config_service, resolver, NULL)); - - // Start 1 requests. - - ProxyInfo info1; - TestCompletionCallback callback1; - int rv = service->ResolveProxy( - GURL("http://www.google.com"), &info1, &callback1, NULL, BoundNetLog()); - EXPECT_EQ(ERR_IO_PENDING, rv); - - // Check that nothing has been sent to the proxy resolver yet. - ASSERT_EQ(0u, resolver->pending_requests().size()); - - // Fail the setting of autodetect script. - EXPECT_EQ(ProxyResolverScriptData::TYPE_AUTO_DETECT, - resolver->pending_set_pac_script_request()->script_data()->type()); - resolver->pending_set_pac_script_request()->CompleteNow(ERR_FAILED); - - // Verify that request ran as expected -- should have fallen back to direct. - EXPECT_EQ(OK, callback1.WaitForResult()); - EXPECT_TRUE(info1.is_direct()); - - // Force the ProxyService to pull down a new proxy configuration. - // (Even though the configuration isn't old/bad). - service->UpdateConfig(BoundNetLog()); - - // Start another request -- the effective configuration has not - // changed, so we shouldn't re-run the autodetect step. - // Rather, it should complete synchronously as direct-connect. - ProxyInfo info2; - TestCompletionCallback callback2; - rv = service->ResolveProxy( - GURL("http://www.google.com"), &info2, &callback2, NULL, BoundNetLog()); - EXPECT_EQ(OK, rv); - - EXPECT_TRUE(info2.is_direct()); -} - // Test that when going from a configuration that required PAC to one // that does NOT, we unset the variable |should_use_proxy_resolver_|. TEST(ProxyServiceTest, UpdateConfigFromPACToDirect) { - ProxyConfig config; - config.set_auto_detect(true); + ProxyConfig config = ProxyConfig::CreateAutoDetect(); MockProxyConfigService* config_service = new MockProxyConfigService(config); MockAsyncProxyResolver* resolver = new MockAsyncProxyResolver; @@ -1587,9 +1564,7 @@ TEST(ProxyServiceTest, UpdateConfigFromPACToDirect) { // // This new configuration no longer has auto_detect set, so // requests should complete synchronously now as direct-connect. - config.set_auto_detect(false); - config_service->config = config; - service->UpdateConfig(BoundNetLog()); + config_service->SetConfig(ProxyConfig::CreateDirect()); // Start another request -- the effective configuration has changed. ProxyInfo info2; |