summaryrefslogtreecommitdiffstats
path: root/net/proxy
diff options
context:
space:
mode:
authoreroman@chromium.org <eroman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-13 22:05:46 +0000
committereroman@chromium.org <eroman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-13 22:05:46 +0000
commit868a86065009b45db729ae0555aa6dbf5364c01a (patch)
tree979fbad3ad1c5eae7fca05f597a0591a5541118e /net/proxy
parentab3b1c11947c0402aff7e25ba10abf63a61e81f5 (diff)
downloadchromium_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.cc157
-rw-r--r--net/proxy/proxy_service.h51
-rw-r--r--net/proxy/proxy_service_unittest.cc234
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