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

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: Fix issue found by unit testing on a slow machine. Created 9 years, 8 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
15 #include <windows.h>
16 #include <winsock2.h>
17 #include <dhcpcsdk.h>
18 #pragma comment(lib, "dhcpcsvc.lib")
19
cpu_(ooo_6.6-7.5) 2011/04/22 20:54:59 can this dhcp dll be delay-loaded?
Jói 2011/05/03 21:20:59 Done, please take a look to make sure I did it cor
20 namespace {
21
22 // Maximum amount of time to wait for response from the Win32 DHCP API.
23 const int kTimeoutMs = 2000;
24
25 } // namespace
26
27 namespace net {
28
29 DhcpProxyScriptAdapterFetcher::DhcpProxyScriptAdapterFetcher(
30 URLRequestContext* url_request_context)
31 : state_(STATE_START),
32 result_(ERR_IO_PENDING),
33 callback_(NULL),
34 ALLOW_THIS_IN_INITIALIZER_LIST(
35 script_fetcher_callback_(
36 this, &DhcpProxyScriptAdapterFetcher::OnFetcherDone)),
37 origin_loop_(base::MessageLoopProxy::CreateForCurrentThread()),
38 url_request_context_(url_request_context) {
39 }
40
41 DhcpProxyScriptAdapterFetcher::~DhcpProxyScriptAdapterFetcher() {
42 Cancel();
43 }
44
45 void DhcpProxyScriptAdapterFetcher::Fetch(
46 const std::string& adapter_name, CompletionCallback* callback) {
47 DCHECK(CalledOnValidThread());
48 DCHECK(state_ == STATE_START);
49 result_ = ERR_IO_PENDING;
50 pac_script_ = string16();
51 state_ = STATE_WAIT_DHCP;
52 callback_ = callback;
53
54 wait_timer_.Start(base::TimeDelta::FromMilliseconds(ImplGetTimeoutMs()),
55 this, &DhcpProxyScriptAdapterFetcher::OnTimeout);
56 bool succeeded = base::WorkerPool::PostTask(
57 FROM_HERE,
58 NewRunnableMethod(
59 this,
60 &DhcpProxyScriptAdapterFetcher::QueryDhcpOnWorkerThread,
61 adapter_name),
62 true);
63 DCHECK(succeeded);
64 }
65
66 void DhcpProxyScriptAdapterFetcher::Cancel() {
67 DCHECK(CalledOnValidThread());
68 callback_ = NULL;
69 wait_timer_.Stop();
70 if (script_fetcher_.get()) {
eroman 2011/04/21 05:22:48 nit: don't need this if, can just reset unconditio
Jói 2011/05/03 21:20:59 Done.
71 script_fetcher_.reset();
72 }
73
74 switch (state_) {
75 case STATE_WAIT_DHCP:
76 // Nothing to do here, we let the worker thread run to completion,
77 // the task it posts back when it completes will check the state.
78 break;
79 case STATE_WAIT_URL:
80 break;
81 case STATE_START:
82 case STATE_FINISH:
83 case STATE_CANCEL:
84 break;
85 }
86
87 if (state_ != STATE_FINISH) {
88 result_ = ERR_ABORTED;
89 state_ = STATE_CANCEL;
90 }
91 }
92
93 bool DhcpProxyScriptAdapterFetcher::DidFinish() const {
94 DCHECK(CalledOnValidThread());
95 return state_ == STATE_FINISH;
96 }
97
98 int DhcpProxyScriptAdapterFetcher::result() const {
99 DCHECK(CalledOnValidThread());
100 return result_;
101 }
102
103 string16 DhcpProxyScriptAdapterFetcher::pac_script() const {
104 DCHECK(CalledOnValidThread());
105 return pac_script_;
106 }
107
108 GURL DhcpProxyScriptAdapterFetcher::pac_url() const {
109 DCHECK(CalledOnValidThread());
110 return pac_url_;
111 }
112
113 void DhcpProxyScriptAdapterFetcher::QueryDhcpOnWorkerThread(
114 const std::string& adapter_name) {
115 std::string url = ImplGetPacURLFromDhcp(adapter_name);
116
117 bool succeeded = origin_loop_->PostTask(
118 FROM_HERE,
119 NewRunnableMethod(this,
120 &DhcpProxyScriptAdapterFetcher::OnQueryDhcpDone,
121 url));
122 DCHECK(succeeded);
123 }
124
125 void DhcpProxyScriptAdapterFetcher::OnQueryDhcpDone(
126 std::string url) {
127 DCHECK(CalledOnValidThread());
128 // Because we can't cancel the call to the Win32 API, we can expect
129 // it to finish while we are in a few different states. The expected
130 // one is WAIT_DHCP, but it could be in CANCEL if Cancel() was called,
131 // or FINISH if timeout occurred.
132 DCHECK(state_ == STATE_WAIT_DHCP || state_ == STATE_CANCEL ||
133 state_ == STATE_FINISH);
134 if (state_ != STATE_WAIT_DHCP)
135 return;
136
137 wait_timer_.Stop();
138
139 if (url.empty()) {
140 result_ = ERR_PAC_NOT_IN_DHCP;
141 TransitionToFinish();
142 } else {
143 state_ = STATE_WAIT_URL;
144 pac_url_ = GURL(url);
145 script_fetcher_.reset(ImplCreateScriptFetcher(pac_url_));
146 script_fetcher_->Fetch(&pac_script_, &script_fetcher_callback_);
147 }
148 }
149
150 void DhcpProxyScriptAdapterFetcher::OnTimeout() {
151 DCHECK(state_ == STATE_WAIT_DHCP);
152 result_ = ERR_TIMED_OUT;
153 TransitionToFinish();
154 }
155
156 void DhcpProxyScriptAdapterFetcher::OnFetcherDone(int result) {
157 DCHECK(CalledOnValidThread());
158 DCHECK(state_ == STATE_WAIT_URL || state_ == STATE_CANCEL);
159 if (state_ == STATE_CANCEL)
160 return;
161
162 // At this point, pac_script_ has already been written to.
163 script_fetcher_.reset();
164 result_ = result;
165 TransitionToFinish();
166 }
167
168 void DhcpProxyScriptAdapterFetcher::TransitionToFinish() {
169 DCHECK(state_ == STATE_WAIT_DHCP || state_ == STATE_WAIT_URL);
170 state_ = STATE_FINISH;
171 callback_->Run(result_);
172 callback_ = NULL;
173 }
174
175 std::string DhcpProxyScriptAdapterFetcher::ImplGetPacURLFromDhcp(
176 const std::string& adapter_name) {
177 return GetPacURLFromDhcp(adapter_name);
178 }
179
180 ProxyScriptFetcher* DhcpProxyScriptAdapterFetcher::ImplCreateScriptFetcher(
181 const GURL& url) {
182 URLProxyScriptFetcher* fetcher =
183 new ProxyScriptFetcherImpl(url_request_context_);
184 fetcher->SetURL(url);
185 return fetcher;
186 }
187
188 int DhcpProxyScriptAdapterFetcher::ImplGetTimeoutMs() const {
189 return kTimeoutMs;
190 }
191
192 std::string DhcpProxyScriptAdapterFetcher::GetPacURLFromDhcp(
193 const std::string& adapter_name) {
194 EnsureDhcpcsvcInit();
195
196 std::wstring adapter_name_wide = base::SysMultiByteToWide(adapter_name,
197 CP_ACP);
198
199 DHCPCAPI_PARAMS_ARRAY send_params = { 0, NULL };
200
201 BYTE option_data[] = { 1, 252 };
202 DHCPCAPI_PARAMS wpad_params = { 0 };
203 wpad_params.OptionId = 252;
204 wpad_params.IsVendor = FALSE; // Surprising, but intentional.
205
206 DHCPCAPI_PARAMS_ARRAY request_params = { 0 };
207 request_params.nParams = 1;
208 request_params.Params = &wpad_params;
209
210 // The maximum message size is typically 4096 bytes on Windows per
211 // http://support.microsoft.com/kb/321592
212 DWORD result_buffer_size = 4096;
213 scoped_ptr_malloc<BYTE> result_buffer;
214 int retry_count = 0;
215 DWORD res = NO_ERROR;
216 do {
217 result_buffer.reset(reinterpret_cast<BYTE*>(malloc(result_buffer_size)));
218
219 // Note that while the DHCPCAPI_REQUEST_SYNCHRONOUS flag seems to indicate
220 // there might be an asynchronous mode, there seems to be (at least in
221 // terms of well-documented use of this API) only a synchronous mode, with
222 // an optional "async notifications later if the option changes" mode.
223 // Even IE9, which we hope to emulate as IE is the most widely deployed
224 // previous implementation of the DHCP aspect of WPAD and the only one
225 // on Windows (Konqueror is the other, on Linux), uses this API with the
226 // synchronous flag. There seem to be several Microsoft Knowledge Base
227 // articles about calls to this function failing when other flags are used
228 // (e.g. http://support.microsoft.com/kb/885270) so we won't take any
229 // chances on non-standard, poorly documented usage.
230 res = ::DhcpRequestParams(DHCPCAPI_REQUEST_SYNCHRONOUS,
231 NULL,
232 const_cast<LPWSTR>(adapter_name_wide.c_str()),
233 NULL,
234 send_params, request_params,
235 result_buffer.get(), &result_buffer_size,
236 NULL);
237 ++retry_count;
238 } while (res == ERROR_MORE_DATA && retry_count <= 3);
239
240 if (res != NO_ERROR) {
241 NOTREACHED();
242 } else if (wpad_params.nBytesData) {
243 // The result should be ASCII, not wide character.
244 DCHECK(strlen(reinterpret_cast<const char*>(wpad_params.Data)) + 1 ==
245 wpad_params.nBytesData);
246 return std::string(reinterpret_cast<const char *>(wpad_params.Data));
247 }
248
249 return "";
250 }
251
252 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698