Roll src/third_party/WebKit a2aeeb7:6373c4f (svn 198318:198336)
[chromium-blink-merge.git] / net / proxy / dhcp_proxy_script_fetcher_win.cc
blobb777da572aef1b4745611e6a58044ddbccadf111
1 // Copyright (c) 2012 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.
5 #include "net/proxy/dhcp_proxy_script_fetcher_win.h"
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/profiler/scoped_tracker.h"
10 #include "base/threading/sequenced_worker_pool.h"
11 #include "net/base/net_errors.h"
12 #include "net/proxy/dhcp_proxy_script_adapter_fetcher_win.h"
14 #include <winsock2.h>
15 #include <iphlpapi.h>
16 #pragma comment(lib, "iphlpapi.lib")
18 namespace {
20 // How many threads to use at maximum to do DHCP lookups. This is
21 // chosen based on the following UMA data:
22 // - When OnWaitTimer fires, ~99.8% of users have 6 or fewer network
23 // adapters enabled for DHCP in total.
24 // - At the same measurement point, ~99.7% of users have 3 or fewer pending
25 // DHCP adapter lookups.
26 // - There is however a very long and thin tail of users who have
27 // systems reporting up to 100+ adapters (this must be some very weird
28 // OS bug (?), probably the cause of http://crbug.com/240034).
30 // The maximum number of threads is chosen such that even systems that
31 // report a huge number of network adapters should not run out of
32 // memory from this number of threads, while giving a good chance of
33 // getting back results for any responsive adapters.
35 // The ~99.8% of systems that have 6 or fewer network adapters will
36 // not grow the thread pool to its maximum size (rather, they will
37 // grow it to 6 or fewer threads) so setting the limit lower would not
38 // improve performance or memory usage on those systems.
39 const int kMaxDhcpLookupThreads = 12;
41 // How long to wait at maximum after we get results (a PAC file or
42 // knowledge that no PAC file is configured) from whichever network
43 // adapter finishes first.
44 const int kMaxWaitAfterFirstResultMs = 400;
46 const int kGetAdaptersAddressesErrors[] = {
47 ERROR_ADDRESS_NOT_ASSOCIATED,
48 ERROR_BUFFER_OVERFLOW,
49 ERROR_INVALID_PARAMETER,
50 ERROR_NOT_ENOUGH_MEMORY,
51 ERROR_NO_DATA,
54 } // namespace
56 namespace net {
58 DhcpProxyScriptFetcherWin::DhcpProxyScriptFetcherWin(
59 URLRequestContext* url_request_context)
60 : state_(STATE_START),
61 num_pending_fetchers_(0),
62 destination_string_(NULL),
63 url_request_context_(url_request_context) {
64 DCHECK(url_request_context_);
66 worker_pool_ = new base::SequencedWorkerPool(kMaxDhcpLookupThreads,
67 "PacDhcpLookup");
70 DhcpProxyScriptFetcherWin::~DhcpProxyScriptFetcherWin() {
71 // Count as user-initiated if we are not yet in STATE_DONE.
72 Cancel();
74 worker_pool_->Shutdown();
77 int DhcpProxyScriptFetcherWin::Fetch(base::string16* utf16_text,
78 const CompletionCallback& callback) {
79 // TODO(joi): Remove ScopedTracker below once crbug.com/476182 is fixed.
80 tracked_objects::ScopedTracker tracking_profile1(
81 FROM_HERE_WITH_EXPLICIT_FUNCTION(
82 "476182 DhcpProxyScriptFetcherWin::Fetch 1"));
84 DCHECK(CalledOnValidThread());
85 if (state_ != STATE_START && state_ != STATE_DONE) {
86 NOTREACHED();
87 return ERR_UNEXPECTED;
90 state_ = STATE_WAIT_ADAPTERS;
91 callback_ = callback;
92 destination_string_ = utf16_text;
94 last_query_ = ImplCreateAdapterQuery();
95 // TODO(joi): Remove ScopedTracker below once crbug.com/476182 is fixed.
96 tracked_objects::ScopedTracker tracking_profile2(
97 FROM_HERE_WITH_EXPLICIT_FUNCTION(
98 "476182 DhcpProxyScriptFetcherWin::Fetch 2"));
99 GetTaskRunner()->PostTaskAndReply(
100 FROM_HERE,
101 base::Bind(
102 &DhcpProxyScriptFetcherWin::AdapterQuery::GetCandidateAdapterNames,
103 last_query_.get()),
104 base::Bind(
105 &DhcpProxyScriptFetcherWin::OnGetCandidateAdapterNamesDone,
106 AsWeakPtr(),
107 last_query_));
109 return ERR_IO_PENDING;
112 void DhcpProxyScriptFetcherWin::Cancel() {
113 DCHECK(CalledOnValidThread());
115 CancelImpl();
118 void DhcpProxyScriptFetcherWin::CancelImpl() {
119 DCHECK(CalledOnValidThread());
121 if (state_ != STATE_DONE) {
122 callback_.Reset();
123 wait_timer_.Stop();
124 state_ = STATE_DONE;
126 for (FetcherVector::iterator it = fetchers_.begin();
127 it != fetchers_.end();
128 ++it) {
129 (*it)->Cancel();
132 fetchers_.clear();
136 void DhcpProxyScriptFetcherWin::OnGetCandidateAdapterNamesDone(
137 scoped_refptr<AdapterQuery> query) {
138 // TODO(joi): Remove ScopedTracker below once crbug.com/476182 is fixed.
139 tracked_objects::ScopedTracker tracking_profile1(
140 FROM_HERE_WITH_EXPLICIT_FUNCTION(
141 "476182 "
142 "DhcpProxyScriptFetcherWin::OnGetCandidateAdapterNamesDone 1"));
144 DCHECK(CalledOnValidThread());
146 // This can happen if this object is reused for multiple queries,
147 // and a previous query was cancelled before it completed.
148 if (query.get() != last_query_.get())
149 return;
150 last_query_ = NULL;
152 // Enable unit tests to wait for this to happen; in production this function
153 // call is a no-op.
154 ImplOnGetCandidateAdapterNamesDone();
156 // We may have been cancelled.
157 if (state_ != STATE_WAIT_ADAPTERS)
158 return;
160 state_ = STATE_NO_RESULTS;
162 // TODO(joi): Remove ScopedTracker below once crbug.com/476182 is fixed.
163 tracked_objects::ScopedTracker tracking_profile2(
164 FROM_HERE_WITH_EXPLICIT_FUNCTION(
165 "476182 "
166 "DhcpProxyScriptFetcherWin::OnGetCandidateAdapterNamesDone 2"));
168 const std::set<std::string>& adapter_names = query->adapter_names();
170 if (adapter_names.empty()) {
171 TransitionToDone();
172 return;
175 // TODO(joi): Remove ScopedTracker below once crbug.com/476182 is fixed.
176 tracked_objects::ScopedTracker tracking_profile3(
177 FROM_HERE_WITH_EXPLICIT_FUNCTION(
178 "476182 "
179 "DhcpProxyScriptFetcherWin::OnGetCandidateAdapterNamesDone 3"));
181 for (std::set<std::string>::const_iterator it = adapter_names.begin();
182 it != adapter_names.end();
183 ++it) {
184 DhcpProxyScriptAdapterFetcher* fetcher(ImplCreateAdapterFetcher());
185 fetcher->Fetch(
186 *it, base::Bind(&DhcpProxyScriptFetcherWin::OnFetcherDone,
187 base::Unretained(this)));
188 fetchers_.push_back(fetcher);
190 num_pending_fetchers_ = fetchers_.size();
193 std::string DhcpProxyScriptFetcherWin::GetFetcherName() const {
194 DCHECK(CalledOnValidThread());
195 return "win";
198 const GURL& DhcpProxyScriptFetcherWin::GetPacURL() const {
199 DCHECK(CalledOnValidThread());
200 DCHECK_EQ(state_, STATE_DONE);
202 return pac_url_;
205 void DhcpProxyScriptFetcherWin::OnFetcherDone(int result) {
206 DCHECK(state_ == STATE_NO_RESULTS || state_ == STATE_SOME_RESULTS);
208 if (--num_pending_fetchers_ == 0) {
209 TransitionToDone();
210 return;
213 // If the only pending adapters are those less preferred than one
214 // with a valid PAC script, we do not need to wait any longer.
215 for (FetcherVector::iterator it = fetchers_.begin();
216 it != fetchers_.end();
217 ++it) {
218 bool did_finish = (*it)->DidFinish();
219 int result = (*it)->GetResult();
220 if (did_finish && result == OK) {
221 TransitionToDone();
222 return;
224 if (!did_finish || result != ERR_PAC_NOT_IN_DHCP) {
225 break;
229 // Once we have a single result, we set a maximum on how long to wait
230 // for the rest of the results.
231 if (state_ == STATE_NO_RESULTS) {
232 state_ = STATE_SOME_RESULTS;
233 wait_timer_.Start(FROM_HERE,
234 ImplGetMaxWait(), this, &DhcpProxyScriptFetcherWin::OnWaitTimer);
238 void DhcpProxyScriptFetcherWin::OnWaitTimer() {
239 DCHECK_EQ(state_, STATE_SOME_RESULTS);
241 TransitionToDone();
244 void DhcpProxyScriptFetcherWin::TransitionToDone() {
245 DCHECK(state_ == STATE_NO_RESULTS || state_ == STATE_SOME_RESULTS);
247 int result = ERR_PAC_NOT_IN_DHCP; // Default if no fetchers.
248 if (!fetchers_.empty()) {
249 // Scan twice for the result; once through the whole list for success,
250 // then if no success, return result for most preferred network adapter,
251 // preferring "real" network errors to the ERR_PAC_NOT_IN_DHCP error.
252 // Default to ERR_ABORTED if no fetcher completed.
253 result = ERR_ABORTED;
254 for (FetcherVector::iterator it = fetchers_.begin();
255 it != fetchers_.end();
256 ++it) {
257 if ((*it)->DidFinish() && (*it)->GetResult() == OK) {
258 result = OK;
259 *destination_string_ = (*it)->GetPacScript();
260 pac_url_ = (*it)->GetPacURL();
261 break;
264 if (result != OK) {
265 destination_string_->clear();
266 for (FetcherVector::iterator it = fetchers_.begin();
267 it != fetchers_.end();
268 ++it) {
269 if ((*it)->DidFinish()) {
270 result = (*it)->GetResult();
271 if (result != ERR_PAC_NOT_IN_DHCP) {
272 break;
279 CompletionCallback callback = callback_;
280 CancelImpl();
281 DCHECK_EQ(state_, STATE_DONE);
282 DCHECK(fetchers_.empty());
283 DCHECK(callback_.is_null()); // Invariant of data.
285 // We may be deleted re-entrantly within this outcall.
286 callback.Run(result);
289 int DhcpProxyScriptFetcherWin::num_pending_fetchers() const {
290 return num_pending_fetchers_;
293 URLRequestContext* DhcpProxyScriptFetcherWin::url_request_context() const {
294 return url_request_context_;
297 scoped_refptr<base::TaskRunner> DhcpProxyScriptFetcherWin::GetTaskRunner() {
298 return worker_pool_->GetTaskRunnerWithShutdownBehavior(
299 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
302 DhcpProxyScriptAdapterFetcher*
303 DhcpProxyScriptFetcherWin::ImplCreateAdapterFetcher() {
304 return new DhcpProxyScriptAdapterFetcher(url_request_context_,
305 GetTaskRunner());
308 DhcpProxyScriptFetcherWin::AdapterQuery*
309 DhcpProxyScriptFetcherWin::ImplCreateAdapterQuery() {
310 return new AdapterQuery();
313 base::TimeDelta DhcpProxyScriptFetcherWin::ImplGetMaxWait() {
314 return base::TimeDelta::FromMilliseconds(kMaxWaitAfterFirstResultMs);
317 bool DhcpProxyScriptFetcherWin::GetCandidateAdapterNames(
318 std::set<std::string>* adapter_names) {
319 DCHECK(adapter_names);
320 adapter_names->clear();
322 // The GetAdaptersAddresses MSDN page recommends using a size of 15000 to
323 // avoid reallocation.
324 ULONG adapters_size = 15000;
325 scoped_ptr<IP_ADAPTER_ADDRESSES, base::FreeDeleter> adapters;
326 ULONG error = ERROR_SUCCESS;
327 int num_tries = 0;
329 do {
330 adapters.reset(static_cast<IP_ADAPTER_ADDRESSES*>(malloc(adapters_size)));
331 // Return only unicast addresses, and skip information we do not need.
332 error = GetAdaptersAddresses(AF_UNSPEC,
333 GAA_FLAG_SKIP_ANYCAST |
334 GAA_FLAG_SKIP_MULTICAST |
335 GAA_FLAG_SKIP_DNS_SERVER |
336 GAA_FLAG_SKIP_FRIENDLY_NAME,
337 NULL,
338 adapters.get(),
339 &adapters_size);
340 ++num_tries;
341 } while (error == ERROR_BUFFER_OVERFLOW && num_tries <= 3);
343 if (error == ERROR_NO_DATA) {
344 // There are no adapters that we care about.
345 return true;
348 if (error != ERROR_SUCCESS) {
349 LOG(WARNING) << "Unexpected error retrieving WPAD configuration from DHCP.";
350 return false;
353 IP_ADAPTER_ADDRESSES* adapter = NULL;
354 for (adapter = adapters.get(); adapter; adapter = adapter->Next) {
355 if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK)
356 continue;
357 if ((adapter->Flags & IP_ADAPTER_DHCP_ENABLED) == 0)
358 continue;
360 DCHECK(adapter->AdapterName);
361 adapter_names->insert(adapter->AdapterName);
364 return true;
367 DhcpProxyScriptFetcherWin::AdapterQuery::AdapterQuery() {
370 void DhcpProxyScriptFetcherWin::AdapterQuery::GetCandidateAdapterNames() {
371 ImplGetCandidateAdapterNames(&adapter_names_);
374 const std::set<std::string>&
375 DhcpProxyScriptFetcherWin::AdapterQuery::adapter_names() const {
376 return adapter_names_;
379 bool DhcpProxyScriptFetcherWin::AdapterQuery::ImplGetCandidateAdapterNames(
380 std::set<std::string>* adapter_names) {
381 return DhcpProxyScriptFetcherWin::GetCandidateAdapterNames(adapter_names);
384 DhcpProxyScriptFetcherWin::AdapterQuery::~AdapterQuery() {
387 } // namespace net