OLD | NEW |
---|---|
(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 | |
20 namespace { | |
21 | |
22 // Maximum amount of time to wait for response from the Win32 DHCP API. | |
23 const int kTimeoutMs = 2000; | |
eroman
2011/04/21 05:22:48
What is the rationale for 2seconds?
Jói
2011/04/21 15:57:38
This is mostly a guess. DHCP should normally be v
| |
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); | |
eroman
2011/04/21 05:22:48
nit: DCHECK_EQ()
Jói
2011/05/03 21:20:59
Done.
| |
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 if (script_fetcher_.get()) { | |
70 script_fetcher_.reset(); | |
71 } | |
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::pac_script() const { | |
103 DCHECK(CalledOnValidThread()); | |
104 return pac_script_; | |
105 } | |
106 | |
107 GURL DhcpProxyScriptAdapterFetcher::pac_url() 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) { | |
126 DCHECK(CalledOnValidThread()); | |
127 DCHECK(state_ == STATE_CANCEL || state_ == STATE_WAIT_DHCP); | |
128 if (state_ == STATE_CANCEL) | |
129 return; | |
130 | |
131 wait_timer_.Stop(); | |
132 | |
133 if (url.empty()) { | |
134 result_ = ERR_PAC_NOT_IN_DHCP; | |
135 TransitionToFinish(); | |
136 } else { | |
137 state_ = STATE_WAIT_URL; | |
138 pac_url_ = GURL(url); | |
eroman
2011/04/21 05:22:48
One possibility to consider is whether the URL str
Jói
2011/05/03 21:20:59
I now have
pac_url_ = GURL(url);
if (pac_url_.
| |
139 script_fetcher_.reset(ImplCreateScriptFetcher(pac_url_)); | |
140 script_fetcher_->Fetch(&pac_script_, &script_fetcher_callback_); | |
141 } | |
142 } | |
143 | |
144 void DhcpProxyScriptAdapterFetcher::OnTimeout() { | |
145 DCHECK(state_ == STATE_WAIT_DHCP); | |
146 result_ = ERR_TIMED_OUT; | |
147 TransitionToFinish(); | |
148 } | |
149 | |
150 void DhcpProxyScriptAdapterFetcher::OnFetcherDone(int result) { | |
151 DCHECK(CalledOnValidThread()); | |
152 DCHECK(state_ == STATE_WAIT_URL || state_ == STATE_CANCEL); | |
153 if (state_ == STATE_CANCEL) | |
154 return; | |
155 | |
156 // At this point, pac_script_ has already been written to. | |
157 script_fetcher_.reset(); | |
158 result_ = result; | |
159 TransitionToFinish(); | |
160 } | |
161 | |
162 void DhcpProxyScriptAdapterFetcher::TransitionToFinish() { | |
163 DCHECK(state_ == STATE_WAIT_DHCP || state_ == STATE_WAIT_URL); | |
164 state_ = STATE_FINISH; | |
165 callback_->Run(result_); | |
166 callback_ = NULL; | |
167 } | |
168 | |
169 std::string DhcpProxyScriptAdapterFetcher::ImplGetPacURLFromDhcp( | |
170 const std::string& adapter_name) { | |
171 return GetPacURLFromDhcp(adapter_name); | |
172 } | |
173 | |
174 ProxyScriptFetcher* DhcpProxyScriptAdapterFetcher::ImplCreateScriptFetcher( | |
175 const GURL& url) { | |
176 URLProxyScriptFetcher* fetcher = | |
177 new ProxyScriptFetcherImpl(url_request_context_); | |
eroman
2011/04/21 05:22:48
This isn't really desirable to be instantiating sp
Jói
2011/04/21 15:57:38
I don't see the benefit of that if we are only try
| |
178 fetcher->SetURL(url); | |
179 return fetcher; | |
180 } | |
181 | |
182 int DhcpProxyScriptAdapterFetcher::ImplGetTimeoutMs() const { | |
eroman
2011/04/21 05:22:48
I don't much care for the use of virtuals. I would
Jói
2011/05/03 21:20:59
As discussed, I changed InitProxyResolver and its
| |
183 return kTimeoutMs; | |
184 } | |
185 | |
186 std::string DhcpProxyScriptAdapterFetcher::GetPacURLFromDhcp( | |
187 const std::string& adapter_name) { | |
188 EnsureDhcpcsvcInit(); | |
189 | |
190 std::wstring adapter_name_wide = base::SysMultiByteToWide(adapter_name, | |
191 CP_ACP); | |
192 | |
193 DHCPCAPI_PARAMS_ARRAY send_params = { 0, NULL }; | |
194 | |
195 BYTE option_data[] = { 1, 252 }; | |
196 DHCPCAPI_PARAMS wpad_params = { 0 }; | |
197 wpad_params.OptionId = 252; | |
198 wpad_params.IsVendor = FALSE; // Surprising, but intentional. | |
199 | |
200 DHCPCAPI_PARAMS_ARRAY request_params = { 0 }; | |
201 request_params.nParams = 1; | |
202 request_params.Params = &wpad_params; | |
203 | |
204 // The maximum message size is typically 4096 bytes on Windows per | |
205 // http://support.microsoft.com/kb/321592 | |
206 DWORD result_buffer_size = 4096; | |
207 scoped_ptr_malloc<BYTE> result_buffer; | |
208 int retry_count = 0; | |
209 DWORD res = NO_ERROR; | |
210 do { | |
211 result_buffer.reset(reinterpret_cast<BYTE*>(malloc(result_buffer_size))); | |
212 | |
213 // Note that while the DHCPCAPI_REQUEST_SYNCHRONOUS flag seems to indicate | |
214 // there might be an asynchronous mode, there seems to be (at least in | |
215 // terms of well-documented use of this API) only a synchronous mode, with | |
216 // an optional "async notifications later if the option changes" mode. | |
217 // Even IE9, which we hope to emulate as IE is the most widely deployed | |
218 // previous implementation of the DHCP aspect of WPAD and the only one | |
219 // on Windows (Konqueror is the other, on Linux), uses this API with the | |
220 // synchronous flag. There seem to be several Microsoft Knowledge Base | |
221 // articles about calls to this function failing when other flags are used | |
222 // (e.g. http://support.microsoft.com/kb/885270) so we won't take any | |
223 // chances on non-standard, poorly documented usage. | |
eroman
2011/04/21 05:22:48
Nice comment block!
Jói
2011/05/03 21:20:59
thx
| |
224 res = ::DhcpRequestParams(DHCPCAPI_REQUEST_SYNCHRONOUS, | |
225 NULL, | |
226 const_cast<LPWSTR>(adapter_name_wide.c_str()), | |
227 NULL, | |
228 send_params, request_params, | |
229 result_buffer.get(), &result_buffer_size, | |
230 NULL); | |
231 ++retry_count; | |
232 } while (res == ERROR_MORE_DATA && retry_count <= 3); | |
233 | |
234 if (res != NO_ERROR) { | |
235 NOTREACHED(); | |
eroman
2011/04/21 05:22:48
nit: I suggest adding return "" here, and removing
Jói
2011/05/03 21:20:59
It's an else if; there can be NO_ERROR and wpad_pa
| |
236 } else if (wpad_params.nBytesData) { | |
237 // The result should be ASCII, not wide character. | |
238 DCHECK(strlen(reinterpret_cast<const char*>(wpad_params.Data)) + 1 == | |
eroman
2011/04/21 05:22:48
nit: DCHECK_EQ()
Jói
2011/05/03 21:20:59
Done.
| |
239 wpad_params.nBytesData); | |
240 return std::string(reinterpret_cast<const char *>(wpad_params.Data)); | |
241 } | |
242 | |
243 return ""; | |
244 } | |
245 | |
246 } // namespace net | |
OLD | NEW |