Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(580)

Unified Diff: net/proxy/dhcp_proxy_script_adapter_fetcher_win.cc

Issue 6831025: Adds support for the DHCP portion of the WPAD (proxy auto-discovery) protocol. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Isolate worker thread to separate object. Refcount context. Fix mandatory PAC. Created 9 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: net/proxy/dhcp_proxy_script_adapter_fetcher_win.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 0000000000000000000000000000000000000000..86fdd63921988b9c915eb4f129b1e8d22d4e8c50
--- /dev/null
+++ b/net/proxy/dhcp_proxy_script_adapter_fetcher_win.cc
@@ -0,0 +1,278 @@
+// 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();
+}
+
+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() {
eroman 2011/05/17 04:39:39 This is a one-shot class right? (I am guessing so,
Jói 2011/05/17 15:38:35 Correct, this is documented on Fetch(): // You
+ 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()) {
+}
+
+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

Powered by Google App Engine
This is Rietveld 408576698