OLD | NEW |
| (Empty) |
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/base/host_resolver_impl.h" | |
6 | |
7 #if defined(OS_WIN) | |
8 #include <Winsock2.h> | |
9 #elif defined(OS_POSIX) | |
10 #include <netdb.h> | |
11 #endif | |
12 | |
13 #include <cmath> | |
14 #include <utility> | |
15 #include <vector> | |
16 | |
17 #include "base/basictypes.h" | |
18 #include "base/bind.h" | |
19 #include "base/bind_helpers.h" | |
20 #include "base/callback.h" | |
21 #include "base/compiler_specific.h" | |
22 #include "base/debug/debugger.h" | |
23 #include "base/debug/stack_trace.h" | |
24 #include "base/message_loop_proxy.h" | |
25 #include "base/metrics/field_trial.h" | |
26 #include "base/metrics/histogram.h" | |
27 #include "base/stl_util.h" | |
28 #include "base/string_util.h" | |
29 #include "base/threading/worker_pool.h" | |
30 #include "base/time.h" | |
31 #include "base/utf_string_conversions.h" | |
32 #include "base/values.h" | |
33 #include "net/base/address_family.h" | |
34 #include "net/base/address_list.h" | |
35 #include "net/base/dns_reloader.h" | |
36 #include "net/base/host_port_pair.h" | |
37 #include "net/base/host_resolver_proc.h" | |
38 #include "net/base/net_errors.h" | |
39 #include "net/base/net_log.h" | |
40 #include "net/base/net_util.h" | |
41 #include "net/dns/address_sorter.h" | |
42 #include "net/dns/dns_client.h" | |
43 #include "net/dns/dns_config_service.h" | |
44 #include "net/dns/dns_protocol.h" | |
45 #include "net/dns/dns_response.h" | |
46 #include "net/dns/dns_transaction.h" | |
47 | |
48 #if defined(OS_WIN) | |
49 #include "net/base/winsock_init.h" | |
50 #endif | |
51 | |
52 namespace net { | |
53 | |
54 namespace { | |
55 | |
56 // Limit the size of hostnames that will be resolved to combat issues in | |
57 // some platform's resolvers. | |
58 const size_t kMaxHostLength = 4096; | |
59 | |
60 // Default TTL for successful resolutions with ProcTask. | |
61 const unsigned kCacheEntryTTLSeconds = 60; | |
62 | |
63 // Default TTL for unsuccessful resolutions with ProcTask. | |
64 const unsigned kNegativeCacheEntryTTLSeconds = 0; | |
65 | |
66 // Minimum TTL for successful resolutions with DnsTask. | |
67 const unsigned kMinimumTTLSeconds = kCacheEntryTTLSeconds; | |
68 | |
69 // Number of consecutive failures of DnsTask (with successful fallback) before | |
70 // the DnsClient is disabled until the next DNS change. | |
71 const unsigned kMaximumDnsFailures = 16; | |
72 | |
73 // We use a separate histogram name for each platform to facilitate the | |
74 // display of error codes by their symbolic name (since each platform has | |
75 // different mappings). | |
76 const char kOSErrorsForGetAddrinfoHistogramName[] = | |
77 #if defined(OS_WIN) | |
78 "Net.OSErrorsForGetAddrinfo_Win"; | |
79 #elif defined(OS_MACOSX) | |
80 "Net.OSErrorsForGetAddrinfo_Mac"; | |
81 #elif defined(OS_LINUX) | |
82 "Net.OSErrorsForGetAddrinfo_Linux"; | |
83 #else | |
84 "Net.OSErrorsForGetAddrinfo"; | |
85 #endif | |
86 | |
87 // Gets a list of the likely error codes that getaddrinfo() can return | |
88 // (non-exhaustive). These are the error codes that we will track via | |
89 // a histogram. | |
90 std::vector<int> GetAllGetAddrinfoOSErrors() { | |
91 int os_errors[] = { | |
92 #if defined(OS_POSIX) | |
93 #if !defined(OS_FREEBSD) | |
94 #if !defined(OS_ANDROID) | |
95 // EAI_ADDRFAMILY has been declared obsolete in Android's and | |
96 // FreeBSD's netdb.h. | |
97 EAI_ADDRFAMILY, | |
98 #endif | |
99 // EAI_NODATA has been declared obsolete in FreeBSD's netdb.h. | |
100 EAI_NODATA, | |
101 #endif | |
102 EAI_AGAIN, | |
103 EAI_BADFLAGS, | |
104 EAI_FAIL, | |
105 EAI_FAMILY, | |
106 EAI_MEMORY, | |
107 EAI_NONAME, | |
108 EAI_SERVICE, | |
109 EAI_SOCKTYPE, | |
110 EAI_SYSTEM, | |
111 #elif defined(OS_WIN) | |
112 // See: http://msdn.microsoft.com/en-us/library/ms738520(VS.85).aspx | |
113 WSA_NOT_ENOUGH_MEMORY, | |
114 WSAEAFNOSUPPORT, | |
115 WSAEINVAL, | |
116 WSAESOCKTNOSUPPORT, | |
117 WSAHOST_NOT_FOUND, | |
118 WSANO_DATA, | |
119 WSANO_RECOVERY, | |
120 WSANOTINITIALISED, | |
121 WSATRY_AGAIN, | |
122 WSATYPE_NOT_FOUND, | |
123 // The following are not in doc, but might be to appearing in results :-(. | |
124 WSA_INVALID_HANDLE, | |
125 #endif | |
126 }; | |
127 | |
128 // Ensure all errors are positive, as histogram only tracks positive values. | |
129 for (size_t i = 0; i < arraysize(os_errors); ++i) { | |
130 os_errors[i] = std::abs(os_errors[i]); | |
131 } | |
132 | |
133 return base::CustomHistogram::ArrayToCustomRanges(os_errors, | |
134 arraysize(os_errors)); | |
135 } | |
136 | |
137 enum DnsResolveStatus { | |
138 RESOLVE_STATUS_DNS_SUCCESS = 0, | |
139 RESOLVE_STATUS_PROC_SUCCESS, | |
140 RESOLVE_STATUS_FAIL, | |
141 RESOLVE_STATUS_SUSPECT_NETBIOS, | |
142 RESOLVE_STATUS_MAX | |
143 }; | |
144 | |
145 void UmaAsyncDnsResolveStatus(DnsResolveStatus result) { | |
146 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.ResolveStatus", | |
147 result, | |
148 RESOLVE_STATUS_MAX); | |
149 } | |
150 | |
151 bool ResemblesNetBIOSName(const std::string& hostname) { | |
152 return (hostname.size() < 16) && (hostname.find('.') == std::string::npos); | |
153 } | |
154 | |
155 // True if |hostname| ends with either ".local" or ".local.". | |
156 bool ResemblesMulticastDNSName(const std::string& hostname) { | |
157 DCHECK(!hostname.empty()); | |
158 const char kSuffix[] = ".local."; | |
159 const size_t kSuffixLen = sizeof(kSuffix) - 1; | |
160 const size_t kSuffixLenTrimmed = kSuffixLen - 1; | |
161 if (hostname[hostname.size() - 1] == '.') { | |
162 return hostname.size() > kSuffixLen && | |
163 !hostname.compare(hostname.size() - kSuffixLen, kSuffixLen, kSuffix); | |
164 } | |
165 return hostname.size() > kSuffixLenTrimmed && | |
166 !hostname.compare(hostname.size() - kSuffixLenTrimmed, kSuffixLenTrimmed, | |
167 kSuffix, kSuffixLenTrimmed); | |
168 } | |
169 | |
170 // Provide a common macro to simplify code and readability. We must use a | |
171 // macro as the underlying HISTOGRAM macro creates static variables. | |
172 #define DNS_HISTOGRAM(name, time) UMA_HISTOGRAM_CUSTOM_TIMES(name, time, \ | |
173 base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromHours(1), 100) | |
174 | |
175 // A macro to simplify code and readability. | |
176 #define DNS_HISTOGRAM_BY_PRIORITY(basename, priority, time) \ | |
177 do { \ | |
178 switch (priority) { \ | |
179 case HIGHEST: DNS_HISTOGRAM(basename "_HIGHEST", time); break; \ | |
180 case MEDIUM: DNS_HISTOGRAM(basename "_MEDIUM", time); break; \ | |
181 case LOW: DNS_HISTOGRAM(basename "_LOW", time); break; \ | |
182 case LOWEST: DNS_HISTOGRAM(basename "_LOWEST", time); break; \ | |
183 case IDLE: DNS_HISTOGRAM(basename "_IDLE", time); break; \ | |
184 default: NOTREACHED(); break; \ | |
185 } \ | |
186 DNS_HISTOGRAM(basename, time); \ | |
187 } while (0) | |
188 | |
189 // Record time from Request creation until a valid DNS response. | |
190 void RecordTotalTime(bool had_dns_config, | |
191 bool speculative, | |
192 base::TimeDelta duration) { | |
193 if (had_dns_config) { | |
194 if (speculative) { | |
195 DNS_HISTOGRAM("AsyncDNS.TotalTime_speculative", duration); | |
196 } else { | |
197 DNS_HISTOGRAM("AsyncDNS.TotalTime", duration); | |
198 } | |
199 } else { | |
200 if (speculative) { | |
201 DNS_HISTOGRAM("DNS.TotalTime_speculative", duration); | |
202 } else { | |
203 DNS_HISTOGRAM("DNS.TotalTime", duration); | |
204 } | |
205 } | |
206 } | |
207 | |
208 void RecordTTL(base::TimeDelta ttl) { | |
209 UMA_HISTOGRAM_CUSTOM_TIMES("AsyncDNS.TTL", ttl, | |
210 base::TimeDelta::FromSeconds(1), | |
211 base::TimeDelta::FromDays(1), 100); | |
212 } | |
213 | |
214 //----------------------------------------------------------------------------- | |
215 | |
216 // Wraps call to SystemHostResolverProc as an instance of HostResolverProc. | |
217 // TODO(szym): This should probably be declared in host_resolver_proc.h. | |
218 class CallSystemHostResolverProc : public HostResolverProc { | |
219 public: | |
220 CallSystemHostResolverProc() : HostResolverProc(NULL) {} | |
221 virtual int Resolve(const std::string& hostname, | |
222 AddressFamily address_family, | |
223 HostResolverFlags host_resolver_flags, | |
224 AddressList* addr_list, | |
225 int* os_error) OVERRIDE { | |
226 return SystemHostResolverProc(hostname, | |
227 address_family, | |
228 host_resolver_flags, | |
229 addr_list, | |
230 os_error); | |
231 } | |
232 | |
233 protected: | |
234 virtual ~CallSystemHostResolverProc() {} | |
235 }; | |
236 | |
237 AddressList EnsurePortOnAddressList(const AddressList& list, uint16 port) { | |
238 if (list.empty() || list.front().port() == port) | |
239 return list; | |
240 return AddressList::CopyWithPort(list, port); | |
241 } | |
242 | |
243 // Creates NetLog parameters when the resolve failed. | |
244 base::Value* NetLogProcTaskFailedCallback(uint32 attempt_number, | |
245 int net_error, | |
246 int os_error, | |
247 NetLog::LogLevel /* log_level */) { | |
248 DictionaryValue* dict = new DictionaryValue(); | |
249 if (attempt_number) | |
250 dict->SetInteger("attempt_number", attempt_number); | |
251 | |
252 dict->SetInteger("net_error", net_error); | |
253 | |
254 if (os_error) { | |
255 dict->SetInteger("os_error", os_error); | |
256 #if defined(OS_POSIX) | |
257 dict->SetString("os_error_string", gai_strerror(os_error)); | |
258 #elif defined(OS_WIN) | |
259 // Map the error code to a human-readable string. | |
260 LPWSTR error_string = NULL; | |
261 int size = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | | |
262 FORMAT_MESSAGE_FROM_SYSTEM, | |
263 0, // Use the internal message table. | |
264 os_error, | |
265 0, // Use default language. | |
266 (LPWSTR)&error_string, | |
267 0, // Buffer size. | |
268 0); // Arguments (unused). | |
269 dict->SetString("os_error_string", WideToUTF8(error_string)); | |
270 LocalFree(error_string); | |
271 #endif | |
272 } | |
273 | |
274 return dict; | |
275 } | |
276 | |
277 // Creates NetLog parameters when the DnsTask failed. | |
278 base::Value* NetLogDnsTaskFailedCallback(int net_error, | |
279 int dns_error, | |
280 NetLog::LogLevel /* log_level */) { | |
281 DictionaryValue* dict = new DictionaryValue(); | |
282 dict->SetInteger("net_error", net_error); | |
283 if (dns_error) | |
284 dict->SetInteger("dns_error", dns_error); | |
285 return dict; | |
286 }; | |
287 | |
288 // Creates NetLog parameters containing the information in a RequestInfo object, | |
289 // along with the associated NetLog::Source. | |
290 base::Value* NetLogRequestInfoCallback(const NetLog::Source& source, | |
291 const HostResolver::RequestInfo* info, | |
292 NetLog::LogLevel /* log_level */) { | |
293 DictionaryValue* dict = new DictionaryValue(); | |
294 source.AddToEventParameters(dict); | |
295 | |
296 dict->SetString("host", info->host_port_pair().ToString()); | |
297 dict->SetInteger("address_family", | |
298 static_cast<int>(info->address_family())); | |
299 dict->SetBoolean("allow_cached_response", info->allow_cached_response()); | |
300 dict->SetBoolean("is_speculative", info->is_speculative()); | |
301 dict->SetInteger("priority", info->priority()); | |
302 return dict; | |
303 } | |
304 | |
305 // Creates NetLog parameters for the creation of a HostResolverImpl::Job. | |
306 base::Value* NetLogJobCreationCallback(const NetLog::Source& source, | |
307 const std::string* host, | |
308 NetLog::LogLevel /* log_level */) { | |
309 DictionaryValue* dict = new DictionaryValue(); | |
310 source.AddToEventParameters(dict); | |
311 dict->SetString("host", *host); | |
312 return dict; | |
313 } | |
314 | |
315 // Creates NetLog parameters for HOST_RESOLVER_IMPL_JOB_ATTACH/DETACH events. | |
316 base::Value* NetLogJobAttachCallback(const NetLog::Source& source, | |
317 RequestPriority priority, | |
318 NetLog::LogLevel /* log_level */) { | |
319 DictionaryValue* dict = new DictionaryValue(); | |
320 source.AddToEventParameters(dict); | |
321 dict->SetInteger("priority", priority); | |
322 return dict; | |
323 } | |
324 | |
325 // Creates NetLog parameters for the DNS_CONFIG_CHANGED event. | |
326 base::Value* NetLogDnsConfigCallback(const DnsConfig* config, | |
327 NetLog::LogLevel /* log_level */) { | |
328 return config->ToValue(); | |
329 } | |
330 | |
331 // The logging routines are defined here because some requests are resolved | |
332 // without a Request object. | |
333 | |
334 // Logs when a request has just been started. | |
335 void LogStartRequest(const BoundNetLog& source_net_log, | |
336 const BoundNetLog& request_net_log, | |
337 const HostResolver::RequestInfo& info) { | |
338 source_net_log.BeginEvent( | |
339 NetLog::TYPE_HOST_RESOLVER_IMPL, | |
340 request_net_log.source().ToEventParametersCallback()); | |
341 | |
342 request_net_log.BeginEvent( | |
343 NetLog::TYPE_HOST_RESOLVER_IMPL_REQUEST, | |
344 base::Bind(&NetLogRequestInfoCallback, source_net_log.source(), &info)); | |
345 } | |
346 | |
347 // Logs when a request has just completed (before its callback is run). | |
348 void LogFinishRequest(const BoundNetLog& source_net_log, | |
349 const BoundNetLog& request_net_log, | |
350 const HostResolver::RequestInfo& info, | |
351 int net_error) { | |
352 request_net_log.EndEventWithNetErrorCode( | |
353 NetLog::TYPE_HOST_RESOLVER_IMPL_REQUEST, net_error); | |
354 source_net_log.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL); | |
355 } | |
356 | |
357 // Logs when a request has been cancelled. | |
358 void LogCancelRequest(const BoundNetLog& source_net_log, | |
359 const BoundNetLog& request_net_log, | |
360 const HostResolverImpl::RequestInfo& info) { | |
361 request_net_log.AddEvent(NetLog::TYPE_CANCELLED); | |
362 request_net_log.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_REQUEST); | |
363 source_net_log.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL); | |
364 } | |
365 | |
366 //----------------------------------------------------------------------------- | |
367 | |
368 // Keeps track of the highest priority. | |
369 class PriorityTracker { | |
370 public: | |
371 explicit PriorityTracker(RequestPriority initial_priority) | |
372 : highest_priority_(initial_priority), total_count_(0) { | |
373 memset(counts_, 0, sizeof(counts_)); | |
374 } | |
375 | |
376 RequestPriority highest_priority() const { | |
377 return highest_priority_; | |
378 } | |
379 | |
380 size_t total_count() const { | |
381 return total_count_; | |
382 } | |
383 | |
384 void Add(RequestPriority req_priority) { | |
385 ++total_count_; | |
386 ++counts_[req_priority]; | |
387 if (highest_priority_ < req_priority) | |
388 highest_priority_ = req_priority; | |
389 } | |
390 | |
391 void Remove(RequestPriority req_priority) { | |
392 DCHECK_GT(total_count_, 0u); | |
393 DCHECK_GT(counts_[req_priority], 0u); | |
394 --total_count_; | |
395 --counts_[req_priority]; | |
396 size_t i; | |
397 for (i = highest_priority_; i > MINIMUM_PRIORITY && !counts_[i]; --i); | |
398 highest_priority_ = static_cast<RequestPriority>(i); | |
399 | |
400 // In absence of requests, default to MINIMUM_PRIORITY. | |
401 if (total_count_ == 0) | |
402 DCHECK_EQ(MINIMUM_PRIORITY, highest_priority_); | |
403 } | |
404 | |
405 private: | |
406 RequestPriority highest_priority_; | |
407 size_t total_count_; | |
408 size_t counts_[NUM_PRIORITIES]; | |
409 }; | |
410 | |
411 } // namespace | |
412 | |
413 //----------------------------------------------------------------------------- | |
414 | |
415 // Holds the data for a request that could not be completed synchronously. | |
416 // It is owned by a Job. Canceled Requests are only marked as canceled rather | |
417 // than removed from the Job's |requests_| list. | |
418 class HostResolverImpl::Request { | |
419 public: | |
420 Request(const BoundNetLog& source_net_log, | |
421 const BoundNetLog& request_net_log, | |
422 const RequestInfo& info, | |
423 const CompletionCallback& callback, | |
424 AddressList* addresses) | |
425 : source_net_log_(source_net_log), | |
426 request_net_log_(request_net_log), | |
427 info_(info), | |
428 job_(NULL), | |
429 callback_(callback), | |
430 addresses_(addresses), | |
431 request_time_(base::TimeTicks::Now()) { | |
432 } | |
433 | |
434 // Mark the request as canceled. | |
435 void MarkAsCanceled() { | |
436 job_ = NULL; | |
437 addresses_ = NULL; | |
438 callback_.Reset(); | |
439 } | |
440 | |
441 bool was_canceled() const { | |
442 return callback_.is_null(); | |
443 } | |
444 | |
445 void set_job(Job* job) { | |
446 DCHECK(job); | |
447 // Identify which job the request is waiting on. | |
448 job_ = job; | |
449 } | |
450 | |
451 // Prepare final AddressList and call completion callback. | |
452 void OnComplete(int error, const AddressList& addr_list) { | |
453 DCHECK(!was_canceled()); | |
454 if (error == OK) | |
455 *addresses_ = EnsurePortOnAddressList(addr_list, info_.port()); | |
456 CompletionCallback callback = callback_; | |
457 MarkAsCanceled(); | |
458 callback.Run(error); | |
459 } | |
460 | |
461 Job* job() const { | |
462 return job_; | |
463 } | |
464 | |
465 // NetLog for the source, passed in HostResolver::Resolve. | |
466 const BoundNetLog& source_net_log() { | |
467 return source_net_log_; | |
468 } | |
469 | |
470 // NetLog for this request. | |
471 const BoundNetLog& request_net_log() { | |
472 return request_net_log_; | |
473 } | |
474 | |
475 const RequestInfo& info() const { | |
476 return info_; | |
477 } | |
478 | |
479 base::TimeTicks request_time() const { | |
480 return request_time_; | |
481 } | |
482 | |
483 private: | |
484 BoundNetLog source_net_log_; | |
485 BoundNetLog request_net_log_; | |
486 | |
487 // The request info that started the request. | |
488 RequestInfo info_; | |
489 | |
490 // The resolve job that this request is dependent on. | |
491 Job* job_; | |
492 | |
493 // The user's callback to invoke when the request completes. | |
494 CompletionCallback callback_; | |
495 | |
496 // The address list to save result into. | |
497 AddressList* addresses_; | |
498 | |
499 const base::TimeTicks request_time_; | |
500 | |
501 DISALLOW_COPY_AND_ASSIGN(Request); | |
502 }; | |
503 | |
504 //------------------------------------------------------------------------------ | |
505 | |
506 // Calls HostResolverProc on the WorkerPool. Performs retries if necessary. | |
507 // | |
508 // Whenever we try to resolve the host, we post a delayed task to check if host | |
509 // resolution (OnLookupComplete) is completed or not. If the original attempt | |
510 // hasn't completed, then we start another attempt for host resolution. We take | |
511 // the results from the first attempt that finishes and ignore the results from | |
512 // all other attempts. | |
513 // | |
514 // TODO(szym): Move to separate source file for testing and mocking. | |
515 // | |
516 class HostResolverImpl::ProcTask | |
517 : public base::RefCountedThreadSafe<HostResolverImpl::ProcTask> { | |
518 public: | |
519 typedef base::Callback<void(int net_error, | |
520 const AddressList& addr_list)> Callback; | |
521 | |
522 ProcTask(const Key& key, | |
523 const ProcTaskParams& params, | |
524 const Callback& callback, | |
525 const BoundNetLog& job_net_log) | |
526 : key_(key), | |
527 params_(params), | |
528 callback_(callback), | |
529 origin_loop_(base::MessageLoopProxy::current()), | |
530 attempt_number_(0), | |
531 completed_attempt_number_(0), | |
532 completed_attempt_error_(ERR_UNEXPECTED), | |
533 had_non_speculative_request_(false), | |
534 net_log_(job_net_log) { | |
535 if (!params_.resolver_proc) | |
536 params_.resolver_proc = HostResolverProc::GetDefault(); | |
537 // If default is unset, use the system proc. | |
538 if (!params_.resolver_proc) | |
539 params_.resolver_proc = new CallSystemHostResolverProc(); | |
540 } | |
541 | |
542 void Start() { | |
543 DCHECK(origin_loop_->BelongsToCurrentThread()); | |
544 net_log_.BeginEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_PROC_TASK); | |
545 StartLookupAttempt(); | |
546 } | |
547 | |
548 // Cancels this ProcTask. It will be orphaned. Any outstanding resolve | |
549 // attempts running on worker threads will continue running. Only once all the | |
550 // attempts complete will the final reference to this ProcTask be released. | |
551 void Cancel() { | |
552 DCHECK(origin_loop_->BelongsToCurrentThread()); | |
553 | |
554 if (was_canceled() || was_completed()) | |
555 return; | |
556 | |
557 callback_.Reset(); | |
558 net_log_.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_PROC_TASK); | |
559 } | |
560 | |
561 void set_had_non_speculative_request() { | |
562 DCHECK(origin_loop_->BelongsToCurrentThread()); | |
563 had_non_speculative_request_ = true; | |
564 } | |
565 | |
566 bool was_canceled() const { | |
567 DCHECK(origin_loop_->BelongsToCurrentThread()); | |
568 return callback_.is_null(); | |
569 } | |
570 | |
571 bool was_completed() const { | |
572 DCHECK(origin_loop_->BelongsToCurrentThread()); | |
573 return completed_attempt_number_ > 0; | |
574 } | |
575 | |
576 private: | |
577 friend class base::RefCountedThreadSafe<ProcTask>; | |
578 ~ProcTask() {} | |
579 | |
580 void StartLookupAttempt() { | |
581 DCHECK(origin_loop_->BelongsToCurrentThread()); | |
582 base::TimeTicks start_time = base::TimeTicks::Now(); | |
583 ++attempt_number_; | |
584 // Dispatch the lookup attempt to a worker thread. | |
585 if (!base::WorkerPool::PostTask( | |
586 FROM_HERE, | |
587 base::Bind(&ProcTask::DoLookup, this, start_time, attempt_number_), | |
588 true)) { | |
589 NOTREACHED(); | |
590 | |
591 // Since we could be running within Resolve() right now, we can't just | |
592 // call OnLookupComplete(). Instead we must wait until Resolve() has | |
593 // returned (IO_PENDING). | |
594 origin_loop_->PostTask( | |
595 FROM_HERE, | |
596 base::Bind(&ProcTask::OnLookupComplete, this, AddressList(), | |
597 start_time, attempt_number_, ERR_UNEXPECTED, 0)); | |
598 return; | |
599 } | |
600 | |
601 net_log_.AddEvent( | |
602 NetLog::TYPE_HOST_RESOLVER_IMPL_ATTEMPT_STARTED, | |
603 NetLog::IntegerCallback("attempt_number", attempt_number_)); | |
604 | |
605 // If we don't get the results within a given time, RetryIfNotComplete | |
606 // will start a new attempt on a different worker thread if none of our | |
607 // outstanding attempts have completed yet. | |
608 if (attempt_number_ <= params_.max_retry_attempts) { | |
609 origin_loop_->PostDelayedTask( | |
610 FROM_HERE, | |
611 base::Bind(&ProcTask::RetryIfNotComplete, this), | |
612 params_.unresponsive_delay); | |
613 } | |
614 } | |
615 | |
616 // WARNING: This code runs inside a worker pool. The shutdown code cannot | |
617 // wait for it to finish, so we must be very careful here about using other | |
618 // objects (like MessageLoops, Singletons, etc). During shutdown these objects | |
619 // may no longer exist. Multiple DoLookups() could be running in parallel, so | |
620 // any state inside of |this| must not mutate . | |
621 void DoLookup(const base::TimeTicks& start_time, | |
622 const uint32 attempt_number) { | |
623 AddressList results; | |
624 int os_error = 0; | |
625 // Running on the worker thread | |
626 int error = params_.resolver_proc->Resolve(key_.hostname, | |
627 key_.address_family, | |
628 key_.host_resolver_flags, | |
629 &results, | |
630 &os_error); | |
631 | |
632 origin_loop_->PostTask( | |
633 FROM_HERE, | |
634 base::Bind(&ProcTask::OnLookupComplete, this, results, start_time, | |
635 attempt_number, error, os_error)); | |
636 } | |
637 | |
638 // Makes next attempt if DoLookup() has not finished (runs on origin thread). | |
639 void RetryIfNotComplete() { | |
640 DCHECK(origin_loop_->BelongsToCurrentThread()); | |
641 | |
642 if (was_completed() || was_canceled()) | |
643 return; | |
644 | |
645 params_.unresponsive_delay *= params_.retry_factor; | |
646 StartLookupAttempt(); | |
647 } | |
648 | |
649 // Callback for when DoLookup() completes (runs on origin thread). | |
650 void OnLookupComplete(const AddressList& results, | |
651 const base::TimeTicks& start_time, | |
652 const uint32 attempt_number, | |
653 int error, | |
654 const int os_error) { | |
655 DCHECK(origin_loop_->BelongsToCurrentThread()); | |
656 DCHECK(error || !results.empty()); | |
657 | |
658 bool was_retry_attempt = attempt_number > 1; | |
659 | |
660 // Ideally the following code would be part of host_resolver_proc.cc, | |
661 // however it isn't safe to call NetworkChangeNotifier from worker threads. | |
662 // So we do it here on the IO thread instead. | |
663 if (error != OK && NetworkChangeNotifier::IsOffline()) | |
664 error = ERR_INTERNET_DISCONNECTED; | |
665 | |
666 // If this is the first attempt that is finishing later, then record data | |
667 // for the first attempt. Won't contaminate with retry attempt's data. | |
668 if (!was_retry_attempt) | |
669 RecordPerformanceHistograms(start_time, error, os_error); | |
670 | |
671 RecordAttemptHistograms(start_time, attempt_number, error, os_error); | |
672 | |
673 if (was_canceled()) | |
674 return; | |
675 | |
676 NetLog::ParametersCallback net_log_callback; | |
677 if (error != OK) { | |
678 net_log_callback = base::Bind(&NetLogProcTaskFailedCallback, | |
679 attempt_number, | |
680 error, | |
681 os_error); | |
682 } else { | |
683 net_log_callback = NetLog::IntegerCallback("attempt_number", | |
684 attempt_number); | |
685 } | |
686 net_log_.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_ATTEMPT_FINISHED, | |
687 net_log_callback); | |
688 | |
689 if (was_completed()) | |
690 return; | |
691 | |
692 // Copy the results from the first worker thread that resolves the host. | |
693 results_ = results; | |
694 completed_attempt_number_ = attempt_number; | |
695 completed_attempt_error_ = error; | |
696 | |
697 if (was_retry_attempt) { | |
698 // If retry attempt finishes before 1st attempt, then get stats on how | |
699 // much time is saved by having spawned an extra attempt. | |
700 retry_attempt_finished_time_ = base::TimeTicks::Now(); | |
701 } | |
702 | |
703 if (error != OK) { | |
704 net_log_callback = base::Bind(&NetLogProcTaskFailedCallback, | |
705 0, error, os_error); | |
706 } else { | |
707 net_log_callback = results_.CreateNetLogCallback(); | |
708 } | |
709 net_log_.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_PROC_TASK, | |
710 net_log_callback); | |
711 | |
712 callback_.Run(error, results_); | |
713 } | |
714 | |
715 void RecordPerformanceHistograms(const base::TimeTicks& start_time, | |
716 const int error, | |
717 const int os_error) const { | |
718 DCHECK(origin_loop_->BelongsToCurrentThread()); | |
719 enum Category { // Used in HISTOGRAM_ENUMERATION. | |
720 RESOLVE_SUCCESS, | |
721 RESOLVE_FAIL, | |
722 RESOLVE_SPECULATIVE_SUCCESS, | |
723 RESOLVE_SPECULATIVE_FAIL, | |
724 RESOLVE_MAX, // Bounding value. | |
725 }; | |
726 int category = RESOLVE_MAX; // Illegal value for later DCHECK only. | |
727 | |
728 base::TimeDelta duration = base::TimeTicks::Now() - start_time; | |
729 if (error == OK) { | |
730 if (had_non_speculative_request_) { | |
731 category = RESOLVE_SUCCESS; | |
732 DNS_HISTOGRAM("DNS.ResolveSuccess", duration); | |
733 } else { | |
734 category = RESOLVE_SPECULATIVE_SUCCESS; | |
735 DNS_HISTOGRAM("DNS.ResolveSpeculativeSuccess", duration); | |
736 } | |
737 | |
738 // Log DNS lookups based on |address_family|. This will help us determine | |
739 // if IPv4 or IPv4/6 lookups are faster or slower. | |
740 switch(key_.address_family) { | |
741 case ADDRESS_FAMILY_IPV4: | |
742 DNS_HISTOGRAM("DNS.ResolveSuccess_FAMILY_IPV4", duration); | |
743 break; | |
744 case ADDRESS_FAMILY_IPV6: | |
745 DNS_HISTOGRAM("DNS.ResolveSuccess_FAMILY_IPV6", duration); | |
746 break; | |
747 case ADDRESS_FAMILY_UNSPECIFIED: | |
748 DNS_HISTOGRAM("DNS.ResolveSuccess_FAMILY_UNSPEC", duration); | |
749 break; | |
750 } | |
751 } else { | |
752 if (had_non_speculative_request_) { | |
753 category = RESOLVE_FAIL; | |
754 DNS_HISTOGRAM("DNS.ResolveFail", duration); | |
755 } else { | |
756 category = RESOLVE_SPECULATIVE_FAIL; | |
757 DNS_HISTOGRAM("DNS.ResolveSpeculativeFail", duration); | |
758 } | |
759 // Log DNS lookups based on |address_family|. This will help us determine | |
760 // if IPv4 or IPv4/6 lookups are faster or slower. | |
761 switch(key_.address_family) { | |
762 case ADDRESS_FAMILY_IPV4: | |
763 DNS_HISTOGRAM("DNS.ResolveFail_FAMILY_IPV4", duration); | |
764 break; | |
765 case ADDRESS_FAMILY_IPV6: | |
766 DNS_HISTOGRAM("DNS.ResolveFail_FAMILY_IPV6", duration); | |
767 break; | |
768 case ADDRESS_FAMILY_UNSPECIFIED: | |
769 DNS_HISTOGRAM("DNS.ResolveFail_FAMILY_UNSPEC", duration); | |
770 break; | |
771 } | |
772 UMA_HISTOGRAM_CUSTOM_ENUMERATION(kOSErrorsForGetAddrinfoHistogramName, | |
773 std::abs(os_error), | |
774 GetAllGetAddrinfoOSErrors()); | |
775 } | |
776 DCHECK_LT(category, static_cast<int>(RESOLVE_MAX)); // Be sure it was set. | |
777 | |
778 UMA_HISTOGRAM_ENUMERATION("DNS.ResolveCategory", category, RESOLVE_MAX); | |
779 | |
780 static const bool show_parallelism_experiment_histograms = | |
781 base::FieldTrialList::TrialExists("DnsParallelism"); | |
782 if (show_parallelism_experiment_histograms) { | |
783 UMA_HISTOGRAM_ENUMERATION( | |
784 base::FieldTrial::MakeName("DNS.ResolveCategory", "DnsParallelism"), | |
785 category, RESOLVE_MAX); | |
786 if (RESOLVE_SUCCESS == category) { | |
787 DNS_HISTOGRAM(base::FieldTrial::MakeName("DNS.ResolveSuccess", | |
788 "DnsParallelism"), duration); | |
789 } | |
790 } | |
791 } | |
792 | |
793 void RecordAttemptHistograms(const base::TimeTicks& start_time, | |
794 const uint32 attempt_number, | |
795 const int error, | |
796 const int os_error) const { | |
797 DCHECK(origin_loop_->BelongsToCurrentThread()); | |
798 bool first_attempt_to_complete = | |
799 completed_attempt_number_ == attempt_number; | |
800 bool is_first_attempt = (attempt_number == 1); | |
801 | |
802 if (first_attempt_to_complete) { | |
803 // If this was first attempt to complete, then record the resolution | |
804 // status of the attempt. | |
805 if (completed_attempt_error_ == OK) { | |
806 UMA_HISTOGRAM_ENUMERATION( | |
807 "DNS.AttemptFirstSuccess", attempt_number, 100); | |
808 } else { | |
809 UMA_HISTOGRAM_ENUMERATION( | |
810 "DNS.AttemptFirstFailure", attempt_number, 100); | |
811 } | |
812 } | |
813 | |
814 if (error == OK) | |
815 UMA_HISTOGRAM_ENUMERATION("DNS.AttemptSuccess", attempt_number, 100); | |
816 else | |
817 UMA_HISTOGRAM_ENUMERATION("DNS.AttemptFailure", attempt_number, 100); | |
818 | |
819 // If first attempt didn't finish before retry attempt, then calculate stats | |
820 // on how much time is saved by having spawned an extra attempt. | |
821 if (!first_attempt_to_complete && is_first_attempt && !was_canceled()) { | |
822 DNS_HISTOGRAM("DNS.AttemptTimeSavedByRetry", | |
823 base::TimeTicks::Now() - retry_attempt_finished_time_); | |
824 } | |
825 | |
826 if (was_canceled() || !first_attempt_to_complete) { | |
827 // Count those attempts which completed after the job was already canceled | |
828 // OR after the job was already completed by an earlier attempt (so in | |
829 // effect). | |
830 UMA_HISTOGRAM_ENUMERATION("DNS.AttemptDiscarded", attempt_number, 100); | |
831 | |
832 // Record if job is canceled. | |
833 if (was_canceled()) | |
834 UMA_HISTOGRAM_ENUMERATION("DNS.AttemptCancelled", attempt_number, 100); | |
835 } | |
836 | |
837 base::TimeDelta duration = base::TimeTicks::Now() - start_time; | |
838 if (error == OK) | |
839 DNS_HISTOGRAM("DNS.AttemptSuccessDuration", duration); | |
840 else | |
841 DNS_HISTOGRAM("DNS.AttemptFailDuration", duration); | |
842 } | |
843 | |
844 // Set on the origin thread, read on the worker thread. | |
845 Key key_; | |
846 | |
847 // Holds an owning reference to the HostResolverProc that we are going to use. | |
848 // This may not be the current resolver procedure by the time we call | |
849 // ResolveAddrInfo, but that's OK... we'll use it anyways, and the owning | |
850 // reference ensures that it remains valid until we are done. | |
851 ProcTaskParams params_; | |
852 | |
853 // The listener to the results of this ProcTask. | |
854 Callback callback_; | |
855 | |
856 // Used to post ourselves onto the origin thread. | |
857 scoped_refptr<base::MessageLoopProxy> origin_loop_; | |
858 | |
859 // Keeps track of the number of attempts we have made so far to resolve the | |
860 // host. Whenever we start an attempt to resolve the host, we increase this | |
861 // number. | |
862 uint32 attempt_number_; | |
863 | |
864 // The index of the attempt which finished first (or 0 if the job is still in | |
865 // progress). | |
866 uint32 completed_attempt_number_; | |
867 | |
868 // The result (a net error code) from the first attempt to complete. | |
869 int completed_attempt_error_; | |
870 | |
871 // The time when retry attempt was finished. | |
872 base::TimeTicks retry_attempt_finished_time_; | |
873 | |
874 // True if a non-speculative request was ever attached to this job | |
875 // (regardless of whether or not it was later canceled. | |
876 // This boolean is used for histogramming the duration of jobs used to | |
877 // service non-speculative requests. | |
878 bool had_non_speculative_request_; | |
879 | |
880 AddressList results_; | |
881 | |
882 BoundNetLog net_log_; | |
883 | |
884 DISALLOW_COPY_AND_ASSIGN(ProcTask); | |
885 }; | |
886 | |
887 //----------------------------------------------------------------------------- | |
888 | |
889 // Wraps a call to TestIPv6Support to be executed on the WorkerPool as it takes | |
890 // 40-100ms. | |
891 class HostResolverImpl::IPv6ProbeJob { | |
892 public: | |
893 IPv6ProbeJob(const base::WeakPtr<HostResolverImpl>& resolver, NetLog* net_log) | |
894 : resolver_(resolver), | |
895 net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_IPV6_PROBE_JOB)), | |
896 result_(false, IPV6_SUPPORT_MAX, OK) { | |
897 DCHECK(resolver); | |
898 net_log_.BeginEvent(NetLog::TYPE_IPV6_PROBE_RUNNING); | |
899 const bool kIsSlow = true; | |
900 base::WorkerPool::PostTaskAndReply( | |
901 FROM_HERE, | |
902 base::Bind(&IPv6ProbeJob::DoProbe, base::Unretained(this)), | |
903 base::Bind(&IPv6ProbeJob::OnProbeComplete, base::Owned(this)), | |
904 kIsSlow); | |
905 } | |
906 | |
907 virtual ~IPv6ProbeJob() {} | |
908 | |
909 private: | |
910 // Runs on worker thread. | |
911 void DoProbe() { | |
912 result_ = TestIPv6Support(); | |
913 } | |
914 | |
915 void OnProbeComplete() { | |
916 net_log_.EndEvent(NetLog::TYPE_IPV6_PROBE_RUNNING, | |
917 base::Bind(&IPv6SupportResult::ToNetLogValue, | |
918 base::Unretained(&result_))); | |
919 if (!resolver_) | |
920 return; | |
921 resolver_->IPv6ProbeSetDefaultAddressFamily( | |
922 result_.ipv6_supported ? ADDRESS_FAMILY_UNSPECIFIED | |
923 : ADDRESS_FAMILY_IPV4); | |
924 } | |
925 | |
926 // Used/set only on origin thread. | |
927 base::WeakPtr<HostResolverImpl> resolver_; | |
928 | |
929 BoundNetLog net_log_; | |
930 | |
931 IPv6SupportResult result_; | |
932 | |
933 DISALLOW_COPY_AND_ASSIGN(IPv6ProbeJob); | |
934 }; | |
935 | |
936 // Wraps a call to HaveOnlyLoopbackAddresses to be executed on the WorkerPool as | |
937 // it takes 40-100ms and should not block initialization. | |
938 class HostResolverImpl::LoopbackProbeJob { | |
939 public: | |
940 explicit LoopbackProbeJob(const base::WeakPtr<HostResolverImpl>& resolver) | |
941 : resolver_(resolver), | |
942 result_(false) { | |
943 DCHECK(resolver); | |
944 const bool kIsSlow = true; | |
945 base::WorkerPool::PostTaskAndReply( | |
946 FROM_HERE, | |
947 base::Bind(&LoopbackProbeJob::DoProbe, base::Unretained(this)), | |
948 base::Bind(&LoopbackProbeJob::OnProbeComplete, base::Owned(this)), | |
949 kIsSlow); | |
950 } | |
951 | |
952 virtual ~LoopbackProbeJob() {} | |
953 | |
954 private: | |
955 // Runs on worker thread. | |
956 void DoProbe() { | |
957 result_ = HaveOnlyLoopbackAddresses(); | |
958 } | |
959 | |
960 void OnProbeComplete() { | |
961 if (!resolver_) | |
962 return; | |
963 resolver_->SetHaveOnlyLoopbackAddresses(result_); | |
964 } | |
965 | |
966 // Used/set only on origin thread. | |
967 base::WeakPtr<HostResolverImpl> resolver_; | |
968 | |
969 bool result_; | |
970 | |
971 DISALLOW_COPY_AND_ASSIGN(LoopbackProbeJob); | |
972 }; | |
973 | |
974 //----------------------------------------------------------------------------- | |
975 | |
976 // Resolves the hostname using DnsTransaction. | |
977 // TODO(szym): This could be moved to separate source file as well. | |
978 class HostResolverImpl::DnsTask : public base::SupportsWeakPtr<DnsTask> { | |
979 public: | |
980 typedef base::Callback<void(int net_error, | |
981 const AddressList& addr_list, | |
982 base::TimeDelta ttl)> Callback; | |
983 | |
984 DnsTask(DnsClient* client, | |
985 const Key& key, | |
986 const Callback& callback, | |
987 const BoundNetLog& job_net_log) | |
988 : client_(client), | |
989 family_(key.address_family), | |
990 callback_(callback), | |
991 net_log_(job_net_log) { | |
992 DCHECK(client); | |
993 DCHECK(!callback.is_null()); | |
994 | |
995 // If unspecified, do IPv4 first, because suffix search will be faster. | |
996 uint16 qtype = (family_ == ADDRESS_FAMILY_IPV6) ? | |
997 dns_protocol::kTypeAAAA : | |
998 dns_protocol::kTypeA; | |
999 transaction_ = client_->GetTransactionFactory()->CreateTransaction( | |
1000 key.hostname, | |
1001 qtype, | |
1002 base::Bind(&DnsTask::OnTransactionComplete, base::Unretained(this), | |
1003 true /* first_query */, base::TimeTicks::Now()), | |
1004 net_log_); | |
1005 } | |
1006 | |
1007 int Start() { | |
1008 net_log_.BeginEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_DNS_TASK); | |
1009 return transaction_->Start(); | |
1010 } | |
1011 | |
1012 private: | |
1013 void OnTransactionComplete(bool first_query, | |
1014 const base::TimeTicks& start_time, | |
1015 DnsTransaction* transaction, | |
1016 int net_error, | |
1017 const DnsResponse* response) { | |
1018 DCHECK(transaction); | |
1019 base::TimeDelta duration = base::TimeTicks::Now() - start_time; | |
1020 // Run |callback_| last since the owning Job will then delete this DnsTask. | |
1021 if (net_error != OK) { | |
1022 DNS_HISTOGRAM("AsyncDNS.TransactionFailure", duration); | |
1023 OnFailure(net_error, DnsResponse::DNS_PARSE_OK); | |
1024 return; | |
1025 } | |
1026 | |
1027 CHECK(response); | |
1028 DNS_HISTOGRAM("AsyncDNS.TransactionSuccess", duration); | |
1029 switch (transaction->GetType()) { | |
1030 case dns_protocol::kTypeA: | |
1031 DNS_HISTOGRAM("AsyncDNS.TransactionSuccess_A", duration); | |
1032 break; | |
1033 case dns_protocol::kTypeAAAA: | |
1034 DNS_HISTOGRAM("AsyncDNS.TransactionSuccess_AAAA", duration); | |
1035 break; | |
1036 } | |
1037 AddressList addr_list; | |
1038 base::TimeDelta ttl; | |
1039 DnsResponse::Result result = response->ParseToAddressList(&addr_list, &ttl); | |
1040 UMA_HISTOGRAM_ENUMERATION("AsyncDNS.ParseToAddressList", | |
1041 result, | |
1042 DnsResponse::DNS_PARSE_RESULT_MAX); | |
1043 if (result != DnsResponse::DNS_PARSE_OK) { | |
1044 // Fail even if the other query succeeds. | |
1045 OnFailure(ERR_DNS_MALFORMED_RESPONSE, result); | |
1046 return; | |
1047 } | |
1048 | |
1049 bool needs_sort = false; | |
1050 if (first_query) { | |
1051 DCHECK(client_->GetConfig()) << | |
1052 "Transaction should have been aborted when config changed!"; | |
1053 if (family_ == ADDRESS_FAMILY_IPV6) { | |
1054 needs_sort = (addr_list.size() > 1); | |
1055 } else if (family_ == ADDRESS_FAMILY_UNSPECIFIED) { | |
1056 first_addr_list_ = addr_list; | |
1057 first_ttl_ = ttl; | |
1058 // Use fully-qualified domain name to avoid search. | |
1059 transaction_ = client_->GetTransactionFactory()->CreateTransaction( | |
1060 response->GetDottedName() + ".", | |
1061 dns_protocol::kTypeAAAA, | |
1062 base::Bind(&DnsTask::OnTransactionComplete, base::Unretained(this), | |
1063 false /* first_query */, base::TimeTicks::Now()), | |
1064 net_log_); | |
1065 net_error = transaction_->Start(); | |
1066 if (net_error != ERR_IO_PENDING) | |
1067 OnFailure(net_error, DnsResponse::DNS_PARSE_OK); | |
1068 return; | |
1069 } | |
1070 } else { | |
1071 DCHECK_EQ(ADDRESS_FAMILY_UNSPECIFIED, family_); | |
1072 bool has_ipv6_addresses = !addr_list.empty(); | |
1073 if (!first_addr_list_.empty()) { | |
1074 ttl = std::min(ttl, first_ttl_); | |
1075 // Place IPv4 addresses after IPv6. | |
1076 addr_list.insert(addr_list.end(), first_addr_list_.begin(), | |
1077 first_addr_list_.end()); | |
1078 } | |
1079 needs_sort = (has_ipv6_addresses && addr_list.size() > 1); | |
1080 } | |
1081 | |
1082 if (addr_list.empty()) { | |
1083 // TODO(szym): Don't fallback to ProcTask in this case. | |
1084 OnFailure(ERR_NAME_NOT_RESOLVED, DnsResponse::DNS_PARSE_OK); | |
1085 return; | |
1086 } | |
1087 | |
1088 if (needs_sort) { | |
1089 // Sort could complete synchronously. | |
1090 client_->GetAddressSorter()->Sort( | |
1091 addr_list, | |
1092 base::Bind(&DnsTask::OnSortComplete, | |
1093 AsWeakPtr(), | |
1094 base::TimeTicks::Now(), | |
1095 ttl)); | |
1096 } else { | |
1097 OnSuccess(addr_list, ttl); | |
1098 } | |
1099 } | |
1100 | |
1101 void OnSortComplete(base::TimeTicks start_time, | |
1102 base::TimeDelta ttl, | |
1103 bool success, | |
1104 const AddressList& addr_list) { | |
1105 if (!success) { | |
1106 DNS_HISTOGRAM("AsyncDNS.SortFailure", | |
1107 base::TimeTicks::Now() - start_time); | |
1108 OnFailure(ERR_DNS_SORT_ERROR, DnsResponse::DNS_PARSE_OK); | |
1109 return; | |
1110 } | |
1111 | |
1112 DNS_HISTOGRAM("AsyncDNS.SortSuccess", | |
1113 base::TimeTicks::Now() - start_time); | |
1114 | |
1115 // AddressSorter prunes unusable destinations. | |
1116 if (addr_list.empty()) { | |
1117 LOG(WARNING) << "Address list empty after RFC3484 sort"; | |
1118 OnFailure(ERR_NAME_NOT_RESOLVED, DnsResponse::DNS_PARSE_OK); | |
1119 return; | |
1120 } | |
1121 | |
1122 OnSuccess(addr_list, ttl); | |
1123 } | |
1124 | |
1125 void OnFailure(int net_error, DnsResponse::Result result) { | |
1126 DCHECK_NE(OK, net_error); | |
1127 net_log_.EndEvent( | |
1128 NetLog::TYPE_HOST_RESOLVER_IMPL_DNS_TASK, | |
1129 base::Bind(&NetLogDnsTaskFailedCallback, net_error, result)); | |
1130 callback_.Run(net_error, AddressList(), base::TimeDelta()); | |
1131 } | |
1132 | |
1133 void OnSuccess(const AddressList& addr_list, base::TimeDelta ttl) { | |
1134 net_log_.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_DNS_TASK, | |
1135 addr_list.CreateNetLogCallback()); | |
1136 callback_.Run(OK, addr_list, ttl); | |
1137 } | |
1138 | |
1139 DnsClient* client_; | |
1140 AddressFamily family_; | |
1141 // The listener to the results of this DnsTask. | |
1142 Callback callback_; | |
1143 const BoundNetLog net_log_; | |
1144 | |
1145 scoped_ptr<DnsTransaction> transaction_; | |
1146 | |
1147 // Results from the first transaction. Used only if |family_| is unspecified. | |
1148 AddressList first_addr_list_; | |
1149 base::TimeDelta first_ttl_; | |
1150 | |
1151 DISALLOW_COPY_AND_ASSIGN(DnsTask); | |
1152 }; | |
1153 | |
1154 //----------------------------------------------------------------------------- | |
1155 | |
1156 // Aggregates all Requests for the same Key. Dispatched via PriorityDispatch. | |
1157 class HostResolverImpl::Job : public PrioritizedDispatcher::Job { | |
1158 public: | |
1159 // Creates new job for |key| where |request_net_log| is bound to the | |
1160 // request that spawned it. | |
1161 Job(const base::WeakPtr<HostResolverImpl>& resolver, | |
1162 const Key& key, | |
1163 RequestPriority priority, | |
1164 const BoundNetLog& request_net_log) | |
1165 : resolver_(resolver), | |
1166 key_(key), | |
1167 priority_tracker_(priority), | |
1168 had_non_speculative_request_(false), | |
1169 had_dns_config_(false), | |
1170 dns_task_error_(OK), | |
1171 creation_time_(base::TimeTicks::Now()), | |
1172 priority_change_time_(creation_time_), | |
1173 net_log_(BoundNetLog::Make(request_net_log.net_log(), | |
1174 NetLog::SOURCE_HOST_RESOLVER_IMPL_JOB)) { | |
1175 request_net_log.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_CREATE_JOB); | |
1176 | |
1177 net_log_.BeginEvent( | |
1178 NetLog::TYPE_HOST_RESOLVER_IMPL_JOB, | |
1179 base::Bind(&NetLogJobCreationCallback, | |
1180 request_net_log.source(), | |
1181 &key_.hostname)); | |
1182 } | |
1183 | |
1184 virtual ~Job() { | |
1185 if (is_running()) { | |
1186 // |resolver_| was destroyed with this Job still in flight. | |
1187 // Clean-up, record in the log, but don't run any callbacks. | |
1188 if (is_proc_running()) { | |
1189 proc_task_->Cancel(); | |
1190 proc_task_ = NULL; | |
1191 } | |
1192 // Clean up now for nice NetLog. | |
1193 dns_task_.reset(NULL); | |
1194 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HOST_RESOLVER_IMPL_JOB, | |
1195 ERR_ABORTED); | |
1196 } else if (is_queued()) { | |
1197 // |resolver_| was destroyed without running this Job. | |
1198 // TODO(szym): is there any benefit in having this distinction? | |
1199 net_log_.AddEvent(NetLog::TYPE_CANCELLED); | |
1200 net_log_.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_JOB); | |
1201 } | |
1202 // else CompleteRequests logged EndEvent. | |
1203 | |
1204 // Log any remaining Requests as cancelled. | |
1205 for (RequestsList::const_iterator it = requests_.begin(); | |
1206 it != requests_.end(); ++it) { | |
1207 Request* req = *it; | |
1208 if (req->was_canceled()) | |
1209 continue; | |
1210 DCHECK_EQ(this, req->job()); | |
1211 LogCancelRequest(req->source_net_log(), req->request_net_log(), | |
1212 req->info()); | |
1213 } | |
1214 } | |
1215 | |
1216 // Add this job to the dispatcher. | |
1217 void Schedule() { | |
1218 handle_ = resolver_->dispatcher_.Add(this, priority()); | |
1219 } | |
1220 | |
1221 void AddRequest(scoped_ptr<Request> req) { | |
1222 DCHECK_EQ(key_.hostname, req->info().hostname()); | |
1223 | |
1224 req->set_job(this); | |
1225 priority_tracker_.Add(req->info().priority()); | |
1226 | |
1227 req->request_net_log().AddEvent( | |
1228 NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_ATTACH, | |
1229 net_log_.source().ToEventParametersCallback()); | |
1230 | |
1231 net_log_.AddEvent( | |
1232 NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_REQUEST_ATTACH, | |
1233 base::Bind(&NetLogJobAttachCallback, | |
1234 req->request_net_log().source(), | |
1235 priority())); | |
1236 | |
1237 // TODO(szym): Check if this is still needed. | |
1238 if (!req->info().is_speculative()) { | |
1239 had_non_speculative_request_ = true; | |
1240 if (proc_task_) | |
1241 proc_task_->set_had_non_speculative_request(); | |
1242 } | |
1243 | |
1244 requests_.push_back(req.release()); | |
1245 | |
1246 UpdatePriority(); | |
1247 } | |
1248 | |
1249 // Marks |req| as cancelled. If it was the last active Request, also finishes | |
1250 // this Job, marking it as cancelled, and deletes it. | |
1251 void CancelRequest(Request* req) { | |
1252 DCHECK_EQ(key_.hostname, req->info().hostname()); | |
1253 DCHECK(!req->was_canceled()); | |
1254 | |
1255 // Don't remove it from |requests_| just mark it canceled. | |
1256 req->MarkAsCanceled(); | |
1257 LogCancelRequest(req->source_net_log(), req->request_net_log(), | |
1258 req->info()); | |
1259 | |
1260 priority_tracker_.Remove(req->info().priority()); | |
1261 net_log_.AddEvent( | |
1262 NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_REQUEST_DETACH, | |
1263 base::Bind(&NetLogJobAttachCallback, | |
1264 req->request_net_log().source(), | |
1265 priority())); | |
1266 | |
1267 if (num_active_requests() > 0) { | |
1268 UpdatePriority(); | |
1269 } else { | |
1270 // If we were called from a Request's callback within CompleteRequests, | |
1271 // that Request could not have been cancelled, so num_active_requests() | |
1272 // could not be 0. Therefore, we are not in CompleteRequests(). | |
1273 CompleteRequestsWithError(OK /* cancelled */); | |
1274 } | |
1275 } | |
1276 | |
1277 // Called from AbortAllInProgressJobs. Completes all requests and destroys | |
1278 // the job. This currently assumes the abort is due to a network change. | |
1279 void Abort() { | |
1280 DCHECK(is_running()); | |
1281 CompleteRequestsWithError(ERR_NETWORK_CHANGED); | |
1282 } | |
1283 | |
1284 // If DnsTask present, abort it and fall back to ProcTask. | |
1285 void AbortDnsTask() { | |
1286 if (dns_task_) { | |
1287 dns_task_.reset(); | |
1288 dns_task_error_ = OK; | |
1289 StartProcTask(); | |
1290 } | |
1291 } | |
1292 | |
1293 // Called by HostResolverImpl when this job is evicted due to queue overflow. | |
1294 // Completes all requests and destroys the job. | |
1295 void OnEvicted() { | |
1296 DCHECK(!is_running()); | |
1297 DCHECK(is_queued()); | |
1298 handle_.Reset(); | |
1299 | |
1300 net_log_.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_EVICTED); | |
1301 | |
1302 // This signals to CompleteRequests that this job never ran. | |
1303 CompleteRequestsWithError(ERR_HOST_RESOLVER_QUEUE_TOO_LARGE); | |
1304 } | |
1305 | |
1306 // Attempts to serve the job from HOSTS. Returns true if succeeded and | |
1307 // this Job was destroyed. | |
1308 bool ServeFromHosts() { | |
1309 DCHECK_GT(num_active_requests(), 0u); | |
1310 AddressList addr_list; | |
1311 if (resolver_->ServeFromHosts(key(), | |
1312 requests_.front()->info(), | |
1313 &addr_list)) { | |
1314 // This will destroy the Job. | |
1315 CompleteRequests( | |
1316 HostCache::Entry(OK, MakeAddressListForRequest(addr_list)), | |
1317 base::TimeDelta()); | |
1318 return true; | |
1319 } | |
1320 return false; | |
1321 } | |
1322 | |
1323 const Key key() const { | |
1324 return key_; | |
1325 } | |
1326 | |
1327 bool is_queued() const { | |
1328 return !handle_.is_null(); | |
1329 } | |
1330 | |
1331 bool is_running() const { | |
1332 return is_dns_running() || is_proc_running(); | |
1333 } | |
1334 | |
1335 private: | |
1336 void UpdatePriority() { | |
1337 if (is_queued()) { | |
1338 if (priority() != static_cast<RequestPriority>(handle_.priority())) | |
1339 priority_change_time_ = base::TimeTicks::Now(); | |
1340 handle_ = resolver_->dispatcher_.ChangePriority(handle_, priority()); | |
1341 } | |
1342 } | |
1343 | |
1344 AddressList MakeAddressListForRequest(const AddressList& list) const { | |
1345 if (requests_.empty()) | |
1346 return list; | |
1347 return AddressList::CopyWithPort(list, requests_.front()->info().port()); | |
1348 } | |
1349 | |
1350 // PriorityDispatch::Job: | |
1351 virtual void Start() OVERRIDE { | |
1352 DCHECK(!is_running()); | |
1353 handle_.Reset(); | |
1354 | |
1355 net_log_.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_STARTED); | |
1356 | |
1357 had_dns_config_ = resolver_->HaveDnsConfig(); | |
1358 | |
1359 base::TimeTicks now = base::TimeTicks::Now(); | |
1360 base::TimeDelta queue_time = now - creation_time_; | |
1361 base::TimeDelta queue_time_after_change = now - priority_change_time_; | |
1362 | |
1363 if (had_dns_config_) { | |
1364 DNS_HISTOGRAM_BY_PRIORITY("AsyncDNS.JobQueueTime", priority(), | |
1365 queue_time); | |
1366 DNS_HISTOGRAM_BY_PRIORITY("AsyncDNS.JobQueueTimeAfterChange", priority(), | |
1367 queue_time_after_change); | |
1368 } else { | |
1369 DNS_HISTOGRAM_BY_PRIORITY("DNS.JobQueueTime", priority(), queue_time); | |
1370 DNS_HISTOGRAM_BY_PRIORITY("DNS.JobQueueTimeAfterChange", priority(), | |
1371 queue_time_after_change); | |
1372 } | |
1373 | |
1374 // Caution: Job::Start must not complete synchronously. | |
1375 if (had_dns_config_ && !ResemblesMulticastDNSName(key_.hostname)) { | |
1376 StartDnsTask(); | |
1377 } else { | |
1378 StartProcTask(); | |
1379 } | |
1380 } | |
1381 | |
1382 // TODO(szym): Since DnsTransaction does not consume threads, we can increase | |
1383 // the limits on |dispatcher_|. But in order to keep the number of WorkerPool | |
1384 // threads low, we will need to use an "inner" PrioritizedDispatcher with | |
1385 // tighter limits. | |
1386 void StartProcTask() { | |
1387 DCHECK(!is_dns_running()); | |
1388 proc_task_ = new ProcTask( | |
1389 key_, | |
1390 resolver_->proc_params_, | |
1391 base::Bind(&Job::OnProcTaskComplete, base::Unretained(this), | |
1392 base::TimeTicks::Now()), | |
1393 net_log_); | |
1394 | |
1395 if (had_non_speculative_request_) | |
1396 proc_task_->set_had_non_speculative_request(); | |
1397 // Start() could be called from within Resolve(), hence it must NOT directly | |
1398 // call OnProcTaskComplete, for example, on synchronous failure. | |
1399 proc_task_->Start(); | |
1400 } | |
1401 | |
1402 // Called by ProcTask when it completes. | |
1403 void OnProcTaskComplete(base::TimeTicks start_time, | |
1404 int net_error, | |
1405 const AddressList& addr_list) { | |
1406 DCHECK(is_proc_running()); | |
1407 | |
1408 if (!resolver_->resolved_known_ipv6_hostname_ && | |
1409 net_error == OK && | |
1410 key_.address_family == ADDRESS_FAMILY_UNSPECIFIED) { | |
1411 if (key_.hostname == "www.google.com") { | |
1412 resolver_->resolved_known_ipv6_hostname_ = true; | |
1413 bool got_ipv6_address = false; | |
1414 for (size_t i = 0; i < addr_list.size(); ++i) { | |
1415 if (addr_list[i].GetFamily() == ADDRESS_FAMILY_IPV6) | |
1416 got_ipv6_address = true; | |
1417 } | |
1418 UMA_HISTOGRAM_BOOLEAN("Net.UnspecResolvedIPv6", got_ipv6_address); | |
1419 } | |
1420 } | |
1421 | |
1422 if (dns_task_error_ != OK) { | |
1423 base::TimeDelta duration = base::TimeTicks::Now() - start_time; | |
1424 if (net_error == OK) { | |
1425 DNS_HISTOGRAM("AsyncDNS.FallbackSuccess", duration); | |
1426 if ((dns_task_error_ == ERR_NAME_NOT_RESOLVED) && | |
1427 ResemblesNetBIOSName(key_.hostname)) { | |
1428 UmaAsyncDnsResolveStatus(RESOLVE_STATUS_SUSPECT_NETBIOS); | |
1429 } else { | |
1430 UmaAsyncDnsResolveStatus(RESOLVE_STATUS_PROC_SUCCESS); | |
1431 } | |
1432 UMA_HISTOGRAM_CUSTOM_ENUMERATION("AsyncDNS.ResolveError", | |
1433 std::abs(dns_task_error_), | |
1434 GetAllErrorCodesForUma()); | |
1435 resolver_->OnDnsTaskResolve(dns_task_error_); | |
1436 } else { | |
1437 DNS_HISTOGRAM("AsyncDNS.FallbackFail", duration); | |
1438 UmaAsyncDnsResolveStatus(RESOLVE_STATUS_FAIL); | |
1439 } | |
1440 } | |
1441 | |
1442 base::TimeDelta ttl = | |
1443 base::TimeDelta::FromSeconds(kNegativeCacheEntryTTLSeconds); | |
1444 if (net_error == OK) | |
1445 ttl = base::TimeDelta::FromSeconds(kCacheEntryTTLSeconds); | |
1446 | |
1447 // Don't store the |ttl| in cache since it's not obtained from the server. | |
1448 CompleteRequests( | |
1449 HostCache::Entry(net_error, MakeAddressListForRequest(addr_list)), | |
1450 ttl); | |
1451 } | |
1452 | |
1453 void StartDnsTask() { | |
1454 DCHECK(resolver_->HaveDnsConfig()); | |
1455 dns_task_.reset(new DnsTask( | |
1456 resolver_->dns_client_.get(), | |
1457 key_, | |
1458 base::Bind(&Job::OnDnsTaskComplete, base::Unretained(this), | |
1459 base::TimeTicks::Now()), | |
1460 net_log_)); | |
1461 | |
1462 int rv = dns_task_->Start(); | |
1463 if (rv != ERR_IO_PENDING) { | |
1464 DCHECK_NE(OK, rv); | |
1465 dns_task_error_ = rv; | |
1466 dns_task_.reset(); | |
1467 StartProcTask(); | |
1468 } | |
1469 } | |
1470 | |
1471 // Called by DnsTask when it completes. | |
1472 void OnDnsTaskComplete(base::TimeTicks start_time, | |
1473 int net_error, | |
1474 const AddressList& addr_list, | |
1475 base::TimeDelta ttl) { | |
1476 DCHECK(is_dns_running()); | |
1477 | |
1478 base::TimeDelta duration = base::TimeTicks::Now() - start_time; | |
1479 if (net_error != OK) { | |
1480 DNS_HISTOGRAM("AsyncDNS.ResolveFail", duration); | |
1481 | |
1482 dns_task_error_ = net_error; | |
1483 dns_task_.reset(); | |
1484 | |
1485 // TODO(szym): Run ServeFromHosts now if nsswitch.conf says so. | |
1486 // http://crbug.com/117655 | |
1487 | |
1488 // TODO(szym): Some net errors indicate lack of connectivity. Starting | |
1489 // ProcTask in that case is a waste of time. | |
1490 StartProcTask(); | |
1491 return; | |
1492 } | |
1493 DNS_HISTOGRAM("AsyncDNS.ResolveSuccess", duration); | |
1494 // Log DNS lookups based on |address_family|. | |
1495 switch(key_.address_family) { | |
1496 case ADDRESS_FAMILY_IPV4: | |
1497 DNS_HISTOGRAM("AsyncDNS.ResolveSuccess_FAMILY_IPV4", duration); | |
1498 break; | |
1499 case ADDRESS_FAMILY_IPV6: | |
1500 DNS_HISTOGRAM("AsyncDNS.ResolveSuccess_FAMILY_IPV6", duration); | |
1501 break; | |
1502 case ADDRESS_FAMILY_UNSPECIFIED: | |
1503 DNS_HISTOGRAM("AsyncDNS.ResolveSuccess_FAMILY_UNSPEC", duration); | |
1504 break; | |
1505 } | |
1506 | |
1507 UmaAsyncDnsResolveStatus(RESOLVE_STATUS_DNS_SUCCESS); | |
1508 RecordTTL(ttl); | |
1509 | |
1510 resolver_->OnDnsTaskResolve(OK); | |
1511 | |
1512 base::TimeDelta bounded_ttl = | |
1513 std::max(ttl, base::TimeDelta::FromSeconds(kMinimumTTLSeconds)); | |
1514 | |
1515 CompleteRequests( | |
1516 HostCache::Entry(net_error, MakeAddressListForRequest(addr_list), ttl), | |
1517 bounded_ttl); | |
1518 } | |
1519 | |
1520 // Performs Job's last rites. Completes all Requests. Deletes this. | |
1521 void CompleteRequests(const HostCache::Entry& entry, | |
1522 base::TimeDelta ttl) { | |
1523 CHECK(resolver_); | |
1524 | |
1525 // This job must be removed from resolver's |jobs_| now to make room for a | |
1526 // new job with the same key in case one of the OnComplete callbacks decides | |
1527 // to spawn one. Consequently, the job deletes itself when CompleteRequests | |
1528 // is done. | |
1529 scoped_ptr<Job> self_deleter(this); | |
1530 | |
1531 resolver_->RemoveJob(this); | |
1532 | |
1533 if (is_running()) { | |
1534 DCHECK(!is_queued()); | |
1535 if (is_proc_running()) { | |
1536 proc_task_->Cancel(); | |
1537 proc_task_ = NULL; | |
1538 } | |
1539 dns_task_.reset(); | |
1540 | |
1541 // Signal dispatcher that a slot has opened. | |
1542 resolver_->dispatcher_.OnJobFinished(); | |
1543 } else if (is_queued()) { | |
1544 resolver_->dispatcher_.Cancel(handle_); | |
1545 handle_.Reset(); | |
1546 } | |
1547 | |
1548 if (num_active_requests() == 0) { | |
1549 net_log_.AddEvent(NetLog::TYPE_CANCELLED); | |
1550 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HOST_RESOLVER_IMPL_JOB, | |
1551 OK); | |
1552 return; | |
1553 } | |
1554 | |
1555 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HOST_RESOLVER_IMPL_JOB, | |
1556 entry.error); | |
1557 | |
1558 DCHECK(!requests_.empty()); | |
1559 | |
1560 if (entry.error == OK) { | |
1561 // Record this histogram here, when we know the system has a valid DNS | |
1562 // configuration. | |
1563 UMA_HISTOGRAM_BOOLEAN("AsyncDNS.HaveDnsConfig", | |
1564 resolver_->received_dns_config_); | |
1565 } | |
1566 | |
1567 bool did_complete = (entry.error != ERR_NETWORK_CHANGED) && | |
1568 (entry.error != ERR_HOST_RESOLVER_QUEUE_TOO_LARGE); | |
1569 if (did_complete) | |
1570 resolver_->CacheResult(key_, entry, ttl); | |
1571 | |
1572 // Complete all of the requests that were attached to the job. | |
1573 for (RequestsList::const_iterator it = requests_.begin(); | |
1574 it != requests_.end(); ++it) { | |
1575 Request* req = *it; | |
1576 | |
1577 if (req->was_canceled()) | |
1578 continue; | |
1579 | |
1580 DCHECK_EQ(this, req->job()); | |
1581 // Update the net log and notify registered observers. | |
1582 LogFinishRequest(req->source_net_log(), req->request_net_log(), | |
1583 req->info(), entry.error); | |
1584 if (did_complete) { | |
1585 // Record effective total time from creation to completion. | |
1586 RecordTotalTime(had_dns_config_, req->info().is_speculative(), | |
1587 base::TimeTicks::Now() - req->request_time()); | |
1588 } | |
1589 req->OnComplete(entry.error, entry.addrlist); | |
1590 | |
1591 // Check if the resolver was destroyed as a result of running the | |
1592 // callback. If it was, we could continue, but we choose to bail. | |
1593 if (!resolver_) | |
1594 return; | |
1595 } | |
1596 } | |
1597 | |
1598 // Convenience wrapper for CompleteRequests in case of failure. | |
1599 void CompleteRequestsWithError(int net_error) { | |
1600 CompleteRequests(HostCache::Entry(net_error, AddressList()), | |
1601 base::TimeDelta()); | |
1602 } | |
1603 | |
1604 RequestPriority priority() const { | |
1605 return priority_tracker_.highest_priority(); | |
1606 } | |
1607 | |
1608 // Number of non-canceled requests in |requests_|. | |
1609 size_t num_active_requests() const { | |
1610 return priority_tracker_.total_count(); | |
1611 } | |
1612 | |
1613 bool is_dns_running() const { | |
1614 return dns_task_.get() != NULL; | |
1615 } | |
1616 | |
1617 bool is_proc_running() const { | |
1618 return proc_task_.get() != NULL; | |
1619 } | |
1620 | |
1621 base::WeakPtr<HostResolverImpl> resolver_; | |
1622 | |
1623 Key key_; | |
1624 | |
1625 // Tracks the highest priority across |requests_|. | |
1626 PriorityTracker priority_tracker_; | |
1627 | |
1628 bool had_non_speculative_request_; | |
1629 | |
1630 // Distinguishes measurements taken while DnsClient was fully configured. | |
1631 bool had_dns_config_; | |
1632 | |
1633 // Result of DnsTask. | |
1634 int dns_task_error_; | |
1635 | |
1636 const base::TimeTicks creation_time_; | |
1637 base::TimeTicks priority_change_time_; | |
1638 | |
1639 BoundNetLog net_log_; | |
1640 | |
1641 // Resolves the host using a HostResolverProc. | |
1642 scoped_refptr<ProcTask> proc_task_; | |
1643 | |
1644 // Resolves the host using a DnsTransaction. | |
1645 scoped_ptr<DnsTask> dns_task_; | |
1646 | |
1647 // All Requests waiting for the result of this Job. Some can be canceled. | |
1648 RequestsList requests_; | |
1649 | |
1650 // A handle used in |HostResolverImpl::dispatcher_|. | |
1651 PrioritizedDispatcher::Handle handle_; | |
1652 }; | |
1653 | |
1654 //----------------------------------------------------------------------------- | |
1655 | |
1656 HostResolverImpl::ProcTaskParams::ProcTaskParams( | |
1657 HostResolverProc* resolver_proc, | |
1658 size_t max_retry_attempts) | |
1659 : resolver_proc(resolver_proc), | |
1660 max_retry_attempts(max_retry_attempts), | |
1661 unresponsive_delay(base::TimeDelta::FromMilliseconds(6000)), | |
1662 retry_factor(2) { | |
1663 } | |
1664 | |
1665 HostResolverImpl::ProcTaskParams::~ProcTaskParams() {} | |
1666 | |
1667 HostResolverImpl::HostResolverImpl( | |
1668 scoped_ptr<HostCache> cache, | |
1669 const PrioritizedDispatcher::Limits& job_limits, | |
1670 const ProcTaskParams& proc_params, | |
1671 NetLog* net_log) | |
1672 : cache_(cache.Pass()), | |
1673 dispatcher_(job_limits), | |
1674 max_queued_jobs_(job_limits.total_jobs * 100u), | |
1675 proc_params_(proc_params), | |
1676 net_log_(net_log), | |
1677 default_address_family_(ADDRESS_FAMILY_UNSPECIFIED), | |
1678 weak_ptr_factory_(this), | |
1679 probe_weak_ptr_factory_(this), | |
1680 received_dns_config_(false), | |
1681 num_dns_failures_(0), | |
1682 ipv6_probe_monitoring_(false), | |
1683 resolved_known_ipv6_hostname_(false), | |
1684 additional_resolver_flags_(0) { | |
1685 | |
1686 DCHECK_GE(dispatcher_.num_priorities(), static_cast<size_t>(NUM_PRIORITIES)); | |
1687 | |
1688 // Maximum of 4 retry attempts for host resolution. | |
1689 static const size_t kDefaultMaxRetryAttempts = 4u; | |
1690 | |
1691 if (proc_params_.max_retry_attempts == HostResolver::kDefaultRetryAttempts) | |
1692 proc_params_.max_retry_attempts = kDefaultMaxRetryAttempts; | |
1693 | |
1694 #if defined(OS_WIN) | |
1695 EnsureWinsockInit(); | |
1696 #endif | |
1697 #if defined(OS_POSIX) && !defined(OS_MACOSX) | |
1698 new LoopbackProbeJob(weak_ptr_factory_.GetWeakPtr()); | |
1699 #endif | |
1700 NetworkChangeNotifier::AddIPAddressObserver(this); | |
1701 NetworkChangeNotifier::AddDNSObserver(this); | |
1702 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD) && \ | |
1703 !defined(OS_ANDROID) | |
1704 EnsureDnsReloaderInit(); | |
1705 #endif | |
1706 | |
1707 // TODO(szym): Remove when received_dns_config_ is removed, once | |
1708 // http://crbug.com/137914 is resolved. | |
1709 { | |
1710 DnsConfig dns_config; | |
1711 NetworkChangeNotifier::GetDnsConfig(&dns_config); | |
1712 received_dns_config_ = dns_config.IsValid(); | |
1713 } | |
1714 } | |
1715 | |
1716 HostResolverImpl::~HostResolverImpl() { | |
1717 // This will also cancel all outstanding requests. | |
1718 STLDeleteValues(&jobs_); | |
1719 | |
1720 NetworkChangeNotifier::RemoveIPAddressObserver(this); | |
1721 NetworkChangeNotifier::RemoveDNSObserver(this); | |
1722 } | |
1723 | |
1724 void HostResolverImpl::SetMaxQueuedJobs(size_t value) { | |
1725 DCHECK_EQ(0u, dispatcher_.num_queued_jobs()); | |
1726 DCHECK_GT(value, 0u); | |
1727 max_queued_jobs_ = value; | |
1728 } | |
1729 | |
1730 int HostResolverImpl::Resolve(const RequestInfo& info, | |
1731 AddressList* addresses, | |
1732 const CompletionCallback& callback, | |
1733 RequestHandle* out_req, | |
1734 const BoundNetLog& source_net_log) { | |
1735 DCHECK(addresses); | |
1736 DCHECK(CalledOnValidThread()); | |
1737 DCHECK_EQ(false, callback.is_null()); | |
1738 | |
1739 // Make a log item for the request. | |
1740 BoundNetLog request_net_log = BoundNetLog::Make(net_log_, | |
1741 NetLog::SOURCE_HOST_RESOLVER_IMPL_REQUEST); | |
1742 | |
1743 LogStartRequest(source_net_log, request_net_log, info); | |
1744 | |
1745 // Build a key that identifies the request in the cache and in the | |
1746 // outstanding jobs map. | |
1747 Key key = GetEffectiveKeyForRequest(info); | |
1748 | |
1749 int rv = ResolveHelper(key, info, addresses, request_net_log); | |
1750 if (rv != ERR_DNS_CACHE_MISS) { | |
1751 LogFinishRequest(source_net_log, request_net_log, info, rv); | |
1752 RecordTotalTime(HaveDnsConfig(), info.is_speculative(), base::TimeDelta()); | |
1753 return rv; | |
1754 } | |
1755 | |
1756 // Next we need to attach our request to a "job". This job is responsible for | |
1757 // calling "getaddrinfo(hostname)" on a worker thread. | |
1758 | |
1759 JobMap::iterator jobit = jobs_.find(key); | |
1760 Job* job; | |
1761 if (jobit == jobs_.end()) { | |
1762 job = new Job(weak_ptr_factory_.GetWeakPtr(), key, info.priority(), | |
1763 request_net_log); | |
1764 job->Schedule(); | |
1765 | |
1766 // Check for queue overflow. | |
1767 if (dispatcher_.num_queued_jobs() > max_queued_jobs_) { | |
1768 Job* evicted = static_cast<Job*>(dispatcher_.EvictOldestLowest()); | |
1769 DCHECK(evicted); | |
1770 evicted->OnEvicted(); // Deletes |evicted|. | |
1771 if (evicted == job) { | |
1772 rv = ERR_HOST_RESOLVER_QUEUE_TOO_LARGE; | |
1773 LogFinishRequest(source_net_log, request_net_log, info, rv); | |
1774 return rv; | |
1775 } | |
1776 } | |
1777 jobs_.insert(jobit, std::make_pair(key, job)); | |
1778 } else { | |
1779 job = jobit->second; | |
1780 } | |
1781 | |
1782 // Can't complete synchronously. Create and attach request. | |
1783 scoped_ptr<Request> req(new Request(source_net_log, | |
1784 request_net_log, | |
1785 info, | |
1786 callback, | |
1787 addresses)); | |
1788 if (out_req) | |
1789 *out_req = reinterpret_cast<RequestHandle>(req.get()); | |
1790 | |
1791 job->AddRequest(req.Pass()); | |
1792 // Completion happens during Job::CompleteRequests(). | |
1793 return ERR_IO_PENDING; | |
1794 } | |
1795 | |
1796 int HostResolverImpl::ResolveHelper(const Key& key, | |
1797 const RequestInfo& info, | |
1798 AddressList* addresses, | |
1799 const BoundNetLog& request_net_log) { | |
1800 // The result of |getaddrinfo| for empty hosts is inconsistent across systems. | |
1801 // On Windows it gives the default interface's address, whereas on Linux it | |
1802 // gives an error. We will make it fail on all platforms for consistency. | |
1803 if (info.hostname().empty() || info.hostname().size() > kMaxHostLength) | |
1804 return ERR_NAME_NOT_RESOLVED; | |
1805 | |
1806 int net_error = ERR_UNEXPECTED; | |
1807 if (ResolveAsIP(key, info, &net_error, addresses)) | |
1808 return net_error; | |
1809 if (ServeFromCache(key, info, &net_error, addresses)) { | |
1810 request_net_log.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_CACHE_HIT); | |
1811 return net_error; | |
1812 } | |
1813 // TODO(szym): Do not do this if nsswitch.conf instructs not to. | |
1814 // http://crbug.com/117655 | |
1815 if (ServeFromHosts(key, info, addresses)) { | |
1816 request_net_log.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_HOSTS_HIT); | |
1817 return OK; | |
1818 } | |
1819 return ERR_DNS_CACHE_MISS; | |
1820 } | |
1821 | |
1822 int HostResolverImpl::ResolveFromCache(const RequestInfo& info, | |
1823 AddressList* addresses, | |
1824 const BoundNetLog& source_net_log) { | |
1825 DCHECK(CalledOnValidThread()); | |
1826 DCHECK(addresses); | |
1827 | |
1828 // Make a log item for the request. | |
1829 BoundNetLog request_net_log = BoundNetLog::Make(net_log_, | |
1830 NetLog::SOURCE_HOST_RESOLVER_IMPL_REQUEST); | |
1831 | |
1832 // Update the net log and notify registered observers. | |
1833 LogStartRequest(source_net_log, request_net_log, info); | |
1834 | |
1835 Key key = GetEffectiveKeyForRequest(info); | |
1836 | |
1837 int rv = ResolveHelper(key, info, addresses, request_net_log); | |
1838 LogFinishRequest(source_net_log, request_net_log, info, rv); | |
1839 return rv; | |
1840 } | |
1841 | |
1842 void HostResolverImpl::CancelRequest(RequestHandle req_handle) { | |
1843 DCHECK(CalledOnValidThread()); | |
1844 Request* req = reinterpret_cast<Request*>(req_handle); | |
1845 DCHECK(req); | |
1846 Job* job = req->job(); | |
1847 DCHECK(job); | |
1848 job->CancelRequest(req); | |
1849 } | |
1850 | |
1851 void HostResolverImpl::SetDefaultAddressFamily(AddressFamily address_family) { | |
1852 DCHECK(CalledOnValidThread()); | |
1853 default_address_family_ = address_family; | |
1854 ipv6_probe_monitoring_ = false; | |
1855 } | |
1856 | |
1857 AddressFamily HostResolverImpl::GetDefaultAddressFamily() const { | |
1858 return default_address_family_; | |
1859 } | |
1860 | |
1861 void HostResolverImpl::ProbeIPv6Support() { | |
1862 DCHECK(CalledOnValidThread()); | |
1863 DCHECK(!ipv6_probe_monitoring_); | |
1864 ipv6_probe_monitoring_ = true; | |
1865 OnIPAddressChanged(); | |
1866 } | |
1867 | |
1868 void HostResolverImpl::SetDnsClientEnabled(bool enabled) { | |
1869 DCHECK(CalledOnValidThread()); | |
1870 #if defined(ENABLE_BUILT_IN_DNS) | |
1871 if (enabled && !dns_client_) { | |
1872 SetDnsClient(DnsClient::CreateClient(net_log_)); | |
1873 } else if (!enabled && dns_client_) { | |
1874 SetDnsClient(scoped_ptr<DnsClient>()); | |
1875 } | |
1876 #endif | |
1877 } | |
1878 | |
1879 HostCache* HostResolverImpl::GetHostCache() { | |
1880 return cache_.get(); | |
1881 } | |
1882 | |
1883 base::Value* HostResolverImpl::GetDnsConfigAsValue() const { | |
1884 // Check if async DNS is disabled. | |
1885 if (!dns_client_.get()) | |
1886 return NULL; | |
1887 | |
1888 // Check if async DNS is enabled, but we currently have no configuration | |
1889 // for it. | |
1890 const DnsConfig* dns_config = dns_client_->GetConfig(); | |
1891 if (dns_config == NULL) | |
1892 return new DictionaryValue(); | |
1893 | |
1894 return dns_config->ToValue(); | |
1895 } | |
1896 | |
1897 bool HostResolverImpl::ResolveAsIP(const Key& key, | |
1898 const RequestInfo& info, | |
1899 int* net_error, | |
1900 AddressList* addresses) { | |
1901 DCHECK(addresses); | |
1902 DCHECK(net_error); | |
1903 IPAddressNumber ip_number; | |
1904 if (!ParseIPLiteralToNumber(key.hostname, &ip_number)) | |
1905 return false; | |
1906 | |
1907 DCHECK_EQ(key.host_resolver_flags & | |
1908 ~(HOST_RESOLVER_CANONNAME | HOST_RESOLVER_LOOPBACK_ONLY | | |
1909 HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6), | |
1910 0) << " Unhandled flag"; | |
1911 bool ipv6_disabled = (default_address_family_ == ADDRESS_FAMILY_IPV4) && | |
1912 !ipv6_probe_monitoring_; | |
1913 *net_error = OK; | |
1914 if ((ip_number.size() == kIPv6AddressSize) && ipv6_disabled) { | |
1915 *net_error = ERR_NAME_NOT_RESOLVED; | |
1916 } else { | |
1917 *addresses = AddressList::CreateFromIPAddress(ip_number, info.port()); | |
1918 if (key.host_resolver_flags & HOST_RESOLVER_CANONNAME) | |
1919 addresses->SetDefaultCanonicalName(); | |
1920 } | |
1921 return true; | |
1922 } | |
1923 | |
1924 bool HostResolverImpl::ServeFromCache(const Key& key, | |
1925 const RequestInfo& info, | |
1926 int* net_error, | |
1927 AddressList* addresses) { | |
1928 DCHECK(addresses); | |
1929 DCHECK(net_error); | |
1930 if (!info.allow_cached_response() || !cache_.get()) | |
1931 return false; | |
1932 | |
1933 const HostCache::Entry* cache_entry = cache_->Lookup( | |
1934 key, base::TimeTicks::Now()); | |
1935 if (!cache_entry) | |
1936 return false; | |
1937 | |
1938 *net_error = cache_entry->error; | |
1939 if (*net_error == OK) { | |
1940 if (cache_entry->has_ttl()) | |
1941 RecordTTL(cache_entry->ttl); | |
1942 *addresses = EnsurePortOnAddressList(cache_entry->addrlist, info.port()); | |
1943 } | |
1944 return true; | |
1945 } | |
1946 | |
1947 bool HostResolverImpl::ServeFromHosts(const Key& key, | |
1948 const RequestInfo& info, | |
1949 AddressList* addresses) { | |
1950 DCHECK(addresses); | |
1951 if (!HaveDnsConfig()) | |
1952 return false; | |
1953 | |
1954 // HOSTS lookups are case-insensitive. | |
1955 std::string hostname = StringToLowerASCII(key.hostname); | |
1956 | |
1957 // If |address_family| is ADDRESS_FAMILY_UNSPECIFIED other implementations | |
1958 // (glibc and c-ares) return the first matching line. We have more | |
1959 // flexibility, but lose implicit ordering. | |
1960 // TODO(szym) http://crbug.com/117850 | |
1961 const DnsHosts& hosts = dns_client_->GetConfig()->hosts; | |
1962 DnsHosts::const_iterator it = hosts.find( | |
1963 DnsHostsKey(hostname, | |
1964 key.address_family == ADDRESS_FAMILY_UNSPECIFIED ? | |
1965 ADDRESS_FAMILY_IPV4 : key.address_family)); | |
1966 | |
1967 if (it == hosts.end()) { | |
1968 if (key.address_family != ADDRESS_FAMILY_UNSPECIFIED) | |
1969 return false; | |
1970 | |
1971 it = hosts.find(DnsHostsKey(hostname, ADDRESS_FAMILY_IPV6)); | |
1972 if (it == hosts.end()) | |
1973 return false; | |
1974 } | |
1975 | |
1976 *addresses = AddressList::CreateFromIPAddress(it->second, info.port()); | |
1977 return true; | |
1978 } | |
1979 | |
1980 void HostResolverImpl::CacheResult(const Key& key, | |
1981 const HostCache::Entry& entry, | |
1982 base::TimeDelta ttl) { | |
1983 if (cache_.get()) | |
1984 cache_->Set(key, entry, base::TimeTicks::Now(), ttl); | |
1985 } | |
1986 | |
1987 void HostResolverImpl::RemoveJob(Job* job) { | |
1988 DCHECK(job); | |
1989 JobMap::iterator it = jobs_.find(job->key()); | |
1990 if (it != jobs_.end() && it->second == job) | |
1991 jobs_.erase(it); | |
1992 } | |
1993 | |
1994 void HostResolverImpl::IPv6ProbeSetDefaultAddressFamily( | |
1995 AddressFamily address_family) { | |
1996 DCHECK(address_family == ADDRESS_FAMILY_UNSPECIFIED || | |
1997 address_family == ADDRESS_FAMILY_IPV4); | |
1998 if (!ipv6_probe_monitoring_) | |
1999 return; | |
2000 if (default_address_family_ != address_family) { | |
2001 VLOG(1) << "IPv6Probe forced AddressFamily setting to " | |
2002 << ((address_family == ADDRESS_FAMILY_UNSPECIFIED) ? | |
2003 "ADDRESS_FAMILY_UNSPECIFIED" : "ADDRESS_FAMILY_IPV4"); | |
2004 } | |
2005 default_address_family_ = address_family; | |
2006 } | |
2007 | |
2008 void HostResolverImpl::SetHaveOnlyLoopbackAddresses(bool result) { | |
2009 if (result) { | |
2010 additional_resolver_flags_ |= HOST_RESOLVER_LOOPBACK_ONLY; | |
2011 } else { | |
2012 additional_resolver_flags_ &= ~HOST_RESOLVER_LOOPBACK_ONLY; | |
2013 } | |
2014 } | |
2015 | |
2016 HostResolverImpl::Key HostResolverImpl::GetEffectiveKeyForRequest( | |
2017 const RequestInfo& info) const { | |
2018 HostResolverFlags effective_flags = | |
2019 info.host_resolver_flags() | additional_resolver_flags_; | |
2020 AddressFamily effective_address_family = info.address_family(); | |
2021 if (effective_address_family == ADDRESS_FAMILY_UNSPECIFIED && | |
2022 default_address_family_ != ADDRESS_FAMILY_UNSPECIFIED) { | |
2023 effective_address_family = default_address_family_; | |
2024 if (ipv6_probe_monitoring_) | |
2025 effective_flags |= HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6; | |
2026 } | |
2027 return Key(info.hostname(), effective_address_family, effective_flags); | |
2028 } | |
2029 | |
2030 void HostResolverImpl::AbortAllInProgressJobs() { | |
2031 // In Abort, a Request callback could spawn new Jobs with matching keys, so | |
2032 // first collect and remove all running jobs from |jobs_|. | |
2033 ScopedVector<Job> jobs_to_abort; | |
2034 for (JobMap::iterator it = jobs_.begin(); it != jobs_.end(); ) { | |
2035 Job* job = it->second; | |
2036 if (job->is_running()) { | |
2037 jobs_to_abort.push_back(job); | |
2038 jobs_.erase(it++); | |
2039 } else { | |
2040 DCHECK(job->is_queued()); | |
2041 ++it; | |
2042 } | |
2043 } | |
2044 | |
2045 // Check if no dispatcher slots leaked out. | |
2046 DCHECK_EQ(dispatcher_.num_running_jobs(), jobs_to_abort.size()); | |
2047 | |
2048 // Life check to bail once |this| is deleted. | |
2049 base::WeakPtr<HostResolverImpl> self = weak_ptr_factory_.GetWeakPtr(); | |
2050 | |
2051 // Then Abort them. | |
2052 for (size_t i = 0; self && i < jobs_to_abort.size(); ++i) { | |
2053 jobs_to_abort[i]->Abort(); | |
2054 jobs_to_abort[i] = NULL; | |
2055 } | |
2056 } | |
2057 | |
2058 void HostResolverImpl::TryServingAllJobsFromHosts() { | |
2059 if (!HaveDnsConfig()) | |
2060 return; | |
2061 | |
2062 // TODO(szym): Do not do this if nsswitch.conf instructs not to. | |
2063 // http://crbug.com/117655 | |
2064 | |
2065 // Life check to bail once |this| is deleted. | |
2066 base::WeakPtr<HostResolverImpl> self = weak_ptr_factory_.GetWeakPtr(); | |
2067 | |
2068 for (JobMap::iterator it = jobs_.begin(); self && it != jobs_.end(); ) { | |
2069 Job* job = it->second; | |
2070 ++it; | |
2071 // This could remove |job| from |jobs_|, but iterator will remain valid. | |
2072 job->ServeFromHosts(); | |
2073 } | |
2074 } | |
2075 | |
2076 void HostResolverImpl::OnIPAddressChanged() { | |
2077 resolved_known_ipv6_hostname_ = false; | |
2078 // Abandon all ProbeJobs. | |
2079 probe_weak_ptr_factory_.InvalidateWeakPtrs(); | |
2080 if (cache_.get()) | |
2081 cache_->clear(); | |
2082 if (ipv6_probe_monitoring_) | |
2083 new IPv6ProbeJob(probe_weak_ptr_factory_.GetWeakPtr(), net_log_); | |
2084 #if defined(OS_POSIX) && !defined(OS_MACOSX) | |
2085 new LoopbackProbeJob(probe_weak_ptr_factory_.GetWeakPtr()); | |
2086 #endif | |
2087 AbortAllInProgressJobs(); | |
2088 // |this| may be deleted inside AbortAllInProgressJobs(). | |
2089 } | |
2090 | |
2091 void HostResolverImpl::OnDNSChanged() { | |
2092 DnsConfig dns_config; | |
2093 NetworkChangeNotifier::GetDnsConfig(&dns_config); | |
2094 if (net_log_) { | |
2095 net_log_->AddGlobalEntry( | |
2096 NetLog::TYPE_DNS_CONFIG_CHANGED, | |
2097 base::Bind(&NetLogDnsConfigCallback, &dns_config)); | |
2098 } | |
2099 | |
2100 // TODO(szym): Remove once http://crbug.com/137914 is resolved. | |
2101 received_dns_config_ = dns_config.IsValid(); | |
2102 | |
2103 num_dns_failures_ = 0; | |
2104 | |
2105 // We want a new DnsSession in place, before we Abort running Jobs, so that | |
2106 // the newly started jobs use the new config. | |
2107 if (dns_client_.get()) { | |
2108 dns_client_->SetConfig(dns_config); | |
2109 if (dns_config.IsValid()) | |
2110 UMA_HISTOGRAM_BOOLEAN("AsyncDNS.DnsClientEnabled", true); | |
2111 } | |
2112 | |
2113 // If the DNS server has changed, existing cached info could be wrong so we | |
2114 // have to drop our internal cache :( Note that OS level DNS caches, such | |
2115 // as NSCD's cache should be dropped automatically by the OS when | |
2116 // resolv.conf changes so we don't need to do anything to clear that cache. | |
2117 if (cache_.get()) | |
2118 cache_->clear(); | |
2119 | |
2120 // Life check to bail once |this| is deleted. | |
2121 base::WeakPtr<HostResolverImpl> self = weak_ptr_factory_.GetWeakPtr(); | |
2122 | |
2123 // Existing jobs will have been sent to the original server so they need to | |
2124 // be aborted. | |
2125 AbortAllInProgressJobs(); | |
2126 | |
2127 // |this| may be deleted inside AbortAllInProgressJobs(). | |
2128 if (self) | |
2129 TryServingAllJobsFromHosts(); | |
2130 } | |
2131 | |
2132 bool HostResolverImpl::HaveDnsConfig() const { | |
2133 // Use DnsClient only if it's fully configured and there is no override by | |
2134 // ScopedDefaultHostResolverProc. | |
2135 // The alternative is to use NetworkChangeNotifier to override DnsConfig, | |
2136 // but that would introduce construction order requirements for NCN and SDHRP. | |
2137 return (dns_client_.get() != NULL) && | |
2138 (dns_client_->GetConfig() != NULL) && | |
2139 !(proc_params_.resolver_proc == NULL && | |
2140 HostResolverProc::GetDefault() != NULL); | |
2141 } | |
2142 | |
2143 void HostResolverImpl::OnDnsTaskResolve(int net_error) { | |
2144 DCHECK(dns_client_); | |
2145 if (net_error == OK) { | |
2146 num_dns_failures_ = 0; | |
2147 return; | |
2148 } | |
2149 ++num_dns_failures_; | |
2150 if (num_dns_failures_ < kMaximumDnsFailures) | |
2151 return; | |
2152 // Disable DnsClient until the next DNS change. | |
2153 for (JobMap::iterator it = jobs_.begin(); it != jobs_.end(); ++it) | |
2154 it->second->AbortDnsTask(); | |
2155 dns_client_->SetConfig(DnsConfig()); | |
2156 UMA_HISTOGRAM_BOOLEAN("AsyncDNS.DnsClientEnabled", false); | |
2157 UMA_HISTOGRAM_CUSTOM_ENUMERATION("AsyncDNS.DnsClientDisabledReason", | |
2158 std::abs(net_error), | |
2159 GetAllErrorCodesForUma()); | |
2160 } | |
2161 | |
2162 void HostResolverImpl::SetDnsClient(scoped_ptr<DnsClient> dns_client) { | |
2163 if (HaveDnsConfig()) { | |
2164 for (JobMap::iterator it = jobs_.begin(); it != jobs_.end(); ++it) | |
2165 it->second->AbortDnsTask(); | |
2166 } | |
2167 dns_client_ = dns_client.Pass(); | |
2168 if (!dns_client_ || dns_client_->GetConfig() || | |
2169 num_dns_failures_ >= kMaximumDnsFailures) { | |
2170 return; | |
2171 } | |
2172 DnsConfig dns_config; | |
2173 NetworkChangeNotifier::GetDnsConfig(&dns_config); | |
2174 dns_client_->SetConfig(dns_config); | |
2175 num_dns_failures_ = 0; | |
2176 if (dns_config.IsValid()) | |
2177 UMA_HISTOGRAM_BOOLEAN("AsyncDNS.DnsClientEnabled", true); | |
2178 } | |
2179 | |
2180 } // namespace net | |
OLD | NEW |