summaryrefslogtreecommitdiffstats
path: root/net/proxy
diff options
context:
space:
mode:
authordarin@google.com <darin@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-08-11 15:40:23 +0000
committerdarin@google.com <darin@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-08-11 15:40:23 +0000
commit928fb58bd99536f16df3739329cbade0853b3309 (patch)
tree7a6433d25d9c5303614f05b6b39cb49e60468d12 /net/proxy
parent6723f835ff0247e5bffad9e92d8d42a8c3ae1b3b (diff)
downloadchromium_src-928fb58bd99536f16df3739329cbade0853b3309.zip
chromium_src-928fb58bd99536f16df3739329cbade0853b3309.tar.gz
chromium_src-928fb58bd99536f16df3739329cbade0853b3309.tar.bz2
Rename HttpProxy* classes to Proxy*. Move them into a net/proxy/ subdirectory.
I'm making this change because proxy resolution is really not specific to the HTTP protocol. We need to use the proxy service in our FTP implementation, for example. I made a separate directory instead of just putting these in base, because I anticipate more files once we have our own PAC implementation. git-svn-id: svn://svn.chromium.org/chrome/trunk/src@651 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/proxy')
-rw-r--r--net/proxy/proxy_resolver_fixed.cc48
-rw-r--r--net/proxy/proxy_resolver_fixed.h54
-rw-r--r--net/proxy/proxy_resolver_winhttp.cc185
-rw-r--r--net/proxy/proxy_resolver_winhttp.h64
-rw-r--r--net/proxy/proxy_service.cc496
-rw-r--r--net/proxy/proxy_service.h303
-rw-r--r--net/proxy/proxy_service_unittest.cc408
7 files changed, 1558 insertions, 0 deletions
diff --git a/net/proxy/proxy_resolver_fixed.cc b/net/proxy/proxy_resolver_fixed.cc
new file mode 100644
index 0000000..412cd2d
--- /dev/null
+++ b/net/proxy/proxy_resolver_fixed.cc
@@ -0,0 +1,48 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "net/proxy/proxy_resolver_fixed.h"
+
+#include "net/base/net_errors.h"
+
+namespace net {
+
+int ProxyResolverFixed::GetProxyConfig(ProxyConfig* config) {
+ config->proxy_server = pi_.proxy_server();
+ return OK;
+}
+
+int ProxyResolverFixed::GetProxyForURL(const std::wstring& query_url,
+ const std::wstring& pac_url,
+ ProxyInfo* results) {
+ NOTREACHED() << "Should not be asked to do proxy auto config";
+ return ERR_FAILED;
+}
+
+} // namespace net
diff --git a/net/proxy/proxy_resolver_fixed.h b/net/proxy/proxy_resolver_fixed.h
new file mode 100644
index 0000000..81283e7
--- /dev/null
+++ b/net/proxy/proxy_resolver_fixed.h
@@ -0,0 +1,54 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef NET_PROXY_PROXY_RESOLVER_FIXED_H_
+#define NET_PROXY_PROXY_RESOLVER_FIXED_H_
+
+#include "net/proxy/proxy_service.h"
+
+namespace net {
+
+// Implementation of ProxyResolver that returns a fixed result.
+class ProxyResolverFixed : public ProxyResolver {
+ public:
+ ProxyResolverFixed(const ProxyInfo& pi) { pi_.Use(pi); }
+
+ // ProxyResolver methods:
+ virtual int GetProxyConfig(ProxyConfig* config);
+ virtual int GetProxyForURL(const std::wstring& query_url,
+ const std::wstring& pac_url,
+ ProxyInfo* results);
+
+ private:
+ ProxyInfo pi_;
+};
+
+} // namespace net
+
+#endif // NET_PROXY_PROXY_RESOLVER_FIXED_H_
diff --git a/net/proxy/proxy_resolver_winhttp.cc b/net/proxy/proxy_resolver_winhttp.cc
new file mode 100644
index 0000000..aa1a296
--- /dev/null
+++ b/net/proxy/proxy_resolver_winhttp.cc
@@ -0,0 +1,185 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "net/proxy/proxy_resolver_winhttp.h"
+
+#include <windows.h>
+#include <winhttp.h>
+
+#include "base/histogram.h"
+#include "net/base/net_errors.h"
+
+#pragma comment(lib, "winhttp.lib")
+
+namespace net {
+
+// A small wrapper for histogramming purposes ;-)
+static BOOL CallWinHttpGetProxyForUrl(HINTERNET session, LPCWSTR url,
+ WINHTTP_AUTOPROXY_OPTIONS* options,
+ WINHTTP_PROXY_INFO* results) {
+ TimeTicks time_start = TimeTicks::Now();
+ BOOL rv = WinHttpGetProxyForUrl(session, url, options, results);
+ TimeDelta time_delta = TimeTicks::Now() - time_start;
+ // Record separately success and failure times since they will have very
+ // different characteristics.
+ if (rv) {
+ UMA_HISTOGRAM_LONG_TIMES(L"Net.GetProxyForUrl_OK", time_delta);
+ } else {
+ UMA_HISTOGRAM_LONG_TIMES(L"Net.GetProxyForUrl_FAIL", time_delta);
+ }
+ return rv;
+}
+
+static void FreeConfig(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG* config) {
+ if (config->lpszAutoConfigUrl)
+ GlobalFree(config->lpszAutoConfigUrl);
+ if (config->lpszProxy)
+ GlobalFree(config->lpszProxy);
+ if (config->lpszProxyBypass)
+ GlobalFree(config->lpszProxyBypass);
+}
+
+static void FreeInfo(WINHTTP_PROXY_INFO* info) {
+ if (info->lpszProxy)
+ GlobalFree(info->lpszProxy);
+ if (info->lpszProxyBypass)
+ GlobalFree(info->lpszProxyBypass);
+}
+
+ProxyResolverWinHttp::ProxyResolverWinHttp()
+ : session_handle_(NULL) {
+}
+
+ProxyResolverWinHttp::~ProxyResolverWinHttp() {
+ CloseWinHttpSession();
+}
+
+int ProxyResolverWinHttp::GetProxyConfig(ProxyConfig* config) {
+ WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ie_config = {0};
+ if (!WinHttpGetIEProxyConfigForCurrentUser(&ie_config)) {
+ LOG(ERROR) << "WinHttpGetIEProxyConfigForCurrentUser failed: " <<
+ GetLastError();
+ return ERR_FAILED; // TODO(darin): Bug 1189288: translate error code.
+ }
+
+ if (ie_config.fAutoDetect)
+ config->auto_detect = true;
+ if (ie_config.lpszProxy)
+ config->proxy_server = ie_config.lpszProxy;
+ if (ie_config.lpszProxyBypass)
+ config->proxy_bypass = ie_config.lpszProxyBypass;
+ if (ie_config.lpszAutoConfigUrl)
+ config->pac_url = ie_config.lpszAutoConfigUrl;
+
+ FreeConfig(&ie_config);
+ return OK;
+}
+
+int ProxyResolverWinHttp::GetProxyForURL(const std::wstring& query_url,
+ const std::wstring& pac_url,
+ ProxyInfo* results) {
+ // If we don't have a WinHTTP session, then create a new one.
+ if (!session_handle_ && !OpenWinHttpSession())
+ return ERR_FAILED;
+
+ // If we have been given an empty PAC url, then use auto-detection.
+ //
+ // NOTE: We just use DNS-based auto-detection here like Firefox. We do this
+ // to avoid WinHTTP's auto-detection code, which while more featureful (it
+ // supports DHCP based auto-detection) also appears to have issues.
+ //
+ WINHTTP_AUTOPROXY_OPTIONS options = {0};
+ options.fAutoLogonIfChallenged = TRUE;
+ options.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
+ options.lpszAutoConfigUrl =
+ pac_url.empty() ? L"http://wpad/wpad.dat" : pac_url.c_str();
+
+ WINHTTP_PROXY_INFO info = {0};
+ DCHECK(session_handle_);
+ if (!CallWinHttpGetProxyForUrl(session_handle_, query_url.c_str(), &options,
+ &info)) {
+ DWORD error = GetLastError();
+ LOG(ERROR) << "WinHttpGetProxyForUrl failed: " << error;
+
+ // If we got here because of RPC timeout during out of process PAC
+ // resolution, no further requests on this session are going to work.
+ if ((ERROR_WINHTTP_TIMEOUT == error) ||
+ (ERROR_WINHTTP_AUTO_PROXY_SERVICE_ERROR == error)) {
+ CloseWinHttpSession();
+ }
+
+ return ERR_FAILED; // TODO(darin): Bug 1189288: translate error code.
+ }
+
+ int rv = OK;
+
+ switch (info.dwAccessType) {
+ case WINHTTP_ACCESS_TYPE_NO_PROXY:
+ results->UseDirect();
+ break;
+ case WINHTTP_ACCESS_TYPE_NAMED_PROXY:
+ results->UseNamedProxy(info.lpszProxy);
+ break;
+ default:
+ NOTREACHED();
+ rv = ERR_FAILED;
+ }
+
+ FreeInfo(&info);
+ return rv;
+}
+
+bool ProxyResolverWinHttp::OpenWinHttpSession() {
+ DCHECK(!session_handle_);
+ session_handle_ = WinHttpOpen(NULL,
+ WINHTTP_ACCESS_TYPE_NO_PROXY,
+ WINHTTP_NO_PROXY_NAME,
+ WINHTTP_NO_PROXY_BYPASS,
+ 0);
+ if (!session_handle_)
+ return false;
+
+ // Since this session handle will never be used for WinHTTP connections,
+ // these timeouts don't really mean much individually. However, WinHTTP's
+ // out of process PAC resolution will use a combined (sum of all timeouts)
+ // value to wait for an RPC reply.
+ BOOL rv = WinHttpSetTimeouts(session_handle_, 10000, 10000, 5000, 5000);
+ DCHECK(rv);
+
+ return true;
+}
+
+void ProxyResolverWinHttp::CloseWinHttpSession() {
+ if (session_handle_) {
+ WinHttpCloseHandle(session_handle_);
+ session_handle_ = NULL;
+ }
+}
+
+} // namespace net
diff --git a/net/proxy/proxy_resolver_winhttp.h b/net/proxy/proxy_resolver_winhttp.h
new file mode 100644
index 0000000..5d2a432
--- /dev/null
+++ b/net/proxy/proxy_resolver_winhttp.h
@@ -0,0 +1,64 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef NET_PROXY_PROXY_RESOLVER_WINHTTP_H_
+#define NET_PROXY_PROXY_RESOLVER_WINHTTP_H_
+
+#include "net/proxy/proxy_service.h"
+
+typedef LPVOID HINTERNET; // From winhttp.h
+
+namespace net {
+
+// An implementation of ProxyResolver that uses WinHTTP and the system
+// proxy settings.
+class ProxyResolverWinHttp : public ProxyResolver {
+ public:
+ ProxyResolverWinHttp();
+ ~ProxyResolverWinHttp();
+
+ // ProxyResolver implementation:
+ virtual int GetProxyConfig(ProxyConfig* config);
+ virtual int GetProxyForURL(const std::wstring& query_url,
+ const std::wstring& pac_url,
+ ProxyInfo* results);
+
+ private:
+ bool OpenWinHttpSession();
+ void CloseWinHttpSession();
+
+ // Proxy configuration is cached on the session handle.
+ HINTERNET session_handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProxyResolverWinHttp);
+};
+
+} // namespace net
+
+#endif // NET_PROXY_PROXY_RESOLVER_WINHTTP_H_
diff --git a/net/proxy/proxy_service.cc b/net/proxy/proxy_service.cc
new file mode 100644
index 0000000..6007909
--- /dev/null
+++ b/net/proxy/proxy_service.cc
@@ -0,0 +1,496 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "net/proxy/proxy_service.h"
+
+#include <windows.h>
+#include <winhttp.h>
+
+#include <algorithm>
+
+#include "base/message_loop.h"
+#include "base/string_tokenizer.h"
+#include "base/string_util.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/net_errors.h"
+
+namespace net {
+
+// ProxyConfig ----------------------------------------------------------------
+
+// static
+ProxyConfig::ID ProxyConfig::last_id_ = ProxyConfig::INVALID_ID;
+
+ProxyConfig::ProxyConfig()
+ : auto_detect(false),
+ id_(++last_id_) {
+}
+
+bool ProxyConfig::Equals(const ProxyConfig& other) const {
+ // The two configs can have different IDs. We are just interested in if they
+ // have the same settings.
+ return auto_detect == other.auto_detect &&
+ pac_url == other.pac_url &&
+ proxy_server == other.proxy_server &&
+ proxy_bypass == other.proxy_bypass;
+}
+
+// ProxyList ------------------------------------------------------------------
+void ProxyList::SetVector(const std::vector<std::wstring>& proxies) {
+ proxies_.clear();
+ std::vector<std::wstring>::const_iterator iter = proxies.begin();
+ for (; iter != proxies.end(); ++iter) {
+ std::wstring proxy_sever;
+ TrimWhitespace(*iter, TRIM_ALL, &proxy_sever);
+ proxies_.push_back(proxy_sever);
+ }
+}
+
+void ProxyList::Set(const std::wstring& proxy_list) {
+ // Extract the different proxies from the list.
+ std::vector<std::wstring> proxies;
+ SplitString(proxy_list, L';', &proxies);
+ SetVector(proxies);
+}
+
+void ProxyList::RemoveBadProxies(const ProxyRetryInfoMap& proxy_retry_info) {
+ std::vector<std::wstring> new_proxy_list;
+ std::vector<std::wstring>::const_iterator iter = proxies_.begin();
+ for (; iter != proxies_.end(); ++iter) {
+ ProxyRetryInfoMap::const_iterator bad_proxy =
+ proxy_retry_info.find(*iter);
+ if (bad_proxy != proxy_retry_info.end()) {
+ // This proxy is bad. Check if it's time to retry.
+ if (bad_proxy->second.bad_until >= TimeTicks::Now()) {
+ // still invalid.
+ continue;
+ }
+ }
+ new_proxy_list.push_back(*iter);
+ }
+
+ proxies_ = new_proxy_list;
+}
+
+std::wstring ProxyList::Get() const {
+ if (!proxies_.empty())
+ return proxies_[0];
+
+ return std::wstring();
+}
+
+std::wstring ProxyList::GetList() const {
+ std::wstring proxy_list;
+ std::vector<std::wstring>::const_iterator iter = proxies_.begin();
+ for (; iter != proxies_.end(); ++iter) {
+ if (!proxy_list.empty())
+ proxy_list += L';';
+
+ proxy_list += *iter;
+ }
+
+ return proxy_list;
+}
+
+bool ProxyList::Fallback(ProxyRetryInfoMap* proxy_retry_info) {
+ // Number of minutes to wait before retrying a bad proxy server.
+ const TimeDelta kProxyRetryDelay = TimeDelta::FromMinutes(5);
+
+ if (proxies_.empty()) {
+ NOTREACHED();
+ return false;
+ }
+
+ // Mark this proxy as bad.
+ ProxyRetryInfoMap::iterator iter = proxy_retry_info->find(proxies_[0]);
+ if (iter != proxy_retry_info->end()) {
+ // TODO(nsylvain): This is not the first time we get this. We should
+ // double the retry time. Bug 997660.
+ iter->second.bad_until = TimeTicks::Now() + iter->second.current_delay;
+ } else {
+ ProxyRetryInfo retry_info;
+ retry_info.current_delay = kProxyRetryDelay;
+ retry_info.bad_until = TimeTicks().Now() + retry_info.current_delay;
+ (*proxy_retry_info)[proxies_[0]] = retry_info;
+ }
+
+ // Remove this proxy from our list.
+ proxies_.erase(proxies_.begin());
+
+ return !proxies_.empty();
+}
+
+// ProxyInfo ------------------------------------------------------------------
+
+ProxyInfo::ProxyInfo()
+ : config_id_(ProxyConfig::INVALID_ID),
+ config_was_tried_(false) {
+}
+
+void ProxyInfo::Use(const ProxyInfo& other) {
+ proxy_list_ = other.proxy_list_;
+}
+
+void ProxyInfo::UseDirect() {
+ proxy_list_.Set(std::wstring());
+}
+
+void ProxyInfo::UseNamedProxy(const std::wstring& proxy_server) {
+ proxy_list_.Set(proxy_server);
+}
+
+void ProxyInfo::Apply(HINTERNET request_handle) {
+ WINHTTP_PROXY_INFO pi;
+ std::wstring proxy; // We need to declare this variable here because
+ // lpszProxy needs to be valid in WinHttpSetOption.
+ if (is_direct()) {
+ pi.dwAccessType = WINHTTP_ACCESS_TYPE_NO_PROXY;
+ pi.lpszProxy = WINHTTP_NO_PROXY_NAME;
+ pi.lpszProxyBypass = WINHTTP_NO_PROXY_BYPASS;
+ } else {
+ proxy = proxy_list_.Get();
+ pi.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
+ pi.lpszProxy = const_cast<LPWSTR>(proxy.c_str());
+ // NOTE: Specifying a bypass list here would serve no purpose.
+ pi.lpszProxyBypass = WINHTTP_NO_PROXY_BYPASS;
+ }
+ WinHttpSetOption(request_handle, WINHTTP_OPTION_PROXY, &pi, sizeof(pi));
+}
+
+// ProxyService::PacRequest ---------------------------------------------------
+
+// We rely on the fact that the origin thread (and its message loop) will not
+// be destroyed until after the PAC thread is destroyed.
+
+class ProxyService::PacRequest :
+ public base::RefCountedThreadSafe<ProxyService::PacRequest> {
+ public:
+ PacRequest(ProxyService* service,
+ const std::wstring& pac_url,
+ CompletionCallback* callback)
+ : service_(service),
+ callback_(callback),
+ results_(NULL),
+ config_id_(service->config_id()),
+ pac_url_(pac_url),
+ origin_loop_(NULL) {
+ // We need to remember original loop if only in case of asynchronous call
+ if (callback_)
+ origin_loop_ = MessageLoop::current();
+ }
+
+ void Query(const std::wstring& url, ProxyInfo* results) {
+ results_ = results;
+ // If we have a valid callback then execute Query asynchronously
+ if (callback_) {
+ AddRef(); // balanced in QueryComplete
+ service_->pac_thread()->message_loop()->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &ProxyService::PacRequest::DoQuery,
+ service_->resolver(), url, pac_url_));
+ } else {
+ DoQuery(service_->resolver(), url, pac_url_);
+ }
+ }
+
+ void Cancel() {
+ // Clear these to inform QueryComplete that it should not try to
+ // access them.
+ service_ = NULL;
+ callback_ = NULL;
+ results_ = NULL;
+ }
+
+ private:
+ // Runs on the PAC thread if a valid callback is provided.
+ void DoQuery(ProxyResolver* resolver,
+ const std::wstring& query_url,
+ const std::wstring& pac_url) {
+ int rv = resolver->GetProxyForURL(query_url, pac_url, &results_buf_);
+ if (origin_loop_) {
+ origin_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &PacRequest::QueryComplete, rv));
+ } else {
+ QueryComplete(rv);
+ }
+ }
+
+ // If a valid callback is provided, this runs on the origin thread to
+ // indicate that the completion callback should be run.
+ void QueryComplete(int result_code) {
+ if (service_)
+ service_->DidCompletePacRequest(config_id_, result_code);
+
+ if (result_code == OK && results_) {
+ results_->Use(results_buf_);
+ results_->RemoveBadProxies(service_->proxy_retry_info_);
+ }
+
+ if (callback_)
+ callback_->Run(result_code);
+
+ if (origin_loop_) {
+ Release(); // balances the AddRef in Query. we may get deleted after
+ // we return.
+ }
+ }
+
+ // Must only be used on the "origin" thread.
+ ProxyService* service_;
+ CompletionCallback* callback_;
+ ProxyInfo* results_;
+ ProxyConfig::ID config_id_;
+
+ // Usable from within DoQuery on the PAC thread.
+ ProxyInfo results_buf_;
+ std::wstring pac_url_;
+ MessageLoop* origin_loop_;
+};
+
+// ProxyService ---------------------------------------------------------------
+
+ProxyService::ProxyService(ProxyResolver* resolver)
+ : resolver_(resolver),
+ config_is_bad_(false) {
+ UpdateConfig();
+}
+
+int ProxyService::ResolveProxy(const GURL& url, ProxyInfo* result,
+ CompletionCallback* callback,
+ PacRequest** pac_request) {
+ // The overhead of calling WinHttpGetIEProxyConfigForCurrentUser is very low.
+ const TimeDelta kProxyConfigMaxAge = TimeDelta::FromSeconds(5);
+
+ // Periodically check for a new config.
+ if ((TimeTicks::Now() - config_last_update_time_) > kProxyConfigMaxAge)
+ UpdateConfig();
+ result->config_id_ = config_.id();
+
+ // Fallback to a "direct" (no proxy) connection if the current configuration
+ // is known to be bad.
+ if (config_is_bad_) {
+ // Reset this flag to false in case the ProxyInfo object is being
+ // re-used by the caller.
+ result->config_was_tried_ = false;
+ } else {
+ // Remember that we are trying to use the current proxy configuration.
+ result->config_was_tried_ = true;
+
+ if (!config_.proxy_server.empty()) {
+ if (ShouldBypassProxyForURL(url)) {
+ result->UseDirect();
+ } else {
+ // If proxies are specified on a per protocol basis, the proxy server
+ // field contains a list the format of which is as below:-
+ // "scheme1=url:port;scheme2=url:port", etc.
+ std::wstring url_scheme = ASCIIToWide(url.scheme());
+
+ WStringTokenizer proxy_server_list(config_.proxy_server, L";");
+ while (proxy_server_list.GetNext()) {
+ WStringTokenizer proxy_server_for_scheme(
+ proxy_server_list.token_begin(), proxy_server_list.token_end(),
+ L"=");
+
+ while (proxy_server_for_scheme.GetNext()) {
+ const std::wstring& proxy_server_scheme =
+ proxy_server_for_scheme.token();
+
+ // If we fail to get the proxy server here, it means that
+ // this is a regular proxy server configuration, i.e. proxies
+ // are not configured per protocol.
+ if (!proxy_server_for_scheme.GetNext()) {
+ result->UseNamedProxy(proxy_server_scheme);
+ return OK;
+ }
+
+ if (proxy_server_scheme == url_scheme) {
+ result->UseNamedProxy(proxy_server_for_scheme.token());
+ return OK;
+ }
+ }
+ }
+ // We failed to find a matching proxy server for the current URL
+ // scheme. Default to direct.
+ result->UseDirect();
+ }
+ return OK;
+ }
+
+ if (!config_.pac_url.empty() || config_.auto_detect) {
+ if (callback) {
+ // Create PAC thread for asynchronous mode.
+ if (!pac_thread_.get()) {
+ pac_thread_.reset(new Thread("pac-thread"));
+ pac_thread_->Start();
+ }
+ } else {
+ // If this request is synchronous, then there's no point
+ // in returning PacRequest instance
+ DCHECK(!pac_request);
+ }
+
+ scoped_refptr<PacRequest> req =
+ new PacRequest(this, config_.pac_url, callback);
+ req->Query(UTF8ToWide(url.spec()), result);
+
+ if (callback) {
+ if (pac_request)
+ *pac_request = req;
+ return ERR_IO_PENDING; // Wait for callback.
+ }
+ return OK;
+ }
+ }
+
+ // otherwise, we have no proxy config
+ result->UseDirect();
+ return OK;
+}
+
+int ProxyService::ReconsiderProxyAfterError(const GURL& url,
+ ProxyInfo* result,
+ CompletionCallback* callback,
+ PacRequest** pac_request) {
+ // Check to see if we have a new config since ResolveProxy was called. We
+ // want to re-run ResolveProxy in two cases: 1) we have a new config, or 2) a
+ // direct connection failed and we never tried the current config.
+
+ bool re_resolve = result->config_id_ != config_.id();
+ if (!re_resolve) {
+ UpdateConfig();
+ if (result->config_id_ != config_.id()) {
+ // A new configuration!
+ re_resolve = true;
+ } else if (!result->config_was_tried_) {
+ // We never tried the proxy configuration since we thought it was bad,
+ // but because we failed to establish a connection, let's try the proxy
+ // configuration again to see if it will work now.
+ config_is_bad_ = false;
+ re_resolve = true;
+ }
+ }
+ if (re_resolve) {
+ // If we have a new config or the config was never tried, we delete the
+ // list of bad proxies and we try again.
+ proxy_retry_info_.clear();
+ return ResolveProxy(url, result, callback, pac_request);
+ }
+
+ // We don't have new proxy settings to try, fallback to the next proxy
+ // in the list.
+ bool was_direct = result->is_direct();
+ if (!was_direct && result->Fallback(&proxy_retry_info_))
+ return OK;
+
+ if (!config_.auto_detect && !config_.proxy_server.empty()) {
+ // If auto detect is on, then we should try a DIRECT connection
+ // as the attempt to reach the proxy failed.
+ return ERR_FAILED;
+ }
+
+ // If we already tried a direct connection, then just give up.
+ if (was_direct)
+ return ERR_FAILED;
+
+ // Try going direct.
+ result->UseDirect();
+ return OK;
+}
+
+void ProxyService::CancelPacRequest(PacRequest* pac_request) {
+ pac_request->Cancel();
+}
+
+void ProxyService::DidCompletePacRequest(int config_id, int result_code) {
+ // If we get an error that indicates a bad PAC config, then we should
+ // remember that, and not try the PAC config again for a while.
+
+ // Our config may have already changed.
+ if (result_code == OK || config_id != config_.id())
+ return;
+
+ // Remember that this configuration doesn't work.
+ config_is_bad_ = true;
+}
+
+void ProxyService::UpdateConfig() {
+ ProxyConfig latest;
+ if (resolver_->GetProxyConfig(&latest) != OK)
+ return;
+ config_last_update_time_ = TimeTicks::Now();
+
+ if (latest.Equals(config_))
+ return;
+
+ config_ = latest;
+ config_is_bad_ = false;
+
+ // We have a new config, we should clear the list of bad proxies.
+ proxy_retry_info_.clear();
+}
+
+bool ProxyService::ShouldBypassProxyForURL(const GURL& url) {
+ std::wstring url_domain = ASCIIToWide(url.scheme());
+ if (!url_domain.empty())
+ url_domain += L"://";
+
+ url_domain += ASCIIToWide(url.host());
+ StringToLowerASCII(url_domain);
+
+ WStringTokenizer proxy_server_bypass_list(config_.proxy_bypass, L";");
+ while (proxy_server_bypass_list.GetNext()) {
+ std::wstring bypass_url_domain = proxy_server_bypass_list.token();
+ if (bypass_url_domain == L"<local>") {
+ // Any name without a DOT (.) is considered to be local.
+ if (url.host().find(L'.') == std::wstring::npos)
+ return true;
+ continue;
+ }
+
+ // The proxy server bypass list can contain entities with http/https
+ // If no scheme is specified then it indicates that all schemes are
+ // allowed for the current entry. For matching this we just use
+ // the protocol scheme of the url passed in.
+ if (bypass_url_domain.find(L"://") == std::wstring::npos) {
+ std::wstring bypass_url_domain_with_scheme = ASCIIToWide(url.scheme());
+ bypass_url_domain_with_scheme += L"://";
+ bypass_url_domain_with_scheme += bypass_url_domain;
+
+ bypass_url_domain = bypass_url_domain_with_scheme;
+ }
+
+ StringToLowerASCII(bypass_url_domain);
+
+ if (MatchPattern(url_domain, bypass_url_domain))
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace net
diff --git a/net/proxy/proxy_service.h b/net/proxy/proxy_service.h
new file mode 100644
index 0000000..c52926e
--- /dev/null
+++ b/net/proxy/proxy_service.h
@@ -0,0 +1,303 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef NET_PROXY_PROXY_SERVICE_H_
+#define NET_PROXY_PROXY_SERVICE_H_
+
+#include <map>
+#include <vector>
+
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "base/thread.h"
+#include "base/time.h"
+#include "net/base/completion_callback.h"
+
+typedef LPVOID HINTERNET; // From winhttp.h
+
+class GURL;
+
+namespace net {
+
+class ProxyInfo;
+class ProxyResolver;
+
+// Proxy configuration used to by the ProxyService.
+class ProxyConfig {
+ public:
+ typedef int ID;
+
+ // Indicates an invalid proxy config.
+ enum { INVALID_ID = 0 };
+
+ ProxyConfig();
+ // Default copy-constructor an assignment operator are OK!
+
+ // Used to numerically identify this configuration.
+ ID id() const { return id_; }
+
+ // True if the proxy configuration should be auto-detected.
+ bool auto_detect;
+
+ // If non-empty, indicates the URL of the proxy auto-config file to use.
+ std::wstring pac_url;
+
+ // If non-empty, indicates the proxy server to use (of the form host:port).
+ std::wstring proxy_server;
+
+ // If non-empty, indicates a comma-delimited list of hosts that should bypass
+ // any proxy configuration. For these hosts, a direct connection should
+ // always be used.
+ std::wstring proxy_bypass;
+
+ // Returns true if the given config is equivalent to this config.
+ bool Equals(const ProxyConfig& other) const;
+
+ private:
+ static int last_id_;
+ int id_;
+};
+
+// Contains the information about when to retry a proxy server.
+struct ProxyRetryInfo {
+ // We should not retry until this time.
+ TimeTicks bad_until;
+
+ // This is the current delay. If the proxy is still bad, we need to increase
+ // this delay.
+ TimeDelta current_delay;
+};
+
+// Map of proxy servers with the associated RetryInfo structures.
+typedef std::map<std::wstring, ProxyRetryInfo> ProxyRetryInfoMap;
+
+// This class can be used to resolve the proxy server to use when loading a
+// HTTP(S) URL. It uses to the given ProxyResolver to handle the actual proxy
+// resolution. See ProxyResolverWinHttp for example. The consumer of this
+// class is responsible for ensuring that the ProxyResolver instance remains
+// valid for the lifetime of the ProxyService.
+class ProxyService {
+ public:
+ explicit ProxyService(ProxyResolver* resolver);
+
+ // Used internally to handle PAC queries.
+ class PacRequest;
+
+ // Returns OK if proxy information could be provided synchronously. Else,
+ // ERR_IO_PENDING is returned to indicate that the result will be available
+ // when the callback is run. The callback is run on the thread that calls
+ // ResolveProxy.
+ //
+ // The caller is responsible for ensuring that |results| and |callback|
+ // remain valid until the callback is run or until |pac_request| is cancelled
+ // via CancelPacRequest. |pac_request| is only valid while the completion
+ // callback is still pending.
+ //
+ // We use the three possible proxy access types in the following order, and
+ // we only use one of them (no falling back to other access types if the
+ // chosen one doesn't work).
+ // 1. named proxy
+ // 2. PAC URL
+ // 3. WPAD auto-detection
+ //
+ int ResolveProxy(const GURL& url,
+ ProxyInfo* results,
+ CompletionCallback* callback,
+ PacRequest** pac_request);
+
+ // This method is called after a failure to connect or resolve a host name.
+ // It gives the proxy service an opportunity to reconsider the proxy to use.
+ // The |results| parameter contains the results returned by an earlier call
+ // to ResolveProxy. The semantics of this call are otherwise similar to
+ // ResolveProxy.
+ //
+ // Returns ERR_FAILED if there is not another proxy config to try.
+ //
+ int ReconsiderProxyAfterError(const GURL& url,
+ ProxyInfo* results,
+ CompletionCallback* callback,
+ PacRequest** pac_request);
+
+ // Call this method with a non-null |pac_request| to cancel the PAC request.
+ void CancelPacRequest(PacRequest* pac_request);
+
+ private:
+ friend class PacRequest;
+
+ ProxyResolver* resolver() { return resolver_; }
+ Thread* pac_thread() { return pac_thread_.get(); }
+
+ // Identifies the proxy configuration.
+ ProxyConfig::ID config_id() const { return config_.id(); }
+
+ // Checks to see if the proxy configuration changed, and then updates config_
+ // to reference the new configuration.
+ void UpdateConfig();
+
+ // 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
+ // error code, indicating a bad proxy configuration.
+ void DidCompletePacRequest(int config_id, int result_code);
+
+ // Returns true if the URL passed in should not go through the proxy server.
+ // 1. If the bypass proxy list contains the string <local> and the URL
+ // passed in is a local URL, i.e. a URL without a DOT (.)
+ // 2. The URL matches one of the entities in the proxy bypass list.
+ bool ShouldBypassProxyForURL(const GURL& url);
+
+ ProxyResolver* resolver_;
+ scoped_ptr<Thread> pac_thread_;
+
+ // We store the IE proxy config and a counter that is incremented each time
+ // the config changes.
+ ProxyConfig config_;
+
+ // Indicates that the configuration is bad and should be ignored.
+ bool config_is_bad_;
+
+ // The time when the proxy configuration was last read from the system.
+ TimeTicks config_last_update_time_;
+
+ // Map of the known bad proxies and the information about the retry time.
+ ProxyRetryInfoMap proxy_retry_info_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProxyService);
+};
+
+// This class is used to hold a list of proxies returned by GetProxyForUrl or
+// manually configured. It handles proxy fallback if multiple servers are
+// specified.
+class ProxyList {
+ public:
+ // Initializes the proxy list to a string containing one or more proxy servers
+ // delimited by a semicolon.
+ void Set(const std::wstring& proxy_list);
+
+ // Initializes the proxy list to a vector containing one or more proxy
+ // servers.
+ void SetVector(const std::vector<std::wstring>& proxy_list);
+
+ // Remove all proxies known to be bad from the proxy list.
+ void RemoveBadProxies(const ProxyRetryInfoMap& proxy_retry_info);
+
+ // Returns the first valid proxy server in the list.
+ std::wstring Get() const;
+
+ // Returns all the valid proxies, delimited by a semicolon.
+ std::wstring GetList() const;
+
+ // Marks the current proxy server as bad and deletes it from the list. The
+ // list of known bad proxies is given by proxy_retry_info. Returns true if
+ // there is another server available in the list.
+ bool Fallback(ProxyRetryInfoMap* proxy_retry_info);
+
+ private:
+ // List of proxies.
+ std::vector<std::wstring> proxies_;
+};
+
+// This object holds proxy information returned by ResolveProxy.
+class ProxyInfo {
+ public:
+ ProxyInfo();
+
+ // Use the same proxy server as the given |proxy_info|.
+ void Use(const ProxyInfo& proxy_info);
+
+ // Use a direct connection.
+ void UseDirect();
+
+ // Use a specific proxy server, of the form: <hostname> [":" <port>]
+ // This may optionally be a semi-colon delimited list of proxy servers.
+ void UseNamedProxy(const std::wstring& proxy_server);
+
+ // Apply this proxy information to the given WinHTTP request handle.
+ void Apply(HINTERNET request_handle);
+
+ // Returns true if this proxy info specifies a direct connection.
+ bool is_direct() const { return proxy_list_.Get().empty(); }
+
+ // Returns the first valid proxy server.
+ std::wstring proxy_server() const { return proxy_list_.Get(); }
+
+ std::string GetProxyServer() const { return WideToASCII(proxy_server()); }
+
+ // Marks the current proxy as bad. Returns true if there is another proxy
+ // available to try in proxy list_.
+ bool Fallback(ProxyRetryInfoMap* proxy_retry_info) {
+ return proxy_list_.Fallback(proxy_retry_info);
+ }
+
+ // Remove all proxies known to be bad from the proxy list.
+ void RemoveBadProxies(const ProxyRetryInfoMap& proxy_retry_info) {
+ proxy_list_.RemoveBadProxies(proxy_retry_info);
+ }
+
+ private:
+ friend class ProxyService;
+
+ // If proxy_list_ is set to empty, then a "direct" connection is indicated.
+ ProxyList proxy_list_;
+
+ // This value identifies the proxy config used to initialize this object.
+ ProxyConfig::ID config_id_;
+
+ // This flag is false when the proxy configuration was known to be bad when
+ // this proxy info was initialized. In such cases, we know that if this
+ // proxy info does not yield a connection that we might want to reconsider
+ // the proxy config given by config_id_.
+ bool config_was_tried_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProxyInfo);
+};
+
+// This interface provides the low-level functions to access the proxy
+// configuration and resolve proxies for given URLs synchronously.
+class ProxyResolver {
+ public:
+ virtual ~ProxyResolver() {}
+
+ // Get the proxy configuration. Returns OK if successful or an error code if
+ // otherwise. |config| should be in its initial state when this method is
+ // called.
+ virtual int GetProxyConfig(ProxyConfig* config) = 0;
+
+ // Query the proxy auto-config file (specified by |pac_url|) for the proxy to
+ // use to load the given |query_url|. Returns OK if successful or an error
+ // code if otherwise.
+ virtual int GetProxyForURL(const std::wstring& query_url,
+ const std::wstring& pac_url,
+ ProxyInfo* results) = 0;
+};
+
+} // namespace net
+
+#endif // NET_PROXY_PROXY_SERVICE_H_
diff --git a/net/proxy/proxy_service_unittest.cc b/net/proxy/proxy_service_unittest.cc
new file mode 100644
index 0000000..f4b9292
--- /dev/null
+++ b/net/proxy/proxy_service_unittest.cc
@@ -0,0 +1,408 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "googleurl/src/gurl.h"
+#include "net/base/net_errors.h"
+#include "net/proxy/proxy_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class MockProxyResolver : public net::ProxyResolver {
+ public:
+ MockProxyResolver() : fail_get_proxy_for_url(false) {
+ config.reset(new net::ProxyConfig);
+ }
+ virtual int GetProxyConfig(net::ProxyConfig* results) {
+ *results = *(config.get());
+ return net::OK;
+ }
+ virtual int GetProxyForURL(const std::wstring& query_url,
+ const std::wstring& pac_url,
+ net::ProxyInfo* results) {
+ if (pac_url != config->pac_url)
+ return net::ERR_INVALID_ARGUMENT;
+ if (fail_get_proxy_for_url)
+ return net::ERR_FAILED;
+ if (GURL(query_url).host() == info_predicate_query_host) {
+ results->Use(info);
+ } else {
+ results->UseDirect();
+ }
+ return net::OK;
+ }
+ scoped_ptr<net::ProxyConfig> config;
+ net::ProxyInfo info;
+
+ // info is only returned if query_url in GetProxyForURL matches this:
+ std::string info_predicate_query_host;
+
+ // If true, then GetProxyForURL will fail, which simulates failure to
+ // download or execute the PAC file.
+ bool fail_get_proxy_for_url;
+};
+
+} // namespace
+
+TEST(ProxyServiceTest, Direct) {
+ MockProxyResolver resolver;
+ net::ProxyService service(&resolver);
+
+ GURL url("http://www.google.com/");
+
+ net::ProxyInfo info;
+ int rv = service.ResolveProxy(url, &info, NULL, NULL);
+ EXPECT_EQ(rv, net::OK);
+ EXPECT_TRUE(info.is_direct());
+}
+
+TEST(ProxyServiceTest, PAC) {
+ MockProxyResolver resolver;
+ resolver.config->pac_url = L"http://foopy/proxy.pac";
+ resolver.info.UseNamedProxy(L"foopy");
+ resolver.info_predicate_query_host = "www.google.com";
+
+ net::ProxyService service(&resolver);
+
+ GURL url("http://www.google.com/");
+
+ net::ProxyInfo info;
+ int rv = service.ResolveProxy(url, &info, NULL, NULL);
+ EXPECT_EQ(rv, net::OK);
+ EXPECT_FALSE(info.is_direct());
+ EXPECT_EQ(info.proxy_server(), L"foopy");
+}
+
+TEST(ProxyServiceTest, PAC_FailoverToDirect) {
+ MockProxyResolver resolver;
+ resolver.config->pac_url = L"http://foopy/proxy.pac";
+ resolver.info.UseNamedProxy(L"foopy:8080");
+ resolver.info_predicate_query_host = "www.google.com";
+
+ net::ProxyService service(&resolver);
+
+ GURL url("http://www.google.com/");
+
+ net::ProxyInfo info;
+ int rv = service.ResolveProxy(url, &info, NULL, NULL);
+ EXPECT_EQ(rv, net::OK);
+ EXPECT_FALSE(info.is_direct());
+ EXPECT_EQ(info.proxy_server(), L"foopy:8080");
+
+ // Now, imagine that connecting to foopy:8080 fails.
+ rv = service.ReconsiderProxyAfterError(url, &info, NULL, NULL);
+ EXPECT_EQ(rv, net::OK);
+ EXPECT_TRUE(info.is_direct());
+}
+
+TEST(ProxyServiceTest, PAC_FailsToDownload) {
+ // Test what happens when we fail to download the PAC URL.
+
+ MockProxyResolver resolver;
+ resolver.config->pac_url = L"http://foopy/proxy.pac";
+ resolver.info.UseNamedProxy(L"foopy:8080");
+ resolver.info_predicate_query_host = "www.google.com";
+ resolver.fail_get_proxy_for_url = true;
+
+ net::ProxyService service(&resolver);
+
+ GURL url("http://www.google.com/");
+ net::ProxyInfo info;
+ int rv = service.ResolveProxy(url, &info, NULL, NULL);
+ EXPECT_EQ(rv, net::OK);
+ EXPECT_TRUE(info.is_direct());
+
+ rv = service.ResolveProxy(url, &info, NULL, NULL);
+ EXPECT_EQ(rv, net::OK);
+ EXPECT_TRUE(info.is_direct());
+
+ resolver.fail_get_proxy_for_url = false;
+ resolver.info.UseNamedProxy(L"foopy_valid:8080");
+
+ // But, if that fails, then we should give the proxy config another shot
+ // since we have never tried it with this URL before.
+ rv = service.ReconsiderProxyAfterError(url, &info, NULL, NULL);
+ EXPECT_EQ(rv, net::OK);
+ EXPECT_FALSE(info.is_direct());
+ EXPECT_EQ(info.proxy_server(), L"foopy_valid:8080");
+}
+
+TEST(ProxyServiceTest, ProxyFallback) {
+ // Test what happens when we specify multiple proxy servers and some of them
+ // are bad.
+
+ MockProxyResolver resolver;
+ resolver.config->pac_url = L"http://foopy/proxy.pac";
+ resolver.info.UseNamedProxy(L"foopy1:8080;foopy2:9090");
+ resolver.info_predicate_query_host = "www.google.com";
+ resolver.fail_get_proxy_for_url = false;
+
+ net::ProxyService service(&resolver);
+
+ GURL url("http://www.google.com/");
+
+ // Get the proxy information.
+ net::ProxyInfo info;
+ int rv = service.ResolveProxy(url, &info, NULL, NULL);
+ EXPECT_EQ(rv, net::OK);
+ EXPECT_FALSE(info.is_direct());
+
+ // The first item is valid.
+ EXPECT_EQ(info.proxy_server(), L"foopy1:8080");
+
+ // Fake an error on the proxy.
+ rv = service.ReconsiderProxyAfterError(url, &info, NULL, NULL);
+ EXPECT_EQ(rv, net::OK);
+
+ // The second proxy should be specified.
+ EXPECT_EQ(info.proxy_server(), L"foopy2:9090");
+
+ // Create a new resolver that returns 3 proxies. The second one is already
+ // known to be bad.
+ resolver.config->pac_url = L"http://foopy/proxy.pac";
+ resolver.info.UseNamedProxy(L"foopy3:7070;foopy1:8080;foopy2:9090");
+ resolver.info_predicate_query_host = "www.google.com";
+ resolver.fail_get_proxy_for_url = false;
+
+ rv = service.ResolveProxy(url, &info, NULL, NULL);
+ EXPECT_EQ(rv, net::OK);
+ EXPECT_FALSE(info.is_direct());
+ EXPECT_EQ(info.proxy_server(), L"foopy3:7070");
+
+ // We fake another error. It should now try the third one.
+ rv = service.ReconsiderProxyAfterError(url, &info, NULL, NULL);
+ EXPECT_EQ(rv, net::OK);
+ EXPECT_EQ(info.proxy_server(), L"foopy2:9090");
+
+ // Fake another error, the last proxy is gone, the list should now be empty.
+ rv = service.ReconsiderProxyAfterError(url, &info, NULL, NULL);
+ EXPECT_EQ(rv, net::OK); // We try direct.
+ EXPECT_TRUE(info.is_direct());
+
+ // If it fails again, we don't have anything else to try.
+ rv = service.ReconsiderProxyAfterError(url, &info, NULL, NULL);
+ EXPECT_EQ(rv, net::ERR_FAILED); // We try direct.
+
+ // TODO(nsylvain): Test that the proxy can be retried after the delay.
+}
+
+TEST(ProxyServiceTest, ProxyFallback_NewSettings) {
+ // Test proxy failover when new settings are available.
+
+ MockProxyResolver resolver;
+ resolver.config->pac_url = L"http://foopy/proxy.pac";
+ resolver.info.UseNamedProxy(L"foopy1:8080;foopy2:9090");
+ resolver.info_predicate_query_host = "www.google.com";
+ resolver.fail_get_proxy_for_url = false;
+
+ net::ProxyService service(&resolver);
+
+ GURL url("http://www.google.com/");
+
+ // Get the proxy information.
+ net::ProxyInfo info;
+ int rv = service.ResolveProxy(url, &info, NULL, NULL);
+ EXPECT_EQ(rv, net::OK);
+ EXPECT_FALSE(info.is_direct());
+
+ // The first item is valid.
+ EXPECT_EQ(info.proxy_server(), L"foopy1:8080");
+
+ // Fake an error on the proxy, and also a new configuration on the proxy.
+ resolver.config.reset(new net::ProxyConfig);
+ resolver.config->pac_url = L"http://foopy-new/proxy.pac";
+
+ rv = service.ReconsiderProxyAfterError(url, &info, NULL, NULL);
+ EXPECT_EQ(rv, net::OK);
+
+ // The first proxy is still there since the configuration changed.
+ EXPECT_EQ(info.proxy_server(), L"foopy1:8080");
+
+ // We fake another error. It should now ignore the first one.
+ rv = service.ReconsiderProxyAfterError(url, &info, NULL, NULL);
+ EXPECT_EQ(rv, net::OK);
+ EXPECT_EQ(info.proxy_server(), L"foopy2:9090");
+
+ // We simulate a new configuration.
+ resolver.config.reset(new net::ProxyConfig);
+ resolver.config->pac_url = L"http://foopy-new2/proxy.pac";
+
+ // We fake anothe error. It should go back to the first proxy.
+ rv = service.ReconsiderProxyAfterError(url, &info, NULL, NULL);
+ EXPECT_EQ(rv, net::OK);
+ EXPECT_EQ(info.proxy_server(), L"foopy1:8080");
+}
+
+TEST(ProxyServiceTest, ProxyFallback_BadConfig) {
+ // Test proxy failover when the configuration is bad.
+
+ MockProxyResolver resolver;
+ resolver.config->pac_url = L"http://foopy/proxy.pac";
+ resolver.info.UseNamedProxy(L"foopy1:8080;foopy2:9090");
+ resolver.info_predicate_query_host = "www.google.com";
+ resolver.fail_get_proxy_for_url = false;
+
+ net::ProxyService service(&resolver);
+
+ GURL url("http://www.google.com/");
+
+ // Get the proxy information.
+ net::ProxyInfo info;
+ int rv = service.ResolveProxy(url, &info, NULL, NULL);
+ EXPECT_EQ(rv, net::OK);
+ EXPECT_FALSE(info.is_direct());
+
+ // The first item is valid.
+ EXPECT_EQ(info.proxy_server(), L"foopy1:8080");
+
+ // Fake a proxy error.
+ rv = service.ReconsiderProxyAfterError(url, &info, NULL, NULL);
+ EXPECT_EQ(rv, net::OK);
+
+ // The first proxy is ignored, and the second one is selected.
+ EXPECT_FALSE(info.is_direct());
+ EXPECT_EQ(info.proxy_server(), L"foopy2:9090");
+
+ // Fake a PAC failure.
+ net::ProxyInfo info2;
+ resolver.fail_get_proxy_for_url = true;
+ rv = service.ResolveProxy(url, &info2, NULL, NULL);
+ EXPECT_EQ(rv, net::OK);
+
+ // No proxy servers are returned. It's a direct connection.
+ EXPECT_TRUE(info2.is_direct());
+
+ // The PAC is now fixed and will return a proxy server.
+ // It should also clear the list of bad proxies.
+ resolver.fail_get_proxy_for_url = false;
+
+ // Try to resolve, it will still return "direct" because we have no reason
+ // to check the config since everything works.
+ net::ProxyInfo info3;
+ rv = service.ResolveProxy(url, &info3, NULL, NULL);
+ EXPECT_EQ(rv, net::OK);
+ EXPECT_TRUE(info3.is_direct());
+
+ // But if the direct connection fails, we check if the ProxyInfo tried to
+ // resolve the proxy before, and if not (like in this case), we give the
+ // PAC another try.
+ rv = service.ReconsiderProxyAfterError(url, &info3, NULL, NULL);
+ EXPECT_EQ(rv, net::OK);
+
+ // The first proxy is still there since the list of bad proxies got cleared.
+ EXPECT_FALSE(info3.is_direct());
+ EXPECT_EQ(info3.proxy_server(), L"foopy1:8080");
+}
+
+TEST(ProxyServiceTest, ProxyBypassList) {
+ // Test what happens when a proxy bypass list is specified.
+
+ MockProxyResolver resolver;
+ resolver.config->proxy_server = L"foopy1:8080;foopy2:9090";
+ resolver.config->auto_detect = false;
+ resolver.config->proxy_bypass = L"<local>";
+
+ net::ProxyService service(&resolver);
+ GURL url("http://www.google.com/");
+ // Get the proxy information.
+ net::ProxyInfo info;
+ int rv = service.ResolveProxy(url, &info, NULL, NULL);
+ EXPECT_EQ(rv, net::OK);
+ EXPECT_FALSE(info.is_direct());
+
+ net::ProxyService service1(&resolver);
+ GURL test_url1("local");
+ net::ProxyInfo info1;
+ rv = service1.ResolveProxy(test_url1, &info1, NULL, NULL);
+ EXPECT_EQ(rv, net::OK);
+ EXPECT_TRUE(info1.is_direct());
+
+ resolver.config->proxy_bypass = L"<local>;*.org";
+ net::ProxyService service2(&resolver);
+ GURL test_url2("http://www.webkit.org");
+ net::ProxyInfo info2;
+ rv = service2.ResolveProxy(test_url2, &info2, NULL, NULL);
+ EXPECT_EQ(rv, net::OK);
+ EXPECT_TRUE(info2.is_direct());
+
+ resolver.config->proxy_bypass = L"<local>;*.org;7*";
+ net::ProxyService service3(&resolver);
+ GURL test_url3("http://74.125.19.147");
+ net::ProxyInfo info3;
+ rv = service3.ResolveProxy(test_url3, &info3, NULL, NULL);
+ EXPECT_EQ(rv, net::OK);
+ EXPECT_TRUE(info3.is_direct());
+
+ resolver.config->proxy_bypass = L"<local>;*.org;";
+ net::ProxyService service4(&resolver);
+ GURL test_url4("http://www.msn.com");
+ net::ProxyInfo info4;
+ rv = service4.ResolveProxy(test_url4, &info4, NULL, NULL);
+ EXPECT_EQ(rv, net::OK);
+ EXPECT_FALSE(info4.is_direct());
+}
+
+TEST(ProxyServiceTest, PerProtocolProxyTests) {
+ MockProxyResolver resolver;
+ resolver.config->proxy_server = L"http=foopy1:8080;https=foopy2:8080";
+ resolver.config->auto_detect = false;
+
+ net::ProxyService service1(&resolver);
+ GURL test_url1("http://www.msn.com");
+ net::ProxyInfo info1;
+ int rv = service1.ResolveProxy(test_url1, &info1, NULL, NULL);
+ EXPECT_EQ(rv, net::OK);
+ EXPECT_FALSE(info1.is_direct());
+ EXPECT_TRUE(info1.proxy_server() == L"foopy1:8080");
+
+ net::ProxyService service2(&resolver);
+ GURL test_url2("ftp://ftp.google.com");
+ net::ProxyInfo info2;
+ rv = service2.ResolveProxy(test_url2, &info2, NULL, NULL);
+ EXPECT_EQ(rv, net::OK);
+ EXPECT_TRUE(info2.is_direct());
+ EXPECT_TRUE(info2.proxy_server() == L"");
+
+ net::ProxyService service3(&resolver);
+ GURL test_url3("https://webbranch.techcu.com");
+ net::ProxyInfo info3;
+ rv = service3.ResolveProxy(test_url3, &info3, NULL, NULL);
+ EXPECT_EQ(rv, net::OK);
+ EXPECT_FALSE(info3.is_direct());
+ EXPECT_TRUE(info3.proxy_server() == L"foopy2:8080");
+
+ resolver.config->proxy_server = L"foopy1:8080";
+ net::ProxyService service4(&resolver);
+ GURL test_url4("www.microsoft.com");
+ net::ProxyInfo info4;
+ rv = service4.ResolveProxy(test_url4, &info4, NULL, NULL);
+ EXPECT_EQ(rv, net::OK);
+ EXPECT_FALSE(info4.is_direct());
+ EXPECT_TRUE(info4.proxy_server() == L"foopy1:8080");
+}