diff options
author | eroman@chromium.org <eroman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-13 22:05:46 +0000 |
---|---|---|
committer | eroman@chromium.org <eroman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-13 22:05:46 +0000 |
commit | 868a86065009b45db729ae0555aa6dbf5364c01a (patch) | |
tree | 979fbad3ad1c5eae7fca05f597a0591a5541118e /net/proxy | |
parent | ab3b1c11947c0402aff7e25ba10abf63a61e81f5 (diff) | |
download | chromium_src-868a86065009b45db729ae0555aa6dbf5364c01a.zip chromium_src-868a86065009b45db729ae0555aa6dbf5364c01a.tar.gz chromium_src-868a86065009b45db729ae0555aa6dbf5364c01a.tar.bz2 |
Don't poll the PAC script during periods of network inactivity.
BUG=109310
Review URL: http://codereview.chromium.org/9139070
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@117709 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/proxy')
-rw-r--r-- | net/proxy/proxy_service.cc | 157 | ||||
-rw-r--r-- | net/proxy/proxy_service.h | 51 | ||||
-rw-r--r-- | net/proxy/proxy_service_unittest.cc | 234 |
3 files changed, 373 insertions, 69 deletions
diff --git a/net/proxy/proxy_service.cc b/net/proxy/proxy_service.cc index fbfe21e..e915c2e 100644 --- a/net/proxy/proxy_service.cc +++ b/net/proxy/proxy_service.cc @@ -77,9 +77,43 @@ const int64 kDelayAfterNetworkChangesMs = 2000; const int64 kInitialPollDelayForErrorMs = 4000; const int64 kInitialPollDelayForSuccessMs = 16000; +// Once the poll delay becomes larger than this, we will stop scheduling using +// a timer, and instead wait for an incoming request to trigger the poll. +const int64 kMaxPollDelayUsingTimer = 16000; + // The maximum poll delay for checking PAC scripts. const int64 kMaxPollDelayMs = 120000; // 2 minutes. +// This is the default policy for polling the PAC script. Checks happen after an +// exponentially increasing delay. The first couple checks will be scheduled +// using a timer. After that the checks will only be done lazily in response to +// a user request. +class DefaultPollPolicy : public ProxyService::PacPollPolicy { + public: + DefaultPollPolicy() {} + + virtual Mode GetInitialDelay(int error, int64* next_delay_ms) const OVERRIDE { + // Use a shorter wait delay if the initial fetch resulted in an error. + *next_delay_ms = (error != OK) ? + kInitialPollDelayForErrorMs : kInitialPollDelayForSuccessMs; + return MODE_USE_TIMER; + } + + virtual Mode GetNextDelay(int64 current_delay_ms, + int64* next_delay_ms) const OVERRIDE { + // Increase the wait delay exponentially up to a maximum. + *next_delay_ms = std::min(current_delay_ms * 2, kMaxPollDelayMs); + + // Once the delays are large enough, stop scheduling using a timer and + // instead only poll during periods of activity. + return (*next_delay_ms <= kMaxPollDelayUsingTimer) ? + MODE_USE_TIMER: MODE_START_AFTER_ACTIVITY; + } + + private: + DISALLOW_COPY_AND_ASSIGN(DefaultPollPolicy); +}; + // Config getter that always returns direct settings. class ProxyConfigServiceDirect : public ProxyConfigService { public: @@ -531,68 +565,67 @@ class ProxyService::ProxyScriptDeciderPoller { proxy_script_fetcher_(proxy_script_fetcher), dhcp_proxy_script_fetcher_(dhcp_proxy_script_fetcher), last_error_(init_net_error), - last_script_data_(init_script_data) { + last_script_data_(init_script_data), + last_poll_time_(base::TimeTicks::Now()) { // Set the initial poll delay -- we check more aggressively right after a // failure in case it was due to a spurious network error. - current_poll_delay_ms_ = GetInitialWaitDelayMs(init_net_error); - - StartPollTimer(); - } - - // ---------------------------------- - // Policy for the retry scheduling. - // ---------------------------------- - - static int64 GetInitialWaitDelayMs(int error) { - switch (poll_policy_) { - case POLL_POLICY_REGULAR: - // Use a shorter wait delay if the initial fetch resulted in an error. - return (error != OK) ? - kInitialPollDelayForErrorMs : kInitialPollDelayForSuccessMs; - case POLL_POLICY_IMMEDIATE: - // Wait a mere 1 millisecond. - return 1; - case POLL_POLICY_NEVER: - // Unreasonably large wait delay, will never fire the check. - return 0xFFFFFFFF; - } - - // Shouldn't be reached - return -1; + next_poll_mode_ = poll_policy()->GetInitialDelay( + init_net_error, &next_poll_delay_ms_); + TryToStartNextPoll(false); } - static int64 GetNextWaitDelayMs(int64 current_delay_ms) { - switch (poll_policy_) { - case POLL_POLICY_REGULAR: - // Increase the delay exponentially up to 2 minutes. - return std::min(current_delay_ms * 2, kMaxPollDelayMs); - case POLL_POLICY_IMMEDIATE: - case POLL_POLICY_NEVER: - return current_delay_ms; - } - - // Shouldn't be reached - return -1; + void OnLazyPoll() { + // We have just been notified of network activity. Use this opportunity to + // see if we can start our next poll. + TryToStartNextPoll(true); } - static PollPolicy set_policy(PollPolicy policy) { - PollPolicy prev = poll_policy_; + static const PacPollPolicy* set_policy(const PacPollPolicy* policy) { + const PacPollPolicy* prev = poll_policy_; poll_policy_ = policy; return prev; } private: + // Returns the effective poll policy (the one injected by unit-tests, or the + // default). + const PacPollPolicy* poll_policy() { + if (poll_policy_) + return poll_policy_; + return &default_poll_policy_; + } + void StartPollTimer() { DCHECK(!decider_.get()); MessageLoop::current()->PostDelayedTask( FROM_HERE, - base::Bind(&ProxyScriptDeciderPoller::OnPollTimerFired, + base::Bind(&ProxyScriptDeciderPoller::DoPoll, weak_factory_.GetWeakPtr()), - current_poll_delay_ms_); + next_poll_delay_ms_); + } + + void TryToStartNextPoll(bool triggered_by_activity) { + switch (next_poll_mode_) { + case PacPollPolicy::MODE_USE_TIMER: + if (!triggered_by_activity) + StartPollTimer(); + break; + + case PacPollPolicy::MODE_START_AFTER_ACTIVITY: + if (triggered_by_activity && !decider_.get()) { + base::TimeDelta elapsed_time = + base::TimeTicks::Now() - last_poll_time_; + if (elapsed_time.InMilliseconds() >= next_poll_delay_ms_) + DoPoll(); + } + break; + } } - void OnPollTimerFired() { + void DoPoll() { + last_poll_time_ = base::TimeTicks::Now(); + // Start the proxy script decider to see if anything has changed. // TODO(eroman): Pass a proper NetLog rather than NULL. decider_.reset(new ProxyScriptDecider( @@ -626,9 +659,11 @@ class ProxyService::ProxyScriptDeciderPoller { decider_.reset(); - // Schedule the next poll check. - current_poll_delay_ms_ = GetNextWaitDelayMs(current_poll_delay_ms_); - StartPollTimer(); + // Decide when the next poll should take place, and possibly start the + // next timer. + next_poll_mode_ = + poll_policy()->GetNextDelay(next_poll_delay_ms_, &next_poll_delay_ms_); + TryToStartNextPoll(false); } bool HasScriptDataChanged(int result, ProxyResolverScriptData* script_data) { @@ -671,16 +706,23 @@ class ProxyService::ProxyScriptDeciderPoller { scoped_refptr<ProxyResolverScriptData> last_script_data_; scoped_ptr<ProxyScriptDecider> decider_; - int64 current_poll_delay_ms_; + int64 next_poll_delay_ms_; + PacPollPolicy::Mode next_poll_mode_; - static PollPolicy poll_policy_; + base::TimeTicks last_poll_time_; + + // Polling policy injected by unit-tests. Otherwise this is NULL and the + // default policy will be used. + static const PacPollPolicy* poll_policy_; + + const DefaultPollPolicy default_poll_policy_; DISALLOW_COPY_AND_ASSIGN(ProxyScriptDeciderPoller); }; // static -ProxyService::PollPolicy ProxyService::ProxyScriptDeciderPoller::poll_policy_ = - ProxyService::POLL_POLICY_REGULAR; +const ProxyService::PacPollPolicy* + ProxyService::ProxyScriptDeciderPoller::poll_policy_ = NULL; // ProxyService::PacRequest --------------------------------------------------- @@ -951,7 +993,12 @@ int ProxyService::ResolveProxy(const GURL& raw_url, net_log.BeginEvent(NetLog::TYPE_PROXY_SERVICE, NULL); + // Notify our polling-based dependencies that a resolve is taking place. + // This way they can schedule their polls in response to network activity. config_service_->OnLazyPoll(); + if (script_poller_.get()) + script_poller_->OnLazyPoll(); + if (current_state_ == STATE_NONE) ApplyProxyConfigIfAvailable(); @@ -1353,11 +1400,17 @@ ProxyConfigService* ProxyService::CreateSystemProxyConfigService( } // static -ProxyService::PollPolicy ProxyService::set_pac_script_poll_policy( - PollPolicy policy) { +const ProxyService::PacPollPolicy* ProxyService::set_pac_script_poll_policy( + const PacPollPolicy* policy) { return ProxyScriptDeciderPoller::set_policy(policy); } +// static +scoped_ptr<ProxyService::PacPollPolicy> + ProxyService::CreateDefaultPacPollPolicy() { + return scoped_ptr<PacPollPolicy>(new DefaultPollPolicy()); +} + void ProxyService::OnProxyConfigChanged( const ProxyConfig& config, ProxyConfigService::ConfigAvailability availability) { diff --git a/net/proxy/proxy_service.h b/net/proxy/proxy_service.h index dd9b712..7fcfa4b 100644 --- a/net/proxy/proxy_service.h +++ b/net/proxy/proxy_service.h @@ -43,11 +43,45 @@ class NET_EXPORT ProxyService : public NetworkChangeNotifier::IPAddressObserver, public ProxyConfigService::Observer, NON_EXPORTED_BASE(public base::NonThreadSafe) { public: - // Only used by unit-tests. - enum PollPolicy { - POLL_POLICY_REGULAR, // Normal PAC poll policy (retry periodically). - POLL_POLICY_NEVER, // Don't re-fetch PAC scripts for changes. - POLL_POLICY_IMMEDIATE, // Check every 1 ms. + // This interface defines the set of policies for when to poll the PAC + // script for changes. + // + // The polling policy decides what the next poll delay should be in + // milliseconds. It also decides how to wait for this delay -- either + // by starting a timer to do the poll at exactly |next_delay_ms| + // (MODE_USE_TIMER) or by waiting for the first network request issued after + // |next_delay_ms| (MODE_START_AFTER_ACTIVITY). + // + // The timer method is more precise and guarantees that polling happens when + // it was requested. However it has the disadvantage of causing spurious CPU + // and network activity. It is a reasonable choice to use for short poll + // intervals which only happen a couple times. + // + // However for repeated timers this will prevent the browser from going + // idle. MODE_START_AFTER_ACTIVITY solves this problem by only polling in + // direct response to network activity. The drawback to + // MODE_START_AFTER_ACTIVITY is since the poll is initiated only after the + // request is received, the first couple requests initiated after a long + // period of inactivity will likely see a stale version of the PAC script + // until the background polling gets a chance to update things. + class NET_EXPORT_PRIVATE PacPollPolicy { + public: + enum Mode { + MODE_USE_TIMER, + MODE_START_AFTER_ACTIVITY, + }; + + virtual ~PacPollPolicy() {} + + // Decides the initial poll delay. |error| is the network error + // code that the most last PAC fetch failed with (or OK if it was a + // success). Implementations must set |next_delay_ms|. + virtual Mode GetInitialDelay(int error, int64* next_delay_ms) const = 0; + + // Decides the next poll delay. |current_delay_ms| is the delay used + // by the preceding poll. Implementations must set |next_delay_ms|. + virtual Mode GetNextDelay(int64 current_delay_ms, + int64* next_delay_ms) const = 0; }; // The instance takes ownership of |config_service| and |resolver|. @@ -246,7 +280,12 @@ class NET_EXPORT ProxyService : public NetworkChangeNotifier::IPAddressObserver, // This method should only be used by unit tests. Returns the previously // active policy. - static PollPolicy set_pac_script_poll_policy(PollPolicy policy); + static const PacPollPolicy* set_pac_script_poll_policy( + const PacPollPolicy* policy); + + // This method should only be used by unit tests. Creates an instance + // of the default internal PacPollPolicy used by ProxyService. + static scoped_ptr<PacPollPolicy> CreateDefaultPacPollPolicy(); private: FRIEND_TEST_ALL_PREFIXES(ProxyServiceTest, UpdateConfigAfterFailedAutodetect); diff --git a/net/proxy/proxy_service_unittest.cc b/net/proxy/proxy_service_unittest.cc index 0be99f2..7d794c9 100644 --- a/net/proxy/proxy_service_unittest.cc +++ b/net/proxy/proxy_service_unittest.cc @@ -28,6 +28,64 @@ namespace net { namespace { +// This polling policy will decide to poll every 1 ms. +class ImmediatePollPolicy : public ProxyService::PacPollPolicy { + public: + ImmediatePollPolicy() {} + + virtual Mode GetInitialDelay(int error, int64* next_delay_ms) const OVERRIDE { + *next_delay_ms = 1; + return MODE_USE_TIMER; + } + + virtual Mode GetNextDelay(int64 current_delay_ms, + int64* next_delay_ms) const OVERRIDE { + return GetInitialDelay(OK, next_delay_ms); + } + + private: + DISALLOW_COPY_AND_ASSIGN(ImmediatePollPolicy); +}; + +// This polling policy chooses a fantastically large delay. In other words, it +// will never trigger a poll +class NeverPollPolicy : public ProxyService::PacPollPolicy { + public: + NeverPollPolicy() {} + + virtual Mode GetInitialDelay(int error, int64* next_delay_ms) const OVERRIDE { + *next_delay_ms = 0xFFFFFFFF; // Big number of milliseconds! + return MODE_USE_TIMER; + } + + virtual Mode GetNextDelay(int64 current_delay_ms, + int64* next_delay_ms) const OVERRIDE { + return GetInitialDelay(OK, next_delay_ms); + } + + private: + DISALLOW_COPY_AND_ASSIGN(NeverPollPolicy); +}; + +// This polling policy starts a poll immediately after network activity. +class ImmediateAfterActivityPollPolicy : public ProxyService::PacPollPolicy { + public: + ImmediateAfterActivityPollPolicy() {} + + virtual Mode GetInitialDelay(int error, int64* next_delay_ms) const OVERRIDE { + *next_delay_ms = 0; + return MODE_START_AFTER_ACTIVITY; + } + + virtual Mode GetNextDelay(int64 current_delay_ms, + int64* next_delay_ms) const OVERRIDE { + return GetInitialDelay(OK, next_delay_ms); + } + + private: + DISALLOW_COPY_AND_ASSIGN(ImmediateAfterActivityPollPolicy); +}; + // This test fixture is used to partially disable the background polling done by // the ProxyService (which it uses to detect whenever its PAC script contents or // WPAD results have changed). @@ -47,8 +105,8 @@ class ProxyServiceTest : public testing::Test { protected: virtual void SetUp() OVERRIDE { testing::Test::SetUp(); - previous_policy_ = ProxyService::set_pac_script_poll_policy( - ProxyService::POLL_POLICY_NEVER); + previous_policy_ = + ProxyService::set_pac_script_poll_policy(&never_poll_policy_); } virtual void TearDown() OVERRIDE { @@ -57,7 +115,9 @@ class ProxyServiceTest : public testing::Test { testing::Test::TearDown(); } - ProxyService::PollPolicy previous_policy_; + private: + NeverPollPolicy never_poll_policy_; + const ProxyService::PacPollPolicy* previous_policy_; }; const char kValidPacScript1[] = "pac-script-v1-FindProxyForURL"; @@ -1956,8 +2016,8 @@ TEST_F(ProxyServiceTest, NetworkChangeTriggersPacRefetch) { TEST_F(ProxyServiceTest, PACScriptRefetchAfterFailure) { // Change the retry policy to wait a mere 1 ms before retrying, so the test // runs quickly. - ProxyService::set_pac_script_poll_policy( - ProxyService::POLL_POLICY_IMMEDIATE); + ImmediatePollPolicy poll_policy; + ProxyService::set_pac_script_poll_policy(&poll_policy); MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac"); @@ -2063,8 +2123,8 @@ TEST_F(ProxyServiceTest, PACScriptRefetchAfterFailure) { TEST_F(ProxyServiceTest, PACScriptRefetchAfterContentChange) { // Change the retry policy to wait a mere 1 ms before retrying, so the test // runs quickly. - ProxyService::set_pac_script_poll_policy( - ProxyService::POLL_POLICY_IMMEDIATE); + ImmediatePollPolicy poll_policy; + ProxyService::set_pac_script_poll_policy(&poll_policy); MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac"); @@ -2175,8 +2235,8 @@ TEST_F(ProxyServiceTest, PACScriptRefetchAfterContentChange) { TEST_F(ProxyServiceTest, PACScriptRefetchAfterContentUnchanged) { // Change the retry policy to wait a mere 1 ms before retrying, so the test // runs quickly. - ProxyService::set_pac_script_poll_policy( - ProxyService::POLL_POLICY_IMMEDIATE); + ImmediatePollPolicy poll_policy; + ProxyService::set_pac_script_poll_policy(&poll_policy); MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac"); @@ -2283,8 +2343,8 @@ TEST_F(ProxyServiceTest, PACScriptRefetchAfterContentUnchanged) { TEST_F(ProxyServiceTest, PACScriptRefetchAfterSuccess) { // Change the retry policy to wait a mere 1 ms before retrying, so the test // runs quickly. - ProxyService::set_pac_script_poll_policy( - ProxyService::POLL_POLICY_IMMEDIATE); + ImmediatePollPolicy poll_policy; + ProxyService::set_pac_script_poll_policy(&poll_policy); MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac"); @@ -2371,4 +2431,156 @@ TEST_F(ProxyServiceTest, PACScriptRefetchAfterSuccess) { EXPECT_TRUE(info2.is_direct()); } +// Tests that the code which decides at what times to poll the PAC +// script follows the expected policy. +TEST_F(ProxyServiceTest, PACScriptPollingPolicy) { + // Retrieve the internal polling policy implementation used by ProxyService. + scoped_ptr<ProxyService::PacPollPolicy> policy = + ProxyService::CreateDefaultPacPollPolicy(); + + ProxyService::PacPollPolicy::Mode mode; + int64 delay_ms = -1; + + // After a failure, we should start polling at 4 seconds. + mode = policy->GetInitialDelay(ERR_FAILED, &delay_ms); + EXPECT_EQ(4000, delay_ms); + EXPECT_EQ(ProxyService::PacPollPolicy::MODE_USE_TIMER, mode); + + // After a success, we should start polling at 16 seconds. + mode = policy->GetInitialDelay(OK, &delay_ms); + EXPECT_EQ(16000, delay_ms); + EXPECT_EQ(ProxyService::PacPollPolicy::MODE_USE_TIMER, mode); + + // The delay should be doubled each time. + mode = policy->GetNextDelay(4000, &delay_ms); + EXPECT_EQ(8000, delay_ms); + EXPECT_EQ(ProxyService::PacPollPolicy::MODE_USE_TIMER, mode); + mode = policy->GetNextDelay(delay_ms, &delay_ms); + EXPECT_EQ(16000, delay_ms); + EXPECT_EQ(ProxyService::PacPollPolicy::MODE_USE_TIMER, mode); + + // Once we reach 32 seconds, the polling should stop being done using + // a timer, however it should keep doubling. + mode = policy->GetNextDelay(delay_ms, &delay_ms); + EXPECT_EQ(32000, delay_ms); + EXPECT_EQ(ProxyService::PacPollPolicy::MODE_START_AFTER_ACTIVITY, mode); + mode = policy->GetNextDelay(delay_ms, &delay_ms); + EXPECT_EQ(64000, delay_ms); + EXPECT_EQ(ProxyService::PacPollPolicy::MODE_START_AFTER_ACTIVITY, mode); + + // Once we reach 2 minutes, the polling delay should stop increasing. + mode = policy->GetNextDelay(delay_ms, &delay_ms); + EXPECT_EQ(120000, delay_ms); + EXPECT_EQ(ProxyService::PacPollPolicy::MODE_START_AFTER_ACTIVITY, mode); + mode = policy->GetNextDelay(delay_ms, &delay_ms); + EXPECT_EQ(120000, delay_ms); + EXPECT_EQ(ProxyService::PacPollPolicy::MODE_START_AFTER_ACTIVITY, mode); +} + +// This tests the polling of the PAC script. Specifically, it tests that +// polling occurs in response to user activity. +TEST_F(ProxyServiceTest, PACScriptRefetchAfterActivity) { + ImmediateAfterActivityPollPolicy poll_policy; + ProxyService::set_pac_script_poll_policy(&poll_policy); + + MockProxyConfigService* config_service = + new MockProxyConfigService("http://foopy/proxy.pac"); + + MockAsyncProxyResolverExpectsBytes* resolver = + new MockAsyncProxyResolverExpectsBytes; + + CapturingNetLog log(CapturingNetLog::kUnbounded); + + ProxyService service(config_service, resolver, &log); + + MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher; + service.SetProxyScriptFetchers(fetcher, + new DoNothingDhcpProxyScriptFetcher()); + + // Start 1 request. + + ProxyInfo info1; + TestCompletionCallback callback1; + int rv = service.ResolveProxy( + GURL("http://request1"), &info1, callback1.callback(), NULL, + BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + // The first request should have triggered initial download of PAC script. + EXPECT_TRUE(fetcher->has_pending_request()); + EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url()); + + // Nothing has been sent to the resolver yet. + EXPECT_TRUE(resolver->pending_requests().empty()); + + // At this point the ProxyService should be waiting for the + // ProxyScriptFetcher to invoke its completion callback, notifying it of + // PAC script download completion. + fetcher->NotifyFetchCompletion(OK, kValidPacScript1); + + // Now that the PAC script is downloaded, the request will have been sent to + // the proxy resolver. + EXPECT_EQ(ASCIIToUTF16(kValidPacScript1), + resolver->pending_set_pac_script_request()->script_data()->utf16()); + resolver->pending_set_pac_script_request()->CompleteNow(OK); + + ASSERT_EQ(1u, resolver->pending_requests().size()); + EXPECT_EQ(GURL("http://request1"), resolver->pending_requests()[0]->url()); + + // Complete the pending request. + resolver->pending_requests()[0]->results()->UseNamedProxy("request1:80"); + resolver->pending_requests()[0]->CompleteNow(OK); + + // Wait for completion callback, and verify that the request ran as expected. + EXPECT_EQ(OK, callback1.WaitForResult()); + EXPECT_EQ("request1:80", info1.proxy_server().ToURI()); + + // At this point we have initialized the proxy service using a PAC script. + // Our PAC poller is set to update ONLY in response to network activity, + // (i.e. another call to ResolveProxy()). + + ASSERT_FALSE(fetcher->has_pending_request()); + ASSERT_TRUE(resolver->pending_requests().empty()); + + // Start a second request. + ProxyInfo info2; + TestCompletionCallback callback2; + rv = service.ResolveProxy( + GURL("http://request2"), &info2, callback2.callback(), NULL, + BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + // This request should have sent work to the resolver; complete it. + ASSERT_EQ(1u, resolver->pending_requests().size()); + EXPECT_EQ(GURL("http://request2"), resolver->pending_requests()[0]->url()); + resolver->pending_requests()[0]->results()->UseNamedProxy("request2:80"); + resolver->pending_requests()[0]->CompleteNow(OK); + + EXPECT_EQ(OK, callback2.WaitForResult()); + EXPECT_EQ("request2:80", info2.proxy_server().ToURI()); + + // In response to getting that resolve request, the poller should have + // started the next poll, and made it as far as to request the download. + + EXPECT_TRUE(fetcher->has_pending_request()); + EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url()); + + // This time we will fail the download, to simulate a PAC script change. + fetcher->NotifyFetchCompletion(ERR_FAILED, ""); + + // Drain the message loop, so ProxyService is notified of the change + // and has a chance to re-configure itself. + MessageLoop::current()->RunAllPending(); + + // Start a third request -- this time we expect to get a direct connection + // since the PAC script poller experienced a failure. + ProxyInfo info3; + TestCompletionCallback callback3; + rv = service.ResolveProxy( + GURL("http://request3"), &info3, callback3.callback(), NULL, + BoundNetLog()); + EXPECT_EQ(OK, rv); + EXPECT_TRUE(info3.is_direct()); +} + } // namespace net |