summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorjoi@chromium.org <joi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-05-17 17:29:05 +0000
committerjoi@chromium.org <joi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-05-17 17:29:05 +0000
commitcfbcab964ac927102ce49c20ea1c4e1bcd667a04 (patch)
treeff34b602cae9072e4bd5b43f75d73abb91ba142b /net
parent0179f39b00af02a9abde26f5003f213e25a4444a (diff)
downloadchromium_src-cfbcab964ac927102ce49c20ea1c4e1bcd667a04.zip
chromium_src-cfbcab964ac927102ce49c20ea1c4e1bcd667a04.tar.gz
chromium_src-cfbcab964ac927102ce49c20ea1c4e1bcd667a04.tar.bz2
Adds support for the DHCP portion of the WPAD (proxy auto-discovery) protocol.
This is Windows-only for now, and is disabled by default. Start Chrome with the flag --enable-dhcp-wpad to enable the feature. See discussion in comment on DhcpProxyScriptFetcherFactory for why this needs to be done in a per-platform way rather than cross-platform. The code is factored so that adding other platform implementations will be straight forward. Most of the implementation is stand-alone and extends the ScriptProxyFetcher class hierarchy (and makes its interface slightly more generic). The integration point into existing code is in InitProxyResolver, which previously handled fallback from DNS auto-detect to custom PAC URL and now does fallback from DHCP to DNS to custom PAC URL. BUG=18575 TEST=net_unittests has good coverage for the new and changed code, but manual tests on a network with a PAC URL configured in DHCP are also needed. Review URL: http://codereview.chromium.org/6831025 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@85646 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/base/net_error_list.h5
-rw-r--r--net/base/net_log_event_type_list.h14
-rw-r--r--net/net.gyp15
-rw-r--r--net/proxy/dhcp_proxy_script_adapter_fetcher_win.cc286
-rw-r--r--net/proxy/dhcp_proxy_script_adapter_fetcher_win.h198
-rw-r--r--net/proxy/dhcp_proxy_script_adapter_fetcher_win_unittest.cc300
-rw-r--r--net/proxy/dhcp_proxy_script_fetcher.cc39
-rw-r--r--net/proxy/dhcp_proxy_script_fetcher.h98
-rw-r--r--net/proxy/dhcp_proxy_script_fetcher_factory.cc56
-rw-r--r--net/proxy/dhcp_proxy_script_fetcher_factory.h68
-rw-r--r--net/proxy/dhcp_proxy_script_fetcher_factory_unittest.cc57
-rw-r--r--net/proxy/dhcp_proxy_script_fetcher_win.cc246
-rw-r--r--net/proxy/dhcp_proxy_script_fetcher_win.h123
-rw-r--r--net/proxy/dhcp_proxy_script_fetcher_win_unittest.cc545
-rw-r--r--net/proxy/dhcpcsvc_init_win.cc40
-rw-r--r--net/proxy/dhcpcsvc_init_win.h20
-rw-r--r--net/proxy/init_proxy_resolver.cc145
-rw-r--r--net/proxy/init_proxy_resolver.h46
-rw-r--r--net/proxy/init_proxy_resolver_unittest.cc186
-rw-r--r--net/proxy/mock_proxy_script_fetcher.cc55
-rw-r--r--net/proxy/mock_proxy_script_fetcher.h44
-rw-r--r--net/proxy/proxy_script_fetcher.h2
-rw-r--r--net/proxy/proxy_script_fetcher_impl.cc2
-rw-r--r--net/proxy/proxy_script_fetcher_impl.h6
-rw-r--r--net/proxy/proxy_service.cc18
-rw-r--r--net/proxy/proxy_service.h21
-rw-r--r--net/proxy/proxy_service_unittest.cc82
27 files changed, 2552 insertions, 165 deletions
diff --git a/net/base/net_error_list.h b/net/base/net_error_list.h
index 9979394..19e5da0 100644
--- a/net/base/net_error_list.h
+++ b/net/base/net_error_list.h
@@ -446,6 +446,11 @@ NET_ERROR(RESPONSE_HEADERS_MULTIPLE_CONTENT_LENGTH, -346)
// headers are missing, so we're expecting additional frames to complete them.
NET_ERROR(INCOMPLETE_SPDY_HEADERS, -347)
+// No PAC URL configuration could be retrieved from DHCP. This can indicate
+// either a failure to retrieve the DHCP configuration, or that there was no
+// PAC URL configured in DHCP.
+NET_ERROR(PAC_NOT_IN_DHCP, -348)
+
// The cache does not have the requested entry.
NET_ERROR(CACHE_MISS, -400)
diff --git a/net/base/net_log_event_type_list.h b/net/base/net_log_event_type_list.h
index 69c49e3..6a8e6b4 100644
--- a/net/base/net_log_event_type_list.h
+++ b/net/base/net_log_event_type_list.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -119,12 +119,12 @@ EVENT_TYPE(INIT_PROXY_RESOLVER_WAIT)
//
// The START event has the parameters:
// {
-// "url": <URL string of script being fetched>,
+// "source": <String describing where PAC script comes from>,
// }
//
// If the fetch failed, then the END phase has these parameters:
// {
-// "error_code": <Net error code integer>,
+// "net_error": <Net error code integer>,
// }
EVENT_TYPE(INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT)
@@ -133,7 +133,7 @@ EVENT_TYPE(INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT)
//
// If the parsing of the script failed, the END phase will have parameters:
// {
-// "error_code": <Net error code integer>,
+// "net_error": <Net error code integer>,
// }
EVENT_TYPE(INIT_PROXY_RESOLVER_SET_PAC_SCRIPT)
@@ -141,9 +141,9 @@ EVENT_TYPE(INIT_PROXY_RESOLVER_SET_PAC_SCRIPT)
// configured script fetcher. (This indicates a configuration error).
EVENT_TYPE(INIT_PROXY_RESOLVER_HAS_NO_FETCHER)
-// This event is emitted after deciding to fall-back to the next PAC
-// script in the list.
-EVENT_TYPE(INIT_PROXY_RESOLVER_FALLING_BACK_TO_NEXT_PAC_URL)
+// This event is emitted after deciding to fall-back to the next source
+// of PAC scripts in the list.
+EVENT_TYPE(INIT_PROXY_RESOLVER_FALLING_BACK_TO_NEXT_PAC_SOURCE)
// ------------------------------------------------------------------------
// ProxyService
diff --git a/net/net.gyp b/net/net.gyp
index 4aeea97..979ff9e 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -407,6 +407,16 @@
'http/proxy_client_socket.h',
'ocsp/nss_ocsp.cc',
'ocsp/nss_ocsp.h',
+ 'proxy/dhcp_proxy_script_fetcher.cc',
+ 'proxy/dhcp_proxy_script_fetcher.h',
+ 'proxy/dhcp_proxy_script_fetcher_factory.cc',
+ 'proxy/dhcp_proxy_script_fetcher_factory.h',
+ 'proxy/dhcp_proxy_script_adapter_fetcher_win.cc',
+ 'proxy/dhcp_proxy_script_adapter_fetcher_win.h',
+ 'proxy/dhcp_proxy_script_fetcher_win.cc',
+ 'proxy/dhcp_proxy_script_fetcher_win.h',
+ 'proxy/dhcpcsvc_init_win.cc',
+ 'proxy/dhcpcsvc_init_win.h',
'proxy/init_proxy_resolver.cc',
'proxy/init_proxy_resolver.h',
'proxy/multi_threaded_proxy_resolver.cc',
@@ -913,6 +923,9 @@
'http/mock_sspi_library_win.h',
'http/mock_sspi_library_win.cc',
'http/url_security_manager_unittest.cc',
+ 'proxy/dhcp_proxy_script_adapter_fetcher_win_unittest.cc',
+ 'proxy/dhcp_proxy_script_fetcher_factory_unittest.cc',
+ 'proxy/dhcp_proxy_script_fetcher_win_unittest.cc',
'proxy/init_proxy_resolver_unittest.cc',
'proxy/multi_threaded_proxy_resolver_unittest.cc',
'proxy/network_delegate_error_observer_unittest.cc',
@@ -1128,6 +1141,8 @@
'disk_cache/disk_cache_test_util.h',
'proxy/mock_proxy_resolver.cc',
'proxy/mock_proxy_resolver.h',
+ 'proxy/mock_proxy_script_fetcher.cc',
+ 'proxy/mock_proxy_script_fetcher.h',
'proxy/proxy_config_service_common_unittest.cc',
'proxy/proxy_config_service_common_unittest.h',
'socket/socket_test_util.cc',
diff --git a/net/proxy/dhcp_proxy_script_adapter_fetcher_win.cc b/net/proxy/dhcp_proxy_script_adapter_fetcher_win.cc
new file mode 100644
index 0000000..f09537c
--- /dev/null
+++ b/net/proxy/dhcp_proxy_script_adapter_fetcher_win.cc
@@ -0,0 +1,286 @@
+// Copyright (c) 2011 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/dhcp_proxy_script_adapter_fetcher_win.h"
+
+#include "base/message_loop_proxy.h"
+#include "base/sys_string_conversions.h"
+#include "base/task.h"
+#include "base/threading/worker_pool.h"
+#include "base/time.h"
+#include "net/base/net_errors.h"
+#include "net/proxy/dhcpcsvc_init_win.h"
+#include "net/proxy/proxy_script_fetcher_impl.h"
+#include "net/url_request/url_request_context.h"
+
+#include <windows.h>
+#include <winsock2.h>
+#include <dhcpcsdk.h>
+#pragma comment(lib, "dhcpcsvc.lib")
+
+namespace {
+
+// Maximum amount of time to wait for response from the Win32 DHCP API.
+const int kTimeoutMs = 2000;
+
+} // namespace
+
+namespace net {
+
+DhcpProxyScriptAdapterFetcher::DhcpProxyScriptAdapterFetcher(
+ URLRequestContext* url_request_context)
+ : state_(STATE_START),
+ result_(ERR_IO_PENDING),
+ callback_(NULL),
+ ALLOW_THIS_IN_INITIALIZER_LIST(
+ script_fetcher_callback_(
+ this, &DhcpProxyScriptAdapterFetcher::OnFetcherDone)),
+ url_request_context_(url_request_context) {
+}
+
+DhcpProxyScriptAdapterFetcher::~DhcpProxyScriptAdapterFetcher() {
+ Cancel();
+
+ // The WeakPtr we passed to the worker thread may be destroyed on the
+ // worker thread. This detaches any outstanding WeakPtr state from
+ // the current thread.
+ base::SupportsWeakPtr<DhcpProxyScriptAdapterFetcher>::DetachFromThread();
+}
+
+void DhcpProxyScriptAdapterFetcher::Fetch(
+ const std::string& adapter_name, CompletionCallback* callback) {
+ DCHECK(CalledOnValidThread());
+ DCHECK_EQ(state_, STATE_START);
+ result_ = ERR_IO_PENDING;
+ pac_script_ = string16();
+ state_ = STATE_WAIT_DHCP;
+ callback_ = callback;
+
+ wait_timer_.Start(ImplGetTimeout(),
+ this, &DhcpProxyScriptAdapterFetcher::OnTimeout);
+ worker_thread_ = ImplCreateWorkerThread(AsWeakPtr());
+ worker_thread_->Start(adapter_name);
+}
+
+void DhcpProxyScriptAdapterFetcher::Cancel() {
+ DCHECK(CalledOnValidThread());
+ callback_ = NULL;
+ wait_timer_.Stop();
+ script_fetcher_.reset();
+
+ switch (state_) {
+ case STATE_WAIT_DHCP:
+ // Nothing to do here, we let the worker thread run to completion,
+ // the task it posts back when it completes will check the state.
+ break;
+ case STATE_WAIT_URL:
+ break;
+ case STATE_START:
+ case STATE_FINISH:
+ case STATE_CANCEL:
+ break;
+ }
+
+ if (state_ != STATE_FINISH) {
+ result_ = ERR_ABORTED;
+ state_ = STATE_CANCEL;
+ }
+}
+
+bool DhcpProxyScriptAdapterFetcher::DidFinish() const {
+ DCHECK(CalledOnValidThread());
+ return state_ == STATE_FINISH;
+}
+
+int DhcpProxyScriptAdapterFetcher::GetResult() const {
+ DCHECK(CalledOnValidThread());
+ return result_;
+}
+
+string16 DhcpProxyScriptAdapterFetcher::GetPacScript() const {
+ DCHECK(CalledOnValidThread());
+ return pac_script_;
+}
+
+GURL DhcpProxyScriptAdapterFetcher::GetPacURL() const {
+ DCHECK(CalledOnValidThread());
+ return pac_url_;
+}
+
+DhcpProxyScriptAdapterFetcher::WorkerThread::WorkerThread(
+ const base::WeakPtr<DhcpProxyScriptAdapterFetcher>& owner)
+ : owner_(owner),
+ origin_loop_(base::MessageLoopProxy::CreateForCurrentThread()) {
+}
+
+DhcpProxyScriptAdapterFetcher::WorkerThread::~WorkerThread() {
+}
+
+void DhcpProxyScriptAdapterFetcher::WorkerThread::Start(
+ const std::string& adapter_name) {
+ bool succeeded = base::WorkerPool::PostTask(
+ FROM_HERE,
+ NewRunnableMethod(
+ this,
+ &DhcpProxyScriptAdapterFetcher::WorkerThread::ThreadFunc,
+ adapter_name),
+ true);
+ DCHECK(succeeded);
+}
+
+void DhcpProxyScriptAdapterFetcher::WorkerThread::ThreadFunc(
+ const std::string& adapter_name) {
+ std::string url = ImplGetPacURLFromDhcp(adapter_name);
+
+ bool succeeded = origin_loop_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(
+ this,
+ &DhcpProxyScriptAdapterFetcher::WorkerThread::OnThreadDone,
+ url));
+ DCHECK(succeeded);
+}
+
+void DhcpProxyScriptAdapterFetcher::WorkerThread::OnThreadDone(
+ const std::string& url) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (owner_)
+ owner_->OnQueryDhcpDone(url);
+}
+
+std::string
+ DhcpProxyScriptAdapterFetcher::WorkerThread::ImplGetPacURLFromDhcp(
+ const std::string& adapter_name) {
+ return DhcpProxyScriptAdapterFetcher::GetPacURLFromDhcp(adapter_name);
+}
+
+void DhcpProxyScriptAdapterFetcher::OnQueryDhcpDone(
+ const std::string& url) {
+ DCHECK(CalledOnValidThread());
+ // Because we can't cancel the call to the Win32 API, we can expect
+ // it to finish while we are in a few different states. The expected
+ // one is WAIT_DHCP, but it could be in CANCEL if Cancel() was called,
+ // or FINISH if timeout occurred.
+ DCHECK(state_ == STATE_WAIT_DHCP || state_ == STATE_CANCEL ||
+ state_ == STATE_FINISH);
+ if (state_ != STATE_WAIT_DHCP)
+ return;
+
+ wait_timer_.Stop();
+
+ pac_url_ = GURL(url);
+ if (pac_url_.is_empty() || !pac_url_.is_valid()) {
+ result_ = ERR_PAC_NOT_IN_DHCP;
+ TransitionToFinish();
+ } else {
+ state_ = STATE_WAIT_URL;
+ script_fetcher_.reset(ImplCreateScriptFetcher());
+ script_fetcher_->Fetch(pac_url_, &pac_script_, &script_fetcher_callback_);
+ }
+}
+
+void DhcpProxyScriptAdapterFetcher::OnTimeout() {
+ DCHECK_EQ(state_, STATE_WAIT_DHCP);
+ result_ = ERR_TIMED_OUT;
+ TransitionToFinish();
+}
+
+void DhcpProxyScriptAdapterFetcher::OnFetcherDone(int result) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(state_ == STATE_WAIT_URL || state_ == STATE_CANCEL);
+ if (state_ == STATE_CANCEL)
+ return;
+
+ // At this point, pac_script_ has already been written to.
+ script_fetcher_.reset();
+ result_ = result;
+ TransitionToFinish();
+}
+
+void DhcpProxyScriptAdapterFetcher::TransitionToFinish() {
+ DCHECK(state_ == STATE_WAIT_DHCP || state_ == STATE_WAIT_URL);
+ state_ = STATE_FINISH;
+ callback_->Run(result_);
+ callback_ = NULL;
+}
+
+ProxyScriptFetcher* DhcpProxyScriptAdapterFetcher::ImplCreateScriptFetcher() {
+ return new ProxyScriptFetcherImpl(url_request_context_);
+}
+
+DhcpProxyScriptAdapterFetcher::WorkerThread*
+ DhcpProxyScriptAdapterFetcher::ImplCreateWorkerThread(
+ const base::WeakPtr<DhcpProxyScriptAdapterFetcher>& owner) {
+ return new WorkerThread(owner);
+}
+
+base::TimeDelta DhcpProxyScriptAdapterFetcher::ImplGetTimeout() const {
+ return base::TimeDelta::FromMilliseconds(kTimeoutMs);
+}
+
+// static
+std::string DhcpProxyScriptAdapterFetcher::GetPacURLFromDhcp(
+ const std::string& adapter_name) {
+ EnsureDhcpcsvcInit();
+
+ std::wstring adapter_name_wide = base::SysMultiByteToWide(adapter_name,
+ CP_ACP);
+
+ DHCPCAPI_PARAMS_ARRAY send_params = { 0, NULL };
+
+ BYTE option_data[] = { 1, 252 };
+ DHCPCAPI_PARAMS wpad_params = { 0 };
+ wpad_params.OptionId = 252;
+ wpad_params.IsVendor = FALSE; // Surprising, but intentional.
+
+ DHCPCAPI_PARAMS_ARRAY request_params = { 0 };
+ request_params.nParams = 1;
+ request_params.Params = &wpad_params;
+
+ // The maximum message size is typically 4096 bytes on Windows per
+ // http://support.microsoft.com/kb/321592
+ DWORD result_buffer_size = 4096;
+ scoped_ptr_malloc<BYTE> result_buffer;
+ int retry_count = 0;
+ DWORD res = NO_ERROR;
+ do {
+ result_buffer.reset(reinterpret_cast<BYTE*>(malloc(result_buffer_size)));
+
+ // Note that while the DHCPCAPI_REQUEST_SYNCHRONOUS flag seems to indicate
+ // there might be an asynchronous mode, there seems to be (at least in
+ // terms of well-documented use of this API) only a synchronous mode, with
+ // an optional "async notifications later if the option changes" mode.
+ // Even IE9, which we hope to emulate as IE is the most widely deployed
+ // previous implementation of the DHCP aspect of WPAD and the only one
+ // on Windows (Konqueror is the other, on Linux), uses this API with the
+ // synchronous flag. There seem to be several Microsoft Knowledge Base
+ // articles about calls to this function failing when other flags are used
+ // (e.g. http://support.microsoft.com/kb/885270) so we won't take any
+ // chances on non-standard, poorly documented usage.
+ res = ::DhcpRequestParams(DHCPCAPI_REQUEST_SYNCHRONOUS,
+ NULL,
+ const_cast<LPWSTR>(adapter_name_wide.c_str()),
+ NULL,
+ send_params, request_params,
+ result_buffer.get(), &result_buffer_size,
+ NULL);
+ ++retry_count;
+ } while (res == ERROR_MORE_DATA && retry_count <= 3);
+
+ if (res != NO_ERROR) {
+ NOTREACHED();
+ } else if (wpad_params.nBytesData) {
+ // The result should be ASCII, not wide character.
+ DCHECK_EQ(strlen(reinterpret_cast<const char*>(wpad_params.Data)) + 1,
+ wpad_params.nBytesData);
+ // Return only up to the first null in case of embedded NULLs; if the
+ // server is giving us back a buffer with embedded NULLs, something is
+ // broken anyway.
+ return std::string(reinterpret_cast<const char *>(wpad_params.Data));
+ }
+
+ return "";
+}
+
+} // namespace net
diff --git a/net/proxy/dhcp_proxy_script_adapter_fetcher_win.h b/net/proxy/dhcp_proxy_script_adapter_fetcher_win.h
new file mode 100644
index 0000000..bab7468
--- /dev/null
+++ b/net/proxy/dhcp_proxy_script_adapter_fetcher_win.h
@@ -0,0 +1,198 @@
+// Copyright (c) 2011 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_DHCP_SCRIPT_ADAPTER_FETCHER_WIN_H_
+#define NET_PROXY_DHCP_SCRIPT_ADAPTER_FETCHER_WIN_H_
+#pragma once
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/string16.h"
+#include "base/threading/non_thread_safe.h"
+#include "base/timer.h"
+#include "net/base/completion_callback.h"
+#include "googleurl/src/gurl.h"
+
+namespace base {
+class MessageLoopProxy;
+}
+
+namespace net {
+
+class ProxyScriptFetcher;
+class URLRequestContext;
+
+// For a given adapter, this class takes care of first doing a DHCP lookup
+// to get the PAC URL, then if there is one, trying to fetch it.
+class DhcpProxyScriptAdapterFetcher
+ : public base::SupportsWeakPtr<DhcpProxyScriptAdapterFetcher>,
+ public base::NonThreadSafe {
+ public:
+ // |url_request_context| must outlive DhcpProxyScriptAdapterFetcher.
+ explicit DhcpProxyScriptAdapterFetcher(
+ URLRequestContext* url_request_context);
+ virtual ~DhcpProxyScriptAdapterFetcher();
+
+ // Starts a fetch. On completion (but not cancellation), |callback|
+ // will be invoked with the network error indicating success or failure
+ // of fetching a DHCP-configured PAC file on this adapter.
+ //
+ // On completion, results can be obtained via |GetPacScript()|, |GetPacURL()|.
+ //
+ // You may only call Fetch() once on a given instance of
+ // DhcpProxyScriptAdapterFetcher.
+ virtual void Fetch(const std::string& adapter_name,
+ CompletionCallback* callback);
+
+ // Cancels the fetch on this adapter.
+ virtual void Cancel();
+
+ // Returns true if in the FINISH state (not CANCEL).
+ virtual bool DidFinish() const;
+
+ // Returns the network error indicating the result of the fetch. Will
+ // return IO_PENDING until the fetch is complete or cancelled. This is
+ // the same network error passed to the |callback| provided to |Fetch()|.
+ virtual int GetResult() const;
+
+ // Returns the contents of the PAC file retrieved. Only valid if
+ // |IsComplete()| is true. Returns the empty string if |GetResult()|
+ // returns anything other than OK.
+ virtual string16 GetPacScript() const;
+
+ // Returns the PAC URL retrieved from DHCP. Only guaranteed to be
+ // valid if |IsComplete()| is true. Returns an empty URL if no URL was
+ // configured in DHCP. May return a valid URL even if |result()| does
+ // not return OK (this would indicate that we found a URL configured in
+ // DHCP but failed to download it).
+ virtual GURL GetPacURL() const;
+
+ // Returns the PAC URL configured in DHCP for the given |adapter_name|, or
+ // the empty string if none is configured.
+ //
+ // This function executes synchronously due to limitations of the Windows
+ // DHCP client API.
+ static std::string GetPacURLFromDhcp(const std::string& adapter_name);
+
+ protected:
+ // This inner class is used to encapsulate the worker thread, which has
+ // only a weak reference back to the main object, so that the main object
+ // can be destroyed before the thread ends. This also keeps the main
+ // object completely thread safe and allows it to be non-refcounted.
+ class WorkerThread : public base::RefCountedThreadSafe<WorkerThread> {
+ public:
+ // Creates and initializes (but does not start) the worker thread.
+ explicit WorkerThread(
+ const base::WeakPtr<DhcpProxyScriptAdapterFetcher>& owner);
+ virtual ~WorkerThread();
+
+ // Starts the worker thread, fetching information for |adapter_name| using
+ // |get_pac_from_url_func|.
+ void Start(const std::string& adapter_name);
+
+ protected:
+ // Virtual method introduced to allow unit testing.
+ virtual std::string ImplGetPacURLFromDhcp(const std::string& adapter_name);
+
+ private:
+ // This is the method that runs on the worker thread.
+ void ThreadFunc(const std::string& adapter_name);
+
+ // Callback for the above; this executes back on the main thread,
+ // not the worker thread.
+ void OnThreadDone(const std::string& url);
+
+ // All work except ThreadFunc and (sometimes) destruction should occur
+ // on the thread that constructs the object.
+ base::ThreadChecker thread_checker_;
+
+ // May only be accessed on the thread that constructs the object.
+ base::WeakPtr<DhcpProxyScriptAdapterFetcher> owner_;
+
+ // Used by worker thread to post a message back to the original
+ // thread. Fine to use a proxy since in the case where the original
+ // thread has gone away, that would mean the |owner_| object is gone
+ // anyway, so there is nobody to receive the result.
+ scoped_refptr<base::MessageLoopProxy> origin_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(WorkerThread);
+ };
+
+ // Event/state transition handlers
+ void OnQueryDhcpDone(const std::string& url);
+ void OnTimeout();
+ void OnFetcherDone(int result);
+ void TransitionToFinish();
+
+ // Virtual methods introduced to allow unit testing.
+ virtual ProxyScriptFetcher* ImplCreateScriptFetcher();
+ virtual WorkerThread* ImplCreateWorkerThread(
+ const base::WeakPtr<DhcpProxyScriptAdapterFetcher>& owner);
+ virtual base::TimeDelta ImplGetTimeout() const;
+
+ // This is the state machine for fetching from a given adapter.
+ //
+ // The state machine goes from START->WAIT_DHCP when it starts
+ // a worker thread to fetch the PAC URL from DHCP.
+ //
+ // In state WAIT_DHCP, if the DHCP query finishes and has no URL, it
+ // moves to state FINISH. If there is a URL, it starts a
+ // ProxyScriptFetcher to fetch it and moves to state WAIT_URL.
+ //
+ // It goes from WAIT_URL->FINISH when the ProxyScriptFetcher completes.
+ //
+ // In state FINISH, completion is indicated to the outer class, with
+ // the results of the fetch if a PAC script was successfully fetched.
+ //
+ // In state WAIT_DHCP, our timeout occurring can push us to FINISH.
+ //
+ // In any state except FINISH, a call to Cancel() will move to state
+ // CANCEL and cause all outstanding work to be cancelled or its
+ // results ignored when available.
+ enum State {
+ STATE_START,
+ STATE_WAIT_DHCP,
+ STATE_WAIT_URL,
+ STATE_FINISH,
+ STATE_CANCEL,
+ };
+
+ // Current state of this state machine.
+ State state_;
+
+ // A network error indicating result of operation.
+ int result_;
+
+ // Empty string or the PAC script downloaded.
+ string16 pac_script_;
+
+ // Empty URL or the PAC URL configured in DHCP.
+ GURL pac_url_;
+
+ // Callback to let our client know we're done. Invalid in states
+ // START, FINISH and CANCEL.
+ CompletionCallback* callback_;
+
+ // Container for our worker thread. NULL if not currently running.
+ scoped_refptr<WorkerThread> worker_thread_;
+
+ // Fetcher to retrieve PAC files once URL is known.
+ scoped_ptr<ProxyScriptFetcher> script_fetcher_;
+
+ // Callback from the script fetcher.
+ CompletionCallbackImpl<DhcpProxyScriptAdapterFetcher>
+ script_fetcher_callback_;
+
+ // Implements a timeout on the call to the Win32 DHCP API.
+ base::OneShotTimer<DhcpProxyScriptAdapterFetcher> wait_timer_;
+
+ scoped_refptr<URLRequestContext> url_request_context_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(DhcpProxyScriptAdapterFetcher);
+};
+
+} // namespace net
+
+#endif // NET_PROXY_DHCP_SCRIPT_ADAPTER_FETCHER_WIN_H_
diff --git a/net/proxy/dhcp_proxy_script_adapter_fetcher_win_unittest.cc b/net/proxy/dhcp_proxy_script_adapter_fetcher_win_unittest.cc
new file mode 100644
index 0000000..b4b05f9
--- /dev/null
+++ b/net/proxy/dhcp_proxy_script_adapter_fetcher_win_unittest.cc
@@ -0,0 +1,300 @@
+// Copyright (c) 2011 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/dhcp_proxy_script_adapter_fetcher_win.h"
+
+#include "base/perftimer.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/timer.h"
+#include "net/base/net_errors.h"
+#include "net/base/test_completion_callback.h"
+#include "net/proxy/mock_proxy_script_fetcher.h"
+#include "net/proxy/proxy_script_fetcher_impl.h"
+#include "net/test/test_server.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+const char* const kPacUrl = "http://pacserver/script.pac";
+
+// In net/proxy/dhcp_proxy_script_fetcher_win_unittest.cc there are a few
+// tests that exercise DhcpProxyScriptAdapterFetcher end-to-end along with
+// DhcpProxyScriptFetcherWin, i.e. it tests the end-to-end usage of Win32
+// APIs and the network. In this file we test only by stubbing out
+// functionality.
+
+// Version of DhcpProxyScriptAdapterFetcher that mocks out dependencies
+// to allow unit testing.
+class MockDhcpProxyScriptAdapterFetcher
+ : public DhcpProxyScriptAdapterFetcher {
+ public:
+ explicit MockDhcpProxyScriptAdapterFetcher(URLRequestContext* context)
+ : DhcpProxyScriptAdapterFetcher(context),
+ dhcp_delay_ms_(1),
+ timeout_ms_(100),
+ configured_url_(kPacUrl),
+ fetcher_delay_ms_(1),
+ fetcher_result_(OK),
+ pac_script_("bingo") {
+ }
+
+ void Cancel() {
+ DhcpProxyScriptAdapterFetcher::Cancel();
+ fetcher_ = NULL;
+ }
+
+ virtual ProxyScriptFetcher* ImplCreateScriptFetcher() OVERRIDE {
+ // We don't maintain ownership of the fetcher, it is transferred to
+ // the caller.
+ fetcher_ = new MockProxyScriptFetcher();
+ if (fetcher_delay_ms_ != -1) {
+ fetcher_timer_.Start(
+ base::TimeDelta::FromMilliseconds(fetcher_delay_ms_),
+ this, &MockDhcpProxyScriptAdapterFetcher::OnFetcherTimer);
+ }
+ return fetcher_;
+ }
+
+ class DelayingWorkerThread : public WorkerThread {
+ public:
+ explicit DelayingWorkerThread(
+ const base::WeakPtr<DhcpProxyScriptAdapterFetcher>& owner)
+ : WorkerThread(owner),
+ test_finished_event_(true, false) {
+ }
+
+ std::string ImplGetPacURLFromDhcp(
+ const std::string& adapter_name) OVERRIDE {
+ PerfTimer timer;
+ test_finished_event_.TimedWait(dhcp_delay_);
+ return configured_url_;
+ }
+
+ base::WaitableEvent test_finished_event_;
+ TimeDelta dhcp_delay_;
+ std::string configured_url_;
+ };
+
+ virtual WorkerThread* ImplCreateWorkerThread(
+ const base::WeakPtr<DhcpProxyScriptAdapterFetcher>& owner) OVERRIDE {
+ worker_thread_ = new DelayingWorkerThread(owner);
+ worker_thread_->dhcp_delay_ = TimeDelta::FromMilliseconds(dhcp_delay_ms_);
+ worker_thread_->configured_url_ = configured_url_;
+ return worker_thread_;
+ }
+
+ // Use a shorter timeout so tests can finish more quickly.
+ virtual base::TimeDelta ImplGetTimeout() const OVERRIDE {
+ return base::TimeDelta::FromMilliseconds(timeout_ms_);
+ }
+
+ void OnFetcherTimer() {
+ // Note that there is an assumption by this mock implementation that
+ // DhcpProxyScriptAdapterFetcher::Fetch will call ImplCreateScriptFetcher
+ // and call Fetch on the fetcher before the message loop is re-entered.
+ // This holds true today, but if you hit this DCHECK the problem can
+ // possibly be resolved by having a separate subclass of
+ // MockProxyScriptFetcher that adds the delay internally (instead of
+ // the simple approach currently used in ImplCreateScriptFetcher above).
+ DCHECK(fetcher_ && fetcher_->has_pending_request());
+ fetcher_->NotifyFetchCompletion(fetcher_result_, pac_script_);
+ fetcher_ = NULL;
+ }
+
+ bool IsWaitingForFetcher() const {
+ return state_ == STATE_WAIT_URL;
+ }
+
+ bool WasCancelled() const {
+ return state_ == STATE_CANCEL;
+ }
+
+ void FinishTest() {
+ DCHECK(worker_thread_);
+ worker_thread_->test_finished_event_.Signal();
+ }
+
+ int dhcp_delay_ms_;
+ int timeout_ms_;
+ std::string configured_url_;
+ int fetcher_delay_ms_;
+ int fetcher_result_;
+ std::string pac_script_;
+ MockProxyScriptFetcher* fetcher_;
+ base::OneShotTimer<MockDhcpProxyScriptAdapterFetcher> fetcher_timer_;
+ scoped_refptr<DelayingWorkerThread> worker_thread_;
+};
+
+class FetcherClient {
+ public:
+ FetcherClient()
+ : url_request_context_(new TestURLRequestContext()),
+ fetcher_(
+ new MockDhcpProxyScriptAdapterFetcher(url_request_context_.get())) {
+ }
+
+ void WaitForResult(int expected_error) {
+ EXPECT_EQ(expected_error, callback_.WaitForResult());
+ }
+
+ void RunTest() {
+ fetcher_->Fetch("adapter name", &callback_);
+ }
+
+ void FinishTestAllowCleanup() {
+ fetcher_->FinishTest();
+ MessageLoop::current()->RunAllPending();
+ }
+
+ TestCompletionCallback callback_;
+ scoped_refptr<URLRequestContext> url_request_context_;
+ scoped_ptr<MockDhcpProxyScriptAdapterFetcher> fetcher_;
+ string16 pac_text_;
+};
+
+TEST(DhcpProxyScriptAdapterFetcher, NormalCaseURLNotInDhcp) {
+ FetcherClient client;
+ client.fetcher_->configured_url_ = "";
+ client.RunTest();
+ client.WaitForResult(ERR_PAC_NOT_IN_DHCP);
+ ASSERT_TRUE(client.fetcher_->DidFinish());
+ EXPECT_EQ(ERR_PAC_NOT_IN_DHCP, client.fetcher_->GetResult());
+ EXPECT_EQ(string16(L""), client.fetcher_->GetPacScript());
+}
+
+TEST(DhcpProxyScriptAdapterFetcher, NormalCaseURLInDhcp) {
+ FetcherClient client;
+ client.RunTest();
+ client.WaitForResult(OK);
+ ASSERT_TRUE(client.fetcher_->DidFinish());
+ EXPECT_EQ(OK, client.fetcher_->GetResult());
+ EXPECT_EQ(string16(L"bingo"), client.fetcher_->GetPacScript());
+ EXPECT_EQ(GURL(kPacUrl), client.fetcher_->GetPacURL());
+}
+
+TEST(DhcpProxyScriptAdapterFetcher, TimeoutDuringDhcp) {
+ // Does a Fetch() with a long enough delay on accessing DHCP that the
+ // fetcher should time out. This is to test a case manual testing found,
+ // where under certain circumstances (e.g. adapter enabled for DHCP and
+ // needs to retrieve its configuration from DHCP, but no DHCP server
+ // present on the network) accessing DHCP can take on the order of tens
+ // of seconds.
+ FetcherClient client;
+ client.fetcher_->dhcp_delay_ms_ = 20 * 1000;
+ client.fetcher_->timeout_ms_ = 25;
+
+ PerfTimer timer;
+ client.RunTest();
+ client.WaitForResult(ERR_TIMED_OUT);
+
+ // The timeout should occur within about 25 ms, way before the 20s set as
+ // the API delay above.
+ ASSERT_GT(base::TimeDelta::FromMilliseconds(35), timer.Elapsed());
+ ASSERT_TRUE(client.fetcher_->DidFinish());
+ EXPECT_EQ(ERR_TIMED_OUT, client.fetcher_->GetResult());
+ EXPECT_EQ(string16(L""), client.fetcher_->GetPacScript());
+ EXPECT_EQ(GURL(), client.fetcher_->GetPacURL());
+ client.FinishTestAllowCleanup();
+}
+
+TEST(DhcpProxyScriptAdapterFetcher, CancelWhileDhcp) {
+ FetcherClient client;
+ client.fetcher_->dhcp_delay_ms_ = 10;
+ client.RunTest();
+ client.fetcher_->Cancel();
+ MessageLoop::current()->RunAllPending();
+ ASSERT_FALSE(client.fetcher_->DidFinish());
+ ASSERT_TRUE(client.fetcher_->WasCancelled());
+ EXPECT_EQ(ERR_ABORTED, client.fetcher_->GetResult());
+ EXPECT_EQ(string16(L""), client.fetcher_->GetPacScript());
+ EXPECT_EQ(GURL(), client.fetcher_->GetPacURL());
+ client.FinishTestAllowCleanup();
+}
+
+TEST(DhcpProxyScriptAdapterFetcher, CancelWhileFetcher) {
+ FetcherClient client;
+ // This causes the mock fetcher not to pretend the
+ // fetcher finishes after a timeout.
+ client.fetcher_->fetcher_delay_ms_ = -1;
+ client.RunTest();
+ int max_loops = 4;
+ while (!client.fetcher_->IsWaitingForFetcher() && max_loops--) {
+ base::PlatformThread::Sleep(10);
+ MessageLoop::current()->RunAllPending();
+ }
+ client.fetcher_->Cancel();
+ MessageLoop::current()->RunAllPending();
+ ASSERT_FALSE(client.fetcher_->DidFinish());
+ ASSERT_TRUE(client.fetcher_->WasCancelled());
+ EXPECT_EQ(ERR_ABORTED, client.fetcher_->GetResult());
+ EXPECT_EQ(string16(L""), client.fetcher_->GetPacScript());
+ // GetPacURL() still returns the URL fetched in this case.
+ EXPECT_EQ(GURL(kPacUrl), client.fetcher_->GetPacURL());
+ client.FinishTestAllowCleanup();
+}
+
+TEST(DhcpProxyScriptAdapterFetcher, CancelAtCompletion) {
+ FetcherClient client;
+ client.RunTest();
+ client.WaitForResult(OK);
+ client.fetcher_->Cancel();
+ // Canceling after you're done should have no effect, so these
+ // are identical expectations to the NormalCaseURLInDhcp test.
+ ASSERT_TRUE(client.fetcher_->DidFinish());
+ EXPECT_EQ(OK, client.fetcher_->GetResult());
+ EXPECT_EQ(string16(L"bingo"), client.fetcher_->GetPacScript());
+ EXPECT_EQ(GURL(kPacUrl), client.fetcher_->GetPacURL());
+ client.FinishTestAllowCleanup();
+}
+
+// Does a real fetch on a mock DHCP configuration.
+class MockDhcpRealFetchProxyScriptAdapterFetcher
+ : public MockDhcpProxyScriptAdapterFetcher {
+ public:
+ explicit MockDhcpRealFetchProxyScriptAdapterFetcher(
+ URLRequestContext* context)
+ : MockDhcpProxyScriptAdapterFetcher(context),
+ url_request_context_(context) {
+ }
+
+ // Returns a real proxy script fetcher.
+ ProxyScriptFetcher* ImplCreateScriptFetcher() OVERRIDE {
+ ProxyScriptFetcher* fetcher =
+ new ProxyScriptFetcherImpl(url_request_context_);
+ return fetcher;
+ }
+
+ URLRequestContext* url_request_context_;
+};
+
+TEST(DhcpProxyScriptAdapterFetcher, MockDhcpRealFetch) {
+ TestServer test_server(
+ TestServer::TYPE_HTTP,
+ FilePath(FILE_PATH_LITERAL("net/data/proxy_script_fetcher_unittest")));
+ ASSERT_TRUE(test_server.Start());
+
+ GURL configured_url = test_server.GetURL("files/downloadable.pac");
+
+ FetcherClient client;
+ scoped_refptr<URLRequestContext> url_request_context(
+ new TestURLRequestContext());
+ client.fetcher_.reset(
+ new MockDhcpRealFetchProxyScriptAdapterFetcher(
+ url_request_context.get()));
+ client.fetcher_->configured_url_ = configured_url.spec();
+ client.RunTest();
+ client.WaitForResult(OK);
+ ASSERT_TRUE(client.fetcher_->DidFinish());
+ EXPECT_EQ(OK, client.fetcher_->GetResult());
+ EXPECT_EQ(string16(L"-downloadable.pac-\n"), client.fetcher_->GetPacScript());
+ EXPECT_EQ(configured_url,
+ client.fetcher_->GetPacURL());
+}
+
+} // namespace
+
+} // namespace net
diff --git a/net/proxy/dhcp_proxy_script_fetcher.cc b/net/proxy/dhcp_proxy_script_fetcher.cc
new file mode 100644
index 0000000..3797ecd
--- /dev/null
+++ b/net/proxy/dhcp_proxy_script_fetcher.cc
@@ -0,0 +1,39 @@
+// Copyright (c) 2011 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/dhcp_proxy_script_fetcher.h"
+
+#include "net/base/net_errors.h"
+
+namespace net {
+
+DhcpProxyScriptFetcher::DhcpProxyScriptFetcher() {
+}
+
+DhcpProxyScriptFetcher::~DhcpProxyScriptFetcher() {
+}
+
+std::string DhcpProxyScriptFetcher::GetFetcherName() const {
+ return "";
+}
+
+DoNothingDhcpProxyScriptFetcher::DoNothingDhcpProxyScriptFetcher() {
+}
+
+DoNothingDhcpProxyScriptFetcher::~DoNothingDhcpProxyScriptFetcher() {
+}
+
+int DoNothingDhcpProxyScriptFetcher::Fetch(string16* utf16_text,
+ CompletionCallback* callback) {
+ return ERR_NOT_IMPLEMENTED;
+}
+
+void DoNothingDhcpProxyScriptFetcher::Cancel() {
+}
+
+const GURL& DoNothingDhcpProxyScriptFetcher::GetPacURL() const {
+ return gurl_;
+}
+
+} // namespace net
diff --git a/net/proxy/dhcp_proxy_script_fetcher.h b/net/proxy/dhcp_proxy_script_fetcher.h
new file mode 100644
index 0000000..adb011d
--- /dev/null
+++ b/net/proxy/dhcp_proxy_script_fetcher.h
@@ -0,0 +1,98 @@
+// Copyright (c) 2011 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_DHCP_SCRIPT_FETCHER_H_
+#define NET_PROXY_DHCP_SCRIPT_FETCHER_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/string16.h"
+#include "net/base/completion_callback.h"
+#include "net/proxy/proxy_script_fetcher.h"
+#include "net/url_request/url_request_context.h"
+
+namespace net {
+
+// Interface for classes that can fetch a proxy script as configured via DHCP.
+//
+// The Fetch method on this interface tries to retrieve the most appropriate
+// PAC script configured via DHCP.
+//
+// Normally there are zero or one DHCP scripts configured, but in the
+// presence of multiple adapters with DHCP enabled, the fetcher resolves
+// which PAC script to use if one or more are available.
+class DhcpProxyScriptFetcher {
+ public:
+ // Destruction should cancel any outstanding requests.
+ virtual ~DhcpProxyScriptFetcher();
+
+ // Attempts to retrieve the most appropriate PAC script configured via DHCP,
+ // and invokes |callback| on completion.
+ //
+ // Returns OK on success, otherwise the error code. If the return code is
+ // ERR_IO_PENDING, then the request completes asynchronously, and |callback|
+ // will be invoked later with the final error code.
+ //
+ // After synchronous or asynchronous completion with a result code of OK,
+ // |*utf16_text| is filled with the response. On failure, the result text is
+ // an empty string, and the result code is a network error. Some special
+ // network errors that may occur are:
+ //
+ // ERR_PAC_NOT_IN_DHCP -- no script configured in DHCP.
+ //
+ // The following all indicate there was one or more script configured
+ // in DHCP but all failed to download, and the error for the most
+ // preferred adapter that had a script configured was what the error
+ // code says:
+ //
+ // ERR_TIMED_OUT -- fetch took too long to complete.
+ // ERR_FILE_TOO_BIG -- response body was too large.
+ // ERR_PAC_STATUS_NOT_OK -- script failed to download.
+ // ERR_NOT_IMPLEMENTED -- script required authentication.
+ //
+ // If the request is cancelled (either using the "Cancel()" method or by
+ // deleting |this|), then no callback is invoked.
+ //
+ // Only one fetch is allowed to be outstanding at a time.
+ virtual int Fetch(string16* utf16_text,
+ CompletionCallback* callback) = 0;
+
+ // Aborts the in-progress fetch (if any).
+ virtual void Cancel() = 0;
+
+ // After successful completion of |Fetch()|, this will return the URL
+ // retrieved from DHCP. It is reset if/when |Fetch()| is called again.
+ virtual const GURL& GetPacURL() const = 0;
+
+ // Intended for unit tests only, so they can test that factories return
+ // the right types under given circumstances.
+ virtual std::string GetFetcherName() const;
+
+ protected:
+ DhcpProxyScriptFetcher();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DhcpProxyScriptFetcher);
+};
+
+// A do-nothing retriever, always returns synchronously with
+// ERR_NOT_IMPLEMENTED result and empty text.
+class DoNothingDhcpProxyScriptFetcher : public DhcpProxyScriptFetcher {
+ public:
+ DoNothingDhcpProxyScriptFetcher();
+ virtual ~DoNothingDhcpProxyScriptFetcher();
+
+ virtual int Fetch(string16* utf16_text,
+ CompletionCallback* callback) OVERRIDE;
+ virtual void Cancel() OVERRIDE;
+ virtual const GURL& GetPacURL() const OVERRIDE;
+ private:
+ GURL gurl_;
+ DISALLOW_COPY_AND_ASSIGN(DoNothingDhcpProxyScriptFetcher);
+};
+
+} // namespace net
+
+#endif // NET_PROXY_DHCP_SCRIPT_FETCHER_H_
diff --git a/net/proxy/dhcp_proxy_script_fetcher_factory.cc b/net/proxy/dhcp_proxy_script_fetcher_factory.cc
new file mode 100644
index 0000000..dfdf58e3
--- /dev/null
+++ b/net/proxy/dhcp_proxy_script_fetcher_factory.cc
@@ -0,0 +1,56 @@
+// Copyright (c) 2011 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/dhcp_proxy_script_fetcher_factory.h"
+
+#include "net/base/net_errors.h"
+#include "net/proxy/dhcp_proxy_script_fetcher.h"
+
+#if defined(OS_WIN)
+#include "net/proxy/dhcp_proxy_script_fetcher_win.h"
+#endif
+
+namespace net {
+
+DhcpProxyScriptFetcherFactory::DhcpProxyScriptFetcherFactory() {
+ // TODO(joi): Change this default, and the comment on |set_enabled()|,
+ // when the time is right.
+ set_enabled(false);
+}
+
+DhcpProxyScriptFetcher* DhcpProxyScriptFetcherFactory::Create(
+ URLRequestContext* context) {
+ if (!feature_enabled_) {
+ return new DoNothingDhcpProxyScriptFetcher();
+ } else {
+ DCHECK(IsSupported());
+ DhcpProxyScriptFetcher* ret = NULL;
+#if defined(OS_WIN)
+ ret = new DhcpProxyScriptFetcherWin(context);
+#endif
+ DCHECK(ret);
+ return ret;
+ }
+}
+
+void DhcpProxyScriptFetcherFactory::set_enabled(bool enabled) {
+ if (IsSupported()) {
+ feature_enabled_ = enabled;
+ }
+}
+
+bool DhcpProxyScriptFetcherFactory::enabled() const {
+ return feature_enabled_;
+}
+
+// static
+bool DhcpProxyScriptFetcherFactory::IsSupported() {
+#if defined(OS_WIN)
+ return true;
+#else
+ return false;
+#endif
+}
+
+} // namespace net
diff --git a/net/proxy/dhcp_proxy_script_fetcher_factory.h b/net/proxy/dhcp_proxy_script_fetcher_factory.h
new file mode 100644
index 0000000..20e4104
--- /dev/null
+++ b/net/proxy/dhcp_proxy_script_fetcher_factory.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2011 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_DHCP_SCRIPT_FETCHER_FACTORY_H_
+#define NET_PROXY_DHCP_SCRIPT_FETCHER_FACTORY_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/memory/singleton.h"
+#include "net/base/completion_callback.h"
+#include "net/url_request/url_request_context.h"
+
+namespace net {
+
+class DhcpProxyScriptFetcher;
+
+// Factory object for creating the appropriate concrete base class of
+// DhcpProxyScriptFetcher for your operating system and settings.
+//
+// You might think we could just implement a DHCP client at the protocol
+// level and have cross-platform support for retrieving PAC configuration
+// from DHCP, but unfortunately the DHCP protocol assumes there is a single
+// client per machine (specifically per network interface card), and there
+// is an implicit state machine between the client and server, so adding a
+// second client to the machine would not be advisable (see e.g. some
+// discussion of what can happen at this URL:
+// http://www.net.princeton.edu/multi-dhcp-one-interface-handling.html).
+//
+// Therefore, we have platform-specific implementations, and so we use
+// this factory to select the right one.
+class DhcpProxyScriptFetcherFactory {
+ public:
+ // Creates a new factory object with default settings.
+ DhcpProxyScriptFetcherFactory();
+
+ // Ownership is transferred to the caller. url_request_context must be valid
+ // and its lifetime must exceed that of the returned DhcpProxyScriptFetcher.
+ //
+ // Note that while a request is in progress, the fetcher may be holding a
+ // reference to |url_request_context|. Be careful not to create cycles
+ // between the fetcher and the context; you can break such cycles by calling
+ // Cancel().
+ DhcpProxyScriptFetcher* Create(URLRequestContext* url_request_context);
+
+ // Attempts to enable/disable the DHCP WPAD feature. Does nothing
+ // if |IsSupported()| returns false.
+ //
+ // The current default is |enabled() == false|.
+ void set_enabled(bool enabled);
+
+ // Returns true if the DHCP WPAD feature is enabled. Always returns
+ // false if |IsSupported()| is false.
+ bool enabled() const;
+
+ // Returns true if the DHCP WPAD feature is supported on the current
+ // operating system.
+ static bool IsSupported();
+
+ protected:
+ bool feature_enabled_;
+
+ DISALLOW_COPY_AND_ASSIGN(DhcpProxyScriptFetcherFactory);
+};
+
+} // namespace net
+
+#endif // NET_PROXY_DHCP_SCRIPT_FETCHER_FACTORY_H_
diff --git a/net/proxy/dhcp_proxy_script_fetcher_factory_unittest.cc b/net/proxy/dhcp_proxy_script_fetcher_factory_unittest.cc
new file mode 100644
index 0000000..2c29120
--- /dev/null
+++ b/net/proxy/dhcp_proxy_script_fetcher_factory_unittest.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 2011 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/dhcp_proxy_script_fetcher.h"
+#include "net/proxy/dhcp_proxy_script_fetcher_factory.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace {
+
+TEST(DhcpProxyScriptFetcherFactoryTest, DoNothingWhenDisabled) {
+ DhcpProxyScriptFetcherFactory factory;
+ // Non-do-nothing fetchers would DCHECK on the NULL pointer.
+ DhcpProxyScriptFetcher* fetcher = factory.Create(NULL);
+ ASSERT_TRUE(fetcher);
+ EXPECT_EQ("", fetcher->GetFetcherName());
+}
+
+#if defined(OS_WIN)
+TEST(DhcpProxyScriptFetcherFactoryTest, WindowsFetcherOnWindows) {
+ DhcpProxyScriptFetcherFactory factory;
+ factory.set_enabled(true);
+
+ scoped_refptr<TestURLRequestContext> context(new TestURLRequestContext());
+ DhcpProxyScriptFetcher* fetcher = factory.Create(context);
+ ASSERT_TRUE(fetcher);
+ EXPECT_EQ("win", fetcher->GetFetcherName());
+}
+#endif // defined(OS_WIN)
+
+TEST(DhcpProxyScriptFetcherFactoryTest, IsSupported) {
+#if defined(OS_WIN)
+ ASSERT_TRUE(DhcpProxyScriptFetcherFactory::IsSupported());
+#else
+ ASSERT_FALSE(DhcpProxyScriptFetcherFactory::IsSupported());
+#endif // defined(OS_WIN)
+}
+
+TEST(DhcpProxyScriptFetcherFactoryTest, SetEnabled) {
+ DhcpProxyScriptFetcherFactory factory;
+ EXPECT_FALSE(factory.enabled());
+
+ factory.set_enabled(false);
+ EXPECT_FALSE(factory.enabled());
+
+ factory.set_enabled(true);
+#if defined(OS_WIN)
+ EXPECT_TRUE(factory.enabled());
+#else
+ EXPECT_FALSE(factory.enabled());
+#endif // defined(OS_WIN)
+}
+
+} // namespace
+} // namespace net
diff --git a/net/proxy/dhcp_proxy_script_fetcher_win.cc b/net/proxy/dhcp_proxy_script_fetcher_win.cc
new file mode 100644
index 0000000..35109f6
--- /dev/null
+++ b/net/proxy/dhcp_proxy_script_fetcher_win.cc
@@ -0,0 +1,246 @@
+// Copyright (c) 2011 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/dhcp_proxy_script_fetcher_win.h"
+
+#include "net/base/net_errors.h"
+#include "net/proxy/dhcp_proxy_script_adapter_fetcher_win.h"
+
+#include <winsock2.h>
+#include <iphlpapi.h>
+#pragma comment(lib, "iphlpapi.lib")
+
+namespace {
+
+// How long to wait at maximum after we get results (a PAC file or
+// knowledge that no PAC file is configured) from whichever network
+// adapter finishes first.
+const int kMaxWaitAfterFirstResultMs = 400;
+
+} // namespace
+
+namespace net {
+
+DhcpProxyScriptFetcherWin::DhcpProxyScriptFetcherWin(
+ URLRequestContext* url_request_context)
+ : state_(STATE_START),
+ ALLOW_THIS_IN_INITIALIZER_LIST(fetcher_callback_(
+ this, &DhcpProxyScriptFetcherWin::OnFetcherDone)),
+ num_pending_fetchers_(0),
+ url_request_context_(url_request_context) {
+ DCHECK(url_request_context_);
+}
+
+DhcpProxyScriptFetcherWin::~DhcpProxyScriptFetcherWin() {
+ Cancel();
+}
+
+int DhcpProxyScriptFetcherWin::Fetch(string16* utf16_text,
+ CompletionCallback* callback) {
+ DCHECK(CalledOnValidThread());
+ if (state_ != STATE_START && state_ != STATE_DONE) {
+ NOTREACHED();
+ return ERR_UNEXPECTED;
+ }
+
+ std::set<std::string> adapter_names;
+ if (!ImplGetCandidateAdapterNames(&adapter_names)) {
+ return ERR_UNEXPECTED;
+ }
+ if (adapter_names.empty()) {
+ return ERR_PAC_NOT_IN_DHCP;
+ }
+
+ state_ = STATE_NO_RESULTS;
+
+ client_callback_ = callback;
+ destination_string_ = utf16_text;
+
+ for (std::set<std::string>::iterator it = adapter_names.begin();
+ it != adapter_names.end();
+ ++it) {
+ DhcpProxyScriptAdapterFetcher* fetcher(ImplCreateAdapterFetcher());
+ fetcher->Fetch(*it, &fetcher_callback_);
+ fetchers_.push_back(fetcher);
+ }
+ num_pending_fetchers_ = fetchers_.size();
+
+ return ERR_IO_PENDING;
+}
+
+void DhcpProxyScriptFetcherWin::Cancel() {
+ DCHECK(CalledOnValidThread());
+
+ if (state_ != STATE_DONE) {
+ wait_timer_.Stop();
+ state_ = STATE_DONE;
+
+ for (FetcherVector::iterator it = fetchers_.begin();
+ it != fetchers_.end();
+ ++it) {
+ (*it)->Cancel();
+ }
+
+ fetchers_.reset();
+ }
+}
+
+std::string DhcpProxyScriptFetcherWin::GetFetcherName() const {
+ DCHECK(CalledOnValidThread());
+ return "win";
+}
+
+const GURL& DhcpProxyScriptFetcherWin::GetPacURL() const {
+ DCHECK(CalledOnValidThread());
+ DCHECK_EQ(state_, STATE_DONE);
+
+ return pac_url_;
+}
+
+void DhcpProxyScriptFetcherWin::OnFetcherDone(int result) {
+ DCHECK(state_ == STATE_NO_RESULTS || state_ == STATE_SOME_RESULTS);
+
+ if (--num_pending_fetchers_ == 0) {
+ TransitionToDone();
+ return;
+ }
+
+ // If the only pending adapters are those less preferred than one
+ // with a valid PAC script, we do not need to wait any longer.
+ for (FetcherVector::iterator it = fetchers_.begin();
+ it != fetchers_.end();
+ ++it) {
+ bool did_finish = (*it)->DidFinish();
+ int result = (*it)->GetResult();
+ if (did_finish && result == OK) {
+ TransitionToDone();
+ return;
+ }
+ if (!did_finish || result != ERR_PAC_NOT_IN_DHCP) {
+ break;
+ }
+ }
+
+ // Once we have a single result, we set a maximum on how long to wait
+ // for the rest of the results.
+ if (state_ == STATE_NO_RESULTS) {
+ state_ = STATE_SOME_RESULTS;
+ wait_timer_.Start(
+ base::TimeDelta::FromMilliseconds(ImplGetMaxWaitMs()),
+ this, &DhcpProxyScriptFetcherWin::OnWaitTimer);
+ }
+}
+
+void DhcpProxyScriptFetcherWin::OnWaitTimer() {
+ DCHECK_EQ(state_, STATE_SOME_RESULTS);
+ TransitionToDone();
+}
+
+void DhcpProxyScriptFetcherWin::TransitionToDone() {
+ DCHECK(state_ == STATE_NO_RESULTS || state_ == STATE_SOME_RESULTS);
+
+ // Should have returned immediately at Fetch() if no adapters to check.
+ DCHECK(!fetchers_.empty());
+
+ // Scan twice for the result; once through the whole list for success,
+ // then if no success, return result for most preferred network adapter,
+ // preferring "real" network errors to the ERR_PAC_NOT_IN_DHCP error.
+ // Default to ERR_ABORTED if no fetcher completed.
+ int result = ERR_ABORTED;
+ for (FetcherVector::iterator it = fetchers_.begin();
+ it != fetchers_.end();
+ ++it) {
+ if ((*it)->DidFinish() && (*it)->GetResult() == OK) {
+ result = OK;
+ *destination_string_ = (*it)->GetPacScript();
+ pac_url_ = (*it)->GetPacURL();
+ break;
+ }
+ }
+ if (result != OK) {
+ destination_string_->clear();
+ for (FetcherVector::iterator it = fetchers_.begin();
+ it != fetchers_.end();
+ ++it) {
+ if ((*it)->DidFinish()) {
+ result = (*it)->GetResult();
+ if (result != ERR_PAC_NOT_IN_DHCP) {
+ break;
+ }
+ }
+ }
+ }
+
+ Cancel();
+ DCHECK_EQ(state_, STATE_DONE);
+ DCHECK(fetchers_.empty());
+
+ client_callback_->Run(result);
+}
+
+DhcpProxyScriptAdapterFetcher*
+ DhcpProxyScriptFetcherWin::ImplCreateAdapterFetcher() {
+ return new DhcpProxyScriptAdapterFetcher(url_request_context_);
+}
+
+bool DhcpProxyScriptFetcherWin::ImplGetCandidateAdapterNames(
+ std::set<std::string>* adapter_names) {
+ return GetCandidateAdapterNames(adapter_names);
+}
+
+int DhcpProxyScriptFetcherWin::ImplGetMaxWaitMs() {
+ return kMaxWaitAfterFirstResultMs;
+}
+
+bool DhcpProxyScriptFetcherWin::GetCandidateAdapterNames(
+ std::set<std::string>* adapter_names) {
+ DCHECK(adapter_names);
+ adapter_names->clear();
+
+ // The GetAdaptersAddresses MSDN page recommends using a size of 15000 to
+ // avoid reallocation.
+ ULONG adapters_size = 15000;
+ scoped_ptr_malloc<IP_ADAPTER_ADDRESSES> adapters;
+ ULONG error = ERROR_SUCCESS;
+ int num_tries = 0;
+ do {
+ adapters.reset(
+ reinterpret_cast<IP_ADAPTER_ADDRESSES*>(malloc(adapters_size)));
+ // Return only unicast addresses, and skip information we do not need.
+ error = GetAdaptersAddresses(AF_UNSPEC,
+ GAA_FLAG_SKIP_ANYCAST |
+ GAA_FLAG_SKIP_MULTICAST |
+ GAA_FLAG_SKIP_DNS_SERVER |
+ GAA_FLAG_SKIP_FRIENDLY_NAME,
+ NULL,
+ adapters.get(),
+ &adapters_size);
+ ++num_tries;
+ } while (error == ERROR_BUFFER_OVERFLOW && num_tries <= 3);
+
+ if (error == ERROR_NO_DATA) {
+ // There are no adapters that we care about.
+ return true;
+ }
+
+ if (error != ERROR_SUCCESS) {
+ LOG(WARNING) << "Unexpected error retrieving WPAD configuration from DHCP.";
+ return false;
+ }
+
+ IP_ADAPTER_ADDRESSES* adapter = NULL;
+ for (adapter = adapters.get(); adapter; adapter = adapter->Next) {
+ if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK)
+ continue;
+ if ((adapter->Flags & IP_ADAPTER_DHCP_ENABLED) == 0)
+ continue;
+
+ DCHECK(adapter->AdapterName);
+ adapter_names->insert(adapter->AdapterName);
+ }
+
+ return true;
+}
+
+} // namespace net
diff --git a/net/proxy/dhcp_proxy_script_fetcher_win.h b/net/proxy/dhcp_proxy_script_fetcher_win.h
new file mode 100644
index 0000000..3783971
--- /dev/null
+++ b/net/proxy/dhcp_proxy_script_fetcher_win.h
@@ -0,0 +1,123 @@
+// Copyright (c) 2011 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_DHCP_PROXY_SCRIPT_FETCHER_WIN_H_
+#define NET_PROXY_DHCP_PROXY_SCRIPT_FETCHER_WIN_H_
+#pragma once
+
+#include <set>
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/threading/non_thread_safe.h"
+#include "base/timer.h"
+#include "net/proxy/dhcp_proxy_script_fetcher.h"
+
+namespace net {
+
+class DhcpProxyScriptAdapterFetcher;
+class URLRequestContext;
+
+// Windows-specific implementation.
+class DhcpProxyScriptFetcherWin
+ : public DhcpProxyScriptFetcher,
+ public base::NonThreadSafe {
+ public:
+ // Creates a DhcpProxyScriptFetcherWin that issues requests through
+ // |url_request_context|. |url_request_context| must remain valid for
+ // the lifetime of DhcpProxyScriptFetcherWin.
+ explicit DhcpProxyScriptFetcherWin(URLRequestContext* url_request_context);
+ virtual ~DhcpProxyScriptFetcherWin();
+
+ // DhcpProxyScriptFetcher implementation.
+ int Fetch(string16* utf16_text, CompletionCallback* callback) OVERRIDE;
+ void Cancel() OVERRIDE;
+ const GURL& GetPacURL() const OVERRIDE;
+ std::string GetFetcherName() const OVERRIDE;
+
+ // Sets |adapter_names| to contain the name of each network adapter on
+ // this machine that has DHCP enabled and is not a loop-back adapter. Returns
+ // false on error.
+ static bool GetCandidateAdapterNames(std::set<std::string>* adapter_names);
+
+ protected:
+ // Event/state transition handlers
+ void OnFetcherDone(int result);
+ void OnWaitTimer();
+ void TransitionToDone();
+
+ // Virtual methods introduced to allow unit testing.
+ virtual DhcpProxyScriptAdapterFetcher* ImplCreateAdapterFetcher();
+ virtual bool ImplGetCandidateAdapterNames(
+ std::set<std::string>* adapter_names);
+ virtual int ImplGetMaxWaitMs();
+
+ // This is the outer state machine for fetching PAC configuration from
+ // DHCP. It relies for sub-states on the state machine of the
+ // DhcpProxyScriptAdapterFetcher class.
+ //
+ // The goal of the implementation is to the following work in parallel
+ // for all network adapters that are using DHCP:
+ // a) Try to get the PAC URL configured in DHCP;
+ // b) If one is configured, try to fetch the PAC URL.
+ // c) Once this is done for all adapters, or a timeout has passed after
+ // it has completed for the fastest adapter, return the PAC file
+ // available for the most preferred network adapter, if any.
+ //
+ // The state machine goes from START->NO_RESULTS when it creates
+ // and starts an DhcpProxyScriptAdapterFetcher for each adapter. It goes
+ // from NO_RESULTS->SOME_RESULTS when it gets the first result; at this
+ // point a wait timer is started. It goes from SOME_RESULTS->DONE in
+ // two cases: All results are known, or the wait timer expired. A call
+ // to Cancel() will also go straight to DONE from any state. Any
+ // way the DONE state is entered, we will at that point cancel any
+ // outstanding work and return the best known PAC script or the empty
+ // string.
+ //
+ // The state machine is reset for each Fetch(), a call to which is
+ // only valid in states START and DONE, as only one Fetch() is
+ // allowed to be outstanding at any given time.
+ enum State {
+ STATE_START,
+ STATE_NO_RESULTS,
+ STATE_SOME_RESULTS,
+ STATE_DONE,
+ };
+
+ // Current state of this state machine.
+ State state_;
+
+ // Vector, in Windows' network adapter preference order, of
+ // DhcpProxyScriptAdapterFetcher objects that are or were attempting
+ // to fetch a PAC file based on DHCP configuration.
+ typedef ScopedVector<DhcpProxyScriptAdapterFetcher> FetcherVector;
+ FetcherVector fetchers_;
+
+ // Callback invoked when any fetcher completes.
+ CompletionCallbackImpl<DhcpProxyScriptFetcherWin> fetcher_callback_;
+
+ // Number of fetchers we are waiting for.
+ int num_pending_fetchers_;
+
+ // Lets our client know we're done. Not valid in states START or DONE.
+ CompletionCallback* client_callback_;
+
+ // Pointer to string we will write results to. Not valid in states
+ // START and DONE.
+ string16* destination_string_;
+
+ // PAC URL retrieved from DHCP, if any. Valid only in state STATE_DONE.
+ GURL pac_url_;
+
+ base::OneShotTimer<DhcpProxyScriptFetcherWin> wait_timer_;
+
+ scoped_refptr<URLRequestContext> url_request_context_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(DhcpProxyScriptFetcherWin);
+};
+
+} // namespace net
+
+#endif // NET_PROXY_DHCP_PROXY_SCRIPT_FETCHER_WIN_H_
diff --git a/net/proxy/dhcp_proxy_script_fetcher_win_unittest.cc b/net/proxy/dhcp_proxy_script_fetcher_win_unittest.cc
new file mode 100644
index 0000000..596e298
--- /dev/null
+++ b/net/proxy/dhcp_proxy_script_fetcher_win_unittest.cc
@@ -0,0 +1,545 @@
+// Copyright (c) 2011 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/dhcp_proxy_script_fetcher_win.h"
+
+#include <vector>
+
+#include "base/message_loop.h"
+#include "base/perftimer.h"
+#include "base/rand_util.h"
+#include "base/threading/platform_thread.h"
+#include "net/base/completion_callback.h"
+#include "net/proxy/dhcp_proxy_script_adapter_fetcher_win.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+TEST(DhcpProxyScriptFetcherWin, AdapterNamesAndPacURLFromDhcp) {
+ // This tests our core Win32 implementation without any of the wrappers
+ // we layer on top to achieve asynchronous and parallel operations.
+ //
+ // We don't make assumptions about the environment this unit test is
+ // running in, so it just exercises the code to make sure there
+ // is no crash and no error returned, but does not assert on the number
+ // of interfaces or the information returned via DHCP.
+ std::set<std::string> adapter_names;
+ DhcpProxyScriptFetcherWin::GetCandidateAdapterNames(&adapter_names);
+ for (std::set<std::string>::const_iterator it = adapter_names.begin();
+ it != adapter_names.end();
+ ++it) {
+ const std::string& adapter_name = *it;
+ std::string pac_url =
+ DhcpProxyScriptAdapterFetcher::GetPacURLFromDhcp(adapter_name);
+ printf("Adapter '%s' has PAC URL '%s' configured in DHCP.\n",
+ adapter_name.c_str(),
+ pac_url.c_str());
+ }
+}
+
+// Helper for RealFetch* tests below.
+class RealFetchTester {
+ public:
+ RealFetchTester()
+ : context_((new TestURLRequestContext())),
+ fetcher_(new DhcpProxyScriptFetcherWin(context_.get())),
+ finished_(false),
+ ALLOW_THIS_IN_INITIALIZER_LIST(
+ completion_callback_(this, &RealFetchTester::OnCompletion)),
+ on_completion_is_error_(false) {
+ // Make sure the test ends.
+ timeout_.Start(
+ base::TimeDelta::FromSeconds(5), this, &RealFetchTester::OnTimeout);
+ }
+
+ void RunTest() {
+ fetcher_->Fetch(&pac_text_, &completion_callback_);
+ }
+
+ void RunTestWithCancel() {
+ RunTest();
+ fetcher_->Cancel();
+ }
+
+ void RunTestWithDeferredCancel() {
+ RunTest();
+ cancel_timer_.Start(base::TimeDelta::FromMilliseconds(1),
+ this, &RealFetchTester::OnCancelTimer);
+ }
+
+ void OnCompletion(int result) {
+ if (on_completion_is_error_) {
+ FAIL() << "Received completion for test in which this is error.";
+ }
+ finished_ = true;
+ printf("Result code %d PAC data length %d\n", result, pac_text_.size());
+ }
+
+ void OnTimeout() {
+ printf("Timeout!");
+ OnCompletion(0);
+ }
+
+ void OnCancelTimer() {
+ fetcher_->Cancel();
+ finished_ = true;
+ }
+
+ void WaitUntilDone() {
+ while (!finished_) {
+ MessageLoop::current()->RunAllPending();
+ }
+ MessageLoop::current()->RunAllPending();
+ }
+
+ // Attempts to give worker threads time to finish. This is currently
+ // very simplistic as completion (via completion callback or cancellation)
+ // immediately "detaches" any worker threads, so the best we can do is give
+ // them a little time. If we start running into Valgrind leaks, we can
+ // do something a bit more clever to track worker threads even when the
+ // DhcpProxyScriptFetcherWin state machine has finished.
+ void FinishTestAllowCleanup() {
+ base::PlatformThread::Sleep(30);
+ }
+
+ scoped_refptr<URLRequestContext> context_;
+ scoped_ptr<DhcpProxyScriptFetcherWin> fetcher_;
+ bool finished_;
+ string16 pac_text_;
+ CompletionCallbackImpl<RealFetchTester> completion_callback_;
+ base::OneShotTimer<RealFetchTester> timeout_;
+ base::OneShotTimer<RealFetchTester> cancel_timer_;
+ bool on_completion_is_error_;
+};
+
+TEST(DhcpProxyScriptFetcherWin, RealFetch) {
+ // This tests a call to Fetch() with no stubbing out of dependencies.
+ //
+ // We don't make assumptions about the environment this unit test is
+ // running in, so it just exercises the code to make sure there
+ // is no crash and no unexpected error returned, but does not assert on
+ // results beyond that.
+ RealFetchTester fetcher;
+ fetcher.RunTest();
+
+ fetcher.WaitUntilDone();
+ printf("PAC URL was %s\n",
+ fetcher.fetcher_->GetPacURL().possibly_invalid_spec().c_str());
+
+ fetcher.FinishTestAllowCleanup();
+}
+
+TEST(DhcpProxyScriptFetcherWin, RealFetchWithCancel) {
+ // Does a Fetch() with an immediate cancel. As before, just
+ // exercises the code without stubbing out dependencies.
+ RealFetchTester fetcher;
+ fetcher.RunTestWithCancel();
+ MessageLoop::current()->RunAllPending();
+
+ // Attempt to avoid Valgrind leak reports in case worker thread is
+ // still running.
+ fetcher.FinishTestAllowCleanup();
+}
+
+// For RealFetchWithDeferredCancel, below.
+class DelayingDhcpProxyScriptAdapterFetcher
+ : public DhcpProxyScriptAdapterFetcher {
+ public:
+ explicit DelayingDhcpProxyScriptAdapterFetcher(
+ URLRequestContext* url_request_context)
+ : DhcpProxyScriptAdapterFetcher(url_request_context) {
+ }
+
+ class DelayingWorkerThread : public WorkerThread {
+ public:
+ explicit DelayingWorkerThread(
+ const base::WeakPtr<DhcpProxyScriptAdapterFetcher>& owner)
+ : WorkerThread(owner) {
+ }
+
+ std::string ImplGetPacURLFromDhcp(
+ const std::string& adapter_name) OVERRIDE {
+ base::PlatformThread::Sleep(20);
+ return WorkerThread::ImplGetPacURLFromDhcp(adapter_name);
+ }
+ };
+
+ WorkerThread* ImplCreateWorkerThread(
+ const base::WeakPtr<DhcpProxyScriptAdapterFetcher>& owner) OVERRIDE {
+ return new DelayingWorkerThread(owner);
+ }
+};
+
+// For RealFetchWithDeferredCancel, below.
+class DelayingDhcpProxyScriptFetcherWin
+ : public DhcpProxyScriptFetcherWin {
+ public:
+ explicit DelayingDhcpProxyScriptFetcherWin(
+ URLRequestContext* context)
+ : DhcpProxyScriptFetcherWin(context) {
+ }
+
+ DhcpProxyScriptAdapterFetcher* ImplCreateAdapterFetcher() OVERRIDE {
+ return new DelayingDhcpProxyScriptAdapterFetcher(url_request_context_);
+ }
+};
+
+TEST(DhcpProxyScriptFetcherWin, RealFetchWithDeferredCancel) {
+ // Does a Fetch() with a slightly delayed cancel. As before, just
+ // exercises the code without stubbing out dependencies, but
+ // introduces a guaranteed 20 ms delay on the worker threads so that
+ // the cancel is called before they complete.
+ RealFetchTester fetcher;
+ fetcher.fetcher_.reset(
+ new DelayingDhcpProxyScriptFetcherWin(fetcher.context_));
+ fetcher.on_completion_is_error_ = true;
+ fetcher.RunTestWithDeferredCancel();
+ fetcher.WaitUntilDone();
+}
+
+// The remaining tests are to exercise our state machine in various
+// situations, with actual network access fully stubbed out.
+
+class DummyDhcpProxyScriptAdapterFetcher
+ : public DhcpProxyScriptAdapterFetcher {
+ public:
+ DummyDhcpProxyScriptAdapterFetcher()
+ : DhcpProxyScriptAdapterFetcher(new TestURLRequestContext()),
+ did_finish_(false),
+ result_(OK),
+ pac_script_(L"bingo"),
+ fetch_delay_ms_(1),
+ client_callback_(NULL) {
+ }
+
+ void Fetch(const std::string& adapter_name,
+ CompletionCallback* callback) OVERRIDE {
+ client_callback_ = callback;
+ timer_.Start(base::TimeDelta::FromMilliseconds(fetch_delay_ms_),
+ this, &DummyDhcpProxyScriptAdapterFetcher::OnTimer);
+ }
+
+ void Cancel() OVERRIDE {
+ timer_.Stop();
+ }
+
+ bool DidFinish() const OVERRIDE {
+ return did_finish_;
+ }
+
+ int GetResult() const OVERRIDE {
+ return result_;
+ }
+
+ string16 GetPacScript() const OVERRIDE {
+ return pac_script_;
+ }
+
+ void OnTimer() {
+ client_callback_->Run(result_);
+ }
+
+ void Configure(
+ bool did_finish, int result, string16 pac_script, int fetch_delay_ms) {
+ did_finish_ = did_finish;
+ result_ = result;
+ pac_script_ = pac_script;
+ fetch_delay_ms_ = fetch_delay_ms;
+ }
+
+ private:
+ bool did_finish_;
+ int result_;
+ string16 pac_script_;
+ int fetch_delay_ms_;
+ CompletionCallback* client_callback_;
+ base::OneShotTimer<DummyDhcpProxyScriptAdapterFetcher> timer_;
+};
+
+class MockDhcpProxyScriptFetcherWin : public DhcpProxyScriptFetcherWin {
+ public:
+ MockDhcpProxyScriptFetcherWin()
+ : DhcpProxyScriptFetcherWin(new TestURLRequestContext()),
+ next_adapter_fetcher_index_(0) {
+ }
+
+ // Adds a fetcher object to the queue of fetchers used by
+ // |ImplCreateAdapterFetcher()|, and its name to the list of adapters
+ // returned by ImplGetCandidateAdapterNames.
+ void PushBackAdapter(const std::string& adapter_name,
+ DhcpProxyScriptAdapterFetcher* fetcher) {
+ adapter_names_.push_back(adapter_name);
+ adapter_fetchers_.push_back(fetcher);
+ }
+
+ void ConfigureAndPushBackAdapter(const std::string& adapter_name,
+ bool did_finish,
+ int result,
+ string16 pac_script,
+ int fetch_delay_ms) {
+ scoped_ptr<DummyDhcpProxyScriptAdapterFetcher> adapter_fetcher(
+ new DummyDhcpProxyScriptAdapterFetcher());
+ adapter_fetcher->Configure(did_finish, result, pac_script, fetch_delay_ms);
+ PushBackAdapter(adapter_name, adapter_fetcher.release());
+ }
+
+ DhcpProxyScriptAdapterFetcher* ImplCreateAdapterFetcher() OVERRIDE {
+ return adapter_fetchers_[next_adapter_fetcher_index_++];
+ }
+
+ bool ImplGetCandidateAdapterNames(
+ std::set<std::string>* adapter_names) OVERRIDE {
+ adapter_names->insert(adapter_names_.begin(), adapter_names_.end());
+ return true;
+ }
+
+ int ImplGetMaxWaitMs() OVERRIDE {
+ return 25;
+ }
+
+ void ResetTestState() {
+ next_adapter_fetcher_index_ = 0;
+ adapter_fetchers_.clear();
+ // String pointers contained herein will have been freed during test.
+ adapter_names_.clear();
+ }
+
+ int next_adapter_fetcher_index_;
+
+ // Ownership is not here; it gets transferred to the implementation
+ // class via ImplCreateAdapterFetcher.
+ std::vector<DhcpProxyScriptAdapterFetcher*> adapter_fetchers_;
+
+ std::vector<std::string> adapter_names_;
+};
+
+class FetcherClient {
+public:
+ FetcherClient()
+ : finished_(false),
+ result_(ERR_UNEXPECTED),
+ ALLOW_THIS_IN_INITIALIZER_LIST(
+ completion_callback_(this, &FetcherClient::OnCompletion)) {
+ }
+
+ void RunTest() {
+ int result = fetcher_.Fetch(&pac_text_, &completion_callback_);
+ ASSERT_EQ(ERR_IO_PENDING, result);
+ }
+
+ void RunImmediateReturnTest() {
+ int result = fetcher_.Fetch(&pac_text_, &completion_callback_);
+ ASSERT_EQ(ERR_PAC_NOT_IN_DHCP, result);
+ }
+
+ void RunMessageLoopUntilComplete() {
+ while (!finished_) {
+ MessageLoop::current()->RunAllPending();
+ }
+ MessageLoop::current()->RunAllPending();
+ }
+
+ void OnCompletion(int result) {
+ finished_ = true;
+ result_ = result;
+ }
+
+ void ResetTestState() {
+ finished_ = false;
+ result_ = ERR_UNEXPECTED;
+ pac_text_ = L"";
+ fetcher_.ResetTestState();
+ }
+
+ MockDhcpProxyScriptFetcherWin fetcher_;
+ bool finished_;
+ int result_;
+ string16 pac_text_;
+ CompletionCallbackImpl<FetcherClient> completion_callback_;
+};
+
+// We separate out each test's logic so that we can easily implement
+// the ReuseFetcher test at the bottom.
+void TestNormalCaseURLConfiguredOneAdapter(FetcherClient* client) {
+ scoped_ptr<DummyDhcpProxyScriptAdapterFetcher> adapter_fetcher(
+ new DummyDhcpProxyScriptAdapterFetcher());
+ adapter_fetcher->Configure(true, OK, L"bingo", 1);
+ client->fetcher_.PushBackAdapter("a", adapter_fetcher.release());
+ client->RunTest();
+ client->RunMessageLoopUntilComplete();
+ ASSERT_EQ(OK, client->result_);
+ ASSERT_EQ(L"bingo", client->pac_text_);
+}
+
+TEST(DhcpProxyScriptFetcherWin, NormalCaseURLConfiguredOneAdapter) {
+ FetcherClient client;
+ TestNormalCaseURLConfiguredOneAdapter(&client);
+}
+
+void TestNormalCaseURLConfiguredMultipleAdapters(FetcherClient* client) {
+ client->fetcher_.ConfigureAndPushBackAdapter(
+ "most_preferred", true, ERR_PAC_NOT_IN_DHCP, L"", 1);
+ client->fetcher_.ConfigureAndPushBackAdapter(
+ "second", true, OK, L"bingo", 50);
+ client->fetcher_.ConfigureAndPushBackAdapter(
+ "third", true, OK, L"rocko", 1);
+ client->RunTest();
+ client->RunMessageLoopUntilComplete();
+ ASSERT_EQ(OK, client->result_);
+ ASSERT_EQ(L"bingo", client->pac_text_);
+}
+
+TEST(DhcpProxyScriptFetcherWin, NormalCaseURLConfiguredMultipleAdapters) {
+ FetcherClient client;
+ TestNormalCaseURLConfiguredMultipleAdapters(&client);
+}
+
+void TestNormalCaseURLConfiguredMultipleAdaptersWithTimeout(
+ FetcherClient* client) {
+ client->fetcher_.ConfigureAndPushBackAdapter(
+ "most_preferred", true, ERR_PAC_NOT_IN_DHCP, L"", 1);
+ // This will time out.
+ client->fetcher_.ConfigureAndPushBackAdapter(
+ "second", false, ERR_IO_PENDING, L"bingo", 1000);
+ client->fetcher_.ConfigureAndPushBackAdapter(
+ "third", true, OK, L"rocko", 1);
+ client->RunTest();
+ client->RunMessageLoopUntilComplete();
+ ASSERT_EQ(OK, client->result_);
+ ASSERT_EQ(L"rocko", client->pac_text_);
+}
+
+TEST(DhcpProxyScriptFetcherWin,
+ NormalCaseURLConfiguredMultipleAdaptersWithTimeout) {
+ FetcherClient client;
+ TestNormalCaseURLConfiguredMultipleAdaptersWithTimeout(&client);
+}
+
+void TestFailureCaseURLConfiguredMultipleAdaptersWithTimeout(
+ FetcherClient* client) {
+ client->fetcher_.ConfigureAndPushBackAdapter(
+ "most_preferred", true, ERR_PAC_NOT_IN_DHCP, L"", 1);
+ // This will time out.
+ client->fetcher_.ConfigureAndPushBackAdapter(
+ "second", false, ERR_IO_PENDING, L"bingo", 1000);
+ // This is the first non-ERR_PAC_NOT_IN_DHCP error and as such
+ // should be chosen.
+ client->fetcher_.ConfigureAndPushBackAdapter(
+ "third", true, ERR_PAC_STATUS_NOT_OK, L"", 1);
+ client->fetcher_.ConfigureAndPushBackAdapter(
+ "fourth", true, ERR_NOT_IMPLEMENTED, L"", 1);
+ client->RunTest();
+ client->RunMessageLoopUntilComplete();
+ ASSERT_EQ(ERR_PAC_STATUS_NOT_OK, client->result_);
+ ASSERT_EQ(L"", client->pac_text_);
+}
+
+TEST(DhcpProxyScriptFetcherWin,
+ FailureCaseURLConfiguredMultipleAdaptersWithTimeout) {
+ FetcherClient client;
+ TestFailureCaseURLConfiguredMultipleAdaptersWithTimeout(&client);
+}
+
+void TestFailureCaseNoURLConfigured(FetcherClient* client) {
+ client->fetcher_.ConfigureAndPushBackAdapter(
+ "most_preferred", true, ERR_PAC_NOT_IN_DHCP, L"", 1);
+ // This will time out.
+ client->fetcher_.ConfigureAndPushBackAdapter(
+ "second", false, ERR_IO_PENDING, L"bingo", 1000);
+ // This is the first non-ERR_PAC_NOT_IN_DHCP error and as such
+ // should be chosen.
+ client->fetcher_.ConfigureAndPushBackAdapter(
+ "third", true, ERR_PAC_NOT_IN_DHCP, L"", 1);
+ client->RunTest();
+ client->RunMessageLoopUntilComplete();
+ ASSERT_EQ(ERR_PAC_NOT_IN_DHCP, client->result_);
+ ASSERT_EQ(L"", client->pac_text_);
+}
+
+TEST(DhcpProxyScriptFetcherWin, FailureCaseNoURLConfigured) {
+ FetcherClient client;
+ TestFailureCaseNoURLConfigured(&client);
+}
+
+void TestFailureCaseNoDhcpAdapters(FetcherClient* client) {
+ client->RunImmediateReturnTest();
+ // In case there are any pending messages that get us in a bad state
+ // (there shouldn't be).
+ MessageLoop::current()->RunAllPending();
+}
+
+TEST(DhcpProxyScriptFetcherWin, FailureCaseNoDhcpAdapters) {
+ FetcherClient client;
+ TestFailureCaseNoDhcpAdapters(&client);
+}
+
+void TestShortCircuitLessPreferredAdapters(FetcherClient* client) {
+ // Here we have a bunch of adapters; the first reports no PAC in DHCP,
+ // the second responds quickly with a PAC file, the rest take a long
+ // time. Verify that we complete quickly and do not wait for the slow
+ // adapters, i.e. we finish before timeout.
+ client->fetcher_.ConfigureAndPushBackAdapter(
+ "1", true, ERR_PAC_NOT_IN_DHCP, L"", 1);
+ client->fetcher_.ConfigureAndPushBackAdapter(
+ "2", true, OK, L"bingo", 1);
+ client->fetcher_.ConfigureAndPushBackAdapter(
+ "3", true, OK, L"wrongo", 1000);
+
+ PerfTimer timer;
+ client->RunTest();
+ client->RunMessageLoopUntilComplete();
+ // Assert that the time passed is just less than the wait timer
+ // timeout (which we have mocked out above to be 25 ms), to avoid
+ // flakiness but still get a strong signal that it was the shortcut
+ // mechanism (in OnFetcherDone) that kicked in.
+ ASSERT_GT(TimeDelta::FromMilliseconds(23), timer.Elapsed());
+}
+
+TEST(DhcpProxyScriptFetcherWin, ShortCircuitLessPreferredAdapters) {
+ FetcherClient client;
+ TestShortCircuitLessPreferredAdapters(&client);
+}
+
+TEST(DhcpProxyScriptFetcherWin, ReuseFetcher) {
+ FetcherClient client;
+
+ // The ProxyScriptFetcher interface stipulates that only a single
+ // |Fetch()| may be in flight at once, but allows reuse, so test
+ // that the state transitions correctly from done to start in all
+ // cases we're testing.
+
+ typedef void (*FetcherClientTestFunction)(FetcherClient*);
+ typedef std::vector<FetcherClientTestFunction> TestVector;
+ TestVector test_functions;
+ test_functions.push_back(TestNormalCaseURLConfiguredOneAdapter);
+ test_functions.push_back(TestNormalCaseURLConfiguredMultipleAdapters);
+ test_functions.push_back(
+ TestNormalCaseURLConfiguredMultipleAdaptersWithTimeout);
+ test_functions.push_back(
+ TestFailureCaseURLConfiguredMultipleAdaptersWithTimeout);
+ test_functions.push_back(TestFailureCaseNoURLConfigured);
+ test_functions.push_back(TestFailureCaseNoDhcpAdapters);
+ test_functions.push_back(TestShortCircuitLessPreferredAdapters);
+
+ std::random_shuffle(test_functions.begin(),
+ test_functions.end(),
+ base::RandGenerator);
+ for (TestVector::const_iterator it = test_functions.begin();
+ it != test_functions.end();
+ ++it) {
+ (*it)(&client);
+ client.ResetTestState();
+ }
+
+ // Re-do the first test to make sure the last test that was run did
+ // not leave things in a bad state.
+ (*test_functions.begin())(&client);
+}
+
+} // namespace
+
+} // namespace net
diff --git a/net/proxy/dhcpcsvc_init_win.cc b/net/proxy/dhcpcsvc_init_win.cc
new file mode 100644
index 0000000..3a0aa02
--- /dev/null
+++ b/net/proxy/dhcpcsvc_init_win.cc
@@ -0,0 +1,40 @@
+// Copyright (c) 2011 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/dhcpcsvc_init_win.h"
+
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+
+#include <dhcpcsdk.h>
+#include <dhcpv6csdk.h>
+
+namespace {
+
+class DhcpcsvcInitSingleton {
+ public:
+ DhcpcsvcInitSingleton() {
+ DWORD version = 0;
+ DWORD err = DhcpCApiInitialize(&version);
+ DCHECK(err == ERROR_SUCCESS); // DCHECK_EQ complains of unsigned mismatch.
+ }
+
+ ~DhcpcsvcInitSingleton() {
+ // Worker pool threads that use the DHCP API may still be running, so skip
+ // cleanup.
+ }
+};
+
+static base::LazyInstance<DhcpcsvcInitSingleton> g_dhcpcsvc_init_singleton(
+ base::LINKER_INITIALIZED);
+
+} // namespace
+
+namespace net {
+
+void EnsureDhcpcsvcInit() {
+ g_dhcpcsvc_init_singleton.Get();
+}
+
+} // namespace net
diff --git a/net/proxy/dhcpcsvc_init_win.h b/net/proxy/dhcpcsvc_init_win.h
new file mode 100644
index 0000000..9c557d0
--- /dev/null
+++ b/net/proxy/dhcpcsvc_init_win.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2011 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_DHCPCSVC_INIT_WIN_H
+#define NET_PROXY_DHCPCSVC_INIT_WIN_H
+#pragma once
+
+namespace net {
+
+// Initialization of the Dhcpcsvc library must happen before any of its
+// calls are made. This function will make sure that the appropriate
+// initialization has been done, and that uninitialization is also
+// performed at static uninitialization time.
+//
+// Note: This initializes only for DHCP, not DHCPv6.
+void EnsureDhcpcsvcInit();
+
+} // namespace net
+
+#endif // NET_PROXY_DHCPCSVC_INIT_WIN_H
diff --git a/net/proxy/init_proxy_resolver.cc b/net/proxy/init_proxy_resolver.cc
index d9d55ea..52c5421 100644
--- a/net/proxy/init_proxy_resolver.cc
+++ b/net/proxy/init_proxy_resolver.cc
@@ -10,6 +10,8 @@
#include "base/string_util.h"
#include "net/base/net_log.h"
#include "net/base/net_errors.h"
+#include "net/proxy/dhcp_proxy_script_fetcher.h"
+#include "net/proxy/dhcp_proxy_script_fetcher_factory.h"
#include "net/proxy/proxy_config.h"
#include "net/proxy/proxy_resolver.h"
#include "net/proxy/proxy_script_fetcher.h"
@@ -30,15 +32,18 @@ namespace net {
// http://code.google.com/p/chromium/issues/detail?id=18575#c20
static const char kWpadUrl[] = "http://wpad/wpad.dat";
-InitProxyResolver::InitProxyResolver(ProxyResolver* resolver,
- ProxyScriptFetcher* proxy_script_fetcher,
- NetLog* net_log)
+InitProxyResolver::InitProxyResolver(
+ ProxyResolver* resolver,
+ ProxyScriptFetcher* proxy_script_fetcher,
+ DhcpProxyScriptFetcher* dhcp_proxy_script_fetcher,
+ NetLog* net_log)
: resolver_(resolver),
proxy_script_fetcher_(proxy_script_fetcher),
+ dhcp_proxy_script_fetcher_(dhcp_proxy_script_fetcher),
ALLOW_THIS_IN_INITIALIZER_LIST(io_callback_(
this, &InitProxyResolver::OnIOCompletion)),
user_callback_(NULL),
- current_pac_url_index_(0u),
+ current_pac_source_index_(0u),
pac_mandatory_(false),
next_state_(STATE_NONE),
net_log_(BoundNetLog::Make(
@@ -70,8 +75,8 @@ int InitProxyResolver::Init(const ProxyConfig& config,
pac_mandatory_ = config.pac_mandatory();
- pac_urls_ = BuildPacUrlsFallbackList(config);
- DCHECK(!pac_urls_.empty());
+ pac_sources_ = BuildPacSourcesFallbackList(config);
+ DCHECK(!pac_sources_.empty());
next_state_ = STATE_WAIT;
@@ -85,16 +90,19 @@ int InitProxyResolver::Init(const ProxyConfig& config,
}
// Initialize the fallback rules.
-// (1) WPAD (DNS).
-// (2) Custom PAC URL.
-InitProxyResolver::UrlList InitProxyResolver::BuildPacUrlsFallbackList(
+// (1) WPAD (DHCP).
+// (2) WPAD (DNS).
+// (3) Custom PAC URL.
+InitProxyResolver::PacSourceList InitProxyResolver::BuildPacSourcesFallbackList(
const ProxyConfig& config) const {
- UrlList pac_urls;
- if (config.auto_detect())
- pac_urls.push_back(PacURL(true, GURL()));
+ PacSourceList pac_sources;
+ if (config.auto_detect()) {
+ pac_sources.push_back(PacSource(PacSource::WPAD_DHCP, GURL()));
+ pac_sources.push_back(PacSource(PacSource::WPAD_DNS, GURL()));
+ }
if (config.has_pac_url())
- pac_urls.push_back(PacURL(false, config.pac_url()));
- return pac_urls;
+ pac_sources.push_back(PacSource(PacSource::CUSTOM, config.pac_url()));
+ return pac_sources;
}
void InitProxyResolver::OnIOCompletion(int result) {
@@ -177,24 +185,32 @@ int InitProxyResolver::DoFetchPacScript() {
next_state_ = STATE_FETCH_PAC_SCRIPT_COMPLETE;
- const PacURL& pac_url = current_pac_url();
+ const PacSource& pac_source = current_pac_source();
- const GURL effective_pac_url =
- pac_url.auto_detect ? GURL(kWpadUrl) : pac_url.url;
+ GURL effective_pac_url;
+ NetLogStringParameter* log_parameter =
+ CreateNetLogParameterAndDetermineURL(pac_source, &effective_pac_url);
net_log_.BeginEvent(
NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT,
- make_scoped_refptr(new NetLogStringParameter(
- "url", effective_pac_url.possibly_invalid_spec())));
+ make_scoped_refptr(log_parameter));
+
+ if (pac_source.type == PacSource::WPAD_DHCP) {
+ if (!dhcp_proxy_script_fetcher_) {
+ net_log_.AddEvent(NetLog::TYPE_INIT_PROXY_RESOLVER_HAS_NO_FETCHER, NULL);
+ return ERR_UNEXPECTED;
+ }
+
+ return dhcp_proxy_script_fetcher_->Fetch(&pac_script_, &io_callback_);
+ }
if (!proxy_script_fetcher_) {
net_log_.AddEvent(NetLog::TYPE_INIT_PROXY_RESOLVER_HAS_NO_FETCHER, NULL);
return ERR_UNEXPECTED;
}
- return proxy_script_fetcher_->Fetch(effective_pac_url,
- &pac_script_,
- &io_callback_);
+ return proxy_script_fetcher_->Fetch(
+ effective_pac_url, &pac_script_, &io_callback_);
}
int InitProxyResolver::DoFetchPacScriptComplete(int result) {
@@ -203,7 +219,7 @@ int InitProxyResolver::DoFetchPacScriptComplete(int result) {
net_log_.EndEventWithNetErrorCode(
NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT, result);
if (result != OK)
- return TryToFallbackPacUrl(result);
+ return TryToFallbackPacSource(result);
next_state_ = STATE_SET_PAC_SCRIPT;
return result;
@@ -212,7 +228,7 @@ int InitProxyResolver::DoFetchPacScriptComplete(int result) {
int InitProxyResolver::DoSetPacScript() {
net_log_.BeginEvent(NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT, NULL);
- const PacURL& pac_url = current_pac_url();
+ const PacSource& pac_source = current_pac_source();
next_state_ = STATE_SET_PAC_SCRIPT_COMPLETE;
@@ -221,9 +237,9 @@ int InitProxyResolver::DoSetPacScript() {
if (resolver_->expects_pac_bytes()) {
script_data = ProxyResolverScriptData::FromUTF16(pac_script_);
} else {
- script_data = pac_url.auto_detect ?
- ProxyResolverScriptData::ForAutoDetect() :
- ProxyResolverScriptData::FromURL(pac_url.url);
+ script_data = pac_source.type == PacSource::CUSTOM ?
+ ProxyResolverScriptData::FromURL(pac_source.url) :
+ ProxyResolverScriptData::ForAutoDetect();
}
return resolver_->SetPacScript(script_data, &io_callback_);
@@ -233,39 +249,59 @@ int InitProxyResolver::DoSetPacScriptComplete(int result) {
net_log_.EndEventWithNetErrorCode(
NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT, result);
if (result != OK)
- return TryToFallbackPacUrl(result);
+ return TryToFallbackPacSource(result);
// Let the caller know which automatic setting we ended up initializing the
// resolver for (there may have been multiple fallbacks to choose from.)
if (effective_config_) {
- if (current_pac_url().auto_detect && resolver_->expects_pac_bytes()) {
- *effective_config_ =
- ProxyConfig::CreateFromCustomPacURL(GURL(kWpadUrl));
- } else if (current_pac_url().auto_detect) {
- *effective_config_ = ProxyConfig::CreateAutoDetect();
- } else {
+ if (current_pac_source().type == PacSource::CUSTOM) {
*effective_config_ =
- ProxyConfig::CreateFromCustomPacURL(current_pac_url().url);
+ ProxyConfig::CreateFromCustomPacURL(current_pac_source().url);
effective_config_->set_pac_mandatory(pac_mandatory_);
+ } else {
+ if (resolver_->expects_pac_bytes()) {
+ GURL auto_detected_url;
+
+ switch (current_pac_source().type) {
+ case PacSource::WPAD_DHCP:
+ auto_detected_url = dhcp_proxy_script_fetcher_->GetPacURL();
+ break;
+
+ case PacSource::WPAD_DNS:
+ auto_detected_url = GURL(kWpadUrl);
+ break;
+
+ default:
+ NOTREACHED();
+ }
+
+ *effective_config_ =
+ ProxyConfig::CreateFromCustomPacURL(auto_detected_url);
+ } else {
+ // The resolver does its own resolution so we cannot know the
+ // URL. Just do the best we can and state that the configuration
+ // is to auto-detect proxy settings.
+ *effective_config_ = ProxyConfig::CreateAutoDetect();
+ }
}
}
return result;
}
-int InitProxyResolver::TryToFallbackPacUrl(int error) {
+int InitProxyResolver::TryToFallbackPacSource(int error) {
DCHECK_LT(error, 0);
- if (current_pac_url_index_ + 1 >= pac_urls_.size()) {
+ if (current_pac_source_index_ + 1 >= pac_sources_.size()) {
// Nothing left to fall back to.
return error;
}
// Advance to next URL in our list.
- ++current_pac_url_index_;
+ ++current_pac_source_index_;
net_log_.AddEvent(
- NetLog::TYPE_INIT_PROXY_RESOLVER_FALLING_BACK_TO_NEXT_PAC_URL, NULL);
+ NetLog::TYPE_INIT_PROXY_RESOLVER_FALLING_BACK_TO_NEXT_PAC_SOURCE, NULL);
next_state_ = GetStartState();
@@ -277,9 +313,34 @@ InitProxyResolver::State InitProxyResolver::GetStartState() const {
STATE_FETCH_PAC_SCRIPT : STATE_SET_PAC_SCRIPT;
}
-const InitProxyResolver::PacURL& InitProxyResolver::current_pac_url() const {
- DCHECK_LT(current_pac_url_index_, pac_urls_.size());
- return pac_urls_[current_pac_url_index_];
+NetLogStringParameter* InitProxyResolver::CreateNetLogParameterAndDetermineURL(
+ const PacSource& pac_source,
+ GURL* effective_pac_url) {
+ DCHECK(effective_pac_url);
+
+ std::string source_field;
+ switch (pac_source.type) {
+ case PacSource::WPAD_DHCP:
+ source_field = "WPAD DHCP";
+ break;
+ case PacSource::WPAD_DNS:
+ *effective_pac_url = GURL(kWpadUrl);
+ source_field = "WPAD DNS: ";
+ source_field += effective_pac_url->possibly_invalid_spec();
+ break;
+ case PacSource::CUSTOM:
+ *effective_pac_url = pac_source.url;
+ source_field = "Custom PAC URL: ";
+ source_field += effective_pac_url->possibly_invalid_spec();
+ break;
+ }
+ return new NetLogStringParameter("source", source_field);
+}
+
+const InitProxyResolver::PacSource&
+ InitProxyResolver::current_pac_source() const {
+ DCHECK_LT(current_pac_source_index_, pac_sources_.size());
+ return pac_sources_[current_pac_source_index_];
}
void InitProxyResolver::OnWaitTimerFired() {
diff --git a/net/proxy/init_proxy_resolver.h b/net/proxy/init_proxy_resolver.h
index 67cf1cb..ffb1b83 100644
--- a/net/proxy/init_proxy_resolver.h
+++ b/net/proxy/init_proxy_resolver.h
@@ -11,15 +11,19 @@
#include "base/string16.h"
#include "base/time.h"
#include "base/timer.h"
+#include "base/scoped_ptr.h"
#include "googleurl/src/gurl.h"
#include "net/base/completion_callback.h"
#include "net/base/net_log.h"
namespace net {
+class DhcpProxyScriptFetcher;
+class NetLogParameter;
class ProxyConfig;
class ProxyResolver;
class ProxyScriptFetcher;
+class URLRequestContext;
// InitProxyResolver is a helper class used by ProxyService to
// initialize a ProxyResolver with the PAC script data specified
@@ -39,10 +43,11 @@ class ProxyScriptFetcher;
//
class InitProxyResolver {
public:
- // |resolver|, |proxy_script_fetcher| and |net_log| must remain valid for
- // the lifespan of InitProxyResolver.
+ // |resolver|, |proxy_script_fetcher|, |dhcp_proxy_script_fetcher| and
+ // |net_log| must remain valid for the lifespan of InitProxyResolver.
InitProxyResolver(ProxyResolver* resolver,
ProxyScriptFetcher* proxy_script_fetcher,
+ DhcpProxyScriptFetcher* dhcp_proxy_script_fetcher,
NetLog* net_log);
// Aborts any in-progress request.
@@ -63,14 +68,23 @@ class InitProxyResolver {
CompletionCallback* callback);
private:
- struct PacURL {
- PacURL(bool auto_detect, const GURL& url)
- : auto_detect(auto_detect), url(url) {}
- bool auto_detect;
- GURL url;
+ // Represents the sources from which we can get PAC files; two types of
+ // auto-detect or a custom URL.
+ struct PacSource {
+ enum Type {
+ WPAD_DHCP,
+ WPAD_DNS,
+ CUSTOM
+ };
+
+ PacSource(Type type, const GURL& url)
+ : type(type), url(url) {}
+
+ Type type;
+ GURL url; // Empty unless |type == PAC_SOURCE_CUSTOM|.
};
- typedef std::vector<PacURL> UrlList;
+ typedef std::vector<PacSource> PacSourceList;
enum State {
STATE_NONE,
@@ -83,7 +97,7 @@ class InitProxyResolver {
};
// Returns ordered list of PAC urls to try for |config|.
- UrlList BuildPacUrlsFallbackList(const ProxyConfig& config) const;
+ PacSourceList BuildPacSourcesFallbackList(const ProxyConfig& config) const;
void OnIOCompletion(int result);
int DoLoop(int result);
@@ -99,17 +113,20 @@ class InitProxyResolver {
int DoSetPacScriptComplete(int result);
// Tries restarting using the next fallback PAC URL:
- // |pac_urls_[++current_pac_url_index]|.
+ // |pac_sources_[++current_pac_source_index]|.
// Returns OK and rewinds the state machine when there
// is something to try, otherwise returns |error|.
- int TryToFallbackPacUrl(int error);
+ int TryToFallbackPacSource(int error);
// Gets the initial state (we skip fetching when the
// ProxyResolver doesn't |expect_pac_bytes()|.
State GetStartState() const;
+ NetLogStringParameter* CreateNetLogParameterAndDetermineURL(
+ const PacSource& pac_source, GURL* effective_pac_url);
+
// Returns the current PAC URL we are fetching/testing.
- const PacURL& current_pac_url() const;
+ const PacSource& current_pac_source() const;
void OnWaitTimerFired();
void DidCompleteInit();
@@ -117,11 +134,12 @@ class InitProxyResolver {
ProxyResolver* resolver_;
ProxyScriptFetcher* proxy_script_fetcher_;
+ DhcpProxyScriptFetcher* dhcp_proxy_script_fetcher_;
CompletionCallbackImpl<InitProxyResolver> io_callback_;
CompletionCallback* user_callback_;
- size_t current_pac_url_index_;
+ size_t current_pac_source_index_;
// Filled when the PAC script fetch completes.
string16 pac_script_;
@@ -130,7 +148,7 @@ class InitProxyResolver {
// (i.e. fallback to direct connections are prohibited).
bool pac_mandatory_;
- UrlList pac_urls_;
+ PacSourceList pac_sources_;
State next_state_;
BoundNetLog net_log_;
diff --git a/net/proxy/init_proxy_resolver_unittest.cc b/net/proxy/init_proxy_resolver_unittest.cc
index b0d416d..628abee 100644
--- a/net/proxy/init_proxy_resolver_unittest.cc
+++ b/net/proxy/init_proxy_resolver_unittest.cc
@@ -11,6 +11,7 @@
#include "net/base/net_log_unittest.h"
#include "net/base/test_completion_callback.h"
#include "net/proxy/init_proxy_resolver.h"
+#include "net/proxy/dhcp_proxy_script_fetcher.h"
#include "net/proxy/proxy_config.h"
#include "net/proxy/proxy_resolver.h"
#include "net/proxy/proxy_script_fetcher.h"
@@ -107,7 +108,7 @@ class RuleBasedProxyScriptFetcher : public ProxyScriptFetcher {
virtual void Cancel() {}
- virtual URLRequestContext* GetRequestContext() { return NULL; }
+ virtual URLRequestContext* GetRequestContext() const { return NULL; }
private:
const Rules* rules_;
@@ -174,6 +175,7 @@ TEST(InitProxyResolverTest, CustomPacSucceeds) {
Rules rules;
RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
RuleBasedProxyScriptFetcher fetcher(&rules);
+ DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
ProxyConfig config;
config.set_pac_url(GURL("http://custom/proxy.pac"));
@@ -182,8 +184,10 @@ TEST(InitProxyResolverTest, CustomPacSucceeds) {
TestCompletionCallback callback;
CapturingNetLog log(CapturingNetLog::kUnbounded);
- InitProxyResolver init(&resolver, &fetcher, &log);
- EXPECT_EQ(OK, init.Init(config, base::TimeDelta(), NULL, &callback));
+ ProxyConfig effective_config;
+ InitProxyResolver init(&resolver, &fetcher, &dhcp_fetcher, &log);
+ EXPECT_EQ(OK, init.Init(
+ config, base::TimeDelta(), &effective_config, &callback));
EXPECT_EQ(rule.text(), resolver.script_data()->utf16());
// Check the NetLog was filled correctly.
@@ -203,6 +207,9 @@ TEST(InitProxyResolverTest, CustomPacSucceeds) {
entries, 4, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
EXPECT_TRUE(LogContainsEndEvent(
entries, 5, NetLog::TYPE_INIT_PROXY_RESOLVER));
+
+ EXPECT_TRUE(effective_config.has_pac_url());
+ EXPECT_EQ(config.pac_url(), effective_config.pac_url());
}
// Fail downloading the custom PAC script.
@@ -210,6 +217,7 @@ TEST(InitProxyResolverTest, CustomPacFails1) {
Rules rules;
RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
RuleBasedProxyScriptFetcher fetcher(&rules);
+ DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
ProxyConfig config;
config.set_pac_url(GURL("http://custom/proxy.pac"));
@@ -218,9 +226,10 @@ TEST(InitProxyResolverTest, CustomPacFails1) {
TestCompletionCallback callback;
CapturingNetLog log(CapturingNetLog::kUnbounded);
- InitProxyResolver init(&resolver, &fetcher, &log);
+ ProxyConfig effective_config;
+ InitProxyResolver init(&resolver, &fetcher, &dhcp_fetcher, &log);
EXPECT_EQ(kFailedDownloading,
- init.Init(config, base::TimeDelta(), NULL, &callback));
+ init.Init(config, base::TimeDelta(), &effective_config, &callback));
EXPECT_EQ(NULL, resolver.script_data());
// Check the NetLog was filled correctly.
@@ -236,6 +245,8 @@ TEST(InitProxyResolverTest, CustomPacFails1) {
entries, 2, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
EXPECT_TRUE(LogContainsEndEvent(
entries, 3, NetLog::TYPE_INIT_PROXY_RESOLVER));
+
+ EXPECT_FALSE(effective_config.has_pac_url());
}
// Fail parsing the custom PAC script.
@@ -243,6 +254,7 @@ TEST(InitProxyResolverTest, CustomPacFails2) {
Rules rules;
RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
RuleBasedProxyScriptFetcher fetcher(&rules);
+ DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
ProxyConfig config;
config.set_pac_url(GURL("http://custom/proxy.pac"));
@@ -250,7 +262,7 @@ TEST(InitProxyResolverTest, CustomPacFails2) {
rules.AddFailParsingRule("http://custom/proxy.pac");
TestCompletionCallback callback;
- InitProxyResolver init(&resolver, &fetcher, NULL);
+ InitProxyResolver init(&resolver, &fetcher, &dhcp_fetcher, NULL);
EXPECT_EQ(kFailedParsing,
init.Init(config, base::TimeDelta(), NULL, &callback));
EXPECT_EQ(NULL, resolver.script_data());
@@ -260,22 +272,24 @@ TEST(InitProxyResolverTest, CustomPacFails2) {
TEST(InitProxyResolverTest, HasNullProxyScriptFetcher) {
Rules rules;
RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
+ DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
ProxyConfig config;
config.set_pac_url(GURL("http://custom/proxy.pac"));
TestCompletionCallback callback;
- InitProxyResolver init(&resolver, NULL, NULL);
+ InitProxyResolver init(&resolver, NULL, &dhcp_fetcher, NULL);
EXPECT_EQ(ERR_UNEXPECTED,
init.Init(config, base::TimeDelta(), NULL, &callback));
EXPECT_EQ(NULL, resolver.script_data());
}
-// Succeeds in choosing autodetect (wpad).
+// Succeeds in choosing autodetect (WPAD DNS).
TEST(InitProxyResolverTest, AutodetectSuccess) {
Rules rules;
RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
RuleBasedProxyScriptFetcher fetcher(&rules);
+ DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
ProxyConfig config;
config.set_auto_detect(true);
@@ -283,9 +297,14 @@ TEST(InitProxyResolverTest, AutodetectSuccess) {
Rules::Rule rule = rules.AddSuccessRule("http://wpad/wpad.dat");
TestCompletionCallback callback;
- InitProxyResolver init(&resolver, &fetcher, NULL);
- EXPECT_EQ(OK, init.Init(config, base::TimeDelta(), NULL, &callback));
+ ProxyConfig effective_config;
+ InitProxyResolver init(&resolver, &fetcher, &dhcp_fetcher, NULL);
+ EXPECT_EQ(OK, init.Init(
+ config, base::TimeDelta(), &effective_config, &callback));
EXPECT_EQ(rule.text(), resolver.script_data()->utf16());
+
+ EXPECT_TRUE(effective_config.has_pac_url());
+ EXPECT_EQ(rule.url, effective_config.pac_url());
}
// Fails at WPAD (downloading), but succeeds in choosing the custom PAC.
@@ -293,6 +312,7 @@ TEST(InitProxyResolverTest, AutodetectFailCustomSuccess1) {
Rules rules;
RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
RuleBasedProxyScriptFetcher fetcher(&rules);
+ DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
ProxyConfig config;
config.set_auto_detect(true);
@@ -302,16 +322,23 @@ TEST(InitProxyResolverTest, AutodetectFailCustomSuccess1) {
Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac");
TestCompletionCallback callback;
- InitProxyResolver init(&resolver, &fetcher, NULL);
- EXPECT_EQ(OK, init.Init(config, base::TimeDelta(), NULL, &callback));
+ ProxyConfig effective_config;
+ InitProxyResolver init(&resolver, &fetcher, &dhcp_fetcher, NULL);
+ EXPECT_EQ(OK, init.Init(
+ config, base::TimeDelta(), &effective_config, &callback));
EXPECT_EQ(rule.text(), resolver.script_data()->utf16());
+
+ EXPECT_TRUE(effective_config.has_pac_url());
+ EXPECT_EQ(rule.url, effective_config.pac_url());
}
-// Fails at WPAD (parsing), but succeeds in choosing the custom PAC.
+// Fails at WPAD (no DHCP config, DNS PAC fails parsing), but succeeds in
+// choosing the custom PAC.
TEST(InitProxyResolverTest, AutodetectFailCustomSuccess2) {
Rules rules;
RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
RuleBasedProxyScriptFetcher fetcher(&rules);
+ DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
ProxyConfig config;
config.set_auto_detect(true);
@@ -325,7 +352,7 @@ TEST(InitProxyResolverTest, AutodetectFailCustomSuccess2) {
CapturingNetLog log(CapturingNetLog::kUnbounded);
ProxyConfig effective_config;
- InitProxyResolver init(&resolver, &fetcher, &log);
+ InitProxyResolver init(&resolver, &fetcher, &dhcp_fetcher, &log);
EXPECT_EQ(OK, init.Init(config, base::TimeDelta(),
&effective_config, &callback));
EXPECT_EQ(rule.text(), resolver.script_data()->utf16());
@@ -336,36 +363,48 @@ TEST(InitProxyResolverTest, AutodetectFailCustomSuccess2) {
ProxyConfig::CreateFromCustomPacURL(GURL("http://custom/proxy.pac"))));
// Check the NetLog was filled correctly.
- // (Note that the Fetch and Set states are repeated since both WPAD and custom
+ // (Note that various states are repeated since both WPAD and custom
// PAC scripts are tried).
CapturingNetLog::EntryList entries;
log.GetEntries(&entries);
- EXPECT_EQ(11u, entries.size());
+ EXPECT_EQ(14u, entries.size());
EXPECT_TRUE(LogContainsBeginEvent(
entries, 0, NetLog::TYPE_INIT_PROXY_RESOLVER));
+ // This is the DHCP phase, which fails fetching rather than parsing, so
+ // there is no pair of SET_PAC_SCRIPT events.
EXPECT_TRUE(LogContainsBeginEvent(
entries, 1, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
EXPECT_TRUE(LogContainsEndEvent(
entries, 2, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
+ EXPECT_TRUE(LogContainsEvent(
+ entries, 3,
+ NetLog::TYPE_INIT_PROXY_RESOLVER_FALLING_BACK_TO_NEXT_PAC_SOURCE,
+ NetLog::PHASE_NONE));
+ // This is the DNS phase, which attempts a fetch but fails.
EXPECT_TRUE(LogContainsBeginEvent(
- entries, 3, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
+ entries, 4, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
EXPECT_TRUE(LogContainsEndEvent(
- entries, 4, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
+ entries, 5, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
+ EXPECT_TRUE(LogContainsBeginEvent(
+ entries, 6, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
+ EXPECT_TRUE(LogContainsEndEvent(
+ entries, 7, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
EXPECT_TRUE(LogContainsEvent(
- entries, 5,
- NetLog::TYPE_INIT_PROXY_RESOLVER_FALLING_BACK_TO_NEXT_PAC_URL,
+ entries, 8,
+ NetLog::TYPE_INIT_PROXY_RESOLVER_FALLING_BACK_TO_NEXT_PAC_SOURCE,
NetLog::PHASE_NONE));
+ // Finally, the custom PAC URL phase.
EXPECT_TRUE(LogContainsBeginEvent(
- entries, 6, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
+ entries, 9, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
EXPECT_TRUE(LogContainsEndEvent(
- entries, 7, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
+ entries, 10, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
EXPECT_TRUE(LogContainsBeginEvent(
- entries, 8, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
+ entries, 11, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
EXPECT_TRUE(LogContainsEndEvent(
- entries, 9, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
+ entries, 12, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
EXPECT_TRUE(LogContainsEndEvent(
- entries, 10, NetLog::TYPE_INIT_PROXY_RESOLVER));
+ entries, 13, NetLog::TYPE_INIT_PROXY_RESOLVER));
}
// Fails at WPAD (downloading), and fails at custom PAC (downloading).
@@ -373,6 +412,7 @@ TEST(InitProxyResolverTest, AutodetectFailCustomFails1) {
Rules rules;
RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
RuleBasedProxyScriptFetcher fetcher(&rules);
+ DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
ProxyConfig config;
config.set_auto_detect(true);
@@ -382,7 +422,7 @@ TEST(InitProxyResolverTest, AutodetectFailCustomFails1) {
rules.AddFailDownloadRule("http://custom/proxy.pac");
TestCompletionCallback callback;
- InitProxyResolver init(&resolver, &fetcher, NULL);
+ InitProxyResolver init(&resolver, &fetcher, &dhcp_fetcher, NULL);
EXPECT_EQ(kFailedDownloading,
init.Init(config, base::TimeDelta(), NULL, &callback));
EXPECT_EQ(NULL, resolver.script_data());
@@ -393,6 +433,7 @@ TEST(InitProxyResolverTest, AutodetectFailCustomFails2) {
Rules rules;
RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
RuleBasedProxyScriptFetcher fetcher(&rules);
+ DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
ProxyConfig config;
config.set_auto_detect(true);
@@ -402,7 +443,7 @@ TEST(InitProxyResolverTest, AutodetectFailCustomFails2) {
rules.AddFailParsingRule("http://custom/proxy.pac");
TestCompletionCallback callback;
- InitProxyResolver init(&resolver, &fetcher, NULL);
+ InitProxyResolver init(&resolver, &fetcher, &dhcp_fetcher, NULL);
EXPECT_EQ(kFailedParsing,
init.Init(config, base::TimeDelta(), NULL, &callback));
EXPECT_EQ(NULL, resolver.script_data());
@@ -415,6 +456,7 @@ TEST(InitProxyResolverTest, AutodetectFailCustomSuccess2_NoFetch) {
Rules rules;
RuleBasedProxyResolver resolver(&rules, false /*expects_pac_bytes*/);
RuleBasedProxyScriptFetcher fetcher(&rules);
+ DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
ProxyConfig config;
config.set_auto_detect(true);
@@ -424,7 +466,7 @@ TEST(InitProxyResolverTest, AutodetectFailCustomSuccess2_NoFetch) {
Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac");
TestCompletionCallback callback;
- InitProxyResolver init(&resolver, &fetcher, NULL);
+ InitProxyResolver init(&resolver, &fetcher, &dhcp_fetcher, NULL);
EXPECT_EQ(OK, init.Init(config, base::TimeDelta(), NULL, &callback));
EXPECT_EQ(rule.url, resolver.script_data()->url());
}
@@ -436,6 +478,7 @@ TEST(InitProxyResolverTest, CustomPacFails1_WithPositiveDelay) {
Rules rules;
RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
RuleBasedProxyScriptFetcher fetcher(&rules);
+ DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
ProxyConfig config;
config.set_pac_url(GURL("http://custom/proxy.pac"));
@@ -444,7 +487,7 @@ TEST(InitProxyResolverTest, CustomPacFails1_WithPositiveDelay) {
TestCompletionCallback callback;
CapturingNetLog log(CapturingNetLog::kUnbounded);
- InitProxyResolver init(&resolver, &fetcher, &log);
+ InitProxyResolver init(&resolver, &fetcher, &dhcp_fetcher, &log);
EXPECT_EQ(ERR_IO_PENDING,
init.Init(config, base::TimeDelta::FromMilliseconds(1),
NULL, &callback));
@@ -478,6 +521,7 @@ TEST(InitProxyResolverTest, CustomPacFails1_WithNegativeDelay) {
Rules rules;
RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
RuleBasedProxyScriptFetcher fetcher(&rules);
+ DoNothingDhcpProxyScriptFetcher dhcp_fetcher;
ProxyConfig config;
config.set_pac_url(GURL("http://custom/proxy.pac"));
@@ -486,7 +530,7 @@ TEST(InitProxyResolverTest, CustomPacFails1_WithNegativeDelay) {
TestCompletionCallback callback;
CapturingNetLog log(CapturingNetLog::kUnbounded);
- InitProxyResolver init(&resolver, &fetcher, &log);
+ InitProxyResolver init(&resolver, &fetcher, &dhcp_fetcher, &log);
EXPECT_EQ(kFailedDownloading,
init.Init(config, base::TimeDelta::FromSeconds(-5),
NULL, &callback));
@@ -507,5 +551,87 @@ TEST(InitProxyResolverTest, CustomPacFails1_WithNegativeDelay) {
entries, 3, NetLog::TYPE_INIT_PROXY_RESOLVER));
}
+class SynchronousSuccessDhcpFetcher : public DhcpProxyScriptFetcher {
+ public:
+ explicit SynchronousSuccessDhcpFetcher(const string16& expected_text)
+ : gurl_("http://dhcppac/"), expected_text_(expected_text) {
+ }
+
+ int Fetch(string16* utf16_text, CompletionCallback* callback) OVERRIDE {
+ *utf16_text = expected_text_;
+ return OK;
+ }
+
+ void Cancel() OVERRIDE {
+ }
+
+ const GURL& GetPacURL() const OVERRIDE {
+ return gurl_;
+ }
+
+ const string16& expected_text() const {
+ return expected_text_;
+ }
+
+ private:
+ GURL gurl_;
+ string16 expected_text_;
+};
+
+// All of the tests above that use InitProxyResolver have tested
+// failure to fetch a PAC file via DHCP configuration, so we now test
+// success at downloading and parsing, and then success at downloading,
+// failure at parsing.
+
+TEST(InitProxyResolverTest, AutodetectDhcpSuccess) {
+ Rules rules;
+ RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
+ RuleBasedProxyScriptFetcher fetcher(&rules);
+ SynchronousSuccessDhcpFetcher dhcp_fetcher(
+ WideToUTF16(L"http://bingo/!valid-script"));
+
+ ProxyConfig config;
+ config.set_auto_detect(true);
+
+ rules.AddSuccessRule("http://bingo/");
+ rules.AddFailDownloadRule("http://wpad/wpad.dat");
+
+ TestCompletionCallback callback;
+ ProxyConfig effective_config;
+ InitProxyResolver init(&resolver, &fetcher, &dhcp_fetcher, NULL);
+ EXPECT_EQ(OK, init.Init(
+ config, base::TimeDelta(), &effective_config, &callback));
+ EXPECT_EQ(dhcp_fetcher.expected_text(),
+ resolver.script_data()->utf16());
+
+ EXPECT_TRUE(effective_config.has_pac_url());
+ EXPECT_EQ(GURL("http://dhcppac/"), effective_config.pac_url());
+}
+
+TEST(InitProxyResolverTest, AutodetectDhcpFailParse) {
+ Rules rules;
+ RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
+ RuleBasedProxyScriptFetcher fetcher(&rules);
+ SynchronousSuccessDhcpFetcher dhcp_fetcher(
+ WideToUTF16(L"http://bingo/!invalid-script"));
+
+ ProxyConfig config;
+ config.set_auto_detect(true);
+
+ rules.AddFailParsingRule("http://bingo/");
+ rules.AddFailDownloadRule("http://wpad/wpad.dat");
+
+ TestCompletionCallback callback;
+ ProxyConfig effective_config;
+ InitProxyResolver init(&resolver, &fetcher, &dhcp_fetcher, NULL);
+ // Since there is fallback to DNS-based WPAD, the final error will be that
+ // it failed downloading, not that it failed parsing.
+ EXPECT_EQ(kFailedDownloading,
+ init.Init(config, base::TimeDelta(), &effective_config, &callback));
+ EXPECT_EQ(NULL, resolver.script_data());
+
+ EXPECT_FALSE(effective_config.has_pac_url());
+}
+
} // namespace
} // namespace net
diff --git a/net/proxy/mock_proxy_script_fetcher.cc b/net/proxy/mock_proxy_script_fetcher.cc
new file mode 100644
index 0000000..3e9b601
--- /dev/null
+++ b/net/proxy/mock_proxy_script_fetcher.cc
@@ -0,0 +1,55 @@
+// Copyright (c) 2011 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/mock_proxy_script_fetcher.h"
+
+#include "base/logging.h"
+#include "base/string16.h"
+#include "base/utf_string_conversions.h"
+#include "net/base/net_errors.h"
+
+namespace net {
+
+MockProxyScriptFetcher::MockProxyScriptFetcher()
+ : pending_request_callback_(NULL), pending_request_text_(NULL) {
+}
+
+// ProxyScriptFetcher implementation.
+int MockProxyScriptFetcher::Fetch(const GURL& url,
+ string16* text,
+ 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_text_ = text;
+ return ERR_IO_PENDING;
+}
+
+void MockProxyScriptFetcher::NotifyFetchCompletion(
+ int result, const std::string& ascii_text) {
+ DCHECK(has_pending_request());
+ *pending_request_text_ = ASCIIToUTF16(ascii_text);
+ CompletionCallback* callback = pending_request_callback_;
+ pending_request_callback_ = NULL;
+ callback->Run(result);
+}
+
+void MockProxyScriptFetcher::Cancel() {
+}
+
+URLRequestContext* MockProxyScriptFetcher::GetRequestContext() const {
+ return NULL;
+}
+
+const GURL& MockProxyScriptFetcher::pending_request_url() const {
+ return pending_request_url_;
+}
+
+bool MockProxyScriptFetcher::has_pending_request() const {
+ return pending_request_callback_ != NULL;
+}
+
+} // namespace net
diff --git a/net/proxy/mock_proxy_script_fetcher.h b/net/proxy/mock_proxy_script_fetcher.h
new file mode 100644
index 0000000..bd6ac42
--- /dev/null
+++ b/net/proxy/mock_proxy_script_fetcher.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2011 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_MOCK_PROXY_SCRIPT_FETCHER_H_
+#define NET_PROXY_MOCK_PROXY_SCRIPT_FETCHER_H_
+#pragma once
+
+#include "base/compiler_specific.h"
+#include "googleurl/src/gurl.h"
+#include "net/proxy/proxy_script_fetcher.h"
+
+#include <string>
+
+namespace net {
+
+class URLRequestContext;
+
+// A mock ProxyScriptFetcher. No result will be returned to the fetch client
+// until we call NotifyFetchCompletion() to set the results.
+class MockProxyScriptFetcher : public ProxyScriptFetcher {
+ public:
+ MockProxyScriptFetcher();
+
+ // ProxyScriptFetcher implementation.
+ virtual int Fetch(const GURL& url,
+ string16* text,
+ CompletionCallback* callback) OVERRIDE;
+ virtual void Cancel() OVERRIDE;
+ virtual URLRequestContext* GetRequestContext() const OVERRIDE;
+
+ void NotifyFetchCompletion(int result, const std::string& ascii_text);
+ const GURL& pending_request_url() const;
+ bool has_pending_request() const;
+
+ private:
+ GURL pending_request_url_;
+ CompletionCallback* pending_request_callback_;
+ string16* pending_request_text_;
+};
+
+} // namespace net
+
+#endif // NET_PROXY_MOCK_PROXY_SCRIPT_FETCHER_H_
diff --git a/net/proxy/proxy_script_fetcher.h b/net/proxy/proxy_script_fetcher.h
index c8cda24..d488375 100644
--- a/net/proxy/proxy_script_fetcher.h
+++ b/net/proxy/proxy_script_fetcher.h
@@ -52,7 +52,7 @@ class ProxyScriptFetcher {
// Returns the request context that this fetcher uses to issue downloads,
// or NULL.
- virtual URLRequestContext* GetRequestContext() = 0;
+ virtual URLRequestContext* GetRequestContext() const = 0;
};
} // namespace net
diff --git a/net/proxy/proxy_script_fetcher_impl.cc b/net/proxy/proxy_script_fetcher_impl.cc
index aabe340..c276c30 100644
--- a/net/proxy/proxy_script_fetcher_impl.cc
+++ b/net/proxy/proxy_script_fetcher_impl.cc
@@ -171,7 +171,7 @@ void ProxyScriptFetcherImpl::Cancel() {
ResetCurRequestState();
}
-URLRequestContext* ProxyScriptFetcherImpl::GetRequestContext() {
+URLRequestContext* ProxyScriptFetcherImpl::GetRequestContext() const {
return url_request_context_;
}
diff --git a/net/proxy/proxy_script_fetcher_impl.h b/net/proxy/proxy_script_fetcher_impl.h
index 419293f..8e9ca3b 100644
--- a/net/proxy/proxy_script_fetcher_impl.h
+++ b/net/proxy/proxy_script_fetcher_impl.h
@@ -45,9 +45,9 @@ class ProxyScriptFetcherImpl : public ProxyScriptFetcher,
// ProxyScriptFetcher methods:
virtual int Fetch(const GURL& url, string16* text,
- CompletionCallback* callback);
- virtual void Cancel();
- virtual URLRequestContext* GetRequestContext();
+ CompletionCallback* callback) OVERRIDE;
+ virtual void Cancel() OVERRIDE;
+ virtual URLRequestContext* GetRequestContext() const OVERRIDE;
// URLRequest::Delegate methods:
virtual void OnAuthRequired(URLRequest* request,
diff --git a/net/proxy/proxy_service.cc b/net/proxy/proxy_service.cc
index a134133..a16cb57 100644
--- a/net/proxy/proxy_service.cc
+++ b/net/proxy/proxy_service.cc
@@ -16,6 +16,7 @@
#include "net/base/net_errors.h"
#include "net/base/net_log.h"
#include "net/base/net_util.h"
+#include "net/proxy/dhcp_proxy_script_fetcher.h"
#include "net/proxy/init_proxy_resolver.h"
#include "net/proxy/multi_threaded_proxy_resolver.h"
#include "net/proxy/network_delegate_error_observer.h"
@@ -409,11 +410,13 @@ ProxyService* ProxyService::CreateUsingV8ProxyResolver(
ProxyConfigService* proxy_config_service,
size_t num_pac_threads,
ProxyScriptFetcher* proxy_script_fetcher,
+ DhcpProxyScriptFetcher* dhcp_proxy_script_fetcher,
HostResolver* host_resolver,
NetLog* net_log,
NetworkDelegate* network_delegate) {
DCHECK(proxy_config_service);
DCHECK(proxy_script_fetcher);
+ DCHECK(dhcp_proxy_script_fetcher);
DCHECK(host_resolver);
if (num_pac_threads == 0)
@@ -433,8 +436,9 @@ ProxyService* ProxyService::CreateUsingV8ProxyResolver(
ProxyService* proxy_service =
new ProxyService(proxy_config_service, proxy_resolver, net_log);
- // Configure PAC script downloads to be issued using |proxy_script_fetcher|.
- proxy_service->SetProxyScriptFetcher(proxy_script_fetcher);
+ // Configure fetchers to use for PAC script downloads and auto-detect.
+ proxy_service->SetProxyScriptFetchers(proxy_script_fetcher,
+ dhcp_proxy_script_fetcher);
return proxy_service;
}
@@ -774,11 +778,13 @@ int ProxyService::DidFinishResolvingProxy(ProxyInfo* result,
return result_code;
}
-void ProxyService::SetProxyScriptFetcher(
- ProxyScriptFetcher* proxy_script_fetcher) {
+void ProxyService::SetProxyScriptFetchers(
+ ProxyScriptFetcher* proxy_script_fetcher,
+ DhcpProxyScriptFetcher* dhcp_proxy_script_fetcher) {
DCHECK(CalledOnValidThread());
State previous_state = ResetProxyConfig(false);
proxy_script_fetcher_.reset(proxy_script_fetcher);
+ dhcp_proxy_script_fetcher_.reset(dhcp_proxy_script_fetcher);
if (previous_state != STATE_NONE)
ApplyProxyConfigIfAvailable();
}
@@ -927,7 +933,9 @@ void ProxyService::InitializeUsingLastFetchedConfig() {
current_state_ = STATE_WAITING_FOR_INIT_PROXY_RESOLVER;
init_proxy_resolver_.reset(
- new InitProxyResolver(resolver_.get(), proxy_script_fetcher_.get(),
+ new InitProxyResolver(resolver_.get(),
+ proxy_script_fetcher_.get(),
+ dhcp_proxy_script_fetcher_.get(),
net_log_));
// If we changed networks recently, we should delay running proxy auto-config.
diff --git a/net/proxy/proxy_service.h b/net/proxy/proxy_service.h
index f7856e5..cee7ef6 100644
--- a/net/proxy/proxy_service.h
+++ b/net/proxy/proxy_service.h
@@ -25,6 +25,7 @@ class MessageLoop;
namespace net {
+class DhcpProxyScriptFetcher;
class HostResolver;
class InitProxyResolver;
class NetworkDelegate;
@@ -98,10 +99,12 @@ class ProxyService : public NetworkChangeNotifier::IPAddressObserver,
// Call this method with a non-null |pac_request| to cancel the PAC request.
void CancelPacRequest(PacRequest* pac_request);
- // Sets the ProxyScriptFetcher dependency. This is needed if the ProxyResolver
- // is of type ProxyResolverWithoutFetch. ProxyService takes ownership of
- // |proxy_script_fetcher|.
- void SetProxyScriptFetcher(ProxyScriptFetcher* proxy_script_fetcher);
+ // Sets the ProxyScriptFetcher and DhcpProxyScriptFetcher dependencies. This
+ // is needed if the ProxyResolver is of type ProxyResolverWithoutFetch.
+ // ProxyService takes ownership of both objects.
+ void SetProxyScriptFetchers(
+ ProxyScriptFetcher* proxy_script_fetcher,
+ DhcpProxyScriptFetcher* dhcp_proxy_script_fetcher);
ProxyScriptFetcher* GetProxyScriptFetcher() const;
// Tells this ProxyService to start using a new ProxyConfigService to
@@ -161,6 +164,10 @@ class ProxyService : public NetworkChangeNotifier::IPAddressObserver,
// |proxy_script_fetcher| specifies the dependency to use for downloading
// any PAC scripts. The resulting ProxyService will take ownership of it.
//
+ // |dhcp_proxy_script_fetcher| specifies the dependency to use for attempting
+ // to retrieve the most appropriate PAC script configured in DHCP. The
+ // resulting ProxyService will take ownership of it.
+ //
// |host_resolver| points to the host resolving dependency the PAC script
// should use for any DNS queries. It must remain valid throughout the
// lifetime of the ProxyService.
@@ -174,6 +181,7 @@ class ProxyService : public NetworkChangeNotifier::IPAddressObserver,
ProxyConfigService* proxy_config_service,
size_t num_pac_threads,
ProxyScriptFetcher* proxy_script_fetcher,
+ DhcpProxyScriptFetcher* dhcp_proxy_script_fetcher,
HostResolver* host_resolver,
NetLog* net_log,
NetworkDelegate* network_delegate);
@@ -319,6 +327,11 @@ class ProxyService : public NetworkChangeNotifier::IPAddressObserver,
// external PAC script fetching.
scoped_ptr<ProxyScriptFetcher> proxy_script_fetcher_;
+ // The fetcher to use when attempting to download the most appropriate PAC
+ // script configured in DHCP, if any. Can be NULL if the ProxyResolver has
+ // no need for DHCP PAC script fetching.
+ scoped_ptr<DhcpProxyScriptFetcher> dhcp_proxy_script_fetcher_;
+
// Callback for when |init_proxy_resolver_| is done.
CompletionCallbackImpl<ProxyService> init_proxy_resolver_callback_;
diff --git a/net/proxy/proxy_service_unittest.cc b/net/proxy/proxy_service_unittest.cc
index f5ae3d7..aac8bc9 100644
--- a/net/proxy/proxy_service_unittest.cc
+++ b/net/proxy/proxy_service_unittest.cc
@@ -15,7 +15,9 @@
#include "net/base/net_log.h"
#include "net/base/net_log_unittest.h"
#include "net/base/test_completion_callback.h"
+#include "net/proxy/dhcp_proxy_script_fetcher.h"
#include "net/proxy/mock_proxy_resolver.h"
+#include "net/proxy/mock_proxy_script_fetcher.h"
#include "net/proxy/proxy_config_service.h"
#include "net/proxy/proxy_resolver.h"
#include "net/proxy/proxy_script_fetcher.h"
@@ -67,53 +69,6 @@ class MockProxyConfigService: public ProxyConfigService {
} // namespace
-// A mock ProxyScriptFetcher. No result will be returned to the fetch client
-// until we call NotifyFetchCompletion() to set the results.
-class MockProxyScriptFetcher : public ProxyScriptFetcher {
- public:
- MockProxyScriptFetcher()
- : pending_request_callback_(NULL), pending_request_text_(NULL) {
- }
-
- // ProxyScriptFetcher implementation.
- virtual int Fetch(const GURL& url,
- string16* text,
- 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_text_ = text;
- return ERR_IO_PENDING;
- }
-
- void NotifyFetchCompletion(int result, const std::string& ascii_text) {
- DCHECK(has_pending_request());
- *pending_request_text_ = ASCIIToUTF16(ascii_text);
- CompletionCallback* callback = pending_request_callback_;
- pending_request_callback_ = NULL;
- callback->Run(result);
- }
-
- virtual void Cancel() {}
-
- virtual URLRequestContext* GetRequestContext() { return NULL; }
-
- const GURL& pending_request_url() const {
- return pending_request_url_;
- }
-
- bool has_pending_request() const {
- return pending_request_callback_ != NULL;
- }
-
- private:
- GURL pending_request_url_;
- CompletionCallback* pending_request_callback_;
- string16* pending_request_text_;
-};
-
TEST(ProxyServiceTest, Direct) {
MockAsyncProxyResolver* resolver = new MockAsyncProxyResolver;
ProxyService service(new MockProxyConfigService(
@@ -449,7 +404,8 @@ TEST(ProxyServiceTest, ProxyResolverFailsParsingJavaScriptMandatoryPac) {
ProxyService service(config_service, resolver, NULL);
MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher;
- service.SetProxyScriptFetcher(fetcher);
+ DhcpProxyScriptFetcher* dhcp_fetcher = new DoNothingDhcpProxyScriptFetcher();
+ service.SetProxyScriptFetchers(fetcher, dhcp_fetcher);
// Start resolve request.
GURL url("http://www.google.com/");
@@ -1183,7 +1139,8 @@ TEST(ProxyServiceTest, InitialPACScriptDownload) {
ProxyService service(config_service, resolver, NULL);
MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher;
- service.SetProxyScriptFetcher(fetcher);
+ service.SetProxyScriptFetchers(fetcher,
+ new DoNothingDhcpProxyScriptFetcher());
// Start 3 requests.
@@ -1262,7 +1219,8 @@ TEST(ProxyServiceTest, ChangeScriptFetcherWhilePACDownloadInProgress) {
ProxyService service(config_service, resolver, NULL);
MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher;
- service.SetProxyScriptFetcher(fetcher);
+ service.SetProxyScriptFetchers(fetcher,
+ new DoNothingDhcpProxyScriptFetcher());
// Start 2 requests.
@@ -1290,7 +1248,8 @@ TEST(ProxyServiceTest, ChangeScriptFetcherWhilePACDownloadInProgress) {
// the initialization with the new fetcher.
fetcher = new MockProxyScriptFetcher;
- service.SetProxyScriptFetcher(fetcher);
+ service.SetProxyScriptFetchers(fetcher,
+ new DoNothingDhcpProxyScriptFetcher());
// Nothing has been sent to the resolver yet.
EXPECT_TRUE(resolver->pending_requests().empty());
@@ -1319,7 +1278,8 @@ TEST(ProxyServiceTest, CancelWhilePACFetching) {
ProxyService service(config_service, resolver, NULL);
MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher;
- service.SetProxyScriptFetcher(fetcher);
+ service.SetProxyScriptFetchers(fetcher,
+ new DoNothingDhcpProxyScriptFetcher());
// Start 3 requests.
ProxyInfo info1;
@@ -1410,7 +1370,8 @@ TEST(ProxyServiceTest, FallbackFromAutodetectToCustomPac) {
ProxyService service(config_service, resolver, NULL);
MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher;
- service.SetProxyScriptFetcher(fetcher);
+ service.SetProxyScriptFetchers(fetcher,
+ new DoNothingDhcpProxyScriptFetcher());
// Start 2 requests.
@@ -1480,7 +1441,8 @@ TEST(ProxyServiceTest, FallbackFromAutodetectToCustomPac2) {
ProxyService service(config_service, resolver, NULL);
MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher;
- service.SetProxyScriptFetcher(fetcher);
+ service.SetProxyScriptFetchers(fetcher,
+ new DoNothingDhcpProxyScriptFetcher());
// Start 2 requests.
@@ -1555,7 +1517,8 @@ TEST(ProxyServiceTest, FallbackFromAutodetectToCustomToManual) {
ProxyService service(config_service, resolver, NULL);
MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher;
- service.SetProxyScriptFetcher(fetcher);
+ service.SetProxyScriptFetchers(fetcher,
+ new DoNothingDhcpProxyScriptFetcher());
// Start 2 requests.
@@ -1612,7 +1575,8 @@ TEST(ProxyServiceTest, BypassDoesntApplyToPac) {
ProxyService service(config_service, resolver, NULL);
MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher;
- service.SetProxyScriptFetcher(fetcher);
+ service.SetProxyScriptFetchers(fetcher,
+ new DoNothingDhcpProxyScriptFetcher());
// Start 1 requests.
@@ -1679,7 +1643,8 @@ TEST(ProxyServiceTest, DeleteWhileInitProxyResolverHasOutstandingFetch) {
ProxyService service(config_service, resolver, NULL);
MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher;
- service.SetProxyScriptFetcher(fetcher);
+ service.SetProxyScriptFetchers(fetcher,
+ new DoNothingDhcpProxyScriptFetcher());
// Start 1 request.
@@ -1810,7 +1775,8 @@ TEST(ProxyServiceTest, NetworkChangeTriggersPacRefetch) {
ProxyService service(config_service, resolver, &log);
MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher;
- service.SetProxyScriptFetcher(fetcher);
+ service.SetProxyScriptFetchers(fetcher,
+ new DoNothingDhcpProxyScriptFetcher());
// Disable the "wait after IP address changes" hack, so this unit-test can
// complete quickly.