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