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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/proxy/dhcp_proxy_script_adapter_fetcher_win.h"
6
7 #include "base/message_loop_proxy.h"
8 #include "base/sys_string_conversions.h"
9 #include "base/task.h"
10 #include "base/threading/worker_pool.h"
11 #include "base/time.h"
12 #include "net/base/net_errors.h"
13 #include "net/proxy/dhcpcsvc_init_win.h"
14 #include "net/proxy/proxy_script_fetcher_impl.h"
15 #include "net/url_request/url_request_context.h"
16
17 #include <windows.h>
18 #include <winsock2.h>
19 #include <dhcpcsdk.h>
20 #pragma comment(lib, "dhcpcsvc.lib")
21
22 namespace {
23
24 // Maximum amount of time to wait for response from the Win32 DHCP API.
25 const int kTimeoutMs = 2000;
26
27 } // namespace
28
29 namespace net {
30
31 DhcpProxyScriptAdapterFetcher::DhcpProxyScriptAdapterFetcher(
32 URLRequestContext* url_request_context)
33 : state_(STATE_START),
34 result_(ERR_IO_PENDING),
35 callback_(NULL),
36 ALLOW_THIS_IN_INITIALIZER_LIST(
37 script_fetcher_callback_(
38 this, &DhcpProxyScriptAdapterFetcher::OnFetcherDone)),
39 url_request_context_(url_request_context) {
40 }
41
42 DhcpProxyScriptAdapterFetcher::~DhcpProxyScriptAdapterFetcher() {
43 Cancel();
44 }
45
46 void DhcpProxyScriptAdapterFetcher::Fetch(
47 const std::string& adapter_name, CompletionCallback* callback) {
48 DCHECK(CalledOnValidThread());
49 DCHECK_EQ(state_, STATE_START);
50 result_ = ERR_IO_PENDING;
51 pac_script_ = string16();
52 state_ = STATE_WAIT_DHCP;
53 callback_ = callback;
54
55 wait_timer_.Start(ImplGetTimeout(),
56 this, &DhcpProxyScriptAdapterFetcher::OnTimeout);
57 worker_thread_ = ImplCreateWorkerThread(AsWeakPtr());
58 worker_thread_->Start(adapter_name);
59 }
60
61 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
62 DCHECK(CalledOnValidThread());
63 callback_ = NULL;
64 wait_timer_.Stop();
65 script_fetcher_.reset();
66
67 switch (state_) {
68 case STATE_WAIT_DHCP:
69 // Nothing to do here, we let the worker thread run to completion,
70 // the task it posts back when it completes will check the state.
71 break;
72 case STATE_WAIT_URL:
73 break;
74 case STATE_START:
75 case STATE_FINISH:
76 case STATE_CANCEL:
77 break;
78 }
79
80 if (state_ != STATE_FINISH) {
81 result_ = ERR_ABORTED;
82 state_ = STATE_CANCEL;
83 }
84 }
85
86 bool DhcpProxyScriptAdapterFetcher::DidFinish() const {
87 DCHECK(CalledOnValidThread());
88 return state_ == STATE_FINISH;
89 }
90
91 int DhcpProxyScriptAdapterFetcher::GetResult() const {
92 DCHECK(CalledOnValidThread());
93 return result_;
94 }
95
96 string16 DhcpProxyScriptAdapterFetcher::GetPacScript() const {
97 DCHECK(CalledOnValidThread());
98 return pac_script_;
99 }
100
101 GURL DhcpProxyScriptAdapterFetcher::GetPacURL() const {
102 DCHECK(CalledOnValidThread());
103 return pac_url_;
104 }
105
106 DhcpProxyScriptAdapterFetcher::WorkerThread::WorkerThread(
107 const base::WeakPtr<DhcpProxyScriptAdapterFetcher>& owner)
108 : owner_(owner),
109 origin_loop_(base::MessageLoopProxy::CreateForCurrentThread()) {
110 }
111
112 void DhcpProxyScriptAdapterFetcher::WorkerThread::Start(
113 const std::string& adapter_name) {
114 bool succeeded = base::WorkerPool::PostTask(
115 FROM_HERE,
116 NewRunnableMethod(
117 this,
118 &DhcpProxyScriptAdapterFetcher::WorkerThread::ThreadFunc,
119 adapter_name),
120 true);
121 DCHECK(succeeded);
122 }
123
124 void DhcpProxyScriptAdapterFetcher::WorkerThread::ThreadFunc(
125 const std::string& adapter_name) {
126 std::string url = ImplGetPacURLFromDhcp(adapter_name);
127
128 bool succeeded = origin_loop_->PostTask(
129 FROM_HERE,
130 NewRunnableMethod(
131 this,
132 &DhcpProxyScriptAdapterFetcher::WorkerThread::OnThreadDone,
133 url));
134 DCHECK(succeeded);
135 }
136
137 void DhcpProxyScriptAdapterFetcher::WorkerThread::OnThreadDone(
138 const std::string& url) {
139 DCHECK(thread_checker_.CalledOnValidThread());
140 if (owner_)
141 owner_->OnQueryDhcpDone(url);
142 }
143
144 std::string
145 DhcpProxyScriptAdapterFetcher::WorkerThread::ImplGetPacURLFromDhcp(
146 const std::string& adapter_name) {
147 return DhcpProxyScriptAdapterFetcher::GetPacURLFromDhcp(adapter_name);
148 }
149
150 void DhcpProxyScriptAdapterFetcher::OnQueryDhcpDone(
151 const std::string& url) {
152 DCHECK(CalledOnValidThread());
153 // Because we can't cancel the call to the Win32 API, we can expect
154 // it to finish while we are in a few different states. The expected
155 // one is WAIT_DHCP, but it could be in CANCEL if Cancel() was called,
156 // or FINISH if timeout occurred.
157 DCHECK(state_ == STATE_WAIT_DHCP || state_ == STATE_CANCEL ||
158 state_ == STATE_FINISH);
159 if (state_ != STATE_WAIT_DHCP)
160 return;
161
162 wait_timer_.Stop();
163
164 pac_url_ = GURL(url);
165 if (pac_url_.is_empty() || !pac_url_.is_valid()) {
166 result_ = ERR_PAC_NOT_IN_DHCP;
167 TransitionToFinish();
168 } else {
169 state_ = STATE_WAIT_URL;
170 script_fetcher_.reset(ImplCreateScriptFetcher());
171 script_fetcher_->Fetch(pac_url_, &pac_script_, &script_fetcher_callback_);
172 }
173 }
174
175 void DhcpProxyScriptAdapterFetcher::OnTimeout() {
176 DCHECK_EQ(state_, STATE_WAIT_DHCP);
177 result_ = ERR_TIMED_OUT;
178 TransitionToFinish();
179 }
180
181 void DhcpProxyScriptAdapterFetcher::OnFetcherDone(int result) {
182 DCHECK(CalledOnValidThread());
183 DCHECK(state_ == STATE_WAIT_URL || state_ == STATE_CANCEL);
184 if (state_ == STATE_CANCEL)
185 return;
186
187 // At this point, pac_script_ has already been written to.
188 script_fetcher_.reset();
189 result_ = result;
190 TransitionToFinish();
191 }
192
193 void DhcpProxyScriptAdapterFetcher::TransitionToFinish() {
194 DCHECK(state_ == STATE_WAIT_DHCP || state_ == STATE_WAIT_URL);
195 state_ = STATE_FINISH;
196 callback_->Run(result_);
197 callback_ = NULL;
198 }
199
200 ProxyScriptFetcher* DhcpProxyScriptAdapterFetcher::ImplCreateScriptFetcher() {
201 return new ProxyScriptFetcherImpl(url_request_context_);
202 }
203
204 DhcpProxyScriptAdapterFetcher::WorkerThread*
205 DhcpProxyScriptAdapterFetcher::ImplCreateWorkerThread(
206 const base::WeakPtr<DhcpProxyScriptAdapterFetcher>& owner) {
207 return new WorkerThread(owner);
208 }
209
210 base::TimeDelta DhcpProxyScriptAdapterFetcher::ImplGetTimeout() const {
211 return base::TimeDelta::FromMilliseconds(kTimeoutMs);
212 }
213
214 // static
215 std::string DhcpProxyScriptAdapterFetcher::GetPacURLFromDhcp(
216 const std::string& adapter_name) {
217 EnsureDhcpcsvcInit();
218
219 std::wstring adapter_name_wide = base::SysMultiByteToWide(adapter_name,
220 CP_ACP);
221
222 DHCPCAPI_PARAMS_ARRAY send_params = { 0, NULL };
223
224 BYTE option_data[] = { 1, 252 };
225 DHCPCAPI_PARAMS wpad_params = { 0 };
226 wpad_params.OptionId = 252;
227 wpad_params.IsVendor = FALSE; // Surprising, but intentional.
228
229 DHCPCAPI_PARAMS_ARRAY request_params = { 0 };
230 request_params.nParams = 1;
231 request_params.Params = &wpad_params;
232
233 // The maximum message size is typically 4096 bytes on Windows per
234 // http://support.microsoft.com/kb/321592
235 DWORD result_buffer_size = 4096;
236 scoped_ptr_malloc<BYTE> result_buffer;
237 int retry_count = 0;
238 DWORD res = NO_ERROR;
239 do {
240 result_buffer.reset(reinterpret_cast<BYTE*>(malloc(result_buffer_size)));
241
242 // Note that while the DHCPCAPI_REQUEST_SYNCHRONOUS flag seems to indicate
243 // there might be an asynchronous mode, there seems to be (at least in
244 // terms of well-documented use of this API) only a synchronous mode, with
245 // an optional "async notifications later if the option changes" mode.
246 // Even IE9, which we hope to emulate as IE is the most widely deployed
247 // previous implementation of the DHCP aspect of WPAD and the only one
248 // on Windows (Konqueror is the other, on Linux), uses this API with the
249 // synchronous flag. There seem to be several Microsoft Knowledge Base
250 // articles about calls to this function failing when other flags are used
251 // (e.g. http://support.microsoft.com/kb/885270) so we won't take any
252 // chances on non-standard, poorly documented usage.
253 res = ::DhcpRequestParams(DHCPCAPI_REQUEST_SYNCHRONOUS,
254 NULL,
255 const_cast<LPWSTR>(adapter_name_wide.c_str()),
256 NULL,
257 send_params, request_params,
258 result_buffer.get(), &result_buffer_size,
259 NULL);
260 ++retry_count;
261 } while (res == ERROR_MORE_DATA && retry_count <= 3);
262
263 if (res != NO_ERROR) {
264 NOTREACHED();
265 } else if (wpad_params.nBytesData) {
266 // The result should be ASCII, not wide character.
267 DCHECK_EQ(strlen(reinterpret_cast<const char*>(wpad_params.Data)) + 1,
268 wpad_params.nBytesData);
269 // Return only up to the first null in case of embedded NULLs; if the
270 // server is giving us back a buffer with embedded NULLs, something is
271 // broken anyway.
272 return std::string(reinterpret_cast<const char *>(wpad_params.Data));
273 }
274
275 return "";
276 }
277
278 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698