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

Side by Side Diff: net/proxy/dhcp_proxy_script_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: Add timeout on Win32 DHCP API. 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_fetcher_win.h"
6
7 #include "net/base/net_errors.h"
8 #include "net/proxy/dhcp_proxy_script_adapter_fetcher_win.h"
9
10 #include <winsock2.h>
11 #include <iphlpapi.h>
12 #pragma comment(lib, "iphlpapi.lib")
13
14 namespace {
15
16 // How long to wait at maximum after we get results (a PAC file or
17 // knowledge that no PAC file is configured) from whichever network
18 // adapter finishes first.
19 const int kMaxWaitAfterFirstResultMs = 400;
20
21 } // namespace
22
23 namespace net {
24
25 WindowsDhcpProxyScriptFetcher::WindowsDhcpProxyScriptFetcher(
26 URLRequestContext* url_request_context)
27 : state_(STATE_START),
28 ALLOW_THIS_IN_INITIALIZER_LIST(fetcher_callback_(
29 this, &WindowsDhcpProxyScriptFetcher::OnFetcherDone)),
30 num_pending_fetchers_(0),
31 url_request_context_(url_request_context) {
32 DCHECK(url_request_context_);
33 }
34
35 WindowsDhcpProxyScriptFetcher::~WindowsDhcpProxyScriptFetcher() {
36 Cancel();
37 }
38
39 int WindowsDhcpProxyScriptFetcher::Fetch(string16* utf16_text,
40 CompletionCallback* callback) {
41 DCHECK(CalledOnValidThread());
42 if (state_ != STATE_START && state_ != STATE_DONE) {
43 NOTREACHED();
44 return ERR_UNEXPECTED;
45 }
46
47 std::set<std::string> adapter_names;
48 if (!ImplGetCandidateAdapterNames(&adapter_names)) {
eroman 2011/04/21 05:22:48 Is this a fast operation? (in other words, is it O
Jói 2011/04/21 15:57:38 I believe so. It calls the GetAdaptersAddresses W
49 return ERR_UNEXPECTED;
50 }
51 if (adapter_names.empty()) {
52 *utf16_text = L"";
eroman 2011/04/21 05:22:48 no need to set this.
Jói 2011/05/03 21:20:59 Done.
53 return ERR_PAC_NOT_IN_DHCP;
54 }
55
56 state_ = STATE_NO_RESULTS;
57
58 client_callback_ = callback;
59 destination_string_ = utf16_text;
60
61 for (std::set<std::string>::iterator it = adapter_names.begin();
62 it != adapter_names.end();
63 ++it) {
64 scoped_refptr<DhcpProxyScriptAdapterFetcher> fetcher(
65 ImplCreateAdapterFetcher());
66 fetcher->Fetch(*it, &fetcher_callback_);
67 fetchers_.push_back(fetcher.get());
68 }
69 num_pending_fetchers_ = fetchers_.size();
70
71 return ERR_IO_PENDING;
72 }
73
74 void WindowsDhcpProxyScriptFetcher::Cancel() {
75 DCHECK(CalledOnValidThread());
76 CancelImpl(true);
77 }
78
79 URLRequestContext* WindowsDhcpProxyScriptFetcher::GetRequestContext() const {
80 DCHECK(CalledOnValidThread());
81 return url_request_context_;
82 }
83
84 std::string WindowsDhcpProxyScriptFetcher::GetFetcherName() const {
85 DCHECK(CalledOnValidThread());
86 return "win";
87 }
88
89 GURL WindowsDhcpProxyScriptFetcher::GetPacURL() const {
90 DCHECK(CalledOnValidThread());
91 DCHECK(state_ == STATE_DONE);
92
93 return pac_url_;
94 }
95
96 void WindowsDhcpProxyScriptFetcher::CancelImpl(bool clear_fetchers) {
97 if (state_ != STATE_DONE) {
98 wait_timer_.Stop();
99 state_ = STATE_DONE;
100
101 for (FetcherList::iterator it = fetchers_.begin();
102 it != fetchers_.end();
103 ++it) {
104 (*it)->Cancel();
105 }
106
107 if (clear_fetchers) {
108 fetchers_.clear();
109 }
110 }
111 }
112
113 void WindowsDhcpProxyScriptFetcher::OnFetcherDone(int result) {
114 DCHECK(state_ == STATE_NO_RESULTS || state_ == STATE_SOME_RESULTS);
115
116 if (--num_pending_fetchers_ == 0) {
117 TransitionToDone();
118 return;
119 }
120
121 // If the only pending adapters are those less preferred than one
122 // with a valid PAC script, we do not need to wait any longer.
123 for (FetcherList::iterator it = fetchers_.begin();
124 it != fetchers_.end();
125 ++it) {
126 bool did_finish = (*it)->DidFinish();
127 int result = (*it)->result();
128 if (did_finish && result == OK) {
129 TransitionToDone();
130 return;
131 }
132 if (!did_finish || result != ERR_PAC_NOT_IN_DHCP) {
133 break;
134 }
135 }
136
137 // Once we have a single result, we set a maximum on how long to wait
138 // for the rest of the results.
139 if (state_ == STATE_NO_RESULTS) {
140 state_ = STATE_SOME_RESULTS;
141 wait_timer_.Start(
eroman 2011/04/21 05:22:48 isn't there another timer already?
Jói 2011/05/03 21:20:59 There is a separate timer in the AdapterFetcher.
142 base::TimeDelta::FromMilliseconds(ImplGetMaxWaitMs()),
143 this, &WindowsDhcpProxyScriptFetcher::OnWaitTimer);
144 }
145 }
146
147 void WindowsDhcpProxyScriptFetcher::OnWaitTimer() {
148 DCHECK(state_ == STATE_SOME_RESULTS);
149 TransitionToDone();
150 }
151
152 void WindowsDhcpProxyScriptFetcher::TransitionToDone() {
153 DCHECK(state_ == STATE_NO_RESULTS || state_ == STATE_SOME_RESULTS);
154
155 // This does not alter state for fetchers that are already done.
156 CancelImpl(false);
157
158 // Should have returned immediately at Fetch() if no adapters to check.
159 DCHECK(!fetchers_.empty());
160
161 // Scan twice for the result; once through the whole list for success,
162 // then if no success, return result for most preferred network adapter,
163 // preferring "real" network errors to the ERR_PAC_NOT_IN_DHCP error.
164 // Default to ERR_ABORTED if no fetcher completed.
165 int result = ERR_ABORTED;
166 for (FetcherList::iterator it = fetchers_.begin();
167 it != fetchers_.end();
168 ++it) {
169 if ((*it)->DidFinish() && (*it)->result() == OK) {
170 result = OK;
171 *destination_string_ = (*it)->pac_script();
172 pac_url_ = (*it)->pac_url();
173 break;
174 }
175 }
176 if (result != OK) {
177 destination_string_->clear();
178 for (FetcherList::iterator it = fetchers_.begin();
179 it != fetchers_.end();
180 ++it) {
181 if ((*it)->DidFinish()) {
182 result = (*it)->result();
183 if (result != ERR_PAC_NOT_IN_DHCP) {
184 break;
185 }
186 }
187 }
188 }
189
190 client_callback_->Run(result);
191 fetchers_.clear();
192 state_ = STATE_DONE;
193 }
194
195 DhcpProxyScriptAdapterFetcher*
196 WindowsDhcpProxyScriptFetcher::ImplCreateAdapterFetcher() {
197 return new DhcpProxyScriptAdapterFetcher(url_request_context_);
198 }
199
200 bool WindowsDhcpProxyScriptFetcher::ImplGetCandidateAdapterNames(
201 std::set<std::string>* adapter_names) {
202 return GetCandidateAdapterNames(adapter_names);
203 }
204
205 int WindowsDhcpProxyScriptFetcher::ImplGetMaxWaitMs() {
206 return kMaxWaitAfterFirstResultMs;
207 }
208
209
210 bool WindowsDhcpProxyScriptFetcher::GetCandidateAdapterNames(
211 std::set<std::string>* adapter_names) {
212 DCHECK(adapter_names);
213
214 // The GetAdaptersAddresses MSDN page recommends using a size of 15000 to
215 // avoid reallocation.
216 ULONG adapters_size = 15000;
217 scoped_ptr_malloc<IP_ADAPTER_ADDRESSES> adapters;
218 ULONG error = ERROR_SUCCESS;
219 int num_tries = 0;
220 do {
221 adapters.reset(
222 reinterpret_cast<IP_ADAPTER_ADDRESSES*>(malloc(adapters_size)));
223 // Return only unicast addresses, and skip information we do not need.
224 error = GetAdaptersAddresses(AF_UNSPEC,
225 GAA_FLAG_SKIP_ANYCAST |
226 GAA_FLAG_SKIP_MULTICAST |
227 GAA_FLAG_SKIP_DNS_SERVER |
228 GAA_FLAG_SKIP_FRIENDLY_NAME,
229 NULL,
230 adapters.get(),
231 &adapters_size);
232 ++num_tries;
233 } while (error == ERROR_BUFFER_OVERFLOW && num_tries <= 3);
234
235 if (error == ERROR_NO_DATA) {
236 // There are no adapters that we care about.
237 return true;
238 }
239
240 if (error != ERROR_SUCCESS) {
241 NOTREACHED();
242 return false;
243 }
244
245 IP_ADAPTER_ADDRESSES* adapter = NULL;
246 for (adapter = adapters.get(); adapter; adapter = adapter->Next) {
247 if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK)
248 continue;
249 if ((adapter->Flags & IP_ADAPTER_DHCP_ENABLED) == 0)
250 continue;
251
252 DCHECK(adapter->AdapterName);
253 adapter_names->insert(adapter->AdapterName);
254 }
255
256 return true;
257 }
258
259 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698