summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorericroman@google.com <ericroman@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-08-04 22:43:12 +0000
committerericroman@google.com <ericroman@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-08-04 22:43:12 +0000
commit620f57148c311c4dc2cf680a4a5861fbdcd29993 (patch)
tree93408ed35ebbaccd8f45734c7267392c81d71ca9 /net
parent289fdf862c649d17ddb2e08295304efb98f641f6 (diff)
downloadchromium_src-620f57148c311c4dc2cf680a4a5861fbdcd29993.zip
chromium_src-620f57148c311c4dc2cf680a4a5861fbdcd29993.tar.gz
chromium_src-620f57148c311c4dc2cf680a4a5861fbdcd29993.tar.bz2
Better match IE's proxy settings.
* When BOTH autodetect and custom PAC script are given, try both. * Use successful PAC parsing as the heuristic for determining when a script is valid (rather than first-request). * Only apply the proxy bypass list when using non-PAC. The high level explanation on how this works: http://sites.google.com/a/chromium.org/dev/developers/design-documents/proxy-settings-fallback BUG= http://crbug.com/18271, http://crbug.com/9985 TEST=unit tests. Review URL: http://codereview.chromium.org/160510 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@22430 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/net.gyp3
-rw-r--r--net/proxy/init_proxy_resolver.cc189
-rw-r--r--net/proxy/init_proxy_resolver.h104
-rw-r--r--net/proxy/init_proxy_resolver_unittest.cc325
-rw-r--r--net/proxy/proxy_resolver.h30
-rw-r--r--net/proxy/proxy_resolver_mac.h6
-rw-r--r--net/proxy/proxy_resolver_perftest.cc6
-rw-r--r--net/proxy/proxy_resolver_v8.cc47
-rw-r--r--net/proxy/proxy_resolver_v8.h7
-rw-r--r--net/proxy/proxy_resolver_v8_unittest.cc95
-rw-r--r--net/proxy/proxy_resolver_winhttp.cc10
-rw-r--r--net/proxy/proxy_resolver_winhttp.h5
-rw-r--r--net/proxy/proxy_script_fetcher.cc11
-rw-r--r--net/proxy/proxy_script_fetcher.h6
-rw-r--r--net/proxy/proxy_script_fetcher_unittest.cc3
-rw-r--r--net/proxy/proxy_service.cc145
-rw-r--r--net/proxy/proxy_service.h50
-rw-r--r--net/proxy/proxy_service_unittest.cc445
-rw-r--r--net/proxy/single_threaded_proxy_resolver.cc119
-rw-r--r--net/proxy/single_threaded_proxy_resolver.h17
-rw-r--r--net/proxy/single_threaded_proxy_resolver_unittest.cc75
21 files changed, 1408 insertions, 290 deletions
diff --git a/net/net.gyp b/net/net.gyp
index ab5972c..3bba793 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -251,6 +251,8 @@
'http/partial_data.h',
'ocsp/nss_ocsp.cc',
'ocsp/nss_ocsp.h',
+ 'proxy/init_proxy_resolver.cc',
+ 'proxy/init_proxy_resolver.h',
'proxy/proxy_config.cc',
'proxy/proxy_config.h',
'proxy/proxy_config_service.h',
@@ -495,6 +497,7 @@
'http/http_transaction_unittest.h',
'http/http_util_unittest.cc',
'http/http_vary_data_unittest.cc',
+ 'proxy/init_proxy_resolver_unittest.cc',
'proxy/proxy_config_service_linux_unittest.cc',
'proxy/proxy_config_service_win_unittest.cc',
'proxy/proxy_config_unittest.cc',
diff --git a/net/proxy/init_proxy_resolver.cc b/net/proxy/init_proxy_resolver.cc
new file mode 100644
index 0000000..7b98c4a
--- /dev/null
+++ b/net/proxy/init_proxy_resolver.cc
@@ -0,0 +1,189 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/proxy/init_proxy_resolver.h"
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "net/base/net_errors.h"
+#include "net/proxy/proxy_config.h"
+#include "net/proxy/proxy_resolver.h"
+#include "net/proxy/proxy_script_fetcher.h"
+
+namespace net {
+
+InitProxyResolver::InitProxyResolver(ProxyResolver* resolver,
+ ProxyScriptFetcher* proxy_script_fetcher)
+ : resolver_(resolver),
+ proxy_script_fetcher_(proxy_script_fetcher),
+ ALLOW_THIS_IN_INITIALIZER_LIST(io_callback_(
+ this, &InitProxyResolver::OnIOCompletion)),
+ user_callback_(NULL),
+ current_pac_url_index_(0u),
+ next_state_(STATE_NONE) {
+}
+
+InitProxyResolver::~InitProxyResolver() {
+ switch (next_state_) {
+ case STATE_FETCH_PAC_SCRIPT_COMPLETE:
+ proxy_script_fetcher_->Cancel();
+ break;
+ case STATE_SET_PAC_SCRIPT_COMPLETE:
+ resolver_->CancelSetPacScript();
+ break;
+ default:
+ break;
+ }
+}
+
+int InitProxyResolver::Init(const ProxyConfig& config,
+ CompletionCallback* callback) {
+ DCHECK_EQ(STATE_NONE, next_state_);
+ DCHECK(callback);
+ DCHECK(config.MayRequirePACResolver());
+
+ pac_urls_ = BuildPacUrlsFallbackList(config);
+ DCHECK(!pac_urls_.empty());
+
+ next_state_ = GetStartState();
+
+ int rv = DoLoop(OK);
+ if (rv == ERR_IO_PENDING)
+ user_callback_ = callback;
+ return rv;
+}
+
+// Initialize the fallback rules.
+// (1) WPAD
+// (2) Custom PAC URL.
+InitProxyResolver::UrlList InitProxyResolver::BuildPacUrlsFallbackList(
+ const ProxyConfig& config) const {
+ UrlList pac_urls;
+ if (config.auto_detect) {
+ GURL pac_url = resolver_->expects_pac_bytes() ?
+ GURL("http://wpad/wpad.dat") : GURL();
+ pac_urls.push_back(pac_url);
+ }
+ if (config.pac_url.is_valid())
+ pac_urls.push_back(config.pac_url);
+ return pac_urls;
+}
+
+void InitProxyResolver::OnIOCompletion(int result) {
+ DCHECK_NE(STATE_NONE, next_state_);
+ int rv = DoLoop(result);
+ if (rv != ERR_IO_PENDING)
+ DoCallback(rv);
+}
+
+int InitProxyResolver::DoLoop(int result) {
+ DCHECK_NE(next_state_, STATE_NONE);
+ int rv = result;
+ do {
+ State state = next_state_;
+ next_state_ = STATE_NONE;
+ switch (state) {
+ case STATE_FETCH_PAC_SCRIPT:
+ DCHECK_EQ(OK, rv);
+ rv = DoFetchPacScript();
+ break;
+ case STATE_FETCH_PAC_SCRIPT_COMPLETE:
+ rv = DoFetchPacScriptComplete(rv);
+ break;
+ case STATE_SET_PAC_SCRIPT:
+ DCHECK_EQ(OK, rv);
+ rv = DoSetPacScript();
+ break;
+ case STATE_SET_PAC_SCRIPT_COMPLETE:
+ rv = DoSetPacScriptComplete(rv);
+ break;
+ default:
+ NOTREACHED() << "bad state";
+ rv = ERR_UNEXPECTED;
+ break;
+ }
+ } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
+ return rv;
+}
+
+void InitProxyResolver::DoCallback(int result) {
+ DCHECK_NE(ERR_IO_PENDING, result);
+ DCHECK(user_callback_);
+ user_callback_->Run(result);
+}
+
+int InitProxyResolver::DoFetchPacScript() {
+ DCHECK(resolver_->expects_pac_bytes());
+
+ next_state_ = STATE_FETCH_PAC_SCRIPT_COMPLETE;
+
+ const GURL& pac_url = current_pac_url();
+
+ LOG(INFO) << "Starting fetch of PAC script " << pac_url;
+
+ return proxy_script_fetcher_->Fetch(pac_url, &pac_bytes_, &io_callback_);
+}
+
+int InitProxyResolver::DoFetchPacScriptComplete(int result) {
+ DCHECK(resolver_->expects_pac_bytes());
+
+ LOG(INFO) << "Completed PAC script fetch of " << current_pac_url()
+ << " with result " << ErrorToString(result)
+ << ". Fetched a total of " << pac_bytes_.size() << " bytes";
+
+ if (result != OK)
+ return TryToFallbackPacUrl(result);
+
+ next_state_ = STATE_SET_PAC_SCRIPT;
+ return result;
+}
+
+int InitProxyResolver::DoSetPacScript() {
+ const GURL& pac_url = current_pac_url();
+
+ next_state_ = STATE_SET_PAC_SCRIPT_COMPLETE;
+
+ return resolver_->expects_pac_bytes() ?
+ resolver_->SetPacScriptByData(pac_bytes_, &io_callback_) :
+ resolver_->SetPacScriptByUrl(pac_url, &io_callback_);
+}
+
+int InitProxyResolver::DoSetPacScriptComplete(int result) {
+ if (result != OK) {
+ LOG(INFO) << "Failed configuring PAC using " << current_pac_url()
+ << " with error " << ErrorToString(result);
+ return TryToFallbackPacUrl(result);
+ }
+ return result;
+}
+
+int InitProxyResolver::TryToFallbackPacUrl(int error) {
+ DCHECK_LT(error, 0);
+
+ if (current_pac_url_index_ + 1 >= pac_urls_.size()) {
+ // Nothing left to fall back to.
+ return error;
+ }
+
+ // Advance to next URL in our list.
+ ++current_pac_url_index_;
+
+ LOG(INFO) << "Falling back to next PAC URL...";
+
+ next_state_ = GetStartState();
+
+ return OK;
+}
+
+InitProxyResolver::State InitProxyResolver::GetStartState() const {
+ return resolver_->expects_pac_bytes() ?
+ STATE_FETCH_PAC_SCRIPT : STATE_SET_PAC_SCRIPT;
+}
+
+const GURL& InitProxyResolver::current_pac_url() const {
+ DCHECK_LT(current_pac_url_index_, pac_urls_.size());
+ return pac_urls_[current_pac_url_index_];
+}
+
+} // namespace net
diff --git a/net/proxy/init_proxy_resolver.h b/net/proxy/init_proxy_resolver.h
new file mode 100644
index 0000000..4da28f2
--- /dev/null
+++ b/net/proxy/init_proxy_resolver.h
@@ -0,0 +1,104 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_PROXY_INIT_PROXY_RESOLVER_H_
+#define NET_PROXY_INIT_PROXY_RESOLVER_H_
+
+#include <string>
+#include <vector>
+
+#include "googleurl/src/gurl.h"
+#include "net/base/completion_callback.h"
+
+namespace net {
+
+class ProxyConfig;
+class ProxyResolver;
+class ProxyScriptFetcher;
+
+// InitProxyResolver is a helper class used by ProxyService to
+// initialize a ProxyResolver with the PAC script data specified
+// by a particular ProxyConfig.
+//
+// This involves trying to use PAC scripts in this order:
+//
+// (1) WPAD if auto-detect is on.
+// (2) Custom PAC script if a URL was given.
+//
+// If no PAC script was successfully downloaded + parsed, then it fails with
+// a network error. Otherwise the proxy resolver is left initialized with
+// the PAC script.
+//
+// Deleting InitProxyResolver while Init() is in progress, will
+// cancel the request.
+//
+class InitProxyResolver {
+ public:
+ // |resolver| and |proxy_script_fetcher| must remain valid for
+ // the lifespan of InitProxyResolver.
+ InitProxyResolver(ProxyResolver* resolver,
+ ProxyScriptFetcher* proxy_script_fetcher);
+
+ // Aborts any in-progress request.
+ ~InitProxyResolver();
+
+ // Apply the PAC settings of |config| to |resolver_|.
+ int Init(const ProxyConfig& config, CompletionCallback* callback);
+
+ private:
+ enum State {
+ STATE_NONE,
+ STATE_FETCH_PAC_SCRIPT,
+ STATE_FETCH_PAC_SCRIPT_COMPLETE,
+ STATE_SET_PAC_SCRIPT,
+ STATE_SET_PAC_SCRIPT_COMPLETE,
+ };
+ typedef std::vector<GURL> UrlList;
+
+ // Returns ordered list of PAC urls to try for |config|.
+ UrlList BuildPacUrlsFallbackList(const ProxyConfig& config) const;
+
+ void OnIOCompletion(int result);
+ int DoLoop(int result);
+ void DoCallback(int result);
+
+ int DoFetchPacScript();
+ int DoFetchPacScriptComplete(int result);
+
+ int DoSetPacScript();
+ int DoSetPacScriptComplete(int result);
+
+ // Tries restarting using the next fallback PAC URL:
+ // |pac_urls_[++current_pac_url_index]|.
+ // Returns OK and rewinds the state machine when there
+ // is something to try, otherwise returns |error|.
+ int TryToFallbackPacUrl(int error);
+
+ // Gets the initial state (we skip fetching when the
+ // ProxyResolver doesn't |expect_pac_bytes()|.
+ State GetStartState() const;
+
+ // Returns the current PAC URL we are fetching/testing.
+ const GURL& current_pac_url() const;
+
+ ProxyResolver* resolver_;
+ ProxyScriptFetcher* proxy_script_fetcher_;
+
+ CompletionCallbackImpl<InitProxyResolver> io_callback_;
+ CompletionCallback* user_callback_;
+
+ size_t current_pac_url_index_;
+
+ // Filled when the PAC script fetch completes.
+ std::string pac_bytes_;
+
+ UrlList pac_urls_;
+ State next_state_;
+
+ DISALLOW_COPY_AND_ASSIGN(InitProxyResolver);
+};
+
+} // namespace net
+
+#endif // NET_PROXY_INIT_PROXY_RESOLVER_H_
diff --git a/net/proxy/init_proxy_resolver_unittest.cc b/net/proxy/init_proxy_resolver_unittest.cc
new file mode 100644
index 0000000..1f4417d
--- /dev/null
+++ b/net/proxy/init_proxy_resolver_unittest.cc
@@ -0,0 +1,325 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <vector>
+
+#include "net/base/net_errors.h"
+#include "net/base/test_completion_callback.h"
+#include "net/proxy/init_proxy_resolver.h"
+#include "net/proxy/proxy_config.h"
+#include "net/proxy/proxy_resolver.h"
+#include "net/proxy/proxy_script_fetcher.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace {
+
+enum Error {
+ kFailedDownloading = -100,
+ kFailedParsing = -200,
+};
+
+class Rules {
+ public:
+ struct Rule {
+ Rule(const GURL& url,
+ int fetch_error,
+ int set_pac_error)
+ : url(url),
+ fetch_error(fetch_error),
+ set_pac_error(set_pac_error) {
+ }
+
+ std::string bytes() const {
+ if (set_pac_error == OK)
+ return url.spec() + "!valid-script";
+ if (fetch_error == OK)
+ return url.spec() + "!invalid-script";
+ return std::string();
+ }
+
+ GURL url;
+ int fetch_error;
+ int set_pac_error;
+ };
+
+ Rule AddSuccessRule(const char* url) {
+ Rule rule(GURL(url), OK /*fetch_error*/, OK /*set_pac_error*/);
+ rules_.push_back(rule);
+ return rule;
+ }
+
+ void AddFailDownloadRule(const char* url) {
+ rules_.push_back(Rule(GURL(url), kFailedDownloading /*fetch_error*/,
+ ERR_UNEXPECTED /*set_pac_error*/));
+ }
+
+ void AddFailParsingRule(const char* url) {
+ rules_.push_back(Rule(GURL(url), OK /*fetch_error*/,
+ kFailedParsing /*set_pac_error*/));
+ }
+
+ const Rule& GetRuleByUrl(const GURL& url) const {
+ for (RuleList::const_iterator it = rules_.begin(); it != rules_.end();
+ ++it) {
+ if (it->url == url)
+ return *it;
+ }
+ CHECK(false) << "Rule not found for " << url;
+ return rules_[0];
+ }
+
+ const Rule& GetRuleByBytes(const std::string& bytes) const {
+ for (RuleList::const_iterator it = rules_.begin(); it != rules_.end();
+ ++it) {
+ if (it->bytes() == bytes)
+ return *it;
+ }
+ CHECK(false) << "Rule not found for " << bytes;
+ return rules_[0];
+ }
+
+ private:
+ typedef std::vector<Rule> RuleList;
+ RuleList rules_;
+};
+
+class RuleBasedProxyScriptFetcher : public ProxyScriptFetcher {
+ public:
+ explicit RuleBasedProxyScriptFetcher(const Rules* rules) : rules_(rules) {}
+
+ // ProxyScriptFetcher implementation.
+ virtual int Fetch(const GURL& url,
+ std::string* bytes,
+ CompletionCallback* callback) {
+ const Rules::Rule& rule = rules_->GetRuleByUrl(url);
+ int rv = rule.fetch_error;
+ EXPECT_NE(ERR_UNEXPECTED, rv);
+ if (rv == OK)
+ *bytes = rule.bytes();
+ return rv;
+ }
+
+ virtual void Cancel() {}
+
+ private:
+ const Rules* rules_;
+};
+
+class RuleBasedProxyResolver : public ProxyResolver {
+ public:
+ RuleBasedProxyResolver(const Rules* rules, bool expects_pac_bytes)
+ : ProxyResolver(expects_pac_bytes), rules_(rules) {}
+
+ // ProxyResolver implementation:
+ virtual int GetProxyForURL(const GURL& /*url*/,
+ ProxyInfo* /*results*/,
+ CompletionCallback* /*callback*/,
+ RequestHandle* /*request_handle*/) {
+ NOTREACHED();
+ return ERR_UNEXPECTED;
+ }
+
+ virtual void CancelRequest(RequestHandle request_handle) {
+ NOTREACHED();
+ }
+
+ virtual int SetPacScript(const GURL& pac_url,
+ const std::string& pac_bytes,
+ CompletionCallback* callback) {
+ const Rules::Rule& rule = expects_pac_bytes() ?
+ rules_->GetRuleByBytes(pac_bytes) :
+ rules_->GetRuleByUrl(pac_url);
+
+ int rv = rule.set_pac_error;
+ EXPECT_NE(ERR_UNEXPECTED, rv);
+
+ if (expects_pac_bytes())
+ EXPECT_EQ(rule.bytes(), pac_bytes);
+ else
+ EXPECT_EQ(rule.url, pac_url);
+
+ if (rv == OK) {
+ pac_bytes_ = pac_bytes;
+ pac_url_ = pac_url;
+ }
+ return rv;
+ }
+
+ const std::string& pac_bytes() const { return pac_bytes_; }
+ const GURL& pac_url() const { return pac_url_; }
+
+ private:
+ const Rules* rules_;
+ std::string pac_bytes_;
+ GURL pac_url_;
+};
+
+// Succeed using custom PAC script.
+TEST(InitProxyResolverTest, CustomPacSucceeds) {
+ Rules rules;
+ RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
+ RuleBasedProxyScriptFetcher fetcher(&rules);
+
+ ProxyConfig config;
+ config.pac_url = GURL("http://custom/proxy.pac");
+
+ Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac");
+
+ TestCompletionCallback callback;
+ InitProxyResolver init(&resolver, &fetcher);
+ EXPECT_EQ(OK, init.Init(config, &callback));
+ EXPECT_EQ(rule.bytes(), resolver.pac_bytes());
+}
+
+// Fail downloading the custom PAC script.
+TEST(InitProxyResolverTest, CustomPacFails1) {
+ Rules rules;
+ RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
+ RuleBasedProxyScriptFetcher fetcher(&rules);
+
+ ProxyConfig config;
+ config.pac_url = GURL("http://custom/proxy.pac");
+
+ rules.AddFailDownloadRule("http://custom/proxy.pac");
+
+ TestCompletionCallback callback;
+ InitProxyResolver init(&resolver, &fetcher);
+ EXPECT_EQ(kFailedDownloading, init.Init(config, &callback));
+ EXPECT_EQ("", resolver.pac_bytes());
+}
+
+// Fail parsing the custom PAC script.
+TEST(InitProxyResolverTest, CustomPacFails2) {
+ Rules rules;
+ RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
+ RuleBasedProxyScriptFetcher fetcher(&rules);
+
+ ProxyConfig config;
+ config.pac_url = GURL("http://custom/proxy.pac");
+
+ rules.AddFailParsingRule("http://custom/proxy.pac");
+
+ TestCompletionCallback callback;
+ InitProxyResolver init(&resolver, &fetcher);
+ EXPECT_EQ(kFailedParsing, init.Init(config, &callback));
+ EXPECT_EQ("", resolver.pac_bytes());
+}
+
+// Succeeds in choosing autodetect (wpad).
+TEST(InitProxyResolverTest, AutodetectSuccess) {
+ Rules rules;
+ RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
+ RuleBasedProxyScriptFetcher fetcher(&rules);
+
+ ProxyConfig config;
+ config.auto_detect = true;
+
+ Rules::Rule rule = rules.AddSuccessRule("http://wpad/wpad.dat");
+
+ TestCompletionCallback callback;
+ InitProxyResolver init(&resolver, &fetcher);
+ EXPECT_EQ(OK, init.Init(config, &callback));
+ EXPECT_EQ(rule.bytes(), resolver.pac_bytes());
+}
+
+// Fails at WPAD (downloading), but succeeds in choosing the custom PAC.
+TEST(InitProxyResolverTest, AutodetectFailCustomSuccess1) {
+ Rules rules;
+ RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
+ RuleBasedProxyScriptFetcher fetcher(&rules);
+
+ ProxyConfig config;
+ config.auto_detect = true;
+ config.pac_url = GURL("http://custom/proxy.pac");
+
+ rules.AddFailDownloadRule("http://wpad/wpad.dat");
+ Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac");
+
+ TestCompletionCallback callback;
+ InitProxyResolver init(&resolver, &fetcher);
+ EXPECT_EQ(OK, init.Init(config, &callback));
+ EXPECT_EQ(rule.bytes(), resolver.pac_bytes());
+}
+
+// Fails at WPAD (parsing), but succeeds in choosing the custom PAC.
+TEST(InitProxyResolverTest, AutodetectFailCustomSuccess2) {
+ Rules rules;
+ RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
+ RuleBasedProxyScriptFetcher fetcher(&rules);
+
+ ProxyConfig config;
+ config.auto_detect = true;
+ config.pac_url = GURL("http://custom/proxy.pac");
+
+ rules.AddFailParsingRule("http://wpad/wpad.dat");
+ Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac");
+
+ TestCompletionCallback callback;
+ InitProxyResolver init(&resolver, &fetcher);
+ EXPECT_EQ(OK, init.Init(config, &callback));
+ EXPECT_EQ(rule.bytes(), resolver.pac_bytes());
+}
+
+// Fails at WPAD (downloading), and fails at custom PAC (downloading).
+TEST(InitProxyResolverTest, AutodetectFailCustomFails1) {
+ Rules rules;
+ RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
+ RuleBasedProxyScriptFetcher fetcher(&rules);
+
+ ProxyConfig config;
+ config.auto_detect = true;
+ config.pac_url = GURL("http://custom/proxy.pac");
+
+ rules.AddFailDownloadRule("http://wpad/wpad.dat");
+ rules.AddFailDownloadRule("http://custom/proxy.pac");
+
+ TestCompletionCallback callback;
+ InitProxyResolver init(&resolver, &fetcher);
+ EXPECT_EQ(kFailedDownloading, init.Init(config, &callback));
+ EXPECT_EQ("", resolver.pac_bytes());
+}
+
+// Fails at WPAD (downloading), and fails at custom PAC (parsing).
+TEST(InitProxyResolverTest, AutodetectFailCustomFails2) {
+ Rules rules;
+ RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
+ RuleBasedProxyScriptFetcher fetcher(&rules);
+
+ ProxyConfig config;
+ config.auto_detect = true;
+ config.pac_url = GURL("http://custom/proxy.pac");
+
+ rules.AddFailDownloadRule("http://wpad/wpad.dat");
+ rules.AddFailParsingRule("http://custom/proxy.pac");
+
+ TestCompletionCallback callback;
+ InitProxyResolver init(&resolver, &fetcher);
+ EXPECT_EQ(kFailedParsing, init.Init(config, &callback));
+ EXPECT_EQ("", resolver.pac_bytes());
+}
+
+// Fails at WPAD (parsing), but succeeds in choosing the custom PAC.
+// This is the same as AutodetectFailCustomSuccess2, but using a ProxyResolver
+// that doesn't |expects_pac_bytes|.
+TEST(InitProxyResolverTest, AutodetectFailCustomSuccess2_NoFetch) {
+ Rules rules;
+ RuleBasedProxyResolver resolver(&rules, false /*expects_pac_bytes*/);
+ RuleBasedProxyScriptFetcher fetcher(&rules);
+
+ ProxyConfig config;
+ config.auto_detect = true;
+ config.pac_url = GURL("http://custom/proxy.pac");
+
+ rules.AddFailParsingRule(""); // Autodetect.
+ Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac");
+
+ TestCompletionCallback callback;
+ InitProxyResolver init(&resolver, &fetcher);
+ EXPECT_EQ(OK, init.Init(config, &callback));
+ EXPECT_EQ(rule.url, resolver.pac_url());
+}
+
+} // namespace
+} // namespace net
diff --git a/net/proxy/proxy_resolver.h b/net/proxy/proxy_resolver.h
index ce0e81d..89fffb1 100644
--- a/net/proxy/proxy_resolver.h
+++ b/net/proxy/proxy_resolver.h
@@ -8,10 +8,9 @@
#include <string>
#include "base/logging.h"
+#include "googleurl/src/gurl.h"
#include "net/base/completion_callback.h"
-class GURL;
-
namespace net {
class ProxyInfo;
@@ -52,29 +51,32 @@ class ProxyResolver {
bool expects_pac_bytes() const { return expects_pac_bytes_; }
// Sets the PAC script backend to use for this proxy resolver (by URL).
- void SetPacScriptByUrl(const GURL& pac_url) {
+ int SetPacScriptByUrl(const GURL& url, CompletionCallback* callback) {
DCHECK(!expects_pac_bytes());
- SetPacScriptByUrlInternal(pac_url);
+ return SetPacScript(url, std::string(), callback);
}
// Sets the PAC script backend to use for this proxy resolver (by contents).
- void SetPacScriptByData(const std::string& bytes) {
+ int SetPacScriptByData(const std::string& bytes,
+ CompletionCallback* callback) {
DCHECK(expects_pac_bytes());
- SetPacScriptByDataInternal(bytes);
+ return SetPacScript(GURL(), bytes, callback);
}
- private:
- // Called to set the PAC script backend to use. If |pac_url| is invalid,
- // this is a request to use WPAD (auto detect).
- virtual void SetPacScriptByUrlInternal(const GURL& pac_url) {
+ // TODO(eroman): Make this =0.
+ virtual void CancelSetPacScript() {
NOTREACHED();
}
- // Called to set the PAC script backend to use. |bytes| may be empty if the
+ private:
+ // Called to set the PAC script backend to use. If |pac_url| is invalid,
+ // this is a request to use WPAD (auto detect). |bytes| may be empty if the
// fetch failed, or if the fetch returned no content.
- virtual void SetPacScriptByDataInternal(const std::string& bytes) {
- NOTREACHED();
- }
+ // Returns ERR_IO_PENDING in the case of asynchronous completion, and notifies
+ // the result through |callback|.
+ virtual int SetPacScript(const GURL& pac_url,
+ const std::string& bytes,
+ CompletionCallback* callback) = 0;
const bool expects_pac_bytes_;
diff --git a/net/proxy/proxy_resolver_mac.h b/net/proxy/proxy_resolver_mac.h
index 58f6096..6bad1d2 100644
--- a/net/proxy/proxy_resolver_mac.h
+++ b/net/proxy/proxy_resolver_mac.h
@@ -8,6 +8,7 @@
#include <string>
#include "googleurl/src/gurl.h"
+#include "net/base/net_errors.h"
#include "net/proxy/proxy_config_service.h"
#include "net/proxy/proxy_resolver.h"
@@ -30,8 +31,11 @@ class ProxyResolverMac : public ProxyResolver {
}
private:
- virtual void SetPacScriptByUrlInternal(const GURL& pac_url) {
+ virtual int SetPacScript(const GURL& pac_url,
+ const std::string& /*pac_bytes*/,
+ CompletionCallback* /*callback*/) {
pac_url_ = pac_url;
+ return OK;
}
GURL pac_url_;
diff --git a/net/proxy/proxy_resolver_perftest.cc b/net/proxy/proxy_resolver_perftest.cc
index cb20ff9..efaf437 100644
--- a/net/proxy/proxy_resolver_perftest.cc
+++ b/net/proxy/proxy_resolver_perftest.cc
@@ -97,7 +97,8 @@ class PacPerfSuiteRunner {
InitHttpServer();
GURL pac_url =
server_->TestServerPage(std::string("files/") + script_name);
- resolver_->SetPacScriptByUrl(pac_url);
+ int rv = resolver_->SetPacScriptByUrl(pac_url, NULL);
+ EXPECT_EQ(net::OK, rv);
} else {
LoadPacScriptIntoResolver(script_name);
}
@@ -163,7 +164,8 @@ class PacPerfSuiteRunner {
ASSERT_TRUE(ok);
// Load the PAC script into the ProxyResolver.
- resolver_->SetPacScriptByData(file_contents);
+ int rv = resolver_->SetPacScriptByData(file_contents, NULL);
+ EXPECT_EQ(net::OK, rv);
}
net::ProxyResolver* resolver_;
diff --git a/net/proxy/proxy_resolver_v8.cc b/net/proxy/proxy_resolver_v8.cc
index 0b77145..8c40ecf 100644
--- a/net/proxy/proxy_resolver_v8.cc
+++ b/net/proxy/proxy_resolver_v8.cc
@@ -53,10 +53,9 @@ bool V8ObjectToString(v8::Handle<v8::Value> object, std::string* result) {
class ProxyResolverV8::Context {
public:
- Context(ProxyResolverJSBindings* js_bindings, const std::string& pac_data)
+ explicit Context(ProxyResolverJSBindings* js_bindings)
: js_bindings_(js_bindings) {
DCHECK(js_bindings != NULL);
- InitV8(pac_data);
}
~Context() {
@@ -72,9 +71,8 @@ class ProxyResolverV8::Context {
v8::Context::Scope function_scope(v8_context_);
- v8::Local<v8::Value> function =
- v8_context_->Global()->Get(v8::String::New("FindProxyForURL"));
- if (!function->IsFunction()) {
+ v8::Local<v8::Value> function;
+ if (!GetFindProxyForURL(&function)) {
js_bindings_->OnError(-1, "FindProxyForURL() is undefined.");
return ERR_PAC_SCRIPT_FAILED;
}
@@ -105,8 +103,7 @@ class ProxyResolverV8::Context {
return OK;
}
- private:
- void InitV8(const std::string& pac_data) {
+ int InitV8(const std::string& pac_data) {
v8::Locker locked;
v8::HandleScope scope;
@@ -145,8 +142,24 @@ class ProxyResolverV8::Context {
if (!code.IsEmpty())
code->Run();
- if (try_catch.HasCaught())
+ if (try_catch.HasCaught()) {
HandleError(try_catch.Message());
+ return ERR_PAC_SCRIPT_FAILED;
+ }
+
+ // At a minimum, the FindProxyForURL() function must be defined for this
+ // to be a legitimiate PAC script.
+ v8::Local<v8::Value> function;
+ if (!GetFindProxyForURL(&function))
+ return ERR_PAC_SCRIPT_FAILED;
+
+ return OK;
+ }
+
+ private:
+ bool GetFindProxyForURL(v8::Local<v8::Value>* function) {
+ *function = v8_context_->Global()->Get(v8::String::New("FindProxyForURL"));
+ return (*function)->IsFunction();
}
// Handle an exception thrown by V8.
@@ -233,8 +246,7 @@ int ProxyResolverV8::GetProxyForURL(const GURL& query_url,
CompletionCallback* /*callback*/,
RequestHandle* /*request*/) {
// If the V8 instance has not been initialized (either because
- // SetPacScriptByData() wasn't called yet, or because it was called with
- // empty string).
+ // SetPacScript() wasn't called yet, or because it failed.
if (!context_.get())
return ERR_FAILED;
@@ -247,10 +259,19 @@ void ProxyResolverV8::CancelRequest(RequestHandle request) {
NOTREACHED();
}
-void ProxyResolverV8::SetPacScriptByDataInternal(const std::string& data) {
+int ProxyResolverV8::SetPacScript(const GURL& /*url*/,
+ const std::string& bytes,
+ CompletionCallback* /*callback*/) {
context_.reset();
- if (!data.empty())
- context_.reset(new Context(js_bindings_.get(), data));
+ if (bytes.empty())
+ return ERR_PAC_SCRIPT_FAILED;
+
+ // Try parsing the PAC script.
+ scoped_ptr<Context> context(new Context(js_bindings_.get()));
+ int rv = context->InitV8(bytes);
+ if (rv == OK)
+ context_.reset(context.release());
+ return rv;
}
} // namespace net
diff --git a/net/proxy/proxy_resolver_v8.h b/net/proxy/proxy_resolver_v8.h
index 6689191..f746ecb 100644
--- a/net/proxy/proxy_resolver_v8.h
+++ b/net/proxy/proxy_resolver_v8.h
@@ -56,12 +56,13 @@ class ProxyResolverV8 : public ProxyResolver {
private:
// Context holds the Javascript state for the most recently loaded PAC
// script. It corresponds with the data from the last call to
- // SetPacScriptByDataInternal().
+ // SetPacScript().
class Context;
// ProxyResolver implementation:
- virtual void SetPacScriptByDataInternal(const std::string& bytes);
-
+ virtual int SetPacScript(const GURL& /*pac_url*/,
+ const std::string& bytes,
+ CompletionCallback* /*callback*/);
scoped_ptr<Context> context_;
scoped_ptr<ProxyResolverJSBindings> js_bindings_;
diff --git a/net/proxy/proxy_resolver_v8_unittest.cc b/net/proxy/proxy_resolver_v8_unittest.cc
index 20d878b..1aecb12 100644
--- a/net/proxy/proxy_resolver_v8_unittest.cc
+++ b/net/proxy/proxy_resolver_v8_unittest.cc
@@ -68,7 +68,7 @@ class ProxyResolverV8WithMockBindings : public net::ProxyResolverV8 {
}
// Initialize with the PAC script data at |filename|.
- void SetPacScriptFromDisk(const char* filename) {
+ int SetPacScriptFromDisk(const char* filename) {
FilePath path;
PathService::Get(base::DIR_SOURCE_ROOT, &path);
path = path.AppendASCII("net");
@@ -81,11 +81,13 @@ class ProxyResolverV8WithMockBindings : public net::ProxyResolverV8 {
bool ok = file_util::ReadFileToString(path, &file_contents);
// If we can't load the file from disk, something is misconfigured.
- LOG_IF(ERROR, !ok) << "Failed to read file: " << path.value();
- ASSERT_TRUE(ok);
+ if (!ok) {
+ LOG(ERROR) << "Failed to read file: " << path.value();
+ return net::ERR_UNEXPECTED;
+ }
// Load the PAC script into the ProxyResolver.
- SetPacScriptByData(file_contents);
+ return SetPacScriptByData(file_contents, NULL);
}
};
@@ -97,10 +99,11 @@ const GURL kPacUrl;
TEST(ProxyResolverV8Test, Direct) {
ProxyResolverV8WithMockBindings resolver;
- resolver.SetPacScriptFromDisk("direct.js");
+ int result = resolver.SetPacScriptFromDisk("direct.js");
+ EXPECT_EQ(net::OK, result);
net::ProxyInfo proxy_info;
- int result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL);
+ result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL);
EXPECT_EQ(net::OK, result);
EXPECT_TRUE(proxy_info.is_direct());
@@ -111,10 +114,11 @@ TEST(ProxyResolverV8Test, Direct) {
TEST(ProxyResolverV8Test, ReturnEmptyString) {
ProxyResolverV8WithMockBindings resolver;
- resolver.SetPacScriptFromDisk("return_empty_string.js");
+ int result = resolver.SetPacScriptFromDisk("return_empty_string.js");
+ EXPECT_EQ(net::OK, result);
net::ProxyInfo proxy_info;
- int result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL);
+ result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL);
EXPECT_EQ(net::OK, result);
EXPECT_TRUE(proxy_info.is_direct());
@@ -125,14 +129,15 @@ TEST(ProxyResolverV8Test, ReturnEmptyString) {
TEST(ProxyResolverV8Test, Basic) {
ProxyResolverV8WithMockBindings resolver;
- resolver.SetPacScriptFromDisk("passthrough.js");
+ int result = resolver.SetPacScriptFromDisk("passthrough.js");
+ EXPECT_EQ(net::OK, result);
// The "FindProxyForURL" of this PAC script simply concatenates all of the
// arguments into a pseudo-host. The purpose of this test is to verify that
// the correct arguments are being passed to FindProxyForURL().
{
net::ProxyInfo proxy_info;
- int result = resolver.GetProxyForURL(GURL("http://query.com/path"),
+ result = resolver.GetProxyForURL(GURL("http://query.com/path"),
&proxy_info, NULL, NULL);
EXPECT_EQ(net::OK, result);
EXPECT_EQ("http.query.com.path.query.com:80",
@@ -168,10 +173,11 @@ TEST(ProxyResolverV8Test, BadReturnType) {
for (size_t i = 0; i < arraysize(filenames); ++i) {
ProxyResolverV8WithMockBindings resolver;
- resolver.SetPacScriptFromDisk(filenames[i]);
+ int result = resolver.SetPacScriptFromDisk(filenames[i]);
+ EXPECT_EQ(net::OK, result);
net::ProxyInfo proxy_info;
- int result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL);
+ result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL);
EXPECT_EQ(net::ERR_PAC_SCRIPT_FAILED, result);
@@ -187,54 +193,46 @@ TEST(ProxyResolverV8Test, BadReturnType) {
// Try using a PAC script which defines no "FindProxyForURL" function.
TEST(ProxyResolverV8Test, NoEntryPoint) {
ProxyResolverV8WithMockBindings resolver;
- resolver.SetPacScriptFromDisk("no_entrypoint.js");
+ int result = resolver.SetPacScriptFromDisk("no_entrypoint.js");
+ EXPECT_EQ(net::ERR_PAC_SCRIPT_FAILED, result);
net::ProxyInfo proxy_info;
- int result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL);
-
- EXPECT_EQ(net::ERR_PAC_SCRIPT_FAILED, result);
+ result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL);
- MockJSBindings* bindings = resolver.mock_js_bindings();
- EXPECT_EQ(0U, bindings->alerts.size());
- ASSERT_EQ(1U, bindings->errors.size());
- EXPECT_EQ("FindProxyForURL() is undefined.", bindings->errors[0]);
- EXPECT_EQ(-1, bindings->errors_line_number[0]);
+ EXPECT_EQ(net::ERR_FAILED, result);
}
// Try loading a malformed PAC script.
TEST(ProxyResolverV8Test, ParseError) {
ProxyResolverV8WithMockBindings resolver;
- resolver.SetPacScriptFromDisk("missing_close_brace.js");
+ int result = resolver.SetPacScriptFromDisk("missing_close_brace.js");
+ EXPECT_EQ(net::ERR_PAC_SCRIPT_FAILED, result);
net::ProxyInfo proxy_info;
- int result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL);
+ result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL);
- EXPECT_EQ(net::ERR_PAC_SCRIPT_FAILED, result);
+ EXPECT_EQ(net::ERR_FAILED, result);
MockJSBindings* bindings = resolver.mock_js_bindings();
EXPECT_EQ(0U, bindings->alerts.size());
- // We get two errors -- one during compilation, and then later when
- // trying to run FindProxyForURL().
- ASSERT_EQ(2U, bindings->errors.size());
+ // We get one error during compilation.
+ ASSERT_EQ(1U, bindings->errors.size());
EXPECT_EQ("Uncaught SyntaxError: Unexpected end of input",
bindings->errors[0]);
EXPECT_EQ(-1, bindings->errors_line_number[0]);
-
- EXPECT_EQ("FindProxyForURL() is undefined.", bindings->errors[1]);
- EXPECT_EQ(-1, bindings->errors_line_number[1]);
}
// Run a PAC script several times, which has side-effects.
TEST(ProxyResolverV8Test, SideEffects) {
ProxyResolverV8WithMockBindings resolver;
- resolver.SetPacScriptFromDisk("side_effects.js");
+ int result = resolver.SetPacScriptFromDisk("side_effects.js");
// The PAC script increments a counter each time we invoke it.
for (int i = 0; i < 3; ++i) {
net::ProxyInfo proxy_info;
- int result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL);
+ result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL);
EXPECT_EQ(net::OK, result);
EXPECT_EQ(StringPrintf("sideffect_%d:80", i),
proxy_info.proxy_server().ToURI());
@@ -242,11 +240,12 @@ TEST(ProxyResolverV8Test, SideEffects) {
// Reload the script -- the javascript environment should be reset, hence
// the counter starts over.
- resolver.SetPacScriptFromDisk("side_effects.js");
+ result = resolver.SetPacScriptFromDisk("side_effects.js");
+ EXPECT_EQ(net::OK, result);
for (int i = 0; i < 3; ++i) {
net::ProxyInfo proxy_info;
- int result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL);
+ result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL);
EXPECT_EQ(net::OK, result);
EXPECT_EQ(StringPrintf("sideffect_%d:80", i),
proxy_info.proxy_server().ToURI());
@@ -256,10 +255,11 @@ TEST(ProxyResolverV8Test, SideEffects) {
// Execute a PAC script which throws an exception in FindProxyForURL.
TEST(ProxyResolverV8Test, UnhandledException) {
ProxyResolverV8WithMockBindings resolver;
- resolver.SetPacScriptFromDisk("unhandled_exception.js");
+ int result = resolver.SetPacScriptFromDisk("unhandled_exception.js");
+ EXPECT_EQ(net::OK, result);
net::ProxyInfo proxy_info;
- int result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL);
+ result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL);
EXPECT_EQ(net::ERR_PAC_SCRIPT_FAILED, result);
@@ -275,10 +275,11 @@ TEST(ProxyResolverV8Test, UnhandledException) {
// host/port doesn't check for non-ascii characters.
TEST(ProxyResolverV8Test, DISABLED_ReturnUnicode) {
ProxyResolverV8WithMockBindings resolver;
- resolver.SetPacScriptFromDisk("return_unicode.js");
+ int result = resolver.SetPacScriptFromDisk("return_unicode.js");
+ EXPECT_EQ(net::OK, result);
net::ProxyInfo proxy_info;
- int result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL);
+ result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL);
// The result from this resolve was unparseable, because it
// wasn't ascii.
@@ -288,10 +289,11 @@ TEST(ProxyResolverV8Test, DISABLED_ReturnUnicode) {
// Test the PAC library functions that we expose in the JS environmnet.
TEST(ProxyResolverV8Test, JavascriptLibrary) {
ProxyResolverV8WithMockBindings resolver;
- resolver.SetPacScriptFromDisk("pac_library_unittest.js");
+ int result = resolver.SetPacScriptFromDisk("pac_library_unittest.js");
+ EXPECT_EQ(net::OK, result);
net::ProxyInfo proxy_info;
- int result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL);
+ result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL);
// If the javascript side of this unit-test fails, it will throw a javascript
// exception. Otherwise it will return "PROXY success:80".
@@ -313,21 +315,23 @@ TEST(ProxyResolverV8Test, NoSetPacScript) {
EXPECT_EQ(net::ERR_FAILED, result);
// Initialize it.
- resolver.SetPacScriptFromDisk("direct.js");
+ result = resolver.SetPacScriptFromDisk("direct.js");
+ EXPECT_EQ(net::OK, result);
// Resolve should now succeed.
result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL);
EXPECT_EQ(net::OK, result);
// Clear it, by initializing with an empty string.
- resolver.SetPacScriptByData(std::string());
+ resolver.SetPacScriptByData(std::string(), NULL);
// Resolve should fail again now.
result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL);
EXPECT_EQ(net::ERR_FAILED, result);
// Load a good script once more.
- resolver.SetPacScriptFromDisk("direct.js");
+ result = resolver.SetPacScriptFromDisk("direct.js");
+ EXPECT_EQ(net::OK, result);
result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL);
EXPECT_EQ(net::OK, result);
@@ -338,10 +342,11 @@ TEST(ProxyResolverV8Test, NoSetPacScript) {
// Test marshalling/un-marshalling of values between C++/V8.
TEST(ProxyResolverV8Test, V8Bindings) {
ProxyResolverV8WithMockBindings resolver;
- resolver.SetPacScriptFromDisk("bindings.js");
+ int result = resolver.SetPacScriptFromDisk("bindings.js");
+ EXPECT_EQ(net::OK, result);
net::ProxyInfo proxy_info;
- int result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL);
+ result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL);
EXPECT_EQ(net::OK, result);
EXPECT_TRUE(proxy_info.is_direct());
diff --git a/net/proxy/proxy_resolver_winhttp.cc b/net/proxy/proxy_resolver_winhttp.cc
index d4bca9c5..4c0848d 100644
--- a/net/proxy/proxy_resolver_winhttp.cc
+++ b/net/proxy/proxy_resolver_winhttp.cc
@@ -70,8 +70,7 @@ int ProxyResolverWinHttp::GetProxyForURL(const GURL& query_url,
options.fAutoLogonIfChallenged = FALSE;
options.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
std::wstring pac_url_wide = ASCIIToWide(pac_url_.spec());
- options.lpszAutoConfigUrl =
- pac_url_wide.empty() ? L"http://wpad/wpad.dat" : pac_url_wide.c_str();
+ options.lpszAutoConfigUrl = pac_url_wide.c_str();
WINHTTP_PROXY_INFO info = {0};
DCHECK(session_handle_);
@@ -140,8 +139,11 @@ void ProxyResolverWinHttp::CancelRequest(RequestHandle request) {
NOTREACHED();
}
-void ProxyResolverWinHttp::SetPacScriptByUrlInternal(const GURL& pac_url) {
- pac_url_ = pac_url;
+int ProxyResolverWinHttp::SetPacScript(const GURL& pac_url,
+ const std::string& /*pac_bytes*/,
+ CompletionCallback* /*callback*/) {
+ pac_url_ = pac_url.is_valid() ? pac_url : GURL("http://wpad/wpad.dat");
+ return OK;
}
bool ProxyResolverWinHttp::OpenWinHttpSession() {
diff --git a/net/proxy/proxy_resolver_winhttp.h b/net/proxy/proxy_resolver_winhttp.h
index 4932ba6..1d9a7e8 100644
--- a/net/proxy/proxy_resolver_winhttp.h
+++ b/net/proxy/proxy_resolver_winhttp.h
@@ -30,8 +30,9 @@ class ProxyResolverWinHttp : public ProxyResolver {
private:
// ProxyResolver implementation:
- virtual void SetPacScriptByUrlInternal(const GURL& pac_url);
-
+ virtual int SetPacScript(const GURL& pac_url,
+ const std::string& /*pac_bytes*/,
+ CompletionCallback* /*callback*/);
bool OpenWinHttpSession();
void CloseWinHttpSession();
diff --git a/net/proxy/proxy_script_fetcher.cc b/net/proxy/proxy_script_fetcher.cc
index eecff19..d93c5c2 100644
--- a/net/proxy/proxy_script_fetcher.cc
+++ b/net/proxy/proxy_script_fetcher.cc
@@ -57,8 +57,8 @@ class ProxyScriptFetcherImpl : public ProxyScriptFetcher,
// ProxyScriptFetcher methods:
- virtual void Fetch(const GURL& url, std::string* bytes,
- CompletionCallback* callback);
+ virtual int Fetch(const GURL& url, std::string* bytes,
+ CompletionCallback* callback);
virtual void Cancel();
// URLRequest::Delegate methods:
@@ -137,9 +137,9 @@ ProxyScriptFetcherImpl::~ProxyScriptFetcherImpl() {
// ensure that the delegate (this) is not called again.
}
-void ProxyScriptFetcherImpl::Fetch(const GURL& url,
- std::string* bytes,
- CompletionCallback* callback) {
+int ProxyScriptFetcherImpl::Fetch(const GURL& url,
+ std::string* bytes,
+ CompletionCallback* callback) {
// It is invalid to call Fetch() while a request is already in progress.
DCHECK(!cur_request_.get());
@@ -171,6 +171,7 @@ void ProxyScriptFetcherImpl::Fetch(const GURL& url,
// Start the request.
cur_request_->Start();
+ return ERR_IO_PENDING;
}
void ProxyScriptFetcherImpl::Cancel() {
diff --git a/net/proxy/proxy_script_fetcher.h b/net/proxy/proxy_script_fetcher.h
index fddbd7b..e88ab6b 100644
--- a/net/proxy/proxy_script_fetcher.h
+++ b/net/proxy/proxy_script_fetcher.h
@@ -9,6 +9,8 @@
#ifndef NET_PROXY_PROXY_SCRIPT_FETCHER_H_
#define NET_PROXY_PROXY_SCRIPT_FETCHER_H_
+#include <string>
+
#include "net/base/completion_callback.h"
#include "testing/gtest/include/gtest/gtest_prod.h"
@@ -37,8 +39,8 @@ class ProxyScriptFetcher {
// deleting |this|), then no callback is invoked.
//
// Only one fetch is allowed to be outstanding at a time.
- virtual void Fetch(const GURL& url, std::string* bytes,
- CompletionCallback* callback) = 0;
+ virtual int Fetch(const GURL& url, std::string* bytes,
+ CompletionCallback* callback) = 0;
// Aborts the in-progress fetch (if any).
virtual void Cancel() = 0;
diff --git a/net/proxy/proxy_script_fetcher_unittest.cc b/net/proxy/proxy_script_fetcher_unittest.cc
index 8a8030b..6dd05dc 100644
--- a/net/proxy/proxy_script_fetcher_unittest.cc
+++ b/net/proxy/proxy_script_fetcher_unittest.cc
@@ -62,7 +62,8 @@ class SynchFetcherThreadHelper {
// Starts fetching the script at |url|. Upon completion |event_| will be
// signalled, and the bytes read will have been written to |fetch_result_|.
void Start(const GURL& url) {
- fetcher_->Fetch(url, &fetch_result_->bytes, &callback_);
+ int rv = fetcher_->Fetch(url, &fetch_result_->bytes, &callback_);
+ EXPECT_EQ(net::ERR_IO_PENDING, rv);
}
void OnFetchCompletion(int result) {
diff --git a/net/proxy/proxy_service.cc b/net/proxy/proxy_service.cc
index ab18956..327e741 100644
--- a/net/proxy/proxy_service.cc
+++ b/net/proxy/proxy_service.cc
@@ -13,6 +13,7 @@
#include "googleurl/src/gurl.h"
#include "net/base/net_errors.h"
#include "net/base/net_util.h"
+#include "net/proxy/init_proxy_resolver.h"
#include "net/proxy/proxy_config_service_fixed.h"
#include "net/proxy/proxy_script_fetcher.h"
#if defined(OS_WIN)
@@ -61,7 +62,11 @@ class ProxyResolverNull : public ProxyResolver {
}
private:
- virtual void SetPacScriptByUrlInternal(const GURL& pac_url) {}
+ virtual int SetPacScript(const GURL& /*pac_url*/,
+ const std::string& /*pac_bytes*/,
+ CompletionCallback* /*callback*/) {
+ return ERR_NOT_IMPLEMENTED;
+ }
};
// ProxyService::PacRequest ---------------------------------------------------
@@ -100,18 +105,25 @@ class ProxyService::PacRequest
return !!resolve_job_;
}
- void StartAndComplete() {
- int rv = Start();
+ void StartAndCompleteCheckingForSynchronous() {
+ int rv = service_->TryToCompleteSynchronously(url_, results_);
+ if (rv == ERR_IO_PENDING)
+ rv = Start();
if (rv != ERR_IO_PENDING)
QueryComplete(rv);
}
- void Cancel() {
+ void CancelResolveJob() {
// The request may already be running in the resolver.
if (is_started()) {
resolver()->CancelRequest(resolve_job_);
resolve_job_ = NULL;
}
+ DCHECK(!is_started());
+ }
+
+ void Cancel() {
+ CancelResolveJob();
// Mark as cancelled, to prevent accessing this again later.
service_ = NULL;
@@ -177,11 +189,8 @@ ProxyService::ProxyService(ProxyConfigService* config_service,
resolver_(resolver),
next_config_id_(1),
config_is_bad_(false),
- ALLOW_THIS_IN_INITIALIZER_LIST(proxy_script_fetcher_callback_(
- this, &ProxyService::OnScriptFetchCompletion)),
- fetched_pac_config_id_(ProxyConfig::INVALID_ID),
- fetched_pac_error_(OK),
- in_progress_fetch_config_id_(ProxyConfig::INVALID_ID) {
+ ALLOW_THIS_IN_INITIALIZER_LIST(init_proxy_resolver_callback_(
+ this, &ProxyService::OnInitProxyResolverComplete)) {
}
// static
@@ -254,7 +263,7 @@ int ProxyService::ResolveProxy(const GURL& raw_url, ProxyInfo* result,
scoped_refptr<PacRequest> req = new PacRequest(this, url, result, callback);
- bool resolver_is_ready = PrepareResolverForRequests();
+ bool resolver_is_ready = !IsInitializingProxyResolver();
if (resolver_is_ready) {
// Start the resolve request.
@@ -290,22 +299,15 @@ int ProxyService::TryToCompleteSynchronously(const GURL& url,
// Remember that we are trying to use the current proxy configuration.
result->config_was_tried_ = true;
+ if (config_.MayRequirePACResolver()) {
+ // Need to go through ProxyResolver for this.
+ return ERR_IO_PENDING;
+ }
+
if (!config_.proxy_rules.empty()) {
ApplyProxyRules(url, config_.proxy_rules, result);
return OK;
}
-
- if (config_.pac_url.is_valid() || config_.auto_detect) {
- // If we failed to download the PAC script, return the network error
- // from the failed download. This is only going to happen for the first
- // request after the failed download -- after that |config_is_bad_| will
- // be set to true, so we short-cuircuit sooner.
- if (fetched_pac_error_ != OK && !IsFetchingPacScript()) {
- DidCompletePacRequest(fetched_pac_config_id_, fetched_pac_error_);
- return fetched_pac_error_;
- }
- return ERR_IO_PENDING;
- }
}
// otherwise, we have no proxy config
@@ -354,7 +356,17 @@ ProxyService::~ProxyService() {
}
}
+void ProxyService::SuspendAllPendingRequests() {
+ for (PendingRequests::iterator it = pending_requests_.begin();
+ it != pending_requests_.end();
+ ++it) {
+ it->get()->CancelResolveJob();
+ }
+}
+
void ProxyService::ResumeAllPendingRequests() {
+ DCHECK(!IsInitializingProxyResolver());
+
// 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
// instances will be Cancel()-ed.
@@ -364,57 +376,27 @@ void ProxyService::ResumeAllPendingRequests() {
it != pending_copy.end();
++it) {
PacRequest* req = it->get();
- if (!req->is_started() && !req->was_cancelled())
- req->StartAndComplete();
+ if (!req->is_started() && !req->was_cancelled()) {
+ // Note that we re-check for synchronous completion, in case we are
+ // no longer using a ProxyResolver (can happen if we fell-back to manual).
+ req->StartAndCompleteCheckingForSynchronous();
+ }
}
}
-bool ProxyService::PrepareResolverForRequests() {
- // While the PAC script is being downloaded, block requests.
- if (IsFetchingPacScript())
- return false;
-
- // Check if a new PAC script needs to be downloaded.
- DCHECK(config_.id() != ProxyConfig::INVALID_ID);
- if (resolver_->expects_pac_bytes() &&
- config_.id() != fetched_pac_config_id_) {
- // For auto-detect we use the well known WPAD url.
- GURL pac_url = config_.auto_detect ?
- GURL("http://wpad/wpad.dat") : config_.pac_url;
-
- in_progress_fetch_config_id_ = config_.id();
-
- LOG(INFO) << "Starting fetch of PAC script " << pac_url
- << " for config_id=" << in_progress_fetch_config_id_;
-
- proxy_script_fetcher_->Fetch(
- pac_url, &in_progress_fetch_bytes_, &proxy_script_fetcher_callback_);
- return false;
+void ProxyService::OnInitProxyResolverComplete(int result) {
+ DCHECK(init_proxy_resolver_.get());
+ DCHECK(config_.MayRequirePACResolver());
+ init_proxy_resolver_.reset();
+
+ if (result != OK) {
+ LOG(INFO) << "Failed configuring with PAC script, falling-back to manual "
+ "proxy servers.";
+ config_.auto_detect = false;
+ config_.pac_url = GURL();
+ DCHECK(!config_.MayRequirePACResolver());
}
- // We are good to go.
- return true;
-}
-
-void ProxyService::OnScriptFetchCompletion(int result) {
- DCHECK(IsFetchingPacScript());
- DCHECK(resolver_->expects_pac_bytes());
-
- LOG(INFO) << "Completed PAC script fetch for config_id="
- << in_progress_fetch_config_id_
- << " with error " << ErrorToString(result)
- << ". Fetched a total of " << in_progress_fetch_bytes_.size()
- << " bytes";
-
- // Notify the ProxyResolver of the new script data (will be empty string if
- // result != OK).
- resolver_->SetPacScriptByData(in_progress_fetch_bytes_);
-
- fetched_pac_config_id_ = in_progress_fetch_config_id_;
- fetched_pac_error_ = result;
- in_progress_fetch_config_id_ = ProxyConfig::INVALID_ID;
- in_progress_fetch_bytes_.clear();
-
// Resume any requests which we had to defer until the PAC script was
// downloaded.
ResumeAllPendingRequests();
@@ -455,6 +437,8 @@ int ProxyService::ReconsiderProxyAfterError(const GURL& url,
if (!was_direct && result->Fallback(&proxy_retry_info_))
return OK;
+ // TODO(eroman): Hmm, this doesn't seem right. For starters just because
+ // auto_detect is true doesn't mean we are actually using it.
if (!config_.auto_detect && !config_.proxy_rules.empty()) {
// If auto detect is on, then we should try a DIRECT connection
// as the attempt to reach the proxy failed.
@@ -587,13 +571,24 @@ void ProxyService::SetConfig(const ProxyConfig& config) {
config_is_bad_ = false;
proxy_retry_info_.clear();
- // Tell the resolver to use the new PAC URL (for resolvers that fetch the
- // PAC script internally).
- // TODO(eroman): this isn't quite right. See http://crbug.com/9985
- if ((config_.pac_url.is_valid() || config_.auto_detect) &&
- !resolver_->expects_pac_bytes()) {
- const GURL pac_url = config_.auto_detect ? GURL() : config_.pac_url;
- resolver_->SetPacScriptByUrl(pac_url);
+ // Cancel any PAC fetching / ProxyResolver::SetPacScript() which was
+ // in progress for the previous configuration.
+ init_proxy_resolver_.reset();
+
+ // 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();
+
+ init_proxy_resolver_.reset(
+ new InitProxyResolver(resolver_.get(), proxy_script_fetcher_.get()));
+ int rv = init_proxy_resolver_->Init(
+ config_, &init_proxy_resolver_callback_);
+ if (rv != ERR_IO_PENDING)
+ OnInitProxyResolverComplete(rv);
}
}
diff --git a/net/proxy/proxy_service.h b/net/proxy/proxy_service.h
index f74b203..a7546f1 100644
--- a/net/proxy/proxy_service.h
+++ b/net/proxy/proxy_service.h
@@ -24,9 +24,10 @@ class URLRequestContext;
namespace net {
-class ProxyScriptFetcher;
+class InitProxyResolver;
class ProxyConfigService;
class ProxyResolver;
+class ProxyScriptFetcher;
// This class can be used to resolve the proxy server to use when loading a
// HTTP(S) URL. It uses the given ProxyResolver to handle the actual proxy
@@ -165,15 +166,16 @@ class ProxyService {
// Tries to update the configuration if it hasn't been checked in a while.
void UpdateConfigIfOld();
- // Returns true if this ProxyService is downloading a PAC script on behalf
- // of ProxyResolverWithoutFetch. Resolve requests will be frozen until
- // the fetch has completed.
- bool IsFetchingPacScript() const {
- return in_progress_fetch_config_id_ != ProxyConfig::INVALID_ID;
+ // 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;
}
- // Callback for when the PAC script has finished downloading.
- void OnScriptFetchCompletion(int result);
+ // Callback for when the proxy resolver has been initialized with a
+ // PAC script.
+ void OnInitProxyResolverComplete(int result);
// Returns ERR_IO_PENDING if the request cannot be completed synchronously.
// Otherwise it fills |result| with the proxy information for |url|.
@@ -185,6 +187,10 @@ class ProxyService {
const ProxyConfig::ProxyRules& rules,
ProxyInfo* result);
+ // Cancel all of the requests sent to the ProxyResolver. These will be
+ // restarted when calling ResumeAllPendingRequests().
+ void SuspendAllPendingRequests();
+
// Sends all the unstarted pending requests off to the resolver.
void ResumeAllPendingRequests();
@@ -194,12 +200,6 @@ class ProxyService {
// Removes |req| from the list of pending requests.
void RemovePendingRequest(PacRequest* req);
- // Returns true if the resolver is all set-up and ready to accept requests.
- // Returns false if requests are blocked (because the PAC script is being
- // downloaded). May have the side-effect of starting the PAC script
- // download.
- bool PrepareResolverForRequests();
-
// Called to indicate that a PacRequest completed. The |config_id| parameter
// indicates the proxy configuration that was queried. |result_code| is OK
// if the PAC file could be downloaded and executed. Otherwise, it is an
@@ -215,6 +215,9 @@ class ProxyService {
// heuristic).
static bool IsLocalName(const GURL& url);
+ // Helper to download the PAC script (wpad + custom) and apply fallback rules.
+ scoped_ptr<InitProxyResolver> init_proxy_resolver_;
+
scoped_ptr<ProxyConfigService> config_service_;
scoped_ptr<ProxyResolver> resolver_;
@@ -242,23 +245,8 @@ class ProxyService {
// external PAC script fetching.
scoped_ptr<ProxyScriptFetcher> proxy_script_fetcher_;
- // Callback for when |proxy_script_fetcher_| is done.
- CompletionCallbackImpl<ProxyService> proxy_script_fetcher_callback_;
-
- // The ID of the configuration for which we last downloaded a PAC script.
- // If no PAC script has been fetched yet, will be ProxyConfig::INVALID_ID.
- ProxyConfig::ID fetched_pac_config_id_;
-
- // The error corresponding with |fetched_pac_config_id_|, or OK.
- int fetched_pac_error_;
-
- // The ID of the configuration for which we are currently downloading the
- // PAC script. If no fetch is in progress, will be ProxyConfig::INVALID_ID.
- ProxyConfig::ID in_progress_fetch_config_id_;
-
- // The results of the current in progress fetch, or empty string.
- std::string in_progress_fetch_bytes_;
-
+ // Callback for when |init_proxy_resolver_| is done.
+ CompletionCallbackImpl<ProxyService> init_proxy_resolver_callback_;
DISALLOW_COPY_AND_ASSIGN(ProxyService);
};
diff --git a/net/proxy/proxy_service_unittest.cc b/net/proxy/proxy_service_unittest.cc
index b6b62b6..0dac40b 100644
--- a/net/proxy/proxy_service_unittest.cc
+++ b/net/proxy/proxy_service_unittest.cc
@@ -15,6 +15,8 @@
#include "net/proxy/proxy_service.h"
#include "testing/gtest/include/gtest/gtest.h"
+// TODO(eroman): Write a test which exercises
+// ProxyService::SuspendAllPendingRequests().
namespace net {
namespace {
@@ -72,6 +74,39 @@ class MockAsyncProxyResolverBase : public ProxyResolver {
MessageLoop* origin_loop_;
};
+ class SetPacScriptRequest {
+ public:
+ SetPacScriptRequest(MockAsyncProxyResolverBase* resolver,
+ const GURL& pac_url,
+ const std::string& pac_bytes,
+ CompletionCallback* callback)
+ : resolver_(resolver),
+ pac_url_(pac_url),
+ pac_bytes_(pac_bytes),
+ callback_(callback),
+ origin_loop_(MessageLoop::current()) {
+ }
+
+ const GURL& pac_url() const { return pac_url_; }
+ const std::string& pac_bytes() const { return pac_bytes_; }
+
+ void CompleteNow(int rv) {
+ CompletionCallback* callback = callback_;
+
+ // Will delete |this|.
+ resolver_->RemovePendingSetPacScriptRequest(this);
+
+ callback->Run(rv);
+ }
+
+ private:
+ MockAsyncProxyResolverBase* resolver_;
+ const GURL pac_url_;
+ const std::string pac_bytes_;
+ CompletionCallback* callback_;
+ MessageLoop* origin_loop_;
+ };
+
typedef std::vector<scoped_refptr<Request> > RequestsList;
// ProxyResolver implementation:
@@ -95,12 +130,14 @@ class MockAsyncProxyResolverBase : public ProxyResolver {
RemovePendingRequest(request);
}
- virtual void SetPacScriptByUrlInternal(const GURL& pac_url) {
- pac_url_ = pac_url;
- }
-
- virtual void SetPacScriptByDataInternal(const std::string& bytes) {
- pac_bytes_ = bytes;
+ virtual int SetPacScript(const GURL& pac_url,
+ const std::string& pac_bytes,
+ CompletionCallback* callback) {
+ EXPECT_EQ(NULL, pending_set_pac_script_request_.get());
+ pending_set_pac_script_request_.reset(
+ new SetPacScriptRequest(this, pac_url, pac_bytes, callback));
+ // Finished when user calls SetPacScriptRequest::CompleteNow().
+ return ERR_IO_PENDING;
}
const RequestsList& pending_requests() const {
@@ -111,12 +148,8 @@ class MockAsyncProxyResolverBase : public ProxyResolver {
return cancelled_requests_;
}
- const GURL& pac_url() const {
- return pac_url_;
- }
-
- const std::string& pac_bytes() const {
- return pac_bytes_;
+ SetPacScriptRequest* pending_set_pac_script_request() const {
+ return pending_set_pac_script_request_.get();
}
void RemovePendingRequest(Request* request) {
@@ -126,6 +159,11 @@ class MockAsyncProxyResolverBase : public ProxyResolver {
pending_requests_.erase(it);
}
+ void RemovePendingSetPacScriptRequest(SetPacScriptRequest* request) {
+ EXPECT_EQ(request, pending_set_pac_script_request());
+ pending_set_pac_script_request_.reset();
+ }
+
protected:
explicit MockAsyncProxyResolverBase(bool expects_pac_bytes)
: ProxyResolver(expects_pac_bytes) {}
@@ -133,8 +171,7 @@ class MockAsyncProxyResolverBase : public ProxyResolver {
private:
RequestsList pending_requests_;
RequestsList cancelled_requests_;
- GURL pac_url_;
- std::string pac_bytes_;
+ scoped_ptr<SetPacScriptRequest> pending_set_pac_script_request_;
};
class MockAsyncProxyResolver : public MockAsyncProxyResolverBase {
@@ -160,21 +197,24 @@ class MockProxyScriptFetcher : public ProxyScriptFetcher {
}
// ProxyScriptFetcher implementation.
- virtual void Fetch(const GURL& url,
- std::string* bytes,
- CompletionCallback* callback) {
+ virtual int Fetch(const GURL& url,
+ std::string* bytes,
+ CompletionCallback* callback) {
DCHECK(!has_pending_request());
// Save the caller's information, and have them wait.
pending_request_url_ = url;
pending_request_callback_ = callback;
pending_request_bytes_ = bytes;
+ return ERR_IO_PENDING;
}
void NotifyFetchCompletion(int result, const std::string& bytes) {
DCHECK(has_pending_request());
*pending_request_bytes_ = bytes;
- pending_request_callback_->Run(result);
+ CompletionCallback* callback = pending_request_callback_;
+ pending_request_callback_ = NULL;
+ callback->Run(result);
}
virtual void Cancel() {}
@@ -223,7 +263,10 @@ TEST(ProxyServiceTest, PAC) {
int rv = service.ResolveProxy(url, &info, &callback, NULL);
EXPECT_EQ(ERR_IO_PENDING, rv);
- EXPECT_EQ(GURL("http://foopy/proxy.pac"), resolver->pac_url());
+ EXPECT_EQ(GURL("http://foopy/proxy.pac"),
+ resolver->pending_set_pac_script_request()->pac_url());
+ resolver->pending_set_pac_script_request()->CompleteNow(OK);
+
ASSERT_EQ(1u, resolver->pending_requests().size());
EXPECT_EQ(url, resolver->pending_requests()[0]->url());
@@ -253,7 +296,10 @@ TEST(ProxyServiceTest, PAC_NoIdentityOrHash) {
int rv = service.ResolveProxy(url, &info, &callback, NULL);
EXPECT_EQ(ERR_IO_PENDING, rv);
- EXPECT_EQ(GURL("http://foopy/proxy.pac"), resolver->pac_url());
+ EXPECT_EQ(GURL("http://foopy/proxy.pac"),
+ resolver->pending_set_pac_script_request()->pac_url());
+ resolver->pending_set_pac_script_request()->CompleteNow(OK);
+
ASSERT_EQ(1u, resolver->pending_requests().size());
// The URL should have been simplified, stripping the username/password/hash.
EXPECT_EQ(GURL("http://www.google.com/?ref"),
@@ -277,7 +323,10 @@ TEST(ProxyServiceTest, PAC_FailoverToDirect) {
int rv = service.ResolveProxy(url, &info, &callback1, NULL);
EXPECT_EQ(ERR_IO_PENDING, rv);
- EXPECT_EQ(GURL("http://foopy/proxy.pac"), resolver->pac_url());
+ EXPECT_EQ(GURL("http://foopy/proxy.pac"),
+ resolver->pending_set_pac_script_request()->pac_url());
+ resolver->pending_set_pac_script_request()->CompleteNow(OK);
+
ASSERT_EQ(1u, resolver->pending_requests().size());
EXPECT_EQ(url, resolver->pending_requests()[0]->url());
@@ -300,6 +349,7 @@ TEST(ProxyServiceTest, ProxyResolverFails) {
// Test what happens when the ProxyResolver fails (this could represent
// a failure to download the PAC script in the case of ProxyResolvers which
// do the fetch internally.)
+ // TODO(eroman): change this comment.
MockProxyConfigService* config_service =
new MockProxyConfigService("http://foopy/proxy.pac");
@@ -315,7 +365,10 @@ TEST(ProxyServiceTest, ProxyResolverFails) {
int rv = service.ResolveProxy(url, &info, &callback1, NULL);
EXPECT_EQ(ERR_IO_PENDING, rv);
- EXPECT_EQ(GURL("http://foopy/proxy.pac"), resolver->pac_url());
+ EXPECT_EQ(GURL("http://foopy/proxy.pac"),
+ resolver->pending_set_pac_script_request()->pac_url());
+ resolver->pending_set_pac_script_request()->CompleteNow(OK);
+
ASSERT_EQ(1u, resolver->pending_requests().size());
EXPECT_EQ(url, resolver->pending_requests()[0]->url());
@@ -369,7 +422,10 @@ TEST(ProxyServiceTest, ProxyFallback) {
int rv = service.ResolveProxy(url, &info, &callback1, NULL);
EXPECT_EQ(ERR_IO_PENDING, rv);
- EXPECT_EQ(GURL("http://foopy/proxy.pac"), resolver->pac_url());
+ EXPECT_EQ(GURL("http://foopy/proxy.pac"),
+ resolver->pending_set_pac_script_request()->pac_url());
+ resolver->pending_set_pac_script_request()->CompleteNow(OK);
+
ASSERT_EQ(1u, resolver->pending_requests().size());
EXPECT_EQ(url, resolver->pending_requests()[0]->url());
@@ -446,7 +502,10 @@ TEST(ProxyServiceTest, ProxyFallback_NewSettings) {
int rv = service.ResolveProxy(url, &info, &callback1, NULL);
EXPECT_EQ(ERR_IO_PENDING, rv);
- EXPECT_EQ(GURL("http://foopy/proxy.pac"), resolver->pac_url());
+ EXPECT_EQ(GURL("http://foopy/proxy.pac"),
+ resolver->pending_set_pac_script_request()->pac_url());
+ resolver->pending_set_pac_script_request()->CompleteNow(OK);
+
ASSERT_EQ(1u, resolver->pending_requests().size());
EXPECT_EQ(url, resolver->pending_requests()[0]->url());
@@ -468,7 +527,10 @@ TEST(ProxyServiceTest, ProxyFallback_NewSettings) {
rv = service.ReconsiderProxyAfterError(url, &info, &callback2, NULL);
EXPECT_EQ(ERR_IO_PENDING, rv);
- EXPECT_EQ(GURL("http://foopy-new/proxy.pac"), resolver->pac_url());
+ EXPECT_EQ(GURL("http://foopy-new/proxy.pac"),
+ resolver->pending_set_pac_script_request()->pac_url());
+ resolver->pending_set_pac_script_request()->CompleteNow(OK);
+
ASSERT_EQ(1u, resolver->pending_requests().size());
EXPECT_EQ(url, resolver->pending_requests()[0]->url());
@@ -495,7 +557,10 @@ TEST(ProxyServiceTest, ProxyFallback_NewSettings) {
rv = service.ReconsiderProxyAfterError(url, &info, &callback4, NULL);
EXPECT_EQ(ERR_IO_PENDING, rv);
- EXPECT_EQ(GURL("http://foopy-new2/proxy.pac"), resolver->pac_url());
+ EXPECT_EQ(GURL("http://foopy-new2/proxy.pac"),
+ resolver->pending_set_pac_script_request()->pac_url());
+ resolver->pending_set_pac_script_request()->CompleteNow(OK);
+
ASSERT_EQ(1u, resolver->pending_requests().size());
EXPECT_EQ(url, resolver->pending_requests()[0]->url());
@@ -525,7 +590,9 @@ TEST(ProxyServiceTest, ProxyFallback_BadConfig) {
int rv = service.ResolveProxy(url, &info, &callback1, NULL);
EXPECT_EQ(ERR_IO_PENDING, rv);
- EXPECT_EQ(GURL("http://foopy/proxy.pac"), resolver->pac_url());
+ EXPECT_EQ(GURL("http://foopy/proxy.pac"),
+ resolver->pending_set_pac_script_request()->pac_url());
+ resolver->pending_set_pac_script_request()->CompleteNow(OK);
ASSERT_EQ(1u, resolver->pending_requests().size());
EXPECT_EQ(url, resolver->pending_requests()[0]->url());
@@ -702,7 +769,7 @@ TEST(ProxyServiceTest, ProxyBypassListWithPorts) {
config.proxy_bypass.push_back("*.example.com:99");
{
ProxyService service(new MockProxyConfigService(config),
- new MockAsyncProxyResolver);
+ new MockAsyncProxyResolver);
{
GURL test_url("http://www.example.com:99");
TestCompletionCallback callback;
@@ -730,7 +797,7 @@ TEST(ProxyServiceTest, ProxyBypassListWithPorts) {
config.proxy_bypass.push_back("*.example.com:80");
{
ProxyService service(new MockProxyConfigService(config),
- new MockAsyncProxyResolver);
+ new MockAsyncProxyResolver);
GURL test_url("http://www.example.com");
TestCompletionCallback callback;
int rv = service.ResolveProxy(test_url, &info, &callback, NULL);
@@ -742,7 +809,7 @@ TEST(ProxyServiceTest, ProxyBypassListWithPorts) {
config.proxy_bypass.push_back("*.example.com");
{
ProxyService service(new MockProxyConfigService(config),
- new MockAsyncProxyResolver);
+ new MockAsyncProxyResolver);
GURL test_url("http://www.example.com:99");
TestCompletionCallback callback;
int rv = service.ResolveProxy(test_url, &info, &callback, NULL);
@@ -755,7 +822,7 @@ TEST(ProxyServiceTest, ProxyBypassListWithPorts) {
config.proxy_bypass.push_back("[3ffe:2a00:100:7031::1]:99");
{
ProxyService service(new MockProxyConfigService(config),
- new MockAsyncProxyResolver);
+ new MockAsyncProxyResolver);
{
GURL test_url("http://[3ffe:2a00:100:7031::1]:99/");
TestCompletionCallback callback;
@@ -779,7 +846,7 @@ TEST(ProxyServiceTest, ProxyBypassListWithPorts) {
config.proxy_bypass.push_back("[3ffe:2a00:100:7031::1]");
{
ProxyService service(new MockProxyConfigService(config),
- new MockAsyncProxyResolver);
+ new MockAsyncProxyResolver);
{
GURL test_url("http://[3ffe:2a00:100:7031::1]:99/");
TestCompletionCallback callback;
@@ -803,7 +870,7 @@ TEST(ProxyServiceTest, PerProtocolProxyTests) {
config.auto_detect = false;
{
ProxyService service(new MockProxyConfigService(config),
- new MockAsyncProxyResolver);
+ new MockAsyncProxyResolver);
GURL test_url("http://www.msn.com");
ProxyInfo info;
TestCompletionCallback callback;
@@ -814,7 +881,7 @@ TEST(ProxyServiceTest, PerProtocolProxyTests) {
}
{
ProxyService service(new MockProxyConfigService(config),
- new MockAsyncProxyResolver);
+ new MockAsyncProxyResolver);
GURL test_url("ftp://ftp.google.com");
ProxyInfo info;
TestCompletionCallback callback;
@@ -825,7 +892,7 @@ TEST(ProxyServiceTest, PerProtocolProxyTests) {
}
{
ProxyService service(new MockProxyConfigService(config),
- new MockAsyncProxyResolver);
+ new MockAsyncProxyResolver);
GURL test_url("https://webbranch.techcu.com");
ProxyInfo info;
TestCompletionCallback callback;
@@ -837,7 +904,7 @@ TEST(ProxyServiceTest, PerProtocolProxyTests) {
{
config.proxy_rules.ParseFromString("foopy1:8080");
ProxyService service(new MockProxyConfigService(config),
- new MockAsyncProxyResolver);
+ new MockAsyncProxyResolver);
GURL test_url("http://www.microsoft.com");
ProxyInfo info;
TestCompletionCallback callback;
@@ -859,7 +926,7 @@ TEST(ProxyServiceTest, DefaultProxyFallbackToSOCKS) {
{
ProxyService service(new MockProxyConfigService(config),
- new MockAsyncProxyResolver);
+ new MockAsyncProxyResolver);
GURL test_url("http://www.msn.com");
ProxyInfo info;
TestCompletionCallback callback;
@@ -870,7 +937,7 @@ TEST(ProxyServiceTest, DefaultProxyFallbackToSOCKS) {
}
{
ProxyService service(new MockProxyConfigService(config),
- new MockAsyncProxyResolver);
+ new MockAsyncProxyResolver);
GURL test_url("ftp://ftp.google.com");
ProxyInfo info;
TestCompletionCallback callback;
@@ -881,7 +948,7 @@ TEST(ProxyServiceTest, DefaultProxyFallbackToSOCKS) {
}
{
ProxyService service(new MockProxyConfigService(config),
- new MockAsyncProxyResolver);
+ new MockAsyncProxyResolver);
GURL test_url("https://webbranch.techcu.com");
ProxyInfo info;
TestCompletionCallback callback;
@@ -892,7 +959,7 @@ TEST(ProxyServiceTest, DefaultProxyFallbackToSOCKS) {
}
{
ProxyService service(new MockProxyConfigService(config),
- new MockAsyncProxyResolver);
+ new MockAsyncProxyResolver);
GURL test_url("unknown://www.microsoft.com");
ProxyInfo info;
TestCompletionCallback callback;
@@ -919,6 +986,16 @@ TEST(ProxyServiceTest, CancelInProgressRequest) {
int rv = service.ResolveProxy(
GURL("http://request1"), &info1, &callback1, NULL);
EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ // Nothing has been sent to the proxy resolver yet, since the proxy
+ // resolver has not been configured yet.
+ ASSERT_EQ(0u, resolver->pending_requests().size());
+
+ // Successfully initialize the PAC script.
+ EXPECT_EQ(GURL("http://foopy/proxy.pac"),
+ resolver->pending_set_pac_script_request()->pac_url());
+ 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());
@@ -1011,9 +1088,11 @@ TEST(ProxyServiceTest, InitialPACScriptDownload) {
// PAC script download completion.
fetcher->NotifyFetchCompletion(OK, "pac-v1");
- // Now that the PAC script is downloaded, everything should have been sent
- // over to the proxy resolver.
- EXPECT_EQ("pac-v1", resolver->pac_bytes());
+ // Now that the PAC script is downloaded, it will have been sent to the proxy
+ // resolver.
+ EXPECT_EQ("pac-v1", resolver->pending_set_pac_script_request()->pac_bytes());
+ resolver->pending_set_pac_script_request()->CompleteNow(OK);
+
ASSERT_EQ(3u, resolver->pending_requests().size());
EXPECT_EQ(GURL("http://request1"), resolver->pending_requests()[0]->url());
EXPECT_EQ(GURL("http://request2"), resolver->pending_requests()[1]->url());
@@ -1092,9 +1171,11 @@ TEST(ProxyServiceTest, CancelWhilePACFetching) {
// PAC script download completion.
fetcher->NotifyFetchCompletion(OK, "pac-v1");
- // Now that the PAC script is downloaded, everything should have been sent
- // over to the proxy resolver.
- EXPECT_EQ("pac-v1", resolver->pac_bytes());
+ // Now that the PAC script is downloaded, it will have been sent to the
+ // proxy resolver.
+ EXPECT_EQ("pac-v1", resolver->pending_set_pac_script_request()->pac_bytes());
+ resolver->pending_set_pac_script_request()->CompleteNow(OK);
+
ASSERT_EQ(1u, resolver->pending_requests().size());
EXPECT_EQ(GURL("http://request3"), resolver->pending_requests()[0]->url());
@@ -1111,12 +1192,280 @@ TEST(ProxyServiceTest, CancelWhilePACFetching) {
EXPECT_FALSE(callback2.have_result()); // Cancelled.
}
+// Test that if auto-detect fails, we fall-back to the custom pac.
+TEST(ProxyServiceTest, FallbackFromAutodetectToCustomPac) {
+ ProxyConfig config;
+ config.auto_detect = true;
+ config.pac_url = GURL("http://foopy/proxy.pac");
+ config.proxy_rules.ParseFromString("http=foopy:80"); // Won't be used.
+
+ MockProxyConfigService* config_service = new MockProxyConfigService(config);
+ MockAsyncProxyResolverExpectsBytes* resolver =
+ new MockAsyncProxyResolverExpectsBytes;
+ ProxyService service(config_service, resolver);
+
+ MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher;
+ service.SetProxyScriptFetcher(fetcher);
+
+ // Start 2 requests.
+
+ ProxyInfo info1;
+ TestCompletionCallback callback1;
+ int rv = service.ResolveProxy(
+ GURL("http://request1"), &info1, &callback1, NULL);
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ ProxyInfo info2;
+ TestCompletionCallback callback2;
+ ProxyService::PacRequest* request2;
+ rv = service.ResolveProxy(
+ GURL("http://request2"), &info2, &callback2, &request2);
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ // Check that nothing has been sent to the proxy resolver yet.
+ ASSERT_EQ(0u, resolver->pending_requests().size());
+
+ // It should be trying to auto-detect first -- FAIL the autodetect during
+ // the script download.
+ EXPECT_TRUE(fetcher->has_pending_request());
+ EXPECT_EQ(GURL("http://wpad/wpad.dat"), fetcher->pending_request_url());
+ fetcher->NotifyFetchCompletion(ERR_FAILED, "");
+
+ // Next it should be trying the custom PAC url.
+ EXPECT_TRUE(fetcher->has_pending_request());
+ EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());
+ fetcher->NotifyFetchCompletion(OK, "custom-pac-script");
+
+ EXPECT_EQ("custom-pac-script",
+ resolver->pending_set_pac_script_request()->pac_bytes());
+ resolver->pending_set_pac_script_request()->CompleteNow(OK);
+
+ // Now finally, the pending requests should have been sent to the resolver
+ // (which was initialized with custom PAC script).
+
+ ASSERT_EQ(2u, resolver->pending_requests().size());
+ EXPECT_EQ(GURL("http://request1"), resolver->pending_requests()[0]->url());
+ EXPECT_EQ(GURL("http://request2"), resolver->pending_requests()[1]->url());
+
+ // Complete the pending requests.
+ resolver->pending_requests()[1]->results()->UseNamedProxy("request2:80");
+ resolver->pending_requests()[1]->CompleteNow(OK);
+ resolver->pending_requests()[0]->results()->UseNamedProxy("request1:80");
+ resolver->pending_requests()[0]->CompleteNow(OK);
+
+ // Verify that requests ran as expected.
+ EXPECT_EQ(OK, callback1.WaitForResult());
+ EXPECT_EQ("request1:80", info1.proxy_server().ToURI());
+
+ EXPECT_EQ(OK, callback2.WaitForResult());
+ EXPECT_EQ("request2:80", info2.proxy_server().ToURI());
+}
+
+// This is the same test as FallbackFromAutodetectToCustomPac, except
+// the auto-detect script fails parsing rather than downloading.
+TEST(ProxyServiceTest, FallbackFromAutodetectToCustomPac2) {
+ ProxyConfig config;
+ config.auto_detect = true;
+ config.pac_url = GURL("http://foopy/proxy.pac");
+ config.proxy_rules.ParseFromString("http=foopy:80"); // Won't be used.
+
+ MockProxyConfigService* config_service = new MockProxyConfigService(config);
+ MockAsyncProxyResolverExpectsBytes* resolver =
+ new MockAsyncProxyResolverExpectsBytes;
+ ProxyService service(config_service, resolver);
+
+ MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher;
+ service.SetProxyScriptFetcher(fetcher);
+
+ // Start 2 requests.
+
+ ProxyInfo info1;
+ TestCompletionCallback callback1;
+ int rv = service.ResolveProxy(
+ GURL("http://request1"), &info1, &callback1, NULL);
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ ProxyInfo info2;
+ TestCompletionCallback callback2;
+ ProxyService::PacRequest* request2;
+ rv = service.ResolveProxy(
+ GURL("http://request2"), &info2, &callback2, &request2);
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ // Check that nothing has been sent to the proxy resolver yet.
+ ASSERT_EQ(0u, resolver->pending_requests().size());
+
+ // It should be trying to auto-detect first -- succeed the download.
+ EXPECT_TRUE(fetcher->has_pending_request());
+ EXPECT_EQ(GURL("http://wpad/wpad.dat"), fetcher->pending_request_url());
+ fetcher->NotifyFetchCompletion(OK, "invalid-script-contents");
+
+ // Simulate a parse error.
+ EXPECT_EQ("invalid-script-contents",
+ resolver->pending_set_pac_script_request()->pac_bytes());
+ resolver->pending_set_pac_script_request()->CompleteNow(
+ ERR_PAC_SCRIPT_FAILED);
+
+ // Next it should be trying the custom PAC url.
+ EXPECT_TRUE(fetcher->has_pending_request());
+ EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());
+ fetcher->NotifyFetchCompletion(OK, "custom-pac-script");
+
+ EXPECT_EQ("custom-pac-script",
+ resolver->pending_set_pac_script_request()->pac_bytes());
+ resolver->pending_set_pac_script_request()->CompleteNow(OK);
+
+ // Now finally, the pending requests should have been sent to the resolver
+ // (which was initialized with custom PAC script).
+
+ ASSERT_EQ(2u, resolver->pending_requests().size());
+ EXPECT_EQ(GURL("http://request1"), resolver->pending_requests()[0]->url());
+ EXPECT_EQ(GURL("http://request2"), resolver->pending_requests()[1]->url());
+
+ // Complete the pending requests.
+ resolver->pending_requests()[1]->results()->UseNamedProxy("request2:80");
+ resolver->pending_requests()[1]->CompleteNow(OK);
+ resolver->pending_requests()[0]->results()->UseNamedProxy("request1:80");
+ resolver->pending_requests()[0]->CompleteNow(OK);
+
+ // Verify that requests ran as expected.
+ EXPECT_EQ(OK, callback1.WaitForResult());
+ EXPECT_EQ("request1:80", info1.proxy_server().ToURI());
+
+ EXPECT_EQ(OK, callback2.WaitForResult());
+ EXPECT_EQ("request2:80", info2.proxy_server().ToURI());
+}
+
+// Test that if all of auto-detect, a custom PAC script, and manual settings
+// are given, then we will try them in that order.
+TEST(ProxyServiceTest, FallbackFromAutodetectToCustomToManual) {
+ ProxyConfig config;
+ config.auto_detect = true;
+ config.pac_url = GURL("http://foopy/proxy.pac");
+ config.proxy_rules.ParseFromString("http=foopy:80");
+
+ MockProxyConfigService* config_service = new MockProxyConfigService(config);
+ MockAsyncProxyResolverExpectsBytes* resolver =
+ new MockAsyncProxyResolverExpectsBytes;
+ ProxyService service(config_service, resolver);
+
+ MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher;
+ service.SetProxyScriptFetcher(fetcher);
+
+ // Start 2 requests.
+
+ ProxyInfo info1;
+ TestCompletionCallback callback1;
+ int rv = service.ResolveProxy(
+ GURL("http://request1"), &info1, &callback1, NULL);
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ ProxyInfo info2;
+ TestCompletionCallback callback2;
+ ProxyService::PacRequest* request2;
+ rv = service.ResolveProxy(
+ GURL("http://request2"), &info2, &callback2, &request2);
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ // Check that nothing has been sent to the proxy resolver yet.
+ ASSERT_EQ(0u, resolver->pending_requests().size());
+
+ // It should be trying to auto-detect first -- fail the download.
+ EXPECT_TRUE(fetcher->has_pending_request());
+ EXPECT_EQ(GURL("http://wpad/wpad.dat"), fetcher->pending_request_url());
+ fetcher->NotifyFetchCompletion(ERR_FAILED, "");
+
+ // Next it should be trying the custom PAC url -- fail the download.
+ EXPECT_TRUE(fetcher->has_pending_request());
+ EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());
+ fetcher->NotifyFetchCompletion(ERR_FAILED, "");
+
+ // Since we never managed to initialize a ProxyResolver, nothing should have
+ // been sent to it.
+ ASSERT_EQ(0u, resolver->pending_requests().size());
+
+ // Verify that requests ran as expected -- they should have fallen back to
+ // the manual proxy configuration for HTTP urls.
+ EXPECT_EQ(OK, callback1.WaitForResult());
+ EXPECT_EQ("foopy:80", info1.proxy_server().ToURI());
+
+ EXPECT_EQ(OK, callback2.WaitForResult());
+ EXPECT_EQ("foopy:80", info2.proxy_server().ToURI());
+}
+
+// Test that the bypass rules are NOT applied when using autodetect.
+TEST(ProxyServiceTest, BypassDoesntApplyToPac) {
+ ProxyConfig config;
+ config.auto_detect = true;
+ config.pac_url = GURL("http://foopy/proxy.pac");
+ config.proxy_rules.ParseFromString("http=foopy:80"); // Not used.
+ config.proxy_bypass.push_back("www.google.com");
+
+ MockProxyConfigService* config_service = new MockProxyConfigService(config);
+ MockAsyncProxyResolverExpectsBytes* resolver =
+ new MockAsyncProxyResolverExpectsBytes;
+ ProxyService service(config_service, resolver);
+
+ MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher;
+ service.SetProxyScriptFetcher(fetcher);
+
+ // Start 1 requests.
+
+ ProxyInfo info1;
+ TestCompletionCallback callback1;
+ int rv = service.ResolveProxy(
+ GURL("http://www.google.com"), &info1, &callback1, NULL);
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ // Check that nothing has been sent to the proxy resolver yet.
+ ASSERT_EQ(0u, resolver->pending_requests().size());
+
+ // It should be trying to auto-detect first -- succeed the download.
+ EXPECT_TRUE(fetcher->has_pending_request());
+ EXPECT_EQ(GURL("http://wpad/wpad.dat"), fetcher->pending_request_url());
+ fetcher->NotifyFetchCompletion(OK, "auto-detect");
+
+ EXPECT_EQ("auto-detect",
+ resolver->pending_set_pac_script_request()->pac_bytes());
+ resolver->pending_set_pac_script_request()->CompleteNow(OK);
+
+ ASSERT_EQ(1u, resolver->pending_requests().size());
+ EXPECT_EQ(GURL("http://www.google.com"),
+ resolver->pending_requests()[0]->url());
+
+ // Complete the pending request.
+ resolver->pending_requests()[0]->results()->UseNamedProxy("request1:80");
+ resolver->pending_requests()[0]->CompleteNow(OK);
+
+ // Verify that request ran as expected.
+ EXPECT_EQ(OK, callback1.WaitForResult());
+ EXPECT_EQ("request1:80", info1.proxy_server().ToURI());
+
+ // Start another request, it should pickup the bypass item.
+ ProxyInfo info2;
+ TestCompletionCallback callback2;
+ rv = service.ResolveProxy(
+ GURL("http://www.google.com"), &info2, &callback2, NULL);
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ ASSERT_EQ(1u, resolver->pending_requests().size());
+ EXPECT_EQ(GURL("http://www.google.com"),
+ resolver->pending_requests()[0]->url());
+
+ // Complete the pending request.
+ 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());
+}
+
TEST(ProxyServiceTest, ResetProxyConfigService) {
ProxyConfig config1;
config1.proxy_rules.ParseFromString("foopy1:8080");
config1.auto_detect = false;
ProxyService service(new MockProxyConfigService(config1),
- new MockAsyncProxyResolverExpectsBytes);
+ new MockAsyncProxyResolverExpectsBytes);
ProxyInfo info;
TestCompletionCallback callback1;
@@ -1136,7 +1485,7 @@ TEST(ProxyServiceTest, ResetProxyConfigService) {
}
TEST(ProxyServiceTest, IsLocalName) {
- const struct {
+ const struct {
const char* url;
bool expected_is_local;
} tests[] = {
diff --git a/net/proxy/single_threaded_proxy_resolver.cc b/net/proxy/single_threaded_proxy_resolver.cc
index e1e621de..02658a6 100644
--- a/net/proxy/single_threaded_proxy_resolver.cc
+++ b/net/proxy/single_threaded_proxy_resolver.cc
@@ -10,28 +10,77 @@
namespace net {
-// Runs on the worker thread to call ProxyResolver::SetPacScriptByUrl or
-// ProxyResolver::SetPacScriptByData.
-// TODO(eroman): the lifetime of this task is ill-defined; |resolver_| must
-// be valid when the task eventually is run. Make this task cancellable.
-class SetPacScriptTask : public Task {
+// SingleThreadedProxyResolver::SetPacScriptTask ------------------------------
+
+// Runs on the worker thread to call ProxyResolver::SetPacScript.
+class SingleThreadedProxyResolver::SetPacScriptTask
+ : public base::RefCountedThreadSafe<
+ SingleThreadedProxyResolver::SetPacScriptTask> {
public:
- SetPacScriptTask(ProxyResolver* resolver,
+ SetPacScriptTask(SingleThreadedProxyResolver* coordinator,
const GURL& pac_url,
- const std::string& bytes)
- : resolver_(resolver), pac_url_(pac_url), bytes_(bytes) {}
-
- virtual void Run() {
- if (resolver_->expects_pac_bytes())
- resolver_->SetPacScriptByData(bytes_);
- else
- resolver_->SetPacScriptByUrl(pac_url_);
+ const std::string& pac_bytes,
+ CompletionCallback* callback)
+ : coordinator_(coordinator),
+ callback_(callback),
+ pac_bytes_(pac_bytes),
+ pac_url_(pac_url),
+ origin_loop_(MessageLoop::current()) {
+ DCHECK(callback);
+ }
+
+ // Start the SetPacScript request on the worker thread.
+ void Start() {
+ // TODO(eroman): Are these manual AddRef / Release necessary?
+ AddRef(); // balanced in RequestComplete
+
+ coordinator_->thread()->message_loop()->PostTask(
+ FROM_HERE, NewRunnableMethod(this, &SetPacScriptTask::DoRequest,
+ coordinator_->resolver_.get()));
}
+ void Cancel() {
+ // Clear these to inform RequestComplete that it should not try to
+ // access them.
+ coordinator_ = NULL;
+ callback_ = NULL;
+ }
+
+ // Returns true if Cancel() has been called.
+ bool was_cancelled() const { return callback_ == NULL; }
+
private:
- ProxyResolver* resolver_;
+ // Runs on the worker thread.
+ void DoRequest(ProxyResolver* resolver) {
+ int rv = resolver->expects_pac_bytes() ?
+ resolver->SetPacScriptByData(pac_bytes_, NULL) :
+ resolver->SetPacScriptByUrl(pac_url_, NULL);
+
+ DCHECK_NE(rv, ERR_IO_PENDING);
+ origin_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &SetPacScriptTask::RequestComplete, rv));
+ }
+
+ // Runs the completion callback on the origin thread.
+ void RequestComplete(int result_code) {
+ // The task may have been cancelled after it was started.
+ if (!was_cancelled()) {
+ CompletionCallback* callback = callback_;
+ coordinator_->RemoveOutstandingSetPacScriptTask(this);
+ callback->Run(result_code);
+ }
+
+ Release(); // Balances the AddRef in Start.
+ }
+
+ // Must only be used on the "origin" thread.
+ SingleThreadedProxyResolver* coordinator_;
+ CompletionCallback* callback_;
+ std::string pac_bytes_;
GURL pac_url_;
- std::string bytes_;
+
+ // Usable from within DoQuery on the worker thread.
+ MessageLoop* origin_loop_;
};
// SingleThreadedProxyResolver::Job -------------------------------------------
@@ -102,7 +151,7 @@ class SingleThreadedProxyResolver::Job
coordinator_->RemoveFrontOfJobsQueueAndStartNext(this);
}
- Release(); // balances the AddRef in Query. we may get deleted after
+ Release(); // Balances the AddRef in Start. We may get deleted after
// we return.
}
@@ -134,6 +183,9 @@ SingleThreadedProxyResolver::~SingleThreadedProxyResolver() {
(*it)->Cancel();
}
+ if (outstanding_set_pac_script_task_)
+ outstanding_set_pac_script_task_->Cancel();
+
// Note that |thread_| is destroyed before |resolver_|. This is important
// since |resolver_| could be running on |thread_|.
}
@@ -182,21 +234,24 @@ void SingleThreadedProxyResolver::CancelRequest(RequestHandle req) {
pending_jobs_.erase(it);
}
-void SingleThreadedProxyResolver::SetPacScriptByUrlInternal(
- const GURL& pac_url) {
- SetPacScriptHelper(pac_url, std::string());
+void SingleThreadedProxyResolver::CancelSetPacScript() {
+ DCHECK(outstanding_set_pac_script_task_);
+ outstanding_set_pac_script_task_->Cancel();
+ outstanding_set_pac_script_task_ = NULL;
}
-void SingleThreadedProxyResolver::SetPacScriptByDataInternal(
- const std::string& bytes) {
- SetPacScriptHelper(GURL(), bytes);
-}
-
-void SingleThreadedProxyResolver::SetPacScriptHelper(const GURL& pac_url,
- const std::string& bytes) {
+int SingleThreadedProxyResolver::SetPacScript(
+ const GURL& pac_url,
+ const std::string& pac_bytes,
+ CompletionCallback* callback) {
EnsureThreadStarted();
- thread()->message_loop()->PostTask(
- FROM_HERE, new SetPacScriptTask(resolver(), pac_url, bytes));
+ DCHECK(!outstanding_set_pac_script_task_);
+
+ SetPacScriptTask* task = new SetPacScriptTask(
+ this, pac_url, pac_bytes, callback);
+ outstanding_set_pac_script_task_ = task;
+ task->Start();
+ return ERR_IO_PENDING;
}
void SingleThreadedProxyResolver::EnsureThreadStarted() {
@@ -228,4 +283,10 @@ void SingleThreadedProxyResolver::RemoveFrontOfJobsQueueAndStartNext(
ProcessPendingJobs();
}
+void SingleThreadedProxyResolver::RemoveOutstandingSetPacScriptTask(
+ SetPacScriptTask* task) {
+ DCHECK_EQ(outstanding_set_pac_script_task_.get(), task);
+ outstanding_set_pac_script_task_ = NULL;
+}
+
} // namespace net
diff --git a/net/proxy/single_threaded_proxy_resolver.h b/net/proxy/single_threaded_proxy_resolver.h
index 010e526..fff23b8 100644
--- a/net/proxy/single_threaded_proxy_resolver.h
+++ b/net/proxy/single_threaded_proxy_resolver.h
@@ -37,6 +37,7 @@ class SingleThreadedProxyResolver : public ProxyResolver {
CompletionCallback* callback,
RequestHandle* request);
virtual void CancelRequest(RequestHandle request);
+ virtual void CancelSetPacScript();
protected:
// The wrapped (synchronous) ProxyResolver.
@@ -47,18 +48,17 @@ class SingleThreadedProxyResolver : public ProxyResolver {
// thread.
class Job;
friend class Job;
+ class SetPacScriptTask;
+ friend class SetPacScriptTask;
// FIFO queue that contains the in-progress job, and any pending jobs.
typedef std::deque<scoped_refptr<Job> > PendingJobsQueue;
base::Thread* thread() { return thread_.get(); }
// ProxyResolver implementation:
- virtual void SetPacScriptByUrlInternal(const GURL& pac_url);
- virtual void SetPacScriptByDataInternal(const std::string& bytes);
-
- // Helper for shared code between SetPacScriptByUrlInternal() and
- // SetPacScriptByDataInternal() -- the unused parameter is set to empty.
- void SetPacScriptHelper(const GURL& pac_url, const std::string& bytes);
+ virtual int SetPacScript(const GURL& pac_url,
+ const std::string& pac_bytes,
+ CompletionCallback* callback);
// Starts the worker thread if it isn't already running.
void EnsureThreadStarted();
@@ -71,6 +71,10 @@ class SingleThreadedProxyResolver : public ProxyResolver {
// DCHECK for verification purposes.
void RemoveFrontOfJobsQueueAndStartNext(Job* expected_job);
+ // Clears |outstanding_set_pac_script_task_|.
+ // Called when |task| has just finished.
+ void RemoveOutstandingSetPacScriptTask(SetPacScriptTask* task);
+
// The synchronous resolver implementation.
scoped_ptr<ProxyResolver> resolver_;
@@ -81,6 +85,7 @@ class SingleThreadedProxyResolver : public ProxyResolver {
scoped_ptr<base::Thread> thread_;
PendingJobsQueue pending_jobs_;
+ scoped_refptr<SetPacScriptTask> outstanding_set_pac_script_task_;
};
} // namespace net
diff --git a/net/proxy/single_threaded_proxy_resolver_unittest.cc b/net/proxy/single_threaded_proxy_resolver_unittest.cc
index 6fe7995..da21ff1 100644
--- a/net/proxy/single_threaded_proxy_resolver_unittest.cc
+++ b/net/proxy/single_threaded_proxy_resolver_unittest.cc
@@ -47,9 +47,12 @@ class MockProxyResolver : public ProxyResolver {
NOTREACHED();
}
- virtual void SetPacScriptByDataInternal(const std::string& bytes) {
+ virtual int SetPacScript(const GURL& pac_url,
+ const std::string& bytes,
+ CompletionCallback* callback) {
CheckIsOnWorkerThread();
last_pac_bytes_ = bytes;
+ return OK;
}
const std::string& last_pac_bytes() const { return last_pac_bytes_; }
@@ -127,9 +130,13 @@ TEST(SingleThreadedProxyResolverTest, Basic) {
EXPECT_TRUE(resolver->expects_pac_bytes());
- // Call SetPacScriptByData() -- we will make sure it reaches the sync resolver
- // later on.
- resolver->SetPacScriptByData("pac script bytes");
+ // Call SetPacScriptByData() -- verify that it reaches the synchronous
+ // resolver.
+ TestCompletionCallback set_script_callback;
+ rv = resolver->SetPacScriptByData("pac script bytes", &set_script_callback);
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ EXPECT_EQ(OK, set_script_callback.WaitForResult());
+ EXPECT_EQ("pac script bytes", mock->last_pac_bytes());
// Start request 0.
TestCompletionCallback callback0;
@@ -143,11 +150,6 @@ TEST(SingleThreadedProxyResolverTest, Basic) {
EXPECT_EQ(0, rv);
EXPECT_EQ("PROXY request0:80", results0.ToPacString());
- // Verify that the data from SetPacScriptByData() reached the resolver.
- // (Since we waited for the first request to complete, we are guaranteed
- // that the earlier post completed).
- EXPECT_EQ("pac script bytes", mock->last_pac_bytes());
-
// Start 3 more requests (request1 to request3).
TestCompletionCallback callback1;
@@ -309,5 +311,60 @@ TEST(SingleThreadedProxyResolverTest, CancelRequestByDeleting) {
EXPECT_FALSE(callback2.have_result());
}
+// Cancel an outstanding call to SetPacScriptByData().
+TEST(SingleThreadedProxyResolverTest, CancelSetPacScript) {
+ BlockableProxyResolver* mock = new BlockableProxyResolver;
+ scoped_ptr<SingleThreadedProxyResolver> resolver(
+ new SingleThreadedProxyResolver(mock));
+
+ int rv;
+
+ // Block the proxy resolver, so no request can complete.
+ mock->Block();
+
+ // Start request 0.
+ ProxyResolver::RequestHandle request0;
+ TestCompletionCallback callback0;
+ ProxyInfo results0;
+ rv = resolver->GetProxyForURL(
+ GURL("http://request0"), &results0, &callback0, &request0);
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ // Wait until requests 0 reaches the worker thread.
+ mock->WaitUntilBlocked();
+
+ TestCompletionCallback set_pac_script_callback;
+ rv = resolver->SetPacScriptByData("data", &set_pac_script_callback);
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ // Cancel the SetPacScriptByData request (it can't have finished yet,
+ // since the single-thread is currently blocked).
+ resolver->CancelSetPacScript();
+
+ // Start 1 more request.
+
+ TestCompletionCallback callback1;
+ ProxyInfo results1;
+ rv = resolver->GetProxyForURL(
+ GURL("http://request1"), &results1, &callback1, NULL);
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ // Unblock the worker thread so the requests can continue running.
+ mock->Unblock();
+
+ // Wait for requests 0 and 1 to finish.
+
+ rv = callback0.WaitForResult();
+ EXPECT_EQ(0, rv);
+ EXPECT_EQ("PROXY request0:80", results0.ToPacString());
+
+ rv = callback1.WaitForResult();
+ EXPECT_EQ(1, rv);
+ EXPECT_EQ("PROXY request1:80", results1.ToPacString());
+
+ // The SetPacScript callback should never have been completed.
+ EXPECT_FALSE(set_pac_script_callback.have_result());
+}
+
} // namespace
} // namespace net