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

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: 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_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)) {
49 return ERR_UNEXPECTED;
50 }
51 if (adapter_names.empty()) {
52 return ERR_PAC_NOT_IN_DHCP;
53 }
54
55 state_ = STATE_NO_RESULTS;
56
57 client_callback_ = callback;
58 destination_string_ = utf16_text;
59
60 for (std::set<std::string>::iterator it = adapter_names.begin();
61 it != adapter_names.end();
62 ++it) {
63 DhcpProxyScriptAdapterFetcher* fetcher(ImplCreateAdapterFetcher());
64 fetcher->Fetch(*it, &fetcher_callback_);
65 fetchers_.push_back(fetcher);
66 }
67 num_pending_fetchers_ = fetchers_.size();
68
69 return ERR_IO_PENDING;
70 }
71
72 void WindowsDhcpProxyScriptFetcher::Cancel() {
73 DCHECK(CalledOnValidThread());
74 CancelImpl(true);
75 }
76
77 std::string WindowsDhcpProxyScriptFetcher::GetFetcherName() const {
78 DCHECK(CalledOnValidThread());
79 return "win";
80 }
81
82 const GURL& WindowsDhcpProxyScriptFetcher::GetPacURL() const {
83 DCHECK(CalledOnValidThread());
84 DCHECK(state_ == STATE_DONE);
eroman 2011/05/17 04:39:39 DCHECK_EQ ?
Jói 2011/05/17 15:38:35 Done.
85
86 return pac_url_;
87 }
88
89 void WindowsDhcpProxyScriptFetcher::CancelImpl(bool clear_fetchers) {
90 if (state_ != STATE_DONE) {
91 wait_timer_.Stop();
92 state_ = STATE_DONE;
93
94 for (FetcherVector::iterator it = fetchers_.begin();
95 it != fetchers_.end();
96 ++it) {
97 (*it)->Cancel();
98 }
99
100 if (clear_fetchers) {
101 fetchers_.reset();
102 }
103 }
104 }
105
106 void WindowsDhcpProxyScriptFetcher::OnFetcherDone(int result) {
107 DCHECK(state_ == STATE_NO_RESULTS || state_ == STATE_SOME_RESULTS);
108
109 if (--num_pending_fetchers_ == 0) {
110 TransitionToDone();
111 return;
112 }
113
114 // If the only pending adapters are those less preferred than one
115 // with a valid PAC script, we do not need to wait any longer.
116 for (FetcherVector::iterator it = fetchers_.begin();
117 it != fetchers_.end();
118 ++it) {
119 bool did_finish = (*it)->DidFinish();
120 int result = (*it)->GetResult();
121 if (did_finish && result == OK) {
122 TransitionToDone();
123 return;
124 }
125 if (!did_finish || result != ERR_PAC_NOT_IN_DHCP) {
126 break;
127 }
128 }
129
130 // Once we have a single result, we set a maximum on how long to wait
131 // for the rest of the results.
132 if (state_ == STATE_NO_RESULTS) {
133 state_ = STATE_SOME_RESULTS;
134 wait_timer_.Start(
135 base::TimeDelta::FromMilliseconds(ImplGetMaxWaitMs()),
136 this, &WindowsDhcpProxyScriptFetcher::OnWaitTimer);
137 }
138 }
139
140 void WindowsDhcpProxyScriptFetcher::OnWaitTimer() {
141 DCHECK_EQ(state_, STATE_SOME_RESULTS);
142 TransitionToDone();
143 }
144
145 void WindowsDhcpProxyScriptFetcher::TransitionToDone() {
146 DCHECK(state_ == STATE_NO_RESULTS || state_ == STATE_SOME_RESULTS);
147
148 // This does not alter state for fetchers that are already done.
149 CancelImpl(false);
eroman 2011/05/17 04:39:39 I don't think we need CancelImpl(false). Can't you
Jói 2011/05/17 15:38:35 By golly, you're right :) Removed the CancelImpl
150
151 // Should have returned immediately at Fetch() if no adapters to check.
152 DCHECK(!fetchers_.empty());
153
154 // Scan twice for the result; once through the whole list for success,
155 // then if no success, return result for most preferred network adapter,
156 // preferring "real" network errors to the ERR_PAC_NOT_IN_DHCP error.
157 // Default to ERR_ABORTED if no fetcher completed.
158 int result = ERR_ABORTED;
159 for (FetcherVector::iterator it = fetchers_.begin();
160 it != fetchers_.end();
161 ++it) {
162 if ((*it)->DidFinish() && (*it)->GetResult() == OK) {
163 result = OK;
164 *destination_string_ = (*it)->GetPacScript();
165 pac_url_ = (*it)->GetPacURL();
166 break;
167 }
168 }
169 if (result != OK) {
170 destination_string_->clear();
171 for (FetcherVector::iterator it = fetchers_.begin();
172 it != fetchers_.end();
173 ++it) {
174 if ((*it)->DidFinish()) {
175 result = (*it)->GetResult();
176 if (result != ERR_PAC_NOT_IN_DHCP) {
177 break;
178 }
179 }
180 }
181 }
182
183 client_callback_->Run(result);
184 fetchers_.reset();
eroman 2011/05/17 04:39:39 Can you reset all the state *before* calling the c
Jói 2011/05/17 15:38:35 Done, thanks for catching that. I had a test for
185 state_ = STATE_DONE;
186 }
187
188 DhcpProxyScriptAdapterFetcher*
189 WindowsDhcpProxyScriptFetcher::ImplCreateAdapterFetcher() {
190 return new DhcpProxyScriptAdapterFetcher(url_request_context_);
191 }
192
193 bool WindowsDhcpProxyScriptFetcher::ImplGetCandidateAdapterNames(
194 std::set<std::string>* adapter_names) {
195 return GetCandidateAdapterNames(adapter_names);
196 }
197
198 int WindowsDhcpProxyScriptFetcher::ImplGetMaxWaitMs() {
199 return kMaxWaitAfterFirstResultMs;
200 }
201
202 bool WindowsDhcpProxyScriptFetcher::GetCandidateAdapterNames(
203 std::set<std::string>* adapter_names) {
204 DCHECK(adapter_names);
205 adapter_names->clear();
206
207 // The GetAdaptersAddresses MSDN page recommends using a size of 15000 to
208 // avoid reallocation.
209 ULONG adapters_size = 15000;
210 scoped_ptr_malloc<IP_ADAPTER_ADDRESSES> adapters;
211 ULONG error = ERROR_SUCCESS;
212 int num_tries = 0;
213 do {
214 adapters.reset(
215 reinterpret_cast<IP_ADAPTER_ADDRESSES*>(malloc(adapters_size)));
216 // Return only unicast addresses, and skip information we do not need.
217 error = GetAdaptersAddresses(AF_UNSPEC,
218 GAA_FLAG_SKIP_ANYCAST |
219 GAA_FLAG_SKIP_MULTICAST |
220 GAA_FLAG_SKIP_DNS_SERVER |
221 GAA_FLAG_SKIP_FRIENDLY_NAME,
222 NULL,
223 adapters.get(),
224 &adapters_size);
225 ++num_tries;
226 } while (error == ERROR_BUFFER_OVERFLOW && num_tries <= 3);
227
228 if (error == ERROR_NO_DATA) {
229 // There are no adapters that we care about.
230 return true;
231 }
232
233 if (error != ERROR_SUCCESS) {
234 LOG(WARNING) << "Unexpected error retrieving WPAD configuration from DHCP.";
235 return false;
236 }
237
238 IP_ADAPTER_ADDRESSES* adapter = NULL;
239 for (adapter = adapters.get(); adapter; adapter = adapter->Next) {
240 if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK)
241 continue;
242 if ((adapter->Flags & IP_ADAPTER_DHCP_ENABLED) == 0)
243 continue;
244
245 DCHECK(adapter->AdapterName);
246 adapter_names->insert(adapter->AdapterName);
247 }
248
249 return true;
250 }
251
252 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698