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

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

Powered by Google App Engine
This is Rietveld 408576698