summaryrefslogtreecommitdiffstats
path: root/net/dns
diff options
context:
space:
mode:
authorphajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-21 01:40:53 +0000
committerphajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-21 01:40:53 +0000
commitf2cb3cf86e4217581443d93e863c891a05066e60 (patch)
treec62110d2347d17e1b1a1f57fa758cf8bba72548b /net/dns
parentb8512263f9556ac705394e9da03d364980f0eadf (diff)
downloadchromium_src-f2cb3cf86e4217581443d93e863c891a05066e60.zip
chromium_src-f2cb3cf86e4217581443d93e863c891a05066e60.tar.gz
chromium_src-f2cb3cf86e4217581443d93e863c891a05066e60.tar.bz2
net: move host_resolver files from net/base to net/dns
BUG=70818 Review URL: https://codereview.chromium.org/12518036 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@189485 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/dns')
-rw-r--r--net/dns/host_resolver.cc148
-rw-r--r--net/dns/host_resolver.h208
-rw-r--r--net/dns/host_resolver_impl.cc2180
-rw-r--r--net/dns/host_resolver_impl.h287
-rw-r--r--net/dns/host_resolver_impl_unittest.cc1481
-rw-r--r--net/dns/host_resolver_proc.cc251
-rw-r--r--net/dns/host_resolver_proc.h96
-rw-r--r--net/dns/mapped_host_resolver.cc67
-rw-r--r--net/dns/mapped_host_resolver.h72
-rw-r--r--net/dns/mapped_host_resolver_unittest.cc219
-rw-r--r--net/dns/mock_host_resolver.cc417
-rw-r--r--net/dns/mock_host_resolver.h254
-rw-r--r--net/dns/single_request_host_resolver.cc77
-rw-r--r--net/dns/single_request_host_resolver.h56
-rw-r--r--net/dns/single_request_host_resolver_unittest.cc124
15 files changed, 5937 insertions, 0 deletions
diff --git a/net/dns/host_resolver.cc b/net/dns/host_resolver.cc
new file mode 100644
index 0000000..6350d2e
--- /dev/null
+++ b/net/dns/host_resolver.cc
@@ -0,0 +1,148 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/dns/host_resolver.h"
+
+#include "base/logging.h"
+#include "base/metrics/field_trial.h"
+#include "base/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "net/base/host_cache.h"
+#include "net/dns/dns_client.h"
+#include "net/dns/dns_config_service.h"
+#include "net/dns/host_resolver_impl.h"
+
+namespace net {
+
+namespace {
+
+// Maximum of 6 concurrent resolver threads (excluding retries).
+// Some routers (or resolvers) appear to start to provide host-not-found if
+// too many simultaneous resolutions are pending. This number needs to be
+// further optimized, but 8 is what FF currently does. We found some routers
+// that limit this to 6, so we're temporarily holding it at that level.
+const size_t kDefaultMaxProcTasks = 6u;
+
+// When configuring from field trial, do not allow
+const size_t kSaneMaxProcTasks = 20u;
+
+PrioritizedDispatcher::Limits GetDispatcherLimits(
+ const HostResolver::Options& options) {
+ PrioritizedDispatcher::Limits limits(NUM_PRIORITIES,
+ options.max_concurrent_resolves);
+
+ // If not using default, do not use the field trial.
+ if (limits.total_jobs != HostResolver::kDefaultParallelism)
+ return limits;
+
+ // Default, without trial is no reserved slots.
+ limits.total_jobs = kDefaultMaxProcTasks;
+
+ // Parallelism is determined by the field trial.
+ std::string group = base::FieldTrialList::FindFullName(
+ "HostResolverDispatch");
+
+ if (group.empty())
+ return limits;
+
+ // The format of the group name is a list of non-negative integers separated
+ // by ':'. Each of the elements in the list corresponds to an element in
+ // |reserved_slots|, except the last one which is the |total_jobs|.
+
+ std::vector<std::string> group_parts;
+ base::SplitString(group, ':', &group_parts);
+ if (group_parts.size() != NUM_PRIORITIES + 1) {
+ NOTREACHED();
+ return limits;
+ }
+
+ std::vector<size_t> parsed(group_parts.size());
+ size_t total_reserved_slots = 0;
+
+ for (size_t i = 0; i < group_parts.size(); ++i) {
+ if (!base::StringToSizeT(group_parts[i], &parsed[i])) {
+ NOTREACHED();
+ return limits;
+ }
+ }
+
+ size_t total_jobs = parsed.back();
+ parsed.pop_back();
+ for (size_t i = 0; i < parsed.size(); ++i) {
+ total_reserved_slots += parsed[i];
+ }
+
+ // There must be some unreserved slots available for the all priorities.
+ if (total_reserved_slots > total_jobs ||
+ (total_reserved_slots == total_jobs && parsed[MINIMUM_PRIORITY] == 0)) {
+ NOTREACHED();
+ return limits;
+ }
+
+ limits.total_jobs = total_jobs;
+ limits.reserved_slots = parsed;
+ return limits;
+}
+
+} // namespace
+
+HostResolver::Options::Options()
+ : max_concurrent_resolves(kDefaultParallelism),
+ max_retry_attempts(kDefaultRetryAttempts),
+ enable_caching(true) {
+}
+
+HostResolver::RequestInfo::RequestInfo(const HostPortPair& host_port_pair)
+ : host_port_pair_(host_port_pair),
+ address_family_(ADDRESS_FAMILY_UNSPECIFIED),
+ host_resolver_flags_(0),
+ allow_cached_response_(true),
+ is_speculative_(false),
+ priority_(MEDIUM) {
+}
+
+HostResolver::~HostResolver() {
+}
+
+AddressFamily HostResolver::GetDefaultAddressFamily() const {
+ return ADDRESS_FAMILY_UNSPECIFIED;
+}
+
+void HostResolver::ProbeIPv6Support() {
+}
+
+void HostResolver::SetDnsClientEnabled(bool enabled) {
+}
+
+HostCache* HostResolver::GetHostCache() {
+ return NULL;
+}
+
+base::Value* HostResolver::GetDnsConfigAsValue() const {
+ return NULL;
+}
+
+// static
+scoped_ptr<HostResolver>
+HostResolver::CreateSystemResolver(const Options& options, NetLog* net_log) {
+ scoped_ptr<HostCache> cache;
+ if (options.enable_caching)
+ cache = HostCache::CreateDefaultCache();
+ return scoped_ptr<HostResolver>(new HostResolverImpl(
+ cache.Pass(),
+ GetDispatcherLimits(options),
+ HostResolverImpl::ProcTaskParams(NULL, options.max_retry_attempts),
+ net_log));
+}
+
+// static
+scoped_ptr<HostResolver>
+HostResolver::CreateDefaultResolver(NetLog* net_log) {
+ return CreateSystemResolver(Options(), net_log);
+}
+
+HostResolver::HostResolver() {
+}
+
+} // namespace net
diff --git a/net/dns/host_resolver.h b/net/dns/host_resolver.h
new file mode 100644
index 0000000..1adf8b7
--- /dev/null
+++ b/net/dns/host_resolver.h
@@ -0,0 +1,208 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_DNS_HOST_RESOLVER_H_
+#define NET_DNS_HOST_RESOLVER_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "net/base/address_family.h"
+#include "net/base/completion_callback.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/net_export.h"
+#include "net/base/net_util.h"
+#include "net/base/request_priority.h"
+
+namespace base {
+class Value;
+}
+
+namespace net {
+
+class AddressList;
+class BoundNetLog;
+class HostCache;
+class HostResolverProc;
+class NetLog;
+
+// This class represents the task of resolving hostnames (or IP address
+// literal) to an AddressList object.
+//
+// HostResolver can handle multiple requests at a time, so when cancelling a
+// request the RequestHandle that was returned by Resolve() needs to be
+// given. A simpler alternative for consumers that only have 1 outstanding
+// request at a time is to create a SingleRequestHostResolver wrapper around
+// HostResolver (which will automatically cancel the single request when it
+// goes out of scope).
+class NET_EXPORT HostResolver {
+ public:
+ // |max_concurrent_resolves| is how many resolve requests will be allowed to
+ // run in parallel. Pass HostResolver::kDefaultParallelism to choose a
+ // default value.
+ // |max_retry_attempts| is the maximum number of times we will retry for host
+ // resolution. Pass HostResolver::kDefaultRetryAttempts to choose a default
+ // value.
+ // |enable_caching| controls whether a HostCache is used.
+ struct NET_EXPORT Options {
+ Options();
+
+ size_t max_concurrent_resolves;
+ size_t max_retry_attempts;
+ bool enable_caching;
+ };
+
+ // The parameters for doing a Resolve(). A hostname and port are required,
+ // the rest are optional (and have reasonable defaults).
+ class NET_EXPORT RequestInfo {
+ public:
+ explicit RequestInfo(const HostPortPair& host_port_pair);
+
+ const HostPortPair& host_port_pair() const { return host_port_pair_; }
+ void set_host_port_pair(const HostPortPair& host_port_pair) {
+ host_port_pair_ = host_port_pair;
+ }
+
+ int port() const { return host_port_pair_.port(); }
+ const std::string& hostname() const { return host_port_pair_.host(); }
+
+ AddressFamily address_family() const { return address_family_; }
+ void set_address_family(AddressFamily address_family) {
+ address_family_ = address_family;
+ }
+
+ HostResolverFlags host_resolver_flags() const {
+ return host_resolver_flags_;
+ }
+ void set_host_resolver_flags(HostResolverFlags host_resolver_flags) {
+ host_resolver_flags_ = host_resolver_flags;
+ }
+
+ bool allow_cached_response() const { return allow_cached_response_; }
+ void set_allow_cached_response(bool b) { allow_cached_response_ = b; }
+
+ bool is_speculative() const { return is_speculative_; }
+ void set_is_speculative(bool b) { is_speculative_ = b; }
+
+ RequestPriority priority() const { return priority_; }
+ void set_priority(RequestPriority priority) { priority_ = priority; }
+
+ private:
+ // The hostname to resolve, and the port to use in resulting sockaddrs.
+ HostPortPair host_port_pair_;
+
+ // The address family to restrict results to.
+ AddressFamily address_family_;
+
+ // Flags to use when resolving this request.
+ HostResolverFlags host_resolver_flags_;
+
+ // Whether it is ok to return a result from the host cache.
+ bool allow_cached_response_;
+
+ // Whether this request was started by the DNS prefetcher.
+ bool is_speculative_;
+
+ // The priority for the request.
+ RequestPriority priority_;
+ };
+
+ // Opaque type used to cancel a request.
+ typedef void* RequestHandle;
+
+ // This value can be passed into CreateSystemResolver as the
+ // |max_concurrent_resolves| parameter. It will select a default level of
+ // concurrency.
+ static const size_t kDefaultParallelism = 0;
+
+ // This value can be passed into CreateSystemResolver as the
+ // |max_retry_attempts| parameter.
+ static const size_t kDefaultRetryAttempts = -1;
+
+ // If any completion callbacks are pending when the resolver is destroyed,
+ // the host resolutions are cancelled, and the completion callbacks will not
+ // be called.
+ virtual ~HostResolver();
+
+ // Resolves the given hostname (or IP address literal), filling out the
+ // |addresses| object upon success. The |info.port| parameter will be set as
+ // the sin(6)_port field of the sockaddr_in{6} struct. Returns OK if
+ // successful or an error code upon failure. Returns
+ // ERR_NAME_NOT_RESOLVED if hostname is invalid, or if it is an
+ // incompatible IP literal (e.g. IPv6 is disabled and it is an IPv6
+ // literal).
+ //
+ // If the operation cannot be completed synchronously, ERR_IO_PENDING will
+ // be returned and the real result code will be passed to the completion
+ // callback. Otherwise the result code is returned immediately from this
+ // call.
+ //
+ // If |out_req| is non-NULL, then |*out_req| will be filled with a handle to
+ // the async request. This handle is not valid after the request has
+ // completed.
+ //
+ // Profiling information for the request is saved to |net_log| if non-NULL.
+ virtual int Resolve(const RequestInfo& info,
+ AddressList* addresses,
+ const CompletionCallback& callback,
+ RequestHandle* out_req,
+ const BoundNetLog& net_log) = 0;
+
+ // Resolves the given hostname (or IP address literal) out of cache or HOSTS
+ // file (if enabled) only. This is guaranteed to complete synchronously.
+ // This acts like |Resolve()| if the hostname is IP literal, or cached value
+ // or HOSTS entry exists. Otherwise, ERR_DNS_CACHE_MISS is returned.
+ virtual int ResolveFromCache(const RequestInfo& info,
+ AddressList* addresses,
+ const BoundNetLog& net_log) = 0;
+
+ // Cancels the specified request. |req| is the handle returned by Resolve().
+ // After a request is canceled, its completion callback will not be called.
+ // CancelRequest must NOT be called after the request's completion callback
+ // has already run or the request was canceled.
+ virtual void CancelRequest(RequestHandle req) = 0;
+
+ // Sets the default AddressFamily to use when requests have left it
+ // unspecified. For example, this could be used to restrict resolution
+ // results to AF_INET by passing in ADDRESS_FAMILY_IPV4, or to
+ // AF_INET6 by passing in ADDRESS_FAMILY_IPV6.
+ virtual void SetDefaultAddressFamily(AddressFamily address_family) {}
+ virtual AddressFamily GetDefaultAddressFamily() const;
+
+ // Continuously observe whether IPv6 is supported, and set the allowable
+ // address family to IPv4 iff IPv6 is not supported.
+ virtual void ProbeIPv6Support();
+
+ // Enable or disable the built-in asynchronous DnsClient.
+ virtual void SetDnsClientEnabled(bool enabled);
+
+ // Returns the HostResolverCache |this| uses, or NULL if there isn't one.
+ // Used primarily to clear the cache and for getting debug information.
+ virtual HostCache* GetHostCache();
+
+ // Returns the current DNS configuration |this| is using, as a Value, or NULL
+ // if it's configured to always use the system host resolver. Caller takes
+ // ownership of the returned Value.
+ virtual base::Value* GetDnsConfigAsValue() const;
+
+ // Creates a HostResolver implementation that queries the underlying system.
+ // (Except if a unit-test has changed the global HostResolverProc using
+ // ScopedHostResolverProc to intercept requests to the system).
+ static scoped_ptr<HostResolver> CreateSystemResolver(
+ const Options& options,
+ NetLog* net_log);
+
+ // As above, but uses default parameters.
+ static scoped_ptr<HostResolver> CreateDefaultResolver(NetLog* net_log);
+
+ protected:
+ HostResolver();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HostResolver);
+};
+
+} // namespace net
+
+#endif // NET_DNS_HOST_RESOLVER_H_
diff --git a/net/dns/host_resolver_impl.cc b/net/dns/host_resolver_impl.cc
new file mode 100644
index 0000000..91475ec
--- /dev/null
+++ b/net/dns/host_resolver_impl.cc
@@ -0,0 +1,2180 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/dns/host_resolver_impl.h"
+
+#if defined(OS_WIN)
+#include <Winsock2.h>
+#elif defined(OS_POSIX)
+#include <netdb.h>
+#endif
+
+#include <cmath>
+#include <utility>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/debug/debugger.h"
+#include "base/debug/stack_trace.h"
+#include "base/message_loop_proxy.h"
+#include "base/metrics/field_trial.h"
+#include "base/metrics/histogram.h"
+#include "base/stl_util.h"
+#include "base/string_util.h"
+#include "base/threading/worker_pool.h"
+#include "base/time.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "net/base/address_family.h"
+#include "net/base/address_list.h"
+#include "net/base/dns_reloader.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_log.h"
+#include "net/base/net_util.h"
+#include "net/dns/address_sorter.h"
+#include "net/dns/dns_client.h"
+#include "net/dns/dns_config_service.h"
+#include "net/dns/dns_protocol.h"
+#include "net/dns/dns_response.h"
+#include "net/dns/dns_transaction.h"
+#include "net/dns/host_resolver_proc.h"
+
+#if defined(OS_WIN)
+#include "net/base/winsock_init.h"
+#endif
+
+namespace net {
+
+namespace {
+
+// Limit the size of hostnames that will be resolved to combat issues in
+// some platform's resolvers.
+const size_t kMaxHostLength = 4096;
+
+// Default TTL for successful resolutions with ProcTask.
+const unsigned kCacheEntryTTLSeconds = 60;
+
+// Default TTL for unsuccessful resolutions with ProcTask.
+const unsigned kNegativeCacheEntryTTLSeconds = 0;
+
+// Minimum TTL for successful resolutions with DnsTask.
+const unsigned kMinimumTTLSeconds = kCacheEntryTTLSeconds;
+
+// Number of consecutive failures of DnsTask (with successful fallback) before
+// the DnsClient is disabled until the next DNS change.
+const unsigned kMaximumDnsFailures = 16;
+
+// We use a separate histogram name for each platform to facilitate the
+// display of error codes by their symbolic name (since each platform has
+// different mappings).
+const char kOSErrorsForGetAddrinfoHistogramName[] =
+#if defined(OS_WIN)
+ "Net.OSErrorsForGetAddrinfo_Win";
+#elif defined(OS_MACOSX)
+ "Net.OSErrorsForGetAddrinfo_Mac";
+#elif defined(OS_LINUX)
+ "Net.OSErrorsForGetAddrinfo_Linux";
+#else
+ "Net.OSErrorsForGetAddrinfo";
+#endif
+
+// Gets a list of the likely error codes that getaddrinfo() can return
+// (non-exhaustive). These are the error codes that we will track via
+// a histogram.
+std::vector<int> GetAllGetAddrinfoOSErrors() {
+ int os_errors[] = {
+#if defined(OS_POSIX)
+#if !defined(OS_FREEBSD)
+#if !defined(OS_ANDROID)
+ // EAI_ADDRFAMILY has been declared obsolete in Android's and
+ // FreeBSD's netdb.h.
+ EAI_ADDRFAMILY,
+#endif
+ // EAI_NODATA has been declared obsolete in FreeBSD's netdb.h.
+ EAI_NODATA,
+#endif
+ EAI_AGAIN,
+ EAI_BADFLAGS,
+ EAI_FAIL,
+ EAI_FAMILY,
+ EAI_MEMORY,
+ EAI_NONAME,
+ EAI_SERVICE,
+ EAI_SOCKTYPE,
+ EAI_SYSTEM,
+#elif defined(OS_WIN)
+ // See: http://msdn.microsoft.com/en-us/library/ms738520(VS.85).aspx
+ WSA_NOT_ENOUGH_MEMORY,
+ WSAEAFNOSUPPORT,
+ WSAEINVAL,
+ WSAESOCKTNOSUPPORT,
+ WSAHOST_NOT_FOUND,
+ WSANO_DATA,
+ WSANO_RECOVERY,
+ WSANOTINITIALISED,
+ WSATRY_AGAIN,
+ WSATYPE_NOT_FOUND,
+ // The following are not in doc, but might be to appearing in results :-(.
+ WSA_INVALID_HANDLE,
+#endif
+ };
+
+ // Ensure all errors are positive, as histogram only tracks positive values.
+ for (size_t i = 0; i < arraysize(os_errors); ++i) {
+ os_errors[i] = std::abs(os_errors[i]);
+ }
+
+ return base::CustomHistogram::ArrayToCustomRanges(os_errors,
+ arraysize(os_errors));
+}
+
+enum DnsResolveStatus {
+ RESOLVE_STATUS_DNS_SUCCESS = 0,
+ RESOLVE_STATUS_PROC_SUCCESS,
+ RESOLVE_STATUS_FAIL,
+ RESOLVE_STATUS_SUSPECT_NETBIOS,
+ RESOLVE_STATUS_MAX
+};
+
+void UmaAsyncDnsResolveStatus(DnsResolveStatus result) {
+ UMA_HISTOGRAM_ENUMERATION("AsyncDNS.ResolveStatus",
+ result,
+ RESOLVE_STATUS_MAX);
+}
+
+bool ResemblesNetBIOSName(const std::string& hostname) {
+ return (hostname.size() < 16) && (hostname.find('.') == std::string::npos);
+}
+
+// True if |hostname| ends with either ".local" or ".local.".
+bool ResemblesMulticastDNSName(const std::string& hostname) {
+ DCHECK(!hostname.empty());
+ const char kSuffix[] = ".local.";
+ const size_t kSuffixLen = sizeof(kSuffix) - 1;
+ const size_t kSuffixLenTrimmed = kSuffixLen - 1;
+ if (hostname[hostname.size() - 1] == '.') {
+ return hostname.size() > kSuffixLen &&
+ !hostname.compare(hostname.size() - kSuffixLen, kSuffixLen, kSuffix);
+ }
+ return hostname.size() > kSuffixLenTrimmed &&
+ !hostname.compare(hostname.size() - kSuffixLenTrimmed, kSuffixLenTrimmed,
+ kSuffix, kSuffixLenTrimmed);
+}
+
+// Provide a common macro to simplify code and readability. We must use a
+// macro as the underlying HISTOGRAM macro creates static variables.
+#define DNS_HISTOGRAM(name, time) UMA_HISTOGRAM_CUSTOM_TIMES(name, time, \
+ base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromHours(1), 100)
+
+// A macro to simplify code and readability.
+#define DNS_HISTOGRAM_BY_PRIORITY(basename, priority, time) \
+ do { \
+ switch (priority) { \
+ case HIGHEST: DNS_HISTOGRAM(basename "_HIGHEST", time); break; \
+ case MEDIUM: DNS_HISTOGRAM(basename "_MEDIUM", time); break; \
+ case LOW: DNS_HISTOGRAM(basename "_LOW", time); break; \
+ case LOWEST: DNS_HISTOGRAM(basename "_LOWEST", time); break; \
+ case IDLE: DNS_HISTOGRAM(basename "_IDLE", time); break; \
+ default: NOTREACHED(); break; \
+ } \
+ DNS_HISTOGRAM(basename, time); \
+ } while (0)
+
+// Record time from Request creation until a valid DNS response.
+void RecordTotalTime(bool had_dns_config,
+ bool speculative,
+ base::TimeDelta duration) {
+ if (had_dns_config) {
+ if (speculative) {
+ DNS_HISTOGRAM("AsyncDNS.TotalTime_speculative", duration);
+ } else {
+ DNS_HISTOGRAM("AsyncDNS.TotalTime", duration);
+ }
+ } else {
+ if (speculative) {
+ DNS_HISTOGRAM("DNS.TotalTime_speculative", duration);
+ } else {
+ DNS_HISTOGRAM("DNS.TotalTime", duration);
+ }
+ }
+}
+
+void RecordTTL(base::TimeDelta ttl) {
+ UMA_HISTOGRAM_CUSTOM_TIMES("AsyncDNS.TTL", ttl,
+ base::TimeDelta::FromSeconds(1),
+ base::TimeDelta::FromDays(1), 100);
+}
+
+//-----------------------------------------------------------------------------
+
+// Wraps call to SystemHostResolverProc as an instance of HostResolverProc.
+// TODO(szym): This should probably be declared in host_resolver_proc.h.
+class CallSystemHostResolverProc : public HostResolverProc {
+ public:
+ CallSystemHostResolverProc() : HostResolverProc(NULL) {}
+ virtual int Resolve(const std::string& hostname,
+ AddressFamily address_family,
+ HostResolverFlags host_resolver_flags,
+ AddressList* addr_list,
+ int* os_error) OVERRIDE {
+ return SystemHostResolverProc(hostname,
+ address_family,
+ host_resolver_flags,
+ addr_list,
+ os_error);
+ }
+
+ protected:
+ virtual ~CallSystemHostResolverProc() {}
+};
+
+AddressList EnsurePortOnAddressList(const AddressList& list, uint16 port) {
+ if (list.empty() || list.front().port() == port)
+ return list;
+ return AddressList::CopyWithPort(list, port);
+}
+
+// Creates NetLog parameters when the resolve failed.
+base::Value* NetLogProcTaskFailedCallback(uint32 attempt_number,
+ int net_error,
+ int os_error,
+ NetLog::LogLevel /* log_level */) {
+ DictionaryValue* dict = new DictionaryValue();
+ if (attempt_number)
+ dict->SetInteger("attempt_number", attempt_number);
+
+ dict->SetInteger("net_error", net_error);
+
+ if (os_error) {
+ dict->SetInteger("os_error", os_error);
+#if defined(OS_POSIX)
+ dict->SetString("os_error_string", gai_strerror(os_error));
+#elif defined(OS_WIN)
+ // Map the error code to a human-readable string.
+ LPWSTR error_string = NULL;
+ int size = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM,
+ 0, // Use the internal message table.
+ os_error,
+ 0, // Use default language.
+ (LPWSTR)&error_string,
+ 0, // Buffer size.
+ 0); // Arguments (unused).
+ dict->SetString("os_error_string", WideToUTF8(error_string));
+ LocalFree(error_string);
+#endif
+ }
+
+ return dict;
+}
+
+// Creates NetLog parameters when the DnsTask failed.
+base::Value* NetLogDnsTaskFailedCallback(int net_error,
+ int dns_error,
+ NetLog::LogLevel /* log_level */) {
+ DictionaryValue* dict = new DictionaryValue();
+ dict->SetInteger("net_error", net_error);
+ if (dns_error)
+ dict->SetInteger("dns_error", dns_error);
+ return dict;
+};
+
+// Creates NetLog parameters containing the information in a RequestInfo object,
+// along with the associated NetLog::Source.
+base::Value* NetLogRequestInfoCallback(const NetLog::Source& source,
+ const HostResolver::RequestInfo* info,
+ NetLog::LogLevel /* log_level */) {
+ DictionaryValue* dict = new DictionaryValue();
+ source.AddToEventParameters(dict);
+
+ dict->SetString("host", info->host_port_pair().ToString());
+ dict->SetInteger("address_family",
+ static_cast<int>(info->address_family()));
+ dict->SetBoolean("allow_cached_response", info->allow_cached_response());
+ dict->SetBoolean("is_speculative", info->is_speculative());
+ dict->SetInteger("priority", info->priority());
+ return dict;
+}
+
+// Creates NetLog parameters for the creation of a HostResolverImpl::Job.
+base::Value* NetLogJobCreationCallback(const NetLog::Source& source,
+ const std::string* host,
+ NetLog::LogLevel /* log_level */) {
+ DictionaryValue* dict = new DictionaryValue();
+ source.AddToEventParameters(dict);
+ dict->SetString("host", *host);
+ return dict;
+}
+
+// Creates NetLog parameters for HOST_RESOLVER_IMPL_JOB_ATTACH/DETACH events.
+base::Value* NetLogJobAttachCallback(const NetLog::Source& source,
+ RequestPriority priority,
+ NetLog::LogLevel /* log_level */) {
+ DictionaryValue* dict = new DictionaryValue();
+ source.AddToEventParameters(dict);
+ dict->SetInteger("priority", priority);
+ return dict;
+}
+
+// Creates NetLog parameters for the DNS_CONFIG_CHANGED event.
+base::Value* NetLogDnsConfigCallback(const DnsConfig* config,
+ NetLog::LogLevel /* log_level */) {
+ return config->ToValue();
+}
+
+// The logging routines are defined here because some requests are resolved
+// without a Request object.
+
+// Logs when a request has just been started.
+void LogStartRequest(const BoundNetLog& source_net_log,
+ const BoundNetLog& request_net_log,
+ const HostResolver::RequestInfo& info) {
+ source_net_log.BeginEvent(
+ NetLog::TYPE_HOST_RESOLVER_IMPL,
+ request_net_log.source().ToEventParametersCallback());
+
+ request_net_log.BeginEvent(
+ NetLog::TYPE_HOST_RESOLVER_IMPL_REQUEST,
+ base::Bind(&NetLogRequestInfoCallback, source_net_log.source(), &info));
+}
+
+// Logs when a request has just completed (before its callback is run).
+void LogFinishRequest(const BoundNetLog& source_net_log,
+ const BoundNetLog& request_net_log,
+ const HostResolver::RequestInfo& info,
+ int net_error) {
+ request_net_log.EndEventWithNetErrorCode(
+ NetLog::TYPE_HOST_RESOLVER_IMPL_REQUEST, net_error);
+ source_net_log.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL);
+}
+
+// Logs when a request has been cancelled.
+void LogCancelRequest(const BoundNetLog& source_net_log,
+ const BoundNetLog& request_net_log,
+ const HostResolverImpl::RequestInfo& info) {
+ request_net_log.AddEvent(NetLog::TYPE_CANCELLED);
+ request_net_log.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_REQUEST);
+ source_net_log.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL);
+}
+
+//-----------------------------------------------------------------------------
+
+// Keeps track of the highest priority.
+class PriorityTracker {
+ public:
+ explicit PriorityTracker(RequestPriority initial_priority)
+ : highest_priority_(initial_priority), total_count_(0) {
+ memset(counts_, 0, sizeof(counts_));
+ }
+
+ RequestPriority highest_priority() const {
+ return highest_priority_;
+ }
+
+ size_t total_count() const {
+ return total_count_;
+ }
+
+ void Add(RequestPriority req_priority) {
+ ++total_count_;
+ ++counts_[req_priority];
+ if (highest_priority_ < req_priority)
+ highest_priority_ = req_priority;
+ }
+
+ void Remove(RequestPriority req_priority) {
+ DCHECK_GT(total_count_, 0u);
+ DCHECK_GT(counts_[req_priority], 0u);
+ --total_count_;
+ --counts_[req_priority];
+ size_t i;
+ for (i = highest_priority_; i > MINIMUM_PRIORITY && !counts_[i]; --i);
+ highest_priority_ = static_cast<RequestPriority>(i);
+
+ // In absence of requests, default to MINIMUM_PRIORITY.
+ if (total_count_ == 0)
+ DCHECK_EQ(MINIMUM_PRIORITY, highest_priority_);
+ }
+
+ private:
+ RequestPriority highest_priority_;
+ size_t total_count_;
+ size_t counts_[NUM_PRIORITIES];
+};
+
+} // namespace
+
+//-----------------------------------------------------------------------------
+
+// Holds the data for a request that could not be completed synchronously.
+// It is owned by a Job. Canceled Requests are only marked as canceled rather
+// than removed from the Job's |requests_| list.
+class HostResolverImpl::Request {
+ public:
+ Request(const BoundNetLog& source_net_log,
+ const BoundNetLog& request_net_log,
+ const RequestInfo& info,
+ const CompletionCallback& callback,
+ AddressList* addresses)
+ : source_net_log_(source_net_log),
+ request_net_log_(request_net_log),
+ info_(info),
+ job_(NULL),
+ callback_(callback),
+ addresses_(addresses),
+ request_time_(base::TimeTicks::Now()) {
+ }
+
+ // Mark the request as canceled.
+ void MarkAsCanceled() {
+ job_ = NULL;
+ addresses_ = NULL;
+ callback_.Reset();
+ }
+
+ bool was_canceled() const {
+ return callback_.is_null();
+ }
+
+ void set_job(Job* job) {
+ DCHECK(job);
+ // Identify which job the request is waiting on.
+ job_ = job;
+ }
+
+ // Prepare final AddressList and call completion callback.
+ void OnComplete(int error, const AddressList& addr_list) {
+ DCHECK(!was_canceled());
+ if (error == OK)
+ *addresses_ = EnsurePortOnAddressList(addr_list, info_.port());
+ CompletionCallback callback = callback_;
+ MarkAsCanceled();
+ callback.Run(error);
+ }
+
+ Job* job() const {
+ return job_;
+ }
+
+ // NetLog for the source, passed in HostResolver::Resolve.
+ const BoundNetLog& source_net_log() {
+ return source_net_log_;
+ }
+
+ // NetLog for this request.
+ const BoundNetLog& request_net_log() {
+ return request_net_log_;
+ }
+
+ const RequestInfo& info() const {
+ return info_;
+ }
+
+ base::TimeTicks request_time() const {
+ return request_time_;
+ }
+
+ private:
+ BoundNetLog source_net_log_;
+ BoundNetLog request_net_log_;
+
+ // The request info that started the request.
+ RequestInfo info_;
+
+ // The resolve job that this request is dependent on.
+ Job* job_;
+
+ // The user's callback to invoke when the request completes.
+ CompletionCallback callback_;
+
+ // The address list to save result into.
+ AddressList* addresses_;
+
+ const base::TimeTicks request_time_;
+
+ DISALLOW_COPY_AND_ASSIGN(Request);
+};
+
+//------------------------------------------------------------------------------
+
+// Calls HostResolverProc on the WorkerPool. Performs retries if necessary.
+//
+// Whenever we try to resolve the host, we post a delayed task to check if host
+// resolution (OnLookupComplete) is completed or not. If the original attempt
+// hasn't completed, then we start another attempt for host resolution. We take
+// the results from the first attempt that finishes and ignore the results from
+// all other attempts.
+//
+// TODO(szym): Move to separate source file for testing and mocking.
+//
+class HostResolverImpl::ProcTask
+ : public base::RefCountedThreadSafe<HostResolverImpl::ProcTask> {
+ public:
+ typedef base::Callback<void(int net_error,
+ const AddressList& addr_list)> Callback;
+
+ ProcTask(const Key& key,
+ const ProcTaskParams& params,
+ const Callback& callback,
+ const BoundNetLog& job_net_log)
+ : key_(key),
+ params_(params),
+ callback_(callback),
+ origin_loop_(base::MessageLoopProxy::current()),
+ attempt_number_(0),
+ completed_attempt_number_(0),
+ completed_attempt_error_(ERR_UNEXPECTED),
+ had_non_speculative_request_(false),
+ net_log_(job_net_log) {
+ if (!params_.resolver_proc)
+ params_.resolver_proc = HostResolverProc::GetDefault();
+ // If default is unset, use the system proc.
+ if (!params_.resolver_proc)
+ params_.resolver_proc = new CallSystemHostResolverProc();
+ }
+
+ void Start() {
+ DCHECK(origin_loop_->BelongsToCurrentThread());
+ net_log_.BeginEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_PROC_TASK);
+ StartLookupAttempt();
+ }
+
+ // Cancels this ProcTask. It will be orphaned. Any outstanding resolve
+ // attempts running on worker threads will continue running. Only once all the
+ // attempts complete will the final reference to this ProcTask be released.
+ void Cancel() {
+ DCHECK(origin_loop_->BelongsToCurrentThread());
+
+ if (was_canceled() || was_completed())
+ return;
+
+ callback_.Reset();
+ net_log_.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_PROC_TASK);
+ }
+
+ void set_had_non_speculative_request() {
+ DCHECK(origin_loop_->BelongsToCurrentThread());
+ had_non_speculative_request_ = true;
+ }
+
+ bool was_canceled() const {
+ DCHECK(origin_loop_->BelongsToCurrentThread());
+ return callback_.is_null();
+ }
+
+ bool was_completed() const {
+ DCHECK(origin_loop_->BelongsToCurrentThread());
+ return completed_attempt_number_ > 0;
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<ProcTask>;
+ ~ProcTask() {}
+
+ void StartLookupAttempt() {
+ DCHECK(origin_loop_->BelongsToCurrentThread());
+ base::TimeTicks start_time = base::TimeTicks::Now();
+ ++attempt_number_;
+ // Dispatch the lookup attempt to a worker thread.
+ if (!base::WorkerPool::PostTask(
+ FROM_HERE,
+ base::Bind(&ProcTask::DoLookup, this, start_time, attempt_number_),
+ true)) {
+ NOTREACHED();
+
+ // Since we could be running within Resolve() right now, we can't just
+ // call OnLookupComplete(). Instead we must wait until Resolve() has
+ // returned (IO_PENDING).
+ origin_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&ProcTask::OnLookupComplete, this, AddressList(),
+ start_time, attempt_number_, ERR_UNEXPECTED, 0));
+ return;
+ }
+
+ net_log_.AddEvent(
+ NetLog::TYPE_HOST_RESOLVER_IMPL_ATTEMPT_STARTED,
+ NetLog::IntegerCallback("attempt_number", attempt_number_));
+
+ // If we don't get the results within a given time, RetryIfNotComplete
+ // will start a new attempt on a different worker thread if none of our
+ // outstanding attempts have completed yet.
+ if (attempt_number_ <= params_.max_retry_attempts) {
+ origin_loop_->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&ProcTask::RetryIfNotComplete, this),
+ params_.unresponsive_delay);
+ }
+ }
+
+ // WARNING: This code runs inside a worker pool. The shutdown code cannot
+ // wait for it to finish, so we must be very careful here about using other
+ // objects (like MessageLoops, Singletons, etc). During shutdown these objects
+ // may no longer exist. Multiple DoLookups() could be running in parallel, so
+ // any state inside of |this| must not mutate .
+ void DoLookup(const base::TimeTicks& start_time,
+ const uint32 attempt_number) {
+ AddressList results;
+ int os_error = 0;
+ // Running on the worker thread
+ int error = params_.resolver_proc->Resolve(key_.hostname,
+ key_.address_family,
+ key_.host_resolver_flags,
+ &results,
+ &os_error);
+
+ origin_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&ProcTask::OnLookupComplete, this, results, start_time,
+ attempt_number, error, os_error));
+ }
+
+ // Makes next attempt if DoLookup() has not finished (runs on origin thread).
+ void RetryIfNotComplete() {
+ DCHECK(origin_loop_->BelongsToCurrentThread());
+
+ if (was_completed() || was_canceled())
+ return;
+
+ params_.unresponsive_delay *= params_.retry_factor;
+ StartLookupAttempt();
+ }
+
+ // Callback for when DoLookup() completes (runs on origin thread).
+ void OnLookupComplete(const AddressList& results,
+ const base::TimeTicks& start_time,
+ const uint32 attempt_number,
+ int error,
+ const int os_error) {
+ DCHECK(origin_loop_->BelongsToCurrentThread());
+ DCHECK(error || !results.empty());
+
+ bool was_retry_attempt = attempt_number > 1;
+
+ // Ideally the following code would be part of host_resolver_proc.cc,
+ // however it isn't safe to call NetworkChangeNotifier from worker threads.
+ // So we do it here on the IO thread instead.
+ if (error != OK && NetworkChangeNotifier::IsOffline())
+ error = ERR_INTERNET_DISCONNECTED;
+
+ // If this is the first attempt that is finishing later, then record data
+ // for the first attempt. Won't contaminate with retry attempt's data.
+ if (!was_retry_attempt)
+ RecordPerformanceHistograms(start_time, error, os_error);
+
+ RecordAttemptHistograms(start_time, attempt_number, error, os_error);
+
+ if (was_canceled())
+ return;
+
+ NetLog::ParametersCallback net_log_callback;
+ if (error != OK) {
+ net_log_callback = base::Bind(&NetLogProcTaskFailedCallback,
+ attempt_number,
+ error,
+ os_error);
+ } else {
+ net_log_callback = NetLog::IntegerCallback("attempt_number",
+ attempt_number);
+ }
+ net_log_.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_ATTEMPT_FINISHED,
+ net_log_callback);
+
+ if (was_completed())
+ return;
+
+ // Copy the results from the first worker thread that resolves the host.
+ results_ = results;
+ completed_attempt_number_ = attempt_number;
+ completed_attempt_error_ = error;
+
+ if (was_retry_attempt) {
+ // If retry attempt finishes before 1st attempt, then get stats on how
+ // much time is saved by having spawned an extra attempt.
+ retry_attempt_finished_time_ = base::TimeTicks::Now();
+ }
+
+ if (error != OK) {
+ net_log_callback = base::Bind(&NetLogProcTaskFailedCallback,
+ 0, error, os_error);
+ } else {
+ net_log_callback = results_.CreateNetLogCallback();
+ }
+ net_log_.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_PROC_TASK,
+ net_log_callback);
+
+ callback_.Run(error, results_);
+ }
+
+ void RecordPerformanceHistograms(const base::TimeTicks& start_time,
+ const int error,
+ const int os_error) const {
+ DCHECK(origin_loop_->BelongsToCurrentThread());
+ enum Category { // Used in HISTOGRAM_ENUMERATION.
+ RESOLVE_SUCCESS,
+ RESOLVE_FAIL,
+ RESOLVE_SPECULATIVE_SUCCESS,
+ RESOLVE_SPECULATIVE_FAIL,
+ RESOLVE_MAX, // Bounding value.
+ };
+ int category = RESOLVE_MAX; // Illegal value for later DCHECK only.
+
+ base::TimeDelta duration = base::TimeTicks::Now() - start_time;
+ if (error == OK) {
+ if (had_non_speculative_request_) {
+ category = RESOLVE_SUCCESS;
+ DNS_HISTOGRAM("DNS.ResolveSuccess", duration);
+ } else {
+ category = RESOLVE_SPECULATIVE_SUCCESS;
+ DNS_HISTOGRAM("DNS.ResolveSpeculativeSuccess", duration);
+ }
+
+ // Log DNS lookups based on |address_family|. This will help us determine
+ // if IPv4 or IPv4/6 lookups are faster or slower.
+ switch(key_.address_family) {
+ case ADDRESS_FAMILY_IPV4:
+ DNS_HISTOGRAM("DNS.ResolveSuccess_FAMILY_IPV4", duration);
+ break;
+ case ADDRESS_FAMILY_IPV6:
+ DNS_HISTOGRAM("DNS.ResolveSuccess_FAMILY_IPV6", duration);
+ break;
+ case ADDRESS_FAMILY_UNSPECIFIED:
+ DNS_HISTOGRAM("DNS.ResolveSuccess_FAMILY_UNSPEC", duration);
+ break;
+ }
+ } else {
+ if (had_non_speculative_request_) {
+ category = RESOLVE_FAIL;
+ DNS_HISTOGRAM("DNS.ResolveFail", duration);
+ } else {
+ category = RESOLVE_SPECULATIVE_FAIL;
+ DNS_HISTOGRAM("DNS.ResolveSpeculativeFail", duration);
+ }
+ // Log DNS lookups based on |address_family|. This will help us determine
+ // if IPv4 or IPv4/6 lookups are faster or slower.
+ switch(key_.address_family) {
+ case ADDRESS_FAMILY_IPV4:
+ DNS_HISTOGRAM("DNS.ResolveFail_FAMILY_IPV4", duration);
+ break;
+ case ADDRESS_FAMILY_IPV6:
+ DNS_HISTOGRAM("DNS.ResolveFail_FAMILY_IPV6", duration);
+ break;
+ case ADDRESS_FAMILY_UNSPECIFIED:
+ DNS_HISTOGRAM("DNS.ResolveFail_FAMILY_UNSPEC", duration);
+ break;
+ }
+ UMA_HISTOGRAM_CUSTOM_ENUMERATION(kOSErrorsForGetAddrinfoHistogramName,
+ std::abs(os_error),
+ GetAllGetAddrinfoOSErrors());
+ }
+ DCHECK_LT(category, static_cast<int>(RESOLVE_MAX)); // Be sure it was set.
+
+ UMA_HISTOGRAM_ENUMERATION("DNS.ResolveCategory", category, RESOLVE_MAX);
+
+ static const bool show_parallelism_experiment_histograms =
+ base::FieldTrialList::TrialExists("DnsParallelism");
+ if (show_parallelism_experiment_histograms) {
+ UMA_HISTOGRAM_ENUMERATION(
+ base::FieldTrial::MakeName("DNS.ResolveCategory", "DnsParallelism"),
+ category, RESOLVE_MAX);
+ if (RESOLVE_SUCCESS == category) {
+ DNS_HISTOGRAM(base::FieldTrial::MakeName("DNS.ResolveSuccess",
+ "DnsParallelism"), duration);
+ }
+ }
+ }
+
+ void RecordAttemptHistograms(const base::TimeTicks& start_time,
+ const uint32 attempt_number,
+ const int error,
+ const int os_error) const {
+ DCHECK(origin_loop_->BelongsToCurrentThread());
+ bool first_attempt_to_complete =
+ completed_attempt_number_ == attempt_number;
+ bool is_first_attempt = (attempt_number == 1);
+
+ if (first_attempt_to_complete) {
+ // If this was first attempt to complete, then record the resolution
+ // status of the attempt.
+ if (completed_attempt_error_ == OK) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "DNS.AttemptFirstSuccess", attempt_number, 100);
+ } else {
+ UMA_HISTOGRAM_ENUMERATION(
+ "DNS.AttemptFirstFailure", attempt_number, 100);
+ }
+ }
+
+ if (error == OK)
+ UMA_HISTOGRAM_ENUMERATION("DNS.AttemptSuccess", attempt_number, 100);
+ else
+ UMA_HISTOGRAM_ENUMERATION("DNS.AttemptFailure", attempt_number, 100);
+
+ // If first attempt didn't finish before retry attempt, then calculate stats
+ // on how much time is saved by having spawned an extra attempt.
+ if (!first_attempt_to_complete && is_first_attempt && !was_canceled()) {
+ DNS_HISTOGRAM("DNS.AttemptTimeSavedByRetry",
+ base::TimeTicks::Now() - retry_attempt_finished_time_);
+ }
+
+ if (was_canceled() || !first_attempt_to_complete) {
+ // Count those attempts which completed after the job was already canceled
+ // OR after the job was already completed by an earlier attempt (so in
+ // effect).
+ UMA_HISTOGRAM_ENUMERATION("DNS.AttemptDiscarded", attempt_number, 100);
+
+ // Record if job is canceled.
+ if (was_canceled())
+ UMA_HISTOGRAM_ENUMERATION("DNS.AttemptCancelled", attempt_number, 100);
+ }
+
+ base::TimeDelta duration = base::TimeTicks::Now() - start_time;
+ if (error == OK)
+ DNS_HISTOGRAM("DNS.AttemptSuccessDuration", duration);
+ else
+ DNS_HISTOGRAM("DNS.AttemptFailDuration", duration);
+ }
+
+ // Set on the origin thread, read on the worker thread.
+ Key key_;
+
+ // Holds an owning reference to the HostResolverProc that we are going to use.
+ // This may not be the current resolver procedure by the time we call
+ // ResolveAddrInfo, but that's OK... we'll use it anyways, and the owning
+ // reference ensures that it remains valid until we are done.
+ ProcTaskParams params_;
+
+ // The listener to the results of this ProcTask.
+ Callback callback_;
+
+ // Used to post ourselves onto the origin thread.
+ scoped_refptr<base::MessageLoopProxy> origin_loop_;
+
+ // Keeps track of the number of attempts we have made so far to resolve the
+ // host. Whenever we start an attempt to resolve the host, we increase this
+ // number.
+ uint32 attempt_number_;
+
+ // The index of the attempt which finished first (or 0 if the job is still in
+ // progress).
+ uint32 completed_attempt_number_;
+
+ // The result (a net error code) from the first attempt to complete.
+ int completed_attempt_error_;
+
+ // The time when retry attempt was finished.
+ base::TimeTicks retry_attempt_finished_time_;
+
+ // True if a non-speculative request was ever attached to this job
+ // (regardless of whether or not it was later canceled.
+ // This boolean is used for histogramming the duration of jobs used to
+ // service non-speculative requests.
+ bool had_non_speculative_request_;
+
+ AddressList results_;
+
+ BoundNetLog net_log_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProcTask);
+};
+
+//-----------------------------------------------------------------------------
+
+// Wraps a call to TestIPv6Support to be executed on the WorkerPool as it takes
+// 40-100ms.
+class HostResolverImpl::IPv6ProbeJob {
+ public:
+ IPv6ProbeJob(const base::WeakPtr<HostResolverImpl>& resolver, NetLog* net_log)
+ : resolver_(resolver),
+ net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_IPV6_PROBE_JOB)),
+ result_(false, IPV6_SUPPORT_MAX, OK) {
+ DCHECK(resolver);
+ net_log_.BeginEvent(NetLog::TYPE_IPV6_PROBE_RUNNING);
+ const bool kIsSlow = true;
+ base::WorkerPool::PostTaskAndReply(
+ FROM_HERE,
+ base::Bind(&IPv6ProbeJob::DoProbe, base::Unretained(this)),
+ base::Bind(&IPv6ProbeJob::OnProbeComplete, base::Owned(this)),
+ kIsSlow);
+ }
+
+ virtual ~IPv6ProbeJob() {}
+
+ private:
+ // Runs on worker thread.
+ void DoProbe() {
+ result_ = TestIPv6Support();
+ }
+
+ void OnProbeComplete() {
+ net_log_.EndEvent(NetLog::TYPE_IPV6_PROBE_RUNNING,
+ base::Bind(&IPv6SupportResult::ToNetLogValue,
+ base::Unretained(&result_)));
+ if (!resolver_)
+ return;
+ resolver_->IPv6ProbeSetDefaultAddressFamily(
+ result_.ipv6_supported ? ADDRESS_FAMILY_UNSPECIFIED
+ : ADDRESS_FAMILY_IPV4);
+ }
+
+ // Used/set only on origin thread.
+ base::WeakPtr<HostResolverImpl> resolver_;
+
+ BoundNetLog net_log_;
+
+ IPv6SupportResult result_;
+
+ DISALLOW_COPY_AND_ASSIGN(IPv6ProbeJob);
+};
+
+// Wraps a call to HaveOnlyLoopbackAddresses to be executed on the WorkerPool as
+// it takes 40-100ms and should not block initialization.
+class HostResolverImpl::LoopbackProbeJob {
+ public:
+ explicit LoopbackProbeJob(const base::WeakPtr<HostResolverImpl>& resolver)
+ : resolver_(resolver),
+ result_(false) {
+ DCHECK(resolver);
+ const bool kIsSlow = true;
+ base::WorkerPool::PostTaskAndReply(
+ FROM_HERE,
+ base::Bind(&LoopbackProbeJob::DoProbe, base::Unretained(this)),
+ base::Bind(&LoopbackProbeJob::OnProbeComplete, base::Owned(this)),
+ kIsSlow);
+ }
+
+ virtual ~LoopbackProbeJob() {}
+
+ private:
+ // Runs on worker thread.
+ void DoProbe() {
+ result_ = HaveOnlyLoopbackAddresses();
+ }
+
+ void OnProbeComplete() {
+ if (!resolver_)
+ return;
+ resolver_->SetHaveOnlyLoopbackAddresses(result_);
+ }
+
+ // Used/set only on origin thread.
+ base::WeakPtr<HostResolverImpl> resolver_;
+
+ bool result_;
+
+ DISALLOW_COPY_AND_ASSIGN(LoopbackProbeJob);
+};
+
+//-----------------------------------------------------------------------------
+
+// Resolves the hostname using DnsTransaction.
+// TODO(szym): This could be moved to separate source file as well.
+class HostResolverImpl::DnsTask : public base::SupportsWeakPtr<DnsTask> {
+ public:
+ typedef base::Callback<void(int net_error,
+ const AddressList& addr_list,
+ base::TimeDelta ttl)> Callback;
+
+ DnsTask(DnsClient* client,
+ const Key& key,
+ const Callback& callback,
+ const BoundNetLog& job_net_log)
+ : client_(client),
+ family_(key.address_family),
+ callback_(callback),
+ net_log_(job_net_log) {
+ DCHECK(client);
+ DCHECK(!callback.is_null());
+
+ // If unspecified, do IPv4 first, because suffix search will be faster.
+ uint16 qtype = (family_ == ADDRESS_FAMILY_IPV6) ?
+ dns_protocol::kTypeAAAA :
+ dns_protocol::kTypeA;
+ transaction_ = client_->GetTransactionFactory()->CreateTransaction(
+ key.hostname,
+ qtype,
+ base::Bind(&DnsTask::OnTransactionComplete, base::Unretained(this),
+ true /* first_query */, base::TimeTicks::Now()),
+ net_log_);
+ }
+
+ int Start() {
+ net_log_.BeginEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_DNS_TASK);
+ return transaction_->Start();
+ }
+
+ private:
+ void OnTransactionComplete(bool first_query,
+ const base::TimeTicks& start_time,
+ DnsTransaction* transaction,
+ int net_error,
+ const DnsResponse* response) {
+ DCHECK(transaction);
+ base::TimeDelta duration = base::TimeTicks::Now() - start_time;
+ // Run |callback_| last since the owning Job will then delete this DnsTask.
+ if (net_error != OK) {
+ DNS_HISTOGRAM("AsyncDNS.TransactionFailure", duration);
+ OnFailure(net_error, DnsResponse::DNS_PARSE_OK);
+ return;
+ }
+
+ CHECK(response);
+ DNS_HISTOGRAM("AsyncDNS.TransactionSuccess", duration);
+ switch (transaction->GetType()) {
+ case dns_protocol::kTypeA:
+ DNS_HISTOGRAM("AsyncDNS.TransactionSuccess_A", duration);
+ break;
+ case dns_protocol::kTypeAAAA:
+ DNS_HISTOGRAM("AsyncDNS.TransactionSuccess_AAAA", duration);
+ break;
+ }
+ AddressList addr_list;
+ base::TimeDelta ttl;
+ DnsResponse::Result result = response->ParseToAddressList(&addr_list, &ttl);
+ UMA_HISTOGRAM_ENUMERATION("AsyncDNS.ParseToAddressList",
+ result,
+ DnsResponse::DNS_PARSE_RESULT_MAX);
+ if (result != DnsResponse::DNS_PARSE_OK) {
+ // Fail even if the other query succeeds.
+ OnFailure(ERR_DNS_MALFORMED_RESPONSE, result);
+ return;
+ }
+
+ bool needs_sort = false;
+ if (first_query) {
+ DCHECK(client_->GetConfig()) <<
+ "Transaction should have been aborted when config changed!";
+ if (family_ == ADDRESS_FAMILY_IPV6) {
+ needs_sort = (addr_list.size() > 1);
+ } else if (family_ == ADDRESS_FAMILY_UNSPECIFIED) {
+ first_addr_list_ = addr_list;
+ first_ttl_ = ttl;
+ // Use fully-qualified domain name to avoid search.
+ transaction_ = client_->GetTransactionFactory()->CreateTransaction(
+ response->GetDottedName() + ".",
+ dns_protocol::kTypeAAAA,
+ base::Bind(&DnsTask::OnTransactionComplete, base::Unretained(this),
+ false /* first_query */, base::TimeTicks::Now()),
+ net_log_);
+ net_error = transaction_->Start();
+ if (net_error != ERR_IO_PENDING)
+ OnFailure(net_error, DnsResponse::DNS_PARSE_OK);
+ return;
+ }
+ } else {
+ DCHECK_EQ(ADDRESS_FAMILY_UNSPECIFIED, family_);
+ bool has_ipv6_addresses = !addr_list.empty();
+ if (!first_addr_list_.empty()) {
+ ttl = std::min(ttl, first_ttl_);
+ // Place IPv4 addresses after IPv6.
+ addr_list.insert(addr_list.end(), first_addr_list_.begin(),
+ first_addr_list_.end());
+ }
+ needs_sort = (has_ipv6_addresses && addr_list.size() > 1);
+ }
+
+ if (addr_list.empty()) {
+ // TODO(szym): Don't fallback to ProcTask in this case.
+ OnFailure(ERR_NAME_NOT_RESOLVED, DnsResponse::DNS_PARSE_OK);
+ return;
+ }
+
+ if (needs_sort) {
+ // Sort could complete synchronously.
+ client_->GetAddressSorter()->Sort(
+ addr_list,
+ base::Bind(&DnsTask::OnSortComplete,
+ AsWeakPtr(),
+ base::TimeTicks::Now(),
+ ttl));
+ } else {
+ OnSuccess(addr_list, ttl);
+ }
+ }
+
+ void OnSortComplete(base::TimeTicks start_time,
+ base::TimeDelta ttl,
+ bool success,
+ const AddressList& addr_list) {
+ if (!success) {
+ DNS_HISTOGRAM("AsyncDNS.SortFailure",
+ base::TimeTicks::Now() - start_time);
+ OnFailure(ERR_DNS_SORT_ERROR, DnsResponse::DNS_PARSE_OK);
+ return;
+ }
+
+ DNS_HISTOGRAM("AsyncDNS.SortSuccess",
+ base::TimeTicks::Now() - start_time);
+
+ // AddressSorter prunes unusable destinations.
+ if (addr_list.empty()) {
+ LOG(WARNING) << "Address list empty after RFC3484 sort";
+ OnFailure(ERR_NAME_NOT_RESOLVED, DnsResponse::DNS_PARSE_OK);
+ return;
+ }
+
+ OnSuccess(addr_list, ttl);
+ }
+
+ void OnFailure(int net_error, DnsResponse::Result result) {
+ DCHECK_NE(OK, net_error);
+ net_log_.EndEvent(
+ NetLog::TYPE_HOST_RESOLVER_IMPL_DNS_TASK,
+ base::Bind(&NetLogDnsTaskFailedCallback, net_error, result));
+ callback_.Run(net_error, AddressList(), base::TimeDelta());
+ }
+
+ void OnSuccess(const AddressList& addr_list, base::TimeDelta ttl) {
+ net_log_.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_DNS_TASK,
+ addr_list.CreateNetLogCallback());
+ callback_.Run(OK, addr_list, ttl);
+ }
+
+ DnsClient* client_;
+ AddressFamily family_;
+ // The listener to the results of this DnsTask.
+ Callback callback_;
+ const BoundNetLog net_log_;
+
+ scoped_ptr<DnsTransaction> transaction_;
+
+ // Results from the first transaction. Used only if |family_| is unspecified.
+ AddressList first_addr_list_;
+ base::TimeDelta first_ttl_;
+
+ DISALLOW_COPY_AND_ASSIGN(DnsTask);
+};
+
+//-----------------------------------------------------------------------------
+
+// Aggregates all Requests for the same Key. Dispatched via PriorityDispatch.
+class HostResolverImpl::Job : public PrioritizedDispatcher::Job {
+ public:
+ // Creates new job for |key| where |request_net_log| is bound to the
+ // request that spawned it.
+ Job(const base::WeakPtr<HostResolverImpl>& resolver,
+ const Key& key,
+ RequestPriority priority,
+ const BoundNetLog& request_net_log)
+ : resolver_(resolver),
+ key_(key),
+ priority_tracker_(priority),
+ had_non_speculative_request_(false),
+ had_dns_config_(false),
+ dns_task_error_(OK),
+ creation_time_(base::TimeTicks::Now()),
+ priority_change_time_(creation_time_),
+ net_log_(BoundNetLog::Make(request_net_log.net_log(),
+ NetLog::SOURCE_HOST_RESOLVER_IMPL_JOB)) {
+ request_net_log.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_CREATE_JOB);
+
+ net_log_.BeginEvent(
+ NetLog::TYPE_HOST_RESOLVER_IMPL_JOB,
+ base::Bind(&NetLogJobCreationCallback,
+ request_net_log.source(),
+ &key_.hostname));
+ }
+
+ virtual ~Job() {
+ if (is_running()) {
+ // |resolver_| was destroyed with this Job still in flight.
+ // Clean-up, record in the log, but don't run any callbacks.
+ if (is_proc_running()) {
+ proc_task_->Cancel();
+ proc_task_ = NULL;
+ }
+ // Clean up now for nice NetLog.
+ dns_task_.reset(NULL);
+ net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HOST_RESOLVER_IMPL_JOB,
+ ERR_ABORTED);
+ } else if (is_queued()) {
+ // |resolver_| was destroyed without running this Job.
+ // TODO(szym): is there any benefit in having this distinction?
+ net_log_.AddEvent(NetLog::TYPE_CANCELLED);
+ net_log_.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_JOB);
+ }
+ // else CompleteRequests logged EndEvent.
+
+ // Log any remaining Requests as cancelled.
+ for (RequestsList::const_iterator it = requests_.begin();
+ it != requests_.end(); ++it) {
+ Request* req = *it;
+ if (req->was_canceled())
+ continue;
+ DCHECK_EQ(this, req->job());
+ LogCancelRequest(req->source_net_log(), req->request_net_log(),
+ req->info());
+ }
+ }
+
+ // Add this job to the dispatcher.
+ void Schedule() {
+ handle_ = resolver_->dispatcher_.Add(this, priority());
+ }
+
+ void AddRequest(scoped_ptr<Request> req) {
+ DCHECK_EQ(key_.hostname, req->info().hostname());
+
+ req->set_job(this);
+ priority_tracker_.Add(req->info().priority());
+
+ req->request_net_log().AddEvent(
+ NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_ATTACH,
+ net_log_.source().ToEventParametersCallback());
+
+ net_log_.AddEvent(
+ NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_REQUEST_ATTACH,
+ base::Bind(&NetLogJobAttachCallback,
+ req->request_net_log().source(),
+ priority()));
+
+ // TODO(szym): Check if this is still needed.
+ if (!req->info().is_speculative()) {
+ had_non_speculative_request_ = true;
+ if (proc_task_)
+ proc_task_->set_had_non_speculative_request();
+ }
+
+ requests_.push_back(req.release());
+
+ UpdatePriority();
+ }
+
+ // Marks |req| as cancelled. If it was the last active Request, also finishes
+ // this Job, marking it as cancelled, and deletes it.
+ void CancelRequest(Request* req) {
+ DCHECK_EQ(key_.hostname, req->info().hostname());
+ DCHECK(!req->was_canceled());
+
+ // Don't remove it from |requests_| just mark it canceled.
+ req->MarkAsCanceled();
+ LogCancelRequest(req->source_net_log(), req->request_net_log(),
+ req->info());
+
+ priority_tracker_.Remove(req->info().priority());
+ net_log_.AddEvent(
+ NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_REQUEST_DETACH,
+ base::Bind(&NetLogJobAttachCallback,
+ req->request_net_log().source(),
+ priority()));
+
+ if (num_active_requests() > 0) {
+ UpdatePriority();
+ } else {
+ // If we were called from a Request's callback within CompleteRequests,
+ // that Request could not have been cancelled, so num_active_requests()
+ // could not be 0. Therefore, we are not in CompleteRequests().
+ CompleteRequestsWithError(OK /* cancelled */);
+ }
+ }
+
+ // Called from AbortAllInProgressJobs. Completes all requests and destroys
+ // the job. This currently assumes the abort is due to a network change.
+ void Abort() {
+ DCHECK(is_running());
+ CompleteRequestsWithError(ERR_NETWORK_CHANGED);
+ }
+
+ // If DnsTask present, abort it and fall back to ProcTask.
+ void AbortDnsTask() {
+ if (dns_task_) {
+ dns_task_.reset();
+ dns_task_error_ = OK;
+ StartProcTask();
+ }
+ }
+
+ // Called by HostResolverImpl when this job is evicted due to queue overflow.
+ // Completes all requests and destroys the job.
+ void OnEvicted() {
+ DCHECK(!is_running());
+ DCHECK(is_queued());
+ handle_.Reset();
+
+ net_log_.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_EVICTED);
+
+ // This signals to CompleteRequests that this job never ran.
+ CompleteRequestsWithError(ERR_HOST_RESOLVER_QUEUE_TOO_LARGE);
+ }
+
+ // Attempts to serve the job from HOSTS. Returns true if succeeded and
+ // this Job was destroyed.
+ bool ServeFromHosts() {
+ DCHECK_GT(num_active_requests(), 0u);
+ AddressList addr_list;
+ if (resolver_->ServeFromHosts(key(),
+ requests_.front()->info(),
+ &addr_list)) {
+ // This will destroy the Job.
+ CompleteRequests(
+ HostCache::Entry(OK, MakeAddressListForRequest(addr_list)),
+ base::TimeDelta());
+ return true;
+ }
+ return false;
+ }
+
+ const Key key() const {
+ return key_;
+ }
+
+ bool is_queued() const {
+ return !handle_.is_null();
+ }
+
+ bool is_running() const {
+ return is_dns_running() || is_proc_running();
+ }
+
+ private:
+ void UpdatePriority() {
+ if (is_queued()) {
+ if (priority() != static_cast<RequestPriority>(handle_.priority()))
+ priority_change_time_ = base::TimeTicks::Now();
+ handle_ = resolver_->dispatcher_.ChangePriority(handle_, priority());
+ }
+ }
+
+ AddressList MakeAddressListForRequest(const AddressList& list) const {
+ if (requests_.empty())
+ return list;
+ return AddressList::CopyWithPort(list, requests_.front()->info().port());
+ }
+
+ // PriorityDispatch::Job:
+ virtual void Start() OVERRIDE {
+ DCHECK(!is_running());
+ handle_.Reset();
+
+ net_log_.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_STARTED);
+
+ had_dns_config_ = resolver_->HaveDnsConfig();
+
+ base::TimeTicks now = base::TimeTicks::Now();
+ base::TimeDelta queue_time = now - creation_time_;
+ base::TimeDelta queue_time_after_change = now - priority_change_time_;
+
+ if (had_dns_config_) {
+ DNS_HISTOGRAM_BY_PRIORITY("AsyncDNS.JobQueueTime", priority(),
+ queue_time);
+ DNS_HISTOGRAM_BY_PRIORITY("AsyncDNS.JobQueueTimeAfterChange", priority(),
+ queue_time_after_change);
+ } else {
+ DNS_HISTOGRAM_BY_PRIORITY("DNS.JobQueueTime", priority(), queue_time);
+ DNS_HISTOGRAM_BY_PRIORITY("DNS.JobQueueTimeAfterChange", priority(),
+ queue_time_after_change);
+ }
+
+ // Caution: Job::Start must not complete synchronously.
+ if (had_dns_config_ && !ResemblesMulticastDNSName(key_.hostname)) {
+ StartDnsTask();
+ } else {
+ StartProcTask();
+ }
+ }
+
+ // TODO(szym): Since DnsTransaction does not consume threads, we can increase
+ // the limits on |dispatcher_|. But in order to keep the number of WorkerPool
+ // threads low, we will need to use an "inner" PrioritizedDispatcher with
+ // tighter limits.
+ void StartProcTask() {
+ DCHECK(!is_dns_running());
+ proc_task_ = new ProcTask(
+ key_,
+ resolver_->proc_params_,
+ base::Bind(&Job::OnProcTaskComplete, base::Unretained(this),
+ base::TimeTicks::Now()),
+ net_log_);
+
+ if (had_non_speculative_request_)
+ proc_task_->set_had_non_speculative_request();
+ // Start() could be called from within Resolve(), hence it must NOT directly
+ // call OnProcTaskComplete, for example, on synchronous failure.
+ proc_task_->Start();
+ }
+
+ // Called by ProcTask when it completes.
+ void OnProcTaskComplete(base::TimeTicks start_time,
+ int net_error,
+ const AddressList& addr_list) {
+ DCHECK(is_proc_running());
+
+ if (!resolver_->resolved_known_ipv6_hostname_ &&
+ net_error == OK &&
+ key_.address_family == ADDRESS_FAMILY_UNSPECIFIED) {
+ if (key_.hostname == "www.google.com") {
+ resolver_->resolved_known_ipv6_hostname_ = true;
+ bool got_ipv6_address = false;
+ for (size_t i = 0; i < addr_list.size(); ++i) {
+ if (addr_list[i].GetFamily() == ADDRESS_FAMILY_IPV6)
+ got_ipv6_address = true;
+ }
+ UMA_HISTOGRAM_BOOLEAN("Net.UnspecResolvedIPv6", got_ipv6_address);
+ }
+ }
+
+ if (dns_task_error_ != OK) {
+ base::TimeDelta duration = base::TimeTicks::Now() - start_time;
+ if (net_error == OK) {
+ DNS_HISTOGRAM("AsyncDNS.FallbackSuccess", duration);
+ if ((dns_task_error_ == ERR_NAME_NOT_RESOLVED) &&
+ ResemblesNetBIOSName(key_.hostname)) {
+ UmaAsyncDnsResolveStatus(RESOLVE_STATUS_SUSPECT_NETBIOS);
+ } else {
+ UmaAsyncDnsResolveStatus(RESOLVE_STATUS_PROC_SUCCESS);
+ }
+ UMA_HISTOGRAM_CUSTOM_ENUMERATION("AsyncDNS.ResolveError",
+ std::abs(dns_task_error_),
+ GetAllErrorCodesForUma());
+ resolver_->OnDnsTaskResolve(dns_task_error_);
+ } else {
+ DNS_HISTOGRAM("AsyncDNS.FallbackFail", duration);
+ UmaAsyncDnsResolveStatus(RESOLVE_STATUS_FAIL);
+ }
+ }
+
+ base::TimeDelta ttl =
+ base::TimeDelta::FromSeconds(kNegativeCacheEntryTTLSeconds);
+ if (net_error == OK)
+ ttl = base::TimeDelta::FromSeconds(kCacheEntryTTLSeconds);
+
+ // Don't store the |ttl| in cache since it's not obtained from the server.
+ CompleteRequests(
+ HostCache::Entry(net_error, MakeAddressListForRequest(addr_list)),
+ ttl);
+ }
+
+ void StartDnsTask() {
+ DCHECK(resolver_->HaveDnsConfig());
+ dns_task_.reset(new DnsTask(
+ resolver_->dns_client_.get(),
+ key_,
+ base::Bind(&Job::OnDnsTaskComplete, base::Unretained(this),
+ base::TimeTicks::Now()),
+ net_log_));
+
+ int rv = dns_task_->Start();
+ if (rv != ERR_IO_PENDING) {
+ DCHECK_NE(OK, rv);
+ dns_task_error_ = rv;
+ dns_task_.reset();
+ StartProcTask();
+ }
+ }
+
+ // Called by DnsTask when it completes.
+ void OnDnsTaskComplete(base::TimeTicks start_time,
+ int net_error,
+ const AddressList& addr_list,
+ base::TimeDelta ttl) {
+ DCHECK(is_dns_running());
+
+ base::TimeDelta duration = base::TimeTicks::Now() - start_time;
+ if (net_error != OK) {
+ DNS_HISTOGRAM("AsyncDNS.ResolveFail", duration);
+
+ dns_task_error_ = net_error;
+ dns_task_.reset();
+
+ // TODO(szym): Run ServeFromHosts now if nsswitch.conf says so.
+ // http://crbug.com/117655
+
+ // TODO(szym): Some net errors indicate lack of connectivity. Starting
+ // ProcTask in that case is a waste of time.
+ StartProcTask();
+ return;
+ }
+ DNS_HISTOGRAM("AsyncDNS.ResolveSuccess", duration);
+ // Log DNS lookups based on |address_family|.
+ switch(key_.address_family) {
+ case ADDRESS_FAMILY_IPV4:
+ DNS_HISTOGRAM("AsyncDNS.ResolveSuccess_FAMILY_IPV4", duration);
+ break;
+ case ADDRESS_FAMILY_IPV6:
+ DNS_HISTOGRAM("AsyncDNS.ResolveSuccess_FAMILY_IPV6", duration);
+ break;
+ case ADDRESS_FAMILY_UNSPECIFIED:
+ DNS_HISTOGRAM("AsyncDNS.ResolveSuccess_FAMILY_UNSPEC", duration);
+ break;
+ }
+
+ UmaAsyncDnsResolveStatus(RESOLVE_STATUS_DNS_SUCCESS);
+ RecordTTL(ttl);
+
+ resolver_->OnDnsTaskResolve(OK);
+
+ base::TimeDelta bounded_ttl =
+ std::max(ttl, base::TimeDelta::FromSeconds(kMinimumTTLSeconds));
+
+ CompleteRequests(
+ HostCache::Entry(net_error, MakeAddressListForRequest(addr_list), ttl),
+ bounded_ttl);
+ }
+
+ // Performs Job's last rites. Completes all Requests. Deletes this.
+ void CompleteRequests(const HostCache::Entry& entry,
+ base::TimeDelta ttl) {
+ CHECK(resolver_);
+
+ // This job must be removed from resolver's |jobs_| now to make room for a
+ // new job with the same key in case one of the OnComplete callbacks decides
+ // to spawn one. Consequently, the job deletes itself when CompleteRequests
+ // is done.
+ scoped_ptr<Job> self_deleter(this);
+
+ resolver_->RemoveJob(this);
+
+ if (is_running()) {
+ DCHECK(!is_queued());
+ if (is_proc_running()) {
+ proc_task_->Cancel();
+ proc_task_ = NULL;
+ }
+ dns_task_.reset();
+
+ // Signal dispatcher that a slot has opened.
+ resolver_->dispatcher_.OnJobFinished();
+ } else if (is_queued()) {
+ resolver_->dispatcher_.Cancel(handle_);
+ handle_.Reset();
+ }
+
+ if (num_active_requests() == 0) {
+ net_log_.AddEvent(NetLog::TYPE_CANCELLED);
+ net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HOST_RESOLVER_IMPL_JOB,
+ OK);
+ return;
+ }
+
+ net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HOST_RESOLVER_IMPL_JOB,
+ entry.error);
+
+ DCHECK(!requests_.empty());
+
+ if (entry.error == OK) {
+ // Record this histogram here, when we know the system has a valid DNS
+ // configuration.
+ UMA_HISTOGRAM_BOOLEAN("AsyncDNS.HaveDnsConfig",
+ resolver_->received_dns_config_);
+ }
+
+ bool did_complete = (entry.error != ERR_NETWORK_CHANGED) &&
+ (entry.error != ERR_HOST_RESOLVER_QUEUE_TOO_LARGE);
+ if (did_complete)
+ resolver_->CacheResult(key_, entry, ttl);
+
+ // Complete all of the requests that were attached to the job.
+ for (RequestsList::const_iterator it = requests_.begin();
+ it != requests_.end(); ++it) {
+ Request* req = *it;
+
+ if (req->was_canceled())
+ continue;
+
+ DCHECK_EQ(this, req->job());
+ // Update the net log and notify registered observers.
+ LogFinishRequest(req->source_net_log(), req->request_net_log(),
+ req->info(), entry.error);
+ if (did_complete) {
+ // Record effective total time from creation to completion.
+ RecordTotalTime(had_dns_config_, req->info().is_speculative(),
+ base::TimeTicks::Now() - req->request_time());
+ }
+ req->OnComplete(entry.error, entry.addrlist);
+
+ // Check if the resolver was destroyed as a result of running the
+ // callback. If it was, we could continue, but we choose to bail.
+ if (!resolver_)
+ return;
+ }
+ }
+
+ // Convenience wrapper for CompleteRequests in case of failure.
+ void CompleteRequestsWithError(int net_error) {
+ CompleteRequests(HostCache::Entry(net_error, AddressList()),
+ base::TimeDelta());
+ }
+
+ RequestPriority priority() const {
+ return priority_tracker_.highest_priority();
+ }
+
+ // Number of non-canceled requests in |requests_|.
+ size_t num_active_requests() const {
+ return priority_tracker_.total_count();
+ }
+
+ bool is_dns_running() const {
+ return dns_task_.get() != NULL;
+ }
+
+ bool is_proc_running() const {
+ return proc_task_.get() != NULL;
+ }
+
+ base::WeakPtr<HostResolverImpl> resolver_;
+
+ Key key_;
+
+ // Tracks the highest priority across |requests_|.
+ PriorityTracker priority_tracker_;
+
+ bool had_non_speculative_request_;
+
+ // Distinguishes measurements taken while DnsClient was fully configured.
+ bool had_dns_config_;
+
+ // Result of DnsTask.
+ int dns_task_error_;
+
+ const base::TimeTicks creation_time_;
+ base::TimeTicks priority_change_time_;
+
+ BoundNetLog net_log_;
+
+ // Resolves the host using a HostResolverProc.
+ scoped_refptr<ProcTask> proc_task_;
+
+ // Resolves the host using a DnsTransaction.
+ scoped_ptr<DnsTask> dns_task_;
+
+ // All Requests waiting for the result of this Job. Some can be canceled.
+ RequestsList requests_;
+
+ // A handle used in |HostResolverImpl::dispatcher_|.
+ PrioritizedDispatcher::Handle handle_;
+};
+
+//-----------------------------------------------------------------------------
+
+HostResolverImpl::ProcTaskParams::ProcTaskParams(
+ HostResolverProc* resolver_proc,
+ size_t max_retry_attempts)
+ : resolver_proc(resolver_proc),
+ max_retry_attempts(max_retry_attempts),
+ unresponsive_delay(base::TimeDelta::FromMilliseconds(6000)),
+ retry_factor(2) {
+}
+
+HostResolverImpl::ProcTaskParams::~ProcTaskParams() {}
+
+HostResolverImpl::HostResolverImpl(
+ scoped_ptr<HostCache> cache,
+ const PrioritizedDispatcher::Limits& job_limits,
+ const ProcTaskParams& proc_params,
+ NetLog* net_log)
+ : cache_(cache.Pass()),
+ dispatcher_(job_limits),
+ max_queued_jobs_(job_limits.total_jobs * 100u),
+ proc_params_(proc_params),
+ net_log_(net_log),
+ default_address_family_(ADDRESS_FAMILY_UNSPECIFIED),
+ weak_ptr_factory_(this),
+ probe_weak_ptr_factory_(this),
+ received_dns_config_(false),
+ num_dns_failures_(0),
+ ipv6_probe_monitoring_(false),
+ resolved_known_ipv6_hostname_(false),
+ additional_resolver_flags_(0) {
+
+ DCHECK_GE(dispatcher_.num_priorities(), static_cast<size_t>(NUM_PRIORITIES));
+
+ // Maximum of 4 retry attempts for host resolution.
+ static const size_t kDefaultMaxRetryAttempts = 4u;
+
+ if (proc_params_.max_retry_attempts == HostResolver::kDefaultRetryAttempts)
+ proc_params_.max_retry_attempts = kDefaultMaxRetryAttempts;
+
+#if defined(OS_WIN)
+ EnsureWinsockInit();
+#endif
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+ new LoopbackProbeJob(weak_ptr_factory_.GetWeakPtr());
+#endif
+ NetworkChangeNotifier::AddIPAddressObserver(this);
+ NetworkChangeNotifier::AddDNSObserver(this);
+#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD) && \
+ !defined(OS_ANDROID)
+ EnsureDnsReloaderInit();
+#endif
+
+ // TODO(szym): Remove when received_dns_config_ is removed, once
+ // http://crbug.com/137914 is resolved.
+ {
+ DnsConfig dns_config;
+ NetworkChangeNotifier::GetDnsConfig(&dns_config);
+ received_dns_config_ = dns_config.IsValid();
+ }
+}
+
+HostResolverImpl::~HostResolverImpl() {
+ // This will also cancel all outstanding requests.
+ STLDeleteValues(&jobs_);
+
+ NetworkChangeNotifier::RemoveIPAddressObserver(this);
+ NetworkChangeNotifier::RemoveDNSObserver(this);
+}
+
+void HostResolverImpl::SetMaxQueuedJobs(size_t value) {
+ DCHECK_EQ(0u, dispatcher_.num_queued_jobs());
+ DCHECK_GT(value, 0u);
+ max_queued_jobs_ = value;
+}
+
+int HostResolverImpl::Resolve(const RequestInfo& info,
+ AddressList* addresses,
+ const CompletionCallback& callback,
+ RequestHandle* out_req,
+ const BoundNetLog& source_net_log) {
+ DCHECK(addresses);
+ DCHECK(CalledOnValidThread());
+ DCHECK_EQ(false, callback.is_null());
+
+ // Make a log item for the request.
+ BoundNetLog request_net_log = BoundNetLog::Make(net_log_,
+ NetLog::SOURCE_HOST_RESOLVER_IMPL_REQUEST);
+
+ LogStartRequest(source_net_log, request_net_log, info);
+
+ // Build a key that identifies the request in the cache and in the
+ // outstanding jobs map.
+ Key key = GetEffectiveKeyForRequest(info);
+
+ int rv = ResolveHelper(key, info, addresses, request_net_log);
+ if (rv != ERR_DNS_CACHE_MISS) {
+ LogFinishRequest(source_net_log, request_net_log, info, rv);
+ RecordTotalTime(HaveDnsConfig(), info.is_speculative(), base::TimeDelta());
+ return rv;
+ }
+
+ // Next we need to attach our request to a "job". This job is responsible for
+ // calling "getaddrinfo(hostname)" on a worker thread.
+
+ JobMap::iterator jobit = jobs_.find(key);
+ Job* job;
+ if (jobit == jobs_.end()) {
+ job = new Job(weak_ptr_factory_.GetWeakPtr(), key, info.priority(),
+ request_net_log);
+ job->Schedule();
+
+ // Check for queue overflow.
+ if (dispatcher_.num_queued_jobs() > max_queued_jobs_) {
+ Job* evicted = static_cast<Job*>(dispatcher_.EvictOldestLowest());
+ DCHECK(evicted);
+ evicted->OnEvicted(); // Deletes |evicted|.
+ if (evicted == job) {
+ rv = ERR_HOST_RESOLVER_QUEUE_TOO_LARGE;
+ LogFinishRequest(source_net_log, request_net_log, info, rv);
+ return rv;
+ }
+ }
+ jobs_.insert(jobit, std::make_pair(key, job));
+ } else {
+ job = jobit->second;
+ }
+
+ // Can't complete synchronously. Create and attach request.
+ scoped_ptr<Request> req(new Request(source_net_log,
+ request_net_log,
+ info,
+ callback,
+ addresses));
+ if (out_req)
+ *out_req = reinterpret_cast<RequestHandle>(req.get());
+
+ job->AddRequest(req.Pass());
+ // Completion happens during Job::CompleteRequests().
+ return ERR_IO_PENDING;
+}
+
+int HostResolverImpl::ResolveHelper(const Key& key,
+ const RequestInfo& info,
+ AddressList* addresses,
+ const BoundNetLog& request_net_log) {
+ // The result of |getaddrinfo| for empty hosts is inconsistent across systems.
+ // On Windows it gives the default interface's address, whereas on Linux it
+ // gives an error. We will make it fail on all platforms for consistency.
+ if (info.hostname().empty() || info.hostname().size() > kMaxHostLength)
+ return ERR_NAME_NOT_RESOLVED;
+
+ int net_error = ERR_UNEXPECTED;
+ if (ResolveAsIP(key, info, &net_error, addresses))
+ return net_error;
+ if (ServeFromCache(key, info, &net_error, addresses)) {
+ request_net_log.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_CACHE_HIT);
+ return net_error;
+ }
+ // TODO(szym): Do not do this if nsswitch.conf instructs not to.
+ // http://crbug.com/117655
+ if (ServeFromHosts(key, info, addresses)) {
+ request_net_log.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_HOSTS_HIT);
+ return OK;
+ }
+ return ERR_DNS_CACHE_MISS;
+}
+
+int HostResolverImpl::ResolveFromCache(const RequestInfo& info,
+ AddressList* addresses,
+ const BoundNetLog& source_net_log) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(addresses);
+
+ // Make a log item for the request.
+ BoundNetLog request_net_log = BoundNetLog::Make(net_log_,
+ NetLog::SOURCE_HOST_RESOLVER_IMPL_REQUEST);
+
+ // Update the net log and notify registered observers.
+ LogStartRequest(source_net_log, request_net_log, info);
+
+ Key key = GetEffectiveKeyForRequest(info);
+
+ int rv = ResolveHelper(key, info, addresses, request_net_log);
+ LogFinishRequest(source_net_log, request_net_log, info, rv);
+ return rv;
+}
+
+void HostResolverImpl::CancelRequest(RequestHandle req_handle) {
+ DCHECK(CalledOnValidThread());
+ Request* req = reinterpret_cast<Request*>(req_handle);
+ DCHECK(req);
+ Job* job = req->job();
+ DCHECK(job);
+ job->CancelRequest(req);
+}
+
+void HostResolverImpl::SetDefaultAddressFamily(AddressFamily address_family) {
+ DCHECK(CalledOnValidThread());
+ default_address_family_ = address_family;
+ ipv6_probe_monitoring_ = false;
+}
+
+AddressFamily HostResolverImpl::GetDefaultAddressFamily() const {
+ return default_address_family_;
+}
+
+void HostResolverImpl::ProbeIPv6Support() {
+ DCHECK(CalledOnValidThread());
+ DCHECK(!ipv6_probe_monitoring_);
+ ipv6_probe_monitoring_ = true;
+ OnIPAddressChanged();
+}
+
+void HostResolverImpl::SetDnsClientEnabled(bool enabled) {
+ DCHECK(CalledOnValidThread());
+#if defined(ENABLE_BUILT_IN_DNS)
+ if (enabled && !dns_client_) {
+ SetDnsClient(DnsClient::CreateClient(net_log_));
+ } else if (!enabled && dns_client_) {
+ SetDnsClient(scoped_ptr<DnsClient>());
+ }
+#endif
+}
+
+HostCache* HostResolverImpl::GetHostCache() {
+ return cache_.get();
+}
+
+base::Value* HostResolverImpl::GetDnsConfigAsValue() const {
+ // Check if async DNS is disabled.
+ if (!dns_client_.get())
+ return NULL;
+
+ // Check if async DNS is enabled, but we currently have no configuration
+ // for it.
+ const DnsConfig* dns_config = dns_client_->GetConfig();
+ if (dns_config == NULL)
+ return new DictionaryValue();
+
+ return dns_config->ToValue();
+}
+
+bool HostResolverImpl::ResolveAsIP(const Key& key,
+ const RequestInfo& info,
+ int* net_error,
+ AddressList* addresses) {
+ DCHECK(addresses);
+ DCHECK(net_error);
+ IPAddressNumber ip_number;
+ if (!ParseIPLiteralToNumber(key.hostname, &ip_number))
+ return false;
+
+ DCHECK_EQ(key.host_resolver_flags &
+ ~(HOST_RESOLVER_CANONNAME | HOST_RESOLVER_LOOPBACK_ONLY |
+ HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6),
+ 0) << " Unhandled flag";
+ bool ipv6_disabled = (default_address_family_ == ADDRESS_FAMILY_IPV4) &&
+ !ipv6_probe_monitoring_;
+ *net_error = OK;
+ if ((ip_number.size() == kIPv6AddressSize) && ipv6_disabled) {
+ *net_error = ERR_NAME_NOT_RESOLVED;
+ } else {
+ *addresses = AddressList::CreateFromIPAddress(ip_number, info.port());
+ if (key.host_resolver_flags & HOST_RESOLVER_CANONNAME)
+ addresses->SetDefaultCanonicalName();
+ }
+ return true;
+}
+
+bool HostResolverImpl::ServeFromCache(const Key& key,
+ const RequestInfo& info,
+ int* net_error,
+ AddressList* addresses) {
+ DCHECK(addresses);
+ DCHECK(net_error);
+ if (!info.allow_cached_response() || !cache_.get())
+ return false;
+
+ const HostCache::Entry* cache_entry = cache_->Lookup(
+ key, base::TimeTicks::Now());
+ if (!cache_entry)
+ return false;
+
+ *net_error = cache_entry->error;
+ if (*net_error == OK) {
+ if (cache_entry->has_ttl())
+ RecordTTL(cache_entry->ttl);
+ *addresses = EnsurePortOnAddressList(cache_entry->addrlist, info.port());
+ }
+ return true;
+}
+
+bool HostResolverImpl::ServeFromHosts(const Key& key,
+ const RequestInfo& info,
+ AddressList* addresses) {
+ DCHECK(addresses);
+ if (!HaveDnsConfig())
+ return false;
+
+ // HOSTS lookups are case-insensitive.
+ std::string hostname = StringToLowerASCII(key.hostname);
+
+ // If |address_family| is ADDRESS_FAMILY_UNSPECIFIED other implementations
+ // (glibc and c-ares) return the first matching line. We have more
+ // flexibility, but lose implicit ordering.
+ // TODO(szym) http://crbug.com/117850
+ const DnsHosts& hosts = dns_client_->GetConfig()->hosts;
+ DnsHosts::const_iterator it = hosts.find(
+ DnsHostsKey(hostname,
+ key.address_family == ADDRESS_FAMILY_UNSPECIFIED ?
+ ADDRESS_FAMILY_IPV4 : key.address_family));
+
+ if (it == hosts.end()) {
+ if (key.address_family != ADDRESS_FAMILY_UNSPECIFIED)
+ return false;
+
+ it = hosts.find(DnsHostsKey(hostname, ADDRESS_FAMILY_IPV6));
+ if (it == hosts.end())
+ return false;
+ }
+
+ *addresses = AddressList::CreateFromIPAddress(it->second, info.port());
+ return true;
+}
+
+void HostResolverImpl::CacheResult(const Key& key,
+ const HostCache::Entry& entry,
+ base::TimeDelta ttl) {
+ if (cache_.get())
+ cache_->Set(key, entry, base::TimeTicks::Now(), ttl);
+}
+
+void HostResolverImpl::RemoveJob(Job* job) {
+ DCHECK(job);
+ JobMap::iterator it = jobs_.find(job->key());
+ if (it != jobs_.end() && it->second == job)
+ jobs_.erase(it);
+}
+
+void HostResolverImpl::IPv6ProbeSetDefaultAddressFamily(
+ AddressFamily address_family) {
+ DCHECK(address_family == ADDRESS_FAMILY_UNSPECIFIED ||
+ address_family == ADDRESS_FAMILY_IPV4);
+ if (!ipv6_probe_monitoring_)
+ return;
+ if (default_address_family_ != address_family) {
+ VLOG(1) << "IPv6Probe forced AddressFamily setting to "
+ << ((address_family == ADDRESS_FAMILY_UNSPECIFIED) ?
+ "ADDRESS_FAMILY_UNSPECIFIED" : "ADDRESS_FAMILY_IPV4");
+ }
+ default_address_family_ = address_family;
+}
+
+void HostResolverImpl::SetHaveOnlyLoopbackAddresses(bool result) {
+ if (result) {
+ additional_resolver_flags_ |= HOST_RESOLVER_LOOPBACK_ONLY;
+ } else {
+ additional_resolver_flags_ &= ~HOST_RESOLVER_LOOPBACK_ONLY;
+ }
+}
+
+HostResolverImpl::Key HostResolverImpl::GetEffectiveKeyForRequest(
+ const RequestInfo& info) const {
+ HostResolverFlags effective_flags =
+ info.host_resolver_flags() | additional_resolver_flags_;
+ AddressFamily effective_address_family = info.address_family();
+ if (effective_address_family == ADDRESS_FAMILY_UNSPECIFIED &&
+ default_address_family_ != ADDRESS_FAMILY_UNSPECIFIED) {
+ effective_address_family = default_address_family_;
+ if (ipv6_probe_monitoring_)
+ effective_flags |= HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6;
+ }
+ return Key(info.hostname(), effective_address_family, effective_flags);
+}
+
+void HostResolverImpl::AbortAllInProgressJobs() {
+ // In Abort, a Request callback could spawn new Jobs with matching keys, so
+ // first collect and remove all running jobs from |jobs_|.
+ ScopedVector<Job> jobs_to_abort;
+ for (JobMap::iterator it = jobs_.begin(); it != jobs_.end(); ) {
+ Job* job = it->second;
+ if (job->is_running()) {
+ jobs_to_abort.push_back(job);
+ jobs_.erase(it++);
+ } else {
+ DCHECK(job->is_queued());
+ ++it;
+ }
+ }
+
+ // Check if no dispatcher slots leaked out.
+ DCHECK_EQ(dispatcher_.num_running_jobs(), jobs_to_abort.size());
+
+ // Life check to bail once |this| is deleted.
+ base::WeakPtr<HostResolverImpl> self = weak_ptr_factory_.GetWeakPtr();
+
+ // Then Abort them.
+ for (size_t i = 0; self && i < jobs_to_abort.size(); ++i) {
+ jobs_to_abort[i]->Abort();
+ jobs_to_abort[i] = NULL;
+ }
+}
+
+void HostResolverImpl::TryServingAllJobsFromHosts() {
+ if (!HaveDnsConfig())
+ return;
+
+ // TODO(szym): Do not do this if nsswitch.conf instructs not to.
+ // http://crbug.com/117655
+
+ // Life check to bail once |this| is deleted.
+ base::WeakPtr<HostResolverImpl> self = weak_ptr_factory_.GetWeakPtr();
+
+ for (JobMap::iterator it = jobs_.begin(); self && it != jobs_.end(); ) {
+ Job* job = it->second;
+ ++it;
+ // This could remove |job| from |jobs_|, but iterator will remain valid.
+ job->ServeFromHosts();
+ }
+}
+
+void HostResolverImpl::OnIPAddressChanged() {
+ resolved_known_ipv6_hostname_ = false;
+ // Abandon all ProbeJobs.
+ probe_weak_ptr_factory_.InvalidateWeakPtrs();
+ if (cache_.get())
+ cache_->clear();
+ if (ipv6_probe_monitoring_)
+ new IPv6ProbeJob(probe_weak_ptr_factory_.GetWeakPtr(), net_log_);
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+ new LoopbackProbeJob(probe_weak_ptr_factory_.GetWeakPtr());
+#endif
+ AbortAllInProgressJobs();
+ // |this| may be deleted inside AbortAllInProgressJobs().
+}
+
+void HostResolverImpl::OnDNSChanged() {
+ DnsConfig dns_config;
+ NetworkChangeNotifier::GetDnsConfig(&dns_config);
+ if (net_log_) {
+ net_log_->AddGlobalEntry(
+ NetLog::TYPE_DNS_CONFIG_CHANGED,
+ base::Bind(&NetLogDnsConfigCallback, &dns_config));
+ }
+
+ // TODO(szym): Remove once http://crbug.com/137914 is resolved.
+ received_dns_config_ = dns_config.IsValid();
+
+ num_dns_failures_ = 0;
+
+ // We want a new DnsSession in place, before we Abort running Jobs, so that
+ // the newly started jobs use the new config.
+ if (dns_client_.get()) {
+ dns_client_->SetConfig(dns_config);
+ if (dns_config.IsValid())
+ UMA_HISTOGRAM_BOOLEAN("AsyncDNS.DnsClientEnabled", true);
+ }
+
+ // If the DNS server has changed, existing cached info could be wrong so we
+ // have to drop our internal cache :( Note that OS level DNS caches, such
+ // as NSCD's cache should be dropped automatically by the OS when
+ // resolv.conf changes so we don't need to do anything to clear that cache.
+ if (cache_.get())
+ cache_->clear();
+
+ // Life check to bail once |this| is deleted.
+ base::WeakPtr<HostResolverImpl> self = weak_ptr_factory_.GetWeakPtr();
+
+ // Existing jobs will have been sent to the original server so they need to
+ // be aborted.
+ AbortAllInProgressJobs();
+
+ // |this| may be deleted inside AbortAllInProgressJobs().
+ if (self)
+ TryServingAllJobsFromHosts();
+}
+
+bool HostResolverImpl::HaveDnsConfig() const {
+ // Use DnsClient only if it's fully configured and there is no override by
+ // ScopedDefaultHostResolverProc.
+ // The alternative is to use NetworkChangeNotifier to override DnsConfig,
+ // but that would introduce construction order requirements for NCN and SDHRP.
+ return (dns_client_.get() != NULL) &&
+ (dns_client_->GetConfig() != NULL) &&
+ !(proc_params_.resolver_proc == NULL &&
+ HostResolverProc::GetDefault() != NULL);
+}
+
+void HostResolverImpl::OnDnsTaskResolve(int net_error) {
+ DCHECK(dns_client_);
+ if (net_error == OK) {
+ num_dns_failures_ = 0;
+ return;
+ }
+ ++num_dns_failures_;
+ if (num_dns_failures_ < kMaximumDnsFailures)
+ return;
+ // Disable DnsClient until the next DNS change.
+ for (JobMap::iterator it = jobs_.begin(); it != jobs_.end(); ++it)
+ it->second->AbortDnsTask();
+ dns_client_->SetConfig(DnsConfig());
+ UMA_HISTOGRAM_BOOLEAN("AsyncDNS.DnsClientEnabled", false);
+ UMA_HISTOGRAM_CUSTOM_ENUMERATION("AsyncDNS.DnsClientDisabledReason",
+ std::abs(net_error),
+ GetAllErrorCodesForUma());
+}
+
+void HostResolverImpl::SetDnsClient(scoped_ptr<DnsClient> dns_client) {
+ if (HaveDnsConfig()) {
+ for (JobMap::iterator it = jobs_.begin(); it != jobs_.end(); ++it)
+ it->second->AbortDnsTask();
+ }
+ dns_client_ = dns_client.Pass();
+ if (!dns_client_ || dns_client_->GetConfig() ||
+ num_dns_failures_ >= kMaximumDnsFailures) {
+ return;
+ }
+ DnsConfig dns_config;
+ NetworkChangeNotifier::GetDnsConfig(&dns_config);
+ dns_client_->SetConfig(dns_config);
+ num_dns_failures_ = 0;
+ if (dns_config.IsValid())
+ UMA_HISTOGRAM_BOOLEAN("AsyncDNS.DnsClientEnabled", true);
+}
+
+} // namespace net
diff --git a/net/dns/host_resolver_impl.h b/net/dns/host_resolver_impl.h
new file mode 100644
index 0000000..2519d13
--- /dev/null
+++ b/net/dns/host_resolver_impl.h
@@ -0,0 +1,287 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_DNS_HOST_RESOLVER_IMPL_H_
+#define NET_DNS_HOST_RESOLVER_IMPL_H_
+
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/non_thread_safe.h"
+#include "base/time.h"
+#include "net/base/capturing_net_log.h"
+#include "net/base/host_cache.h"
+#include "net/base/net_export.h"
+#include "net/base/network_change_notifier.h"
+#include "net/base/prioritized_dispatcher.h"
+#include "net/dns/host_resolver.h"
+#include "net/dns/host_resolver_proc.h"
+
+namespace net {
+
+class BoundNetLog;
+class DnsClient;
+class NetLog;
+
+// For each hostname that is requested, HostResolver creates a
+// HostResolverImpl::Job. When this job gets dispatched it creates a ProcTask
+// which runs the given HostResolverProc on a WorkerPool thread. If requests for
+// that same host are made during the job's lifetime, they are attached to the
+// existing job rather than creating a new one. This avoids doing parallel
+// resolves for the same host.
+//
+// The way these classes fit together is illustrated by:
+//
+//
+// +----------- HostResolverImpl -------------+
+// | | |
+// Job Job Job
+// (for host1, fam1) (for host2, fam2) (for hostx, famx)
+// / | | / | | / | |
+// Request ... Request Request ... Request Request ... Request
+// (port1) (port2) (port3) (port4) (port5) (portX)
+//
+// When a HostResolverImpl::Job finishes, the callbacks of each waiting request
+// are run on the origin thread.
+//
+// Thread safety: This class is not threadsafe, and must only be called
+// from one thread!
+//
+// The HostResolverImpl enforces limits on the maximum number of concurrent
+// threads using PrioritizedDispatcher::Limits.
+//
+// Jobs are ordered in the queue based on their priority and order of arrival.
+class NET_EXPORT HostResolverImpl
+ : public HostResolver,
+ NON_EXPORTED_BASE(public base::NonThreadSafe),
+ public NetworkChangeNotifier::IPAddressObserver,
+ public NetworkChangeNotifier::DNSObserver {
+ public:
+ // Parameters for ProcTask which resolves hostnames using HostResolveProc.
+ //
+ // |resolver_proc| is used to perform the actual resolves; it must be
+ // thread-safe since it is run from multiple worker threads. If
+ // |resolver_proc| is NULL then the default host resolver procedure is
+ // used (which is SystemHostResolverProc except if overridden).
+ //
+ // For each attempt, we could start another attempt if host is not resolved
+ // within |unresponsive_delay| time. We keep attempting to resolve the host
+ // for |max_retry_attempts|. For every retry attempt, we grow the
+ // |unresponsive_delay| by the |retry_factor| amount (that is retry interval
+ // is multiplied by the retry factor each time). Once we have retried
+ // |max_retry_attempts|, we give up on additional attempts.
+ //
+ struct NET_EXPORT_PRIVATE ProcTaskParams {
+ // Sets up defaults.
+ ProcTaskParams(HostResolverProc* resolver_proc, size_t max_retry_attempts);
+
+ ~ProcTaskParams();
+
+ // The procedure to use for resolving host names. This will be NULL, except
+ // in the case of unit-tests which inject custom host resolving behaviors.
+ scoped_refptr<HostResolverProc> resolver_proc;
+
+ // Maximum number retry attempts to resolve the hostname.
+ // Pass HostResolver::kDefaultRetryAttempts to choose a default value.
+ size_t max_retry_attempts;
+
+ // This is the limit after which we make another attempt to resolve the host
+ // if the worker thread has not responded yet.
+ base::TimeDelta unresponsive_delay;
+
+ // Factor to grow |unresponsive_delay| when we re-re-try.
+ uint32 retry_factor;
+ };
+
+ // Creates a HostResolver that first uses the local cache |cache|, and then
+ // falls back to |proc_params.resolver_proc|.
+ //
+ // If |cache| is NULL, then no caching is used. Otherwise we take
+ // ownership of the |cache| pointer, and will free it during destruction.
+ //
+ // |job_limits| specifies the maximum number of jobs that the resolver will
+ // run at once. This upper-bounds the total number of outstanding
+ // DNS transactions (not counting retransmissions and retries).
+ //
+ // |net_log| must remain valid for the life of the HostResolverImpl.
+ HostResolverImpl(scoped_ptr<HostCache> cache,
+ const PrioritizedDispatcher::Limits& job_limits,
+ const ProcTaskParams& proc_params,
+ NetLog* net_log);
+
+ // If any completion callbacks are pending when the resolver is destroyed,
+ // the host resolutions are cancelled, and the completion callbacks will not
+ // be called.
+ virtual ~HostResolverImpl();
+
+ // Configures maximum number of Jobs in the queue. Exposed for testing.
+ // Only allowed when the queue is empty.
+ void SetMaxQueuedJobs(size_t value);
+
+ // Set the DnsClient to be used for resolution. In case of failure, the
+ // HostResolverProc from ProcTaskParams will be queried. If the DnsClient is
+ // not pre-configured with a valid DnsConfig, a new config is fetched from
+ // NetworkChangeNotifier.
+ void SetDnsClient(scoped_ptr<DnsClient> dns_client);
+
+ // HostResolver methods:
+ virtual int Resolve(const RequestInfo& info,
+ AddressList* addresses,
+ const CompletionCallback& callback,
+ RequestHandle* out_req,
+ const BoundNetLog& source_net_log) OVERRIDE;
+ virtual int ResolveFromCache(const RequestInfo& info,
+ AddressList* addresses,
+ const BoundNetLog& source_net_log) OVERRIDE;
+ virtual void CancelRequest(RequestHandle req) OVERRIDE;
+ virtual void SetDefaultAddressFamily(AddressFamily address_family) OVERRIDE;
+ virtual AddressFamily GetDefaultAddressFamily() const OVERRIDE;
+ virtual void ProbeIPv6Support() OVERRIDE;
+ virtual void SetDnsClientEnabled(bool enabled) OVERRIDE;
+ virtual HostCache* GetHostCache() OVERRIDE;
+ virtual base::Value* GetDnsConfigAsValue() const OVERRIDE;
+
+ private:
+ friend class HostResolverImplTest;
+ class Job;
+ class ProcTask;
+ class IPv6ProbeJob;
+ class LoopbackProbeJob;
+ class DnsTask;
+ class Request;
+ typedef HostCache::Key Key;
+ typedef std::map<Key, Job*> JobMap;
+ typedef ScopedVector<Request> RequestsList;
+
+ // Helper used by |Resolve()| and |ResolveFromCache()|. Performs IP
+ // literal, cache and HOSTS lookup (if enabled), returns OK if successful,
+ // ERR_NAME_NOT_RESOLVED if either hostname is invalid or IP literal is
+ // incompatible, ERR_DNS_CACHE_MISS if entry was not found in cache and HOSTS.
+ int ResolveHelper(const Key& key,
+ const RequestInfo& info,
+ AddressList* addresses,
+ const BoundNetLog& request_net_log);
+
+ // Tries to resolve |key| as an IP, returns true and sets |net_error| if
+ // succeeds, returns false otherwise.
+ bool ResolveAsIP(const Key& key,
+ const RequestInfo& info,
+ int* net_error,
+ AddressList* addresses);
+
+ // If |key| is not found in cache returns false, otherwise returns
+ // true, sets |net_error| to the cached error code and fills |addresses|
+ // if it is a positive entry.
+ bool ServeFromCache(const Key& key,
+ const RequestInfo& info,
+ int* net_error,
+ AddressList* addresses);
+
+ // If we have a DnsClient with a valid DnsConfig, and |key| is found in the
+ // HOSTS file, returns true and fills |addresses|. Otherwise returns false.
+ bool ServeFromHosts(const Key& key,
+ const RequestInfo& info,
+ AddressList* addresses);
+
+ // Callback from IPv6 probe activity.
+ void IPv6ProbeSetDefaultAddressFamily(AddressFamily address_family);
+
+ // Callback from HaveOnlyLoopbackAddresses probe.
+ void SetHaveOnlyLoopbackAddresses(bool result);
+
+ // Returns the (hostname, address_family) key to use for |info|, choosing an
+ // "effective" address family by inheriting the resolver's default address
+ // family when the request leaves it unspecified.
+ Key GetEffectiveKeyForRequest(const RequestInfo& info) const;
+
+ // Records the result in cache if cache is present.
+ void CacheResult(const Key& key,
+ const HostCache::Entry& entry,
+ base::TimeDelta ttl);
+
+ // Removes |job| from |jobs_|, only if it exists.
+ void RemoveJob(Job* job);
+
+ // Aborts all in progress jobs with ERR_NETWORK_CHANGED and notifies their
+ // requests. Might start new jobs.
+ void AbortAllInProgressJobs();
+
+ // Attempts to serve each Job in |jobs_| from the HOSTS file if we have
+ // a DnsClient with a valid DnsConfig.
+ void TryServingAllJobsFromHosts();
+
+ // NetworkChangeNotifier::IPAddressObserver:
+ virtual void OnIPAddressChanged() OVERRIDE;
+
+ // NetworkChangeNotifier::DNSObserver:
+ virtual void OnDNSChanged() OVERRIDE;
+
+ // True if have a DnsClient with a valid DnsConfig.
+ bool HaveDnsConfig() const;
+
+ // Called when a host name is successfully resolved and DnsTask was run on it
+ // and resulted in |net_error|.
+ void OnDnsTaskResolve(int net_error);
+
+ // Allows the tests to catch slots leaking out of the dispatcher.
+ size_t num_running_jobs_for_tests() const {
+ return dispatcher_.num_running_jobs();
+ }
+
+ // Cache of host resolution results.
+ scoped_ptr<HostCache> cache_;
+
+ // Map from HostCache::Key to a Job.
+ JobMap jobs_;
+
+ // Starts Jobs according to their priority and the configured limits.
+ PrioritizedDispatcher dispatcher_;
+
+ // Limit on the maximum number of jobs queued in |dispatcher_|.
+ size_t max_queued_jobs_;
+
+ // Parameters for ProcTask.
+ ProcTaskParams proc_params_;
+
+ NetLog* net_log_;
+
+ // Address family to use when the request doesn't specify one.
+ AddressFamily default_address_family_;
+
+ base::WeakPtrFactory<HostResolverImpl> weak_ptr_factory_;
+
+ base::WeakPtrFactory<HostResolverImpl> probe_weak_ptr_factory_;
+
+ // If present, used by DnsTask and ServeFromHosts to resolve requests.
+ scoped_ptr<DnsClient> dns_client_;
+
+ // True if received valid config from |dns_config_service_|. Temporary, used
+ // to measure performance of DnsConfigService: http://crbug.com/125599
+ bool received_dns_config_;
+
+ // Number of consecutive failures of DnsTask, counted when fallback succeeds.
+ unsigned num_dns_failures_;
+
+ // Indicate if probing is done after each network change event to set address
+ // family. When false, explicit setting of address family is used and results
+ // of the IPv6 probe job are ignored.
+ bool ipv6_probe_monitoring_;
+
+ // True iff ProcTask has successfully resolved a hostname known to have IPv6
+ // addresses using ADDRESS_FAMILY_UNSPECIFIED. Reset on IP address change.
+ bool resolved_known_ipv6_hostname_;
+
+ // Any resolver flags that should be added to a request by default.
+ HostResolverFlags additional_resolver_flags_;
+
+ DISALLOW_COPY_AND_ASSIGN(HostResolverImpl);
+};
+
+} // namespace net
+
+#endif // NET_DNS_HOST_RESOLVER_IMPL_H_
diff --git a/net/dns/host_resolver_impl_unittest.cc b/net/dns/host_resolver_impl_unittest.cc
new file mode 100644
index 0000000..4880b65
--- /dev/null
+++ b/net/dns/host_resolver_impl_unittest.cc
@@ -0,0 +1,1481 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/dns/host_resolver_impl.h"
+
+#include <algorithm>
+#include <string>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_vector.h"
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "base/stringprintf.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "base/test/test_timeouts.h"
+#include "base/time.h"
+#include "net/base/address_list.h"
+#include "net/base/host_cache.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_util.h"
+#include "net/dns/dns_client.h"
+#include "net/dns/dns_test_util.h"
+#include "net/dns/mock_host_resolver.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+const size_t kMaxJobs = 10u;
+const size_t kMaxRetryAttempts = 4u;
+
+PrioritizedDispatcher::Limits DefaultLimits() {
+ PrioritizedDispatcher::Limits limits(NUM_PRIORITIES, kMaxJobs);
+ return limits;
+}
+
+HostResolverImpl::ProcTaskParams DefaultParams(
+ HostResolverProc* resolver_proc) {
+ return HostResolverImpl::ProcTaskParams(resolver_proc, kMaxRetryAttempts);
+}
+
+// A HostResolverProc that pushes each host mapped into a list and allows
+// waiting for a specific number of requests. Unlike RuleBasedHostResolverProc
+// it never calls SystemHostResolverProc. By default resolves all hostnames to
+// "127.0.0.1". After AddRule(), it resolves only names explicitly specified.
+class MockHostResolverProc : public HostResolverProc {
+ public:
+ struct ResolveKey {
+ ResolveKey(const std::string& hostname, AddressFamily address_family)
+ : hostname(hostname), address_family(address_family) {}
+ bool operator<(const ResolveKey& other) const {
+ return address_family < other.address_family ||
+ (address_family == other.address_family && hostname < other.hostname);
+ }
+ std::string hostname;
+ AddressFamily address_family;
+ };
+
+ typedef std::vector<ResolveKey> CaptureList;
+
+ MockHostResolverProc()
+ : HostResolverProc(NULL),
+ num_requests_waiting_(0),
+ num_slots_available_(0),
+ requests_waiting_(&lock_),
+ slots_available_(&lock_) {
+ }
+
+ // Waits until |count| calls to |Resolve| are blocked. Returns false when
+ // timed out.
+ bool WaitFor(unsigned count) {
+ base::AutoLock lock(lock_);
+ base::Time start_time = base::Time::Now();
+ while (num_requests_waiting_ < count) {
+ requests_waiting_.TimedWait(TestTimeouts::action_timeout());
+ if (base::Time::Now() > start_time + TestTimeouts::action_timeout())
+ return false;
+ }
+ return true;
+ }
+
+ // Signals |count| waiting calls to |Resolve|. First come first served.
+ void SignalMultiple(unsigned count) {
+ base::AutoLock lock(lock_);
+ num_slots_available_ += count;
+ slots_available_.Broadcast();
+ }
+
+ // Signals all waiting calls to |Resolve|. Beware of races.
+ void SignalAll() {
+ base::AutoLock lock(lock_);
+ num_slots_available_ = num_requests_waiting_;
+ slots_available_.Broadcast();
+ }
+
+ void AddRule(const std::string& hostname, AddressFamily family,
+ const AddressList& result) {
+ base::AutoLock lock(lock_);
+ rules_[ResolveKey(hostname, family)] = result;
+ }
+
+ void AddRule(const std::string& hostname, AddressFamily family,
+ const std::string& ip_list) {
+ AddressList result;
+ int rv = ParseAddressList(ip_list, "", &result);
+ DCHECK_EQ(OK, rv);
+ AddRule(hostname, family, result);
+ }
+
+ void AddRuleForAllFamilies(const std::string& hostname,
+ const std::string& ip_list) {
+ AddressList result;
+ int rv = ParseAddressList(ip_list, "", &result);
+ DCHECK_EQ(OK, rv);
+ AddRule(hostname, ADDRESS_FAMILY_UNSPECIFIED, result);
+ AddRule(hostname, ADDRESS_FAMILY_IPV4, result);
+ AddRule(hostname, ADDRESS_FAMILY_IPV6, result);
+ }
+
+ virtual int Resolve(const std::string& hostname,
+ AddressFamily address_family,
+ HostResolverFlags host_resolver_flags,
+ AddressList* addrlist,
+ int* os_error) OVERRIDE {
+ base::AutoLock lock(lock_);
+ capture_list_.push_back(ResolveKey(hostname, address_family));
+ ++num_requests_waiting_;
+ requests_waiting_.Broadcast();
+ while (!num_slots_available_)
+ slots_available_.Wait();
+ DCHECK_GT(num_requests_waiting_, 0u);
+ --num_slots_available_;
+ --num_requests_waiting_;
+ if (rules_.empty()) {
+ int rv = ParseAddressList("127.0.0.1", "", addrlist);
+ DCHECK_EQ(OK, rv);
+ return OK;
+ }
+ ResolveKey key(hostname, address_family);
+ if (rules_.count(key) == 0)
+ return ERR_NAME_NOT_RESOLVED;
+ *addrlist = rules_[key];
+ return OK;
+ }
+
+ CaptureList GetCaptureList() const {
+ CaptureList copy;
+ {
+ base::AutoLock lock(lock_);
+ copy = capture_list_;
+ }
+ return copy;
+ }
+
+ bool HasBlockedRequests() const {
+ base::AutoLock lock(lock_);
+ return num_requests_waiting_ > num_slots_available_;
+ }
+
+ protected:
+ virtual ~MockHostResolverProc() {}
+
+ private:
+ mutable base::Lock lock_;
+ std::map<ResolveKey, AddressList> rules_;
+ CaptureList capture_list_;
+ unsigned num_requests_waiting_;
+ unsigned num_slots_available_;
+ base::ConditionVariable requests_waiting_;
+ base::ConditionVariable slots_available_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockHostResolverProc);
+};
+
+// A wrapper for requests to a HostResolver.
+class Request {
+ public:
+ // Base class of handlers to be executed on completion of requests.
+ struct Handler {
+ virtual ~Handler() {}
+ virtual void Handle(Request* request) = 0;
+ };
+
+ Request(const HostResolver::RequestInfo& info,
+ size_t index,
+ HostResolver* resolver,
+ Handler* handler)
+ : info_(info),
+ index_(index),
+ resolver_(resolver),
+ handler_(handler),
+ quit_on_complete_(false),
+ result_(ERR_UNEXPECTED),
+ handle_(NULL) {}
+
+ int Resolve() {
+ DCHECK(resolver_);
+ DCHECK(!handle_);
+ list_ = AddressList();
+ result_ = resolver_->Resolve(
+ info_, &list_, base::Bind(&Request::OnComplete, base::Unretained(this)),
+ &handle_, BoundNetLog());
+ if (!list_.empty())
+ EXPECT_EQ(OK, result_);
+ return result_;
+ }
+
+ int ResolveFromCache() {
+ DCHECK(resolver_);
+ DCHECK(!handle_);
+ return resolver_->ResolveFromCache(info_, &list_, BoundNetLog());
+ }
+
+ void Cancel() {
+ DCHECK(resolver_);
+ DCHECK(handle_);
+ resolver_->CancelRequest(handle_);
+ handle_ = NULL;
+ }
+
+ const HostResolver::RequestInfo& info() const { return info_; }
+ size_t index() const { return index_; }
+ const AddressList& list() const { return list_; }
+ int result() const { return result_; }
+ bool completed() const { return result_ != ERR_IO_PENDING; }
+ bool pending() const { return handle_ != NULL; }
+
+ bool HasAddress(const std::string& address, int port) const {
+ IPAddressNumber ip;
+ bool rv = ParseIPLiteralToNumber(address, &ip);
+ DCHECK(rv);
+ return std::find(list_.begin(),
+ list_.end(),
+ IPEndPoint(ip, port)) != list_.end();
+ }
+
+ // Returns the number of addresses in |list_|.
+ unsigned NumberOfAddresses() const {
+ return list_.size();
+ }
+
+ bool HasOneAddress(const std::string& address, int port) const {
+ return HasAddress(address, port) && (NumberOfAddresses() == 1u);
+ }
+
+ // Returns ERR_UNEXPECTED if timed out.
+ int WaitForResult() {
+ if (completed())
+ return result_;
+ base::CancelableClosure closure(MessageLoop::QuitClosure());
+ MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ closure.callback(),
+ TestTimeouts::action_max_timeout());
+ quit_on_complete_ = true;
+ MessageLoop::current()->Run();
+ bool did_quit = !quit_on_complete_;
+ quit_on_complete_ = false;
+ closure.Cancel();
+ if (did_quit)
+ return result_;
+ else
+ return ERR_UNEXPECTED;
+ }
+
+ private:
+ void OnComplete(int rv) {
+ EXPECT_TRUE(pending());
+ EXPECT_EQ(ERR_IO_PENDING, result_);
+ EXPECT_NE(ERR_IO_PENDING, rv);
+ result_ = rv;
+ handle_ = NULL;
+ if (!list_.empty()) {
+ EXPECT_EQ(OK, result_);
+ EXPECT_EQ(info_.port(), list_.front().port());
+ }
+ if (handler_)
+ handler_->Handle(this);
+ if (quit_on_complete_) {
+ MessageLoop::current()->Quit();
+ quit_on_complete_ = false;
+ }
+ }
+
+ HostResolver::RequestInfo info_;
+ size_t index_;
+ HostResolver* resolver_;
+ Handler* handler_;
+ bool quit_on_complete_;
+
+ AddressList list_;
+ int result_;
+ HostResolver::RequestHandle handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(Request);
+};
+
+// Using LookupAttemptHostResolverProc simulate very long lookups, and control
+// which attempt resolves the host.
+class LookupAttemptHostResolverProc : public HostResolverProc {
+ public:
+ LookupAttemptHostResolverProc(HostResolverProc* previous,
+ int attempt_number_to_resolve,
+ int total_attempts)
+ : HostResolverProc(previous),
+ attempt_number_to_resolve_(attempt_number_to_resolve),
+ current_attempt_number_(0),
+ total_attempts_(total_attempts),
+ total_attempts_resolved_(0),
+ resolved_attempt_number_(0),
+ all_done_(&lock_) {
+ }
+
+ // Test harness will wait for all attempts to finish before checking the
+ // results.
+ void WaitForAllAttemptsToFinish(const base::TimeDelta& wait_time) {
+ base::TimeTicks end_time = base::TimeTicks::Now() + wait_time;
+ {
+ base::AutoLock auto_lock(lock_);
+ while (total_attempts_resolved_ != total_attempts_ &&
+ base::TimeTicks::Now() < end_time) {
+ all_done_.TimedWait(end_time - base::TimeTicks::Now());
+ }
+ }
+ }
+
+ // All attempts will wait for an attempt to resolve the host.
+ void WaitForAnAttemptToComplete() {
+ base::TimeDelta wait_time = base::TimeDelta::FromSeconds(60);
+ base::TimeTicks end_time = base::TimeTicks::Now() + wait_time;
+ {
+ base::AutoLock auto_lock(lock_);
+ while (resolved_attempt_number_ == 0 && base::TimeTicks::Now() < end_time)
+ all_done_.TimedWait(end_time - base::TimeTicks::Now());
+ }
+ all_done_.Broadcast(); // Tell all waiting attempts to proceed.
+ }
+
+ // Returns the number of attempts that have finished the Resolve() method.
+ int total_attempts_resolved() { return total_attempts_resolved_; }
+
+ // Returns the first attempt that that has resolved the host.
+ int resolved_attempt_number() { return resolved_attempt_number_; }
+
+ // HostResolverProc methods.
+ virtual int Resolve(const std::string& host,
+ AddressFamily address_family,
+ HostResolverFlags host_resolver_flags,
+ AddressList* addrlist,
+ int* os_error) OVERRIDE {
+ bool wait_for_right_attempt_to_complete = true;
+ {
+ base::AutoLock auto_lock(lock_);
+ ++current_attempt_number_;
+ if (current_attempt_number_ == attempt_number_to_resolve_) {
+ resolved_attempt_number_ = current_attempt_number_;
+ wait_for_right_attempt_to_complete = false;
+ }
+ }
+
+ if (wait_for_right_attempt_to_complete)
+ // Wait for the attempt_number_to_resolve_ attempt to resolve.
+ WaitForAnAttemptToComplete();
+
+ int result = ResolveUsingPrevious(host, address_family, host_resolver_flags,
+ addrlist, os_error);
+
+ {
+ base::AutoLock auto_lock(lock_);
+ ++total_attempts_resolved_;
+ }
+
+ all_done_.Broadcast(); // Tell all attempts to proceed.
+
+ // Since any negative number is considered a network error, with -1 having
+ // special meaning (ERR_IO_PENDING). We could return the attempt that has
+ // resolved the host as a negative number. For example, if attempt number 3
+ // resolves the host, then this method returns -4.
+ if (result == OK)
+ return -1 - resolved_attempt_number_;
+ else
+ return result;
+ }
+
+ protected:
+ virtual ~LookupAttemptHostResolverProc() {}
+
+ private:
+ int attempt_number_to_resolve_;
+ int current_attempt_number_; // Incremented whenever Resolve is called.
+ int total_attempts_;
+ int total_attempts_resolved_;
+ int resolved_attempt_number_;
+
+ // All attempts wait for right attempt to be resolve.
+ base::Lock lock_;
+ base::ConditionVariable all_done_;
+};
+
+} // namespace
+
+class HostResolverImplTest : public testing::Test {
+ public:
+ static const int kDefaultPort = 80;
+
+ HostResolverImplTest() : proc_(new MockHostResolverProc()) {}
+
+ protected:
+ // A Request::Handler which is a proxy to the HostResolverImplTest fixture.
+ struct Handler : public Request::Handler {
+ virtual ~Handler() {}
+
+ // Proxy functions so that classes derived from Handler can access them.
+ Request* CreateRequest(const HostResolver::RequestInfo& info) {
+ return test->CreateRequest(info);
+ }
+ Request* CreateRequest(const std::string& hostname, int port) {
+ return test->CreateRequest(hostname, port);
+ }
+ Request* CreateRequest(const std::string& hostname) {
+ return test->CreateRequest(hostname);
+ }
+ ScopedVector<Request>& requests() { return test->requests_; }
+
+ void DeleteResolver() { test->resolver_.reset(); }
+
+ HostResolverImplTest* test;
+ };
+
+ void CreateResolver() {
+ resolver_.reset(new HostResolverImpl(
+ HostCache::CreateDefaultCache(),
+ DefaultLimits(),
+ DefaultParams(proc_),
+ NULL));
+ }
+
+ // This HostResolverImpl will only allow 1 outstanding resolve at a time and
+ // perform no retries.
+ void CreateSerialResolver() {
+ HostResolverImpl::ProcTaskParams params = DefaultParams(proc_);
+ params.max_retry_attempts = 0u;
+ PrioritizedDispatcher::Limits limits(NUM_PRIORITIES, 1);
+ resolver_.reset(new HostResolverImpl(
+ HostCache::CreateDefaultCache(),
+ limits,
+ params,
+ NULL));
+ }
+
+ // The Request will not be made until a call to |Resolve()|, and the Job will
+ // not start until released by |proc_->SignalXXX|.
+ Request* CreateRequest(const HostResolver::RequestInfo& info) {
+ Request* req = new Request(info, requests_.size(), resolver_.get(),
+ handler_.get());
+ requests_.push_back(req);
+ return req;
+ }
+
+ Request* CreateRequest(const std::string& hostname,
+ int port,
+ RequestPriority priority,
+ AddressFamily family) {
+ HostResolver::RequestInfo info(HostPortPair(hostname, port));
+ info.set_priority(priority);
+ info.set_address_family(family);
+ return CreateRequest(info);
+ }
+
+ Request* CreateRequest(const std::string& hostname,
+ int port,
+ RequestPriority priority) {
+ return CreateRequest(hostname, port, priority, ADDRESS_FAMILY_UNSPECIFIED);
+ }
+
+ Request* CreateRequest(const std::string& hostname, int port) {
+ return CreateRequest(hostname, port, MEDIUM);
+ }
+
+ Request* CreateRequest(const std::string& hostname) {
+ return CreateRequest(hostname, kDefaultPort);
+ }
+
+ virtual void SetUp() OVERRIDE {
+ CreateResolver();
+ }
+
+ virtual void TearDown() OVERRIDE {
+ if (resolver_.get())
+ EXPECT_EQ(0u, resolver_->num_running_jobs_for_tests());
+ EXPECT_FALSE(proc_->HasBlockedRequests());
+ }
+
+ void set_handler(Handler* handler) {
+ handler_.reset(handler);
+ handler_->test = this;
+ }
+
+ // Friendship is not inherited, so use proxies to access those.
+ size_t num_running_jobs() const {
+ DCHECK(resolver_.get());
+ return resolver_->num_running_jobs_for_tests();
+ }
+
+ scoped_refptr<MockHostResolverProc> proc_;
+ scoped_ptr<HostResolverImpl> resolver_;
+ ScopedVector<Request> requests_;
+
+ scoped_ptr<Handler> handler_;
+};
+
+TEST_F(HostResolverImplTest, AsynchronousLookup) {
+ proc_->AddRuleForAllFamilies("just.testing", "192.168.1.42");
+ proc_->SignalMultiple(1u);
+
+ Request* req = CreateRequest("just.testing", 80);
+ EXPECT_EQ(ERR_IO_PENDING, req->Resolve());
+ EXPECT_EQ(OK, req->WaitForResult());
+
+ EXPECT_TRUE(req->HasOneAddress("192.168.1.42", 80));
+
+ EXPECT_EQ("just.testing", proc_->GetCaptureList()[0].hostname);
+}
+
+TEST_F(HostResolverImplTest, FailedAsynchronousLookup) {
+ proc_->AddRuleForAllFamilies("", "0.0.0.0"); // Default to failures.
+ proc_->SignalMultiple(1u);
+
+ Request* req = CreateRequest("just.testing", 80);
+ EXPECT_EQ(ERR_IO_PENDING, req->Resolve());
+ EXPECT_EQ(ERR_NAME_NOT_RESOLVED, req->WaitForResult());
+
+ EXPECT_EQ("just.testing", proc_->GetCaptureList()[0].hostname);
+
+ // Also test that the error is not cached.
+ EXPECT_EQ(ERR_DNS_CACHE_MISS, req->ResolveFromCache());
+}
+
+TEST_F(HostResolverImplTest, AbortedAsynchronousLookup) {
+ Request* req0 = CreateRequest("just.testing", 80);
+ EXPECT_EQ(ERR_IO_PENDING, req0->Resolve());
+
+ EXPECT_TRUE(proc_->WaitFor(1u));
+
+ // Resolver is destroyed while job is running on WorkerPool.
+ resolver_.reset();
+
+ proc_->SignalAll();
+
+ // To ensure there was no spurious callback, complete with a new resolver.
+ CreateResolver();
+ Request* req1 = CreateRequest("just.testing", 80);
+ EXPECT_EQ(ERR_IO_PENDING, req1->Resolve());
+
+ proc_->SignalMultiple(2u);
+
+ EXPECT_EQ(OK, req1->WaitForResult());
+
+ // This request was canceled.
+ EXPECT_FALSE(req0->completed());
+}
+
+TEST_F(HostResolverImplTest, NumericIPv4Address) {
+ // Stevens says dotted quads with AI_UNSPEC resolve to a single sockaddr_in.
+ Request* req = CreateRequest("127.1.2.3", 5555);
+ EXPECT_EQ(OK, req->Resolve());
+
+ EXPECT_TRUE(req->HasOneAddress("127.1.2.3", 5555));
+}
+
+TEST_F(HostResolverImplTest, NumericIPv6Address) {
+ // Resolve a plain IPv6 address. Don't worry about [brackets], because
+ // the caller should have removed them.
+ Request* req = CreateRequest("2001:db8::1", 5555);
+ EXPECT_EQ(OK, req->Resolve());
+
+ EXPECT_TRUE(req->HasOneAddress("2001:db8::1", 5555));
+}
+
+TEST_F(HostResolverImplTest, EmptyHost) {
+ Request* req = CreateRequest("", 5555);
+ EXPECT_EQ(ERR_NAME_NOT_RESOLVED, req->Resolve());
+}
+
+TEST_F(HostResolverImplTest, LongHost) {
+ Request* req = CreateRequest(std::string(4097, 'a'), 5555);
+ EXPECT_EQ(ERR_NAME_NOT_RESOLVED, req->Resolve());
+}
+
+TEST_F(HostResolverImplTest, DeDupeRequests) {
+ // Start 5 requests, duplicating hosts "a" and "b". Since the resolver_proc is
+ // blocked, these should all pile up until we signal it.
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("a", 80)->Resolve());
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("b", 80)->Resolve());
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("b", 81)->Resolve());
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("a", 82)->Resolve());
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("b", 83)->Resolve());
+
+ proc_->SignalMultiple(2u); // One for "a", one for "b".
+
+ for (size_t i = 0; i < requests_.size(); ++i) {
+ EXPECT_EQ(OK, requests_[i]->WaitForResult()) << i;
+ }
+}
+
+TEST_F(HostResolverImplTest, CancelMultipleRequests) {
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("a", 80)->Resolve());
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("b", 80)->Resolve());
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("b", 81)->Resolve());
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("a", 82)->Resolve());
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("b", 83)->Resolve());
+
+ // Cancel everything except request for ("a", 82).
+ requests_[0]->Cancel();
+ requests_[1]->Cancel();
+ requests_[2]->Cancel();
+ requests_[4]->Cancel();
+
+ proc_->SignalMultiple(2u); // One for "a", one for "b".
+
+ EXPECT_EQ(OK, requests_[3]->WaitForResult());
+}
+
+TEST_F(HostResolverImplTest, CanceledRequestsReleaseJobSlots) {
+ // Fill up the dispatcher and queue.
+ for (unsigned i = 0; i < kMaxJobs + 1; ++i) {
+ std::string hostname = "a_";
+ hostname[1] = 'a' + i;
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest(hostname, 80)->Resolve());
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest(hostname, 81)->Resolve());
+ }
+
+ EXPECT_TRUE(proc_->WaitFor(kMaxJobs));
+
+ // Cancel all but last two.
+ for (unsigned i = 0; i < requests_.size() - 2; ++i) {
+ requests_[i]->Cancel();
+ }
+
+ EXPECT_TRUE(proc_->WaitFor(kMaxJobs + 1));
+
+ proc_->SignalAll();
+
+ size_t num_requests = requests_.size();
+ EXPECT_EQ(OK, requests_[num_requests - 1]->WaitForResult());
+ EXPECT_EQ(OK, requests_[num_requests - 2]->result());
+}
+
+TEST_F(HostResolverImplTest, CancelWithinCallback) {
+ struct MyHandler : public Handler {
+ virtual void Handle(Request* req) OVERRIDE {
+ // Port 80 is the first request that the callback will be invoked for.
+ // While we are executing within that callback, cancel the other requests
+ // in the job and start another request.
+ if (req->index() == 0) {
+ // Once "a:80" completes, it will cancel "a:81" and "a:82".
+ requests()[1]->Cancel();
+ requests()[2]->Cancel();
+ }
+ }
+ };
+ set_handler(new MyHandler());
+
+ for (size_t i = 0; i < 4; ++i) {
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("a", 80 + i)->Resolve()) << i;
+ }
+
+ proc_->SignalMultiple(2u); // One for "a". One for "finalrequest".
+
+ EXPECT_EQ(OK, requests_[0]->WaitForResult());
+
+ Request* final_request = CreateRequest("finalrequest", 70);
+ EXPECT_EQ(ERR_IO_PENDING, final_request->Resolve());
+ EXPECT_EQ(OK, final_request->WaitForResult());
+ EXPECT_TRUE(requests_[3]->completed());
+}
+
+TEST_F(HostResolverImplTest, DeleteWithinCallback) {
+ struct MyHandler : public Handler {
+ virtual void Handle(Request* req) OVERRIDE {
+ EXPECT_EQ("a", req->info().hostname());
+ EXPECT_EQ(80, req->info().port());
+
+ DeleteResolver();
+
+ // Quit after returning from OnCompleted (to give it a chance at
+ // incorrectly running the cancelled tasks).
+ MessageLoop::current()->PostTask(FROM_HERE, MessageLoop::QuitClosure());
+ }
+ };
+ set_handler(new MyHandler());
+
+ for (size_t i = 0; i < 4; ++i) {
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("a", 80 + i)->Resolve()) << i;
+ }
+
+ proc_->SignalMultiple(1u); // One for "a".
+
+ // |MyHandler| will send quit message once all the requests have finished.
+ MessageLoop::current()->Run();
+}
+
+TEST_F(HostResolverImplTest, DeleteWithinAbortedCallback) {
+ struct MyHandler : public Handler {
+ virtual void Handle(Request* req) OVERRIDE {
+ EXPECT_EQ("a", req->info().hostname());
+ EXPECT_EQ(80, req->info().port());
+
+ DeleteResolver();
+
+ // Quit after returning from OnCompleted (to give it a chance at
+ // incorrectly running the cancelled tasks).
+ MessageLoop::current()->PostTask(FROM_HERE, MessageLoop::QuitClosure());
+ }
+ };
+ set_handler(new MyHandler());
+
+ // This test assumes that the Jobs will be Aborted in order ["a", "b"]
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("a", 80)->Resolve());
+ // HostResolverImpl will be deleted before later Requests can complete.
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("a", 81)->Resolve());
+ // Job for 'b' will be aborted before it can complete.
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("b", 82)->Resolve());
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("b", 83)->Resolve());
+
+ EXPECT_TRUE(proc_->WaitFor(1u));
+
+ // Triggering an IP address change.
+ NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
+
+ // |MyHandler| will send quit message once all the requests have finished.
+ MessageLoop::current()->Run();
+
+ EXPECT_EQ(ERR_NETWORK_CHANGED, requests_[0]->result());
+ EXPECT_EQ(ERR_IO_PENDING, requests_[1]->result());
+ EXPECT_EQ(ERR_IO_PENDING, requests_[2]->result());
+ EXPECT_EQ(ERR_IO_PENDING, requests_[3]->result());
+ // Clean up.
+ proc_->SignalMultiple(requests_.size());
+}
+
+TEST_F(HostResolverImplTest, StartWithinCallback) {
+ struct MyHandler : public Handler {
+ virtual void Handle(Request* req) OVERRIDE {
+ if (req->index() == 0) {
+ // On completing the first request, start another request for "a".
+ // Since caching is disabled, this will result in another async request.
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("a", 70)->Resolve());
+ }
+ }
+ };
+ set_handler(new MyHandler());
+
+ // Turn off caching for this host resolver.
+ resolver_.reset(new HostResolverImpl(
+ scoped_ptr<HostCache>(),
+ DefaultLimits(),
+ DefaultParams(proc_),
+ NULL));
+
+ for (size_t i = 0; i < 4; ++i) {
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("a", 80 + i)->Resolve()) << i;
+ }
+
+ proc_->SignalMultiple(2u); // One for "a". One for the second "a".
+
+ EXPECT_EQ(OK, requests_[0]->WaitForResult());
+ ASSERT_EQ(5u, requests_.size());
+ EXPECT_EQ(OK, requests_.back()->WaitForResult());
+
+ EXPECT_EQ(2u, proc_->GetCaptureList().size());
+}
+
+TEST_F(HostResolverImplTest, BypassCache) {
+ struct MyHandler : public Handler {
+ virtual void Handle(Request* req) OVERRIDE {
+ if (req->index() == 0) {
+ // On completing the first request, start another request for "a".
+ // Since caching is enabled, this should complete synchronously.
+ std::string hostname = req->info().hostname();
+ EXPECT_EQ(OK, CreateRequest(hostname, 70)->Resolve());
+ EXPECT_EQ(OK, CreateRequest(hostname, 75)->ResolveFromCache());
+
+ // Ok good. Now make sure that if we ask to bypass the cache, it can no
+ // longer service the request synchronously.
+ HostResolver::RequestInfo info(HostPortPair(hostname, 71));
+ info.set_allow_cached_response(false);
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest(info)->Resolve());
+ } else if (71 == req->info().port()) {
+ // Test is done.
+ MessageLoop::current()->Quit();
+ } else {
+ FAIL() << "Unexpected request";
+ }
+ }
+ };
+ set_handler(new MyHandler());
+
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("a", 80)->Resolve());
+ proc_->SignalMultiple(3u); // Only need two, but be generous.
+
+ // |verifier| will send quit message once all the requests have finished.
+ MessageLoop::current()->Run();
+ EXPECT_EQ(2u, proc_->GetCaptureList().size());
+}
+
+// Test that IP address changes flush the cache.
+TEST_F(HostResolverImplTest, FlushCacheOnIPAddressChange) {
+ proc_->SignalMultiple(2u); // One before the flush, one after.
+
+ Request* req = CreateRequest("host1", 70);
+ EXPECT_EQ(ERR_IO_PENDING, req->Resolve());
+ EXPECT_EQ(OK, req->WaitForResult());
+
+ req = CreateRequest("host1", 75);
+ EXPECT_EQ(OK, req->Resolve()); // Should complete synchronously.
+
+ // Flush cache by triggering an IP address change.
+ NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
+ MessageLoop::current()->RunUntilIdle(); // Notification happens async.
+
+ // Resolve "host1" again -- this time it won't be served from cache, so it
+ // will complete asynchronously.
+ req = CreateRequest("host1", 80);
+ EXPECT_EQ(ERR_IO_PENDING, req->Resolve());
+ EXPECT_EQ(OK, req->WaitForResult());
+}
+
+// Test that IP address changes send ERR_NETWORK_CHANGED to pending requests.
+TEST_F(HostResolverImplTest, AbortOnIPAddressChanged) {
+ Request* req = CreateRequest("host1", 70);
+ EXPECT_EQ(ERR_IO_PENDING, req->Resolve());
+
+ EXPECT_TRUE(proc_->WaitFor(1u));
+ // Triggering an IP address change.
+ NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
+ MessageLoop::current()->RunUntilIdle(); // Notification happens async.
+ proc_->SignalAll();
+
+ EXPECT_EQ(ERR_NETWORK_CHANGED, req->WaitForResult());
+ EXPECT_EQ(0u, resolver_->GetHostCache()->size());
+}
+
+// Obey pool constraints after IP address has changed.
+TEST_F(HostResolverImplTest, ObeyPoolConstraintsAfterIPAddressChange) {
+ // Runs at most one job at a time.
+ CreateSerialResolver();
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("a")->Resolve());
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("b")->Resolve());
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("c")->Resolve());
+
+ EXPECT_TRUE(proc_->WaitFor(1u));
+ // Triggering an IP address change.
+ NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
+ MessageLoop::current()->RunUntilIdle(); // Notification happens async.
+ proc_->SignalMultiple(3u); // Let the false-start go so that we can catch it.
+
+ EXPECT_EQ(ERR_NETWORK_CHANGED, requests_[0]->WaitForResult());
+
+ EXPECT_EQ(1u, num_running_jobs());
+
+ EXPECT_FALSE(requests_[1]->completed());
+ EXPECT_FALSE(requests_[2]->completed());
+
+ EXPECT_EQ(OK, requests_[2]->WaitForResult());
+ EXPECT_EQ(OK, requests_[1]->result());
+}
+
+// Tests that a new Request made from the callback of a previously aborted one
+// will not be aborted.
+TEST_F(HostResolverImplTest, AbortOnlyExistingRequestsOnIPAddressChange) {
+ struct MyHandler : public Handler {
+ virtual void Handle(Request* req) OVERRIDE {
+ // Start new request for a different hostname to ensure that the order
+ // of jobs in HostResolverImpl is not stable.
+ std::string hostname;
+ if (req->index() == 0)
+ hostname = "zzz";
+ else if (req->index() == 1)
+ hostname = "aaa";
+ else if (req->index() == 2)
+ hostname = "eee";
+ else
+ return; // A request started from within MyHandler.
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest(hostname)->Resolve()) << hostname;
+ }
+ };
+ set_handler(new MyHandler());
+
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("bbb")->Resolve());
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("eee")->Resolve());
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("ccc")->Resolve());
+
+ // Wait until all are blocked;
+ EXPECT_TRUE(proc_->WaitFor(3u));
+ // Trigger an IP address change.
+ NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
+ // This should abort all running jobs.
+ MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(ERR_NETWORK_CHANGED, requests_[0]->result());
+ EXPECT_EQ(ERR_NETWORK_CHANGED, requests_[1]->result());
+ EXPECT_EQ(ERR_NETWORK_CHANGED, requests_[2]->result());
+ ASSERT_EQ(6u, requests_.size());
+ // Unblock all calls to proc.
+ proc_->SignalMultiple(requests_.size());
+ // Run until the re-started requests finish.
+ EXPECT_EQ(OK, requests_[3]->WaitForResult());
+ EXPECT_EQ(OK, requests_[4]->WaitForResult());
+ EXPECT_EQ(OK, requests_[5]->WaitForResult());
+ // Verify that results of aborted Jobs were not cached.
+ EXPECT_EQ(6u, proc_->GetCaptureList().size());
+ EXPECT_EQ(3u, resolver_->GetHostCache()->size());
+}
+
+// Tests that when the maximum threads is set to 1, requests are dequeued
+// in order of priority.
+TEST_F(HostResolverImplTest, HigherPriorityRequestsStartedFirst) {
+ CreateSerialResolver();
+
+ // Note that at this point the MockHostResolverProc is blocked, so any
+ // requests we make will not complete.
+ CreateRequest("req0", 80, LOW);
+ CreateRequest("req1", 80, MEDIUM);
+ CreateRequest("req2", 80, MEDIUM);
+ CreateRequest("req3", 80, LOW);
+ CreateRequest("req4", 80, HIGHEST);
+ CreateRequest("req5", 80, LOW);
+ CreateRequest("req6", 80, LOW);
+ CreateRequest("req5", 80, HIGHEST);
+
+ for (size_t i = 0; i < requests_.size(); ++i) {
+ EXPECT_EQ(ERR_IO_PENDING, requests_[i]->Resolve()) << i;
+ }
+
+ // Unblock the resolver thread so the requests can run.
+ proc_->SignalMultiple(requests_.size()); // More than needed.
+
+ // Wait for all the requests to complete succesfully.
+ for (size_t i = 0; i < requests_.size(); ++i) {
+ EXPECT_EQ(OK, requests_[i]->WaitForResult()) << i;
+ }
+
+ // Since we have restricted to a single concurrent thread in the jobpool,
+ // the requests should complete in order of priority (with the exception
+ // of the first request, which gets started right away, since there is
+ // nothing outstanding).
+ MockHostResolverProc::CaptureList capture_list = proc_->GetCaptureList();
+ ASSERT_EQ(7u, capture_list.size());
+
+ EXPECT_EQ("req0", capture_list[0].hostname);
+ EXPECT_EQ("req4", capture_list[1].hostname);
+ EXPECT_EQ("req5", capture_list[2].hostname);
+ EXPECT_EQ("req1", capture_list[3].hostname);
+ EXPECT_EQ("req2", capture_list[4].hostname);
+ EXPECT_EQ("req3", capture_list[5].hostname);
+ EXPECT_EQ("req6", capture_list[6].hostname);
+}
+
+// Try cancelling a job which has not started yet.
+TEST_F(HostResolverImplTest, CancelPendingRequest) {
+ CreateSerialResolver();
+
+ CreateRequest("req0", 80, LOWEST);
+ CreateRequest("req1", 80, HIGHEST); // Will cancel.
+ CreateRequest("req2", 80, MEDIUM);
+ CreateRequest("req3", 80, LOW);
+ CreateRequest("req4", 80, HIGHEST); // Will cancel.
+ CreateRequest("req5", 80, LOWEST); // Will cancel.
+ CreateRequest("req6", 80, MEDIUM);
+
+ // Start all of the requests.
+ for (size_t i = 0; i < requests_.size(); ++i) {
+ EXPECT_EQ(ERR_IO_PENDING, requests_[i]->Resolve()) << i;
+ }
+
+ // Cancel some requests
+ requests_[1]->Cancel();
+ requests_[4]->Cancel();
+ requests_[5]->Cancel();
+
+ // Unblock the resolver thread so the requests can run.
+ proc_->SignalMultiple(requests_.size()); // More than needed.
+
+ // Wait for all the requests to complete succesfully.
+ for (size_t i = 0; i < requests_.size(); ++i) {
+ if (!requests_[i]->pending())
+ continue; // Don't wait for the requests we cancelled.
+ EXPECT_EQ(OK, requests_[i]->WaitForResult()) << i;
+ }
+
+ // Verify that they called out the the resolver proc (which runs on the
+ // resolver thread) in the expected order.
+ MockHostResolverProc::CaptureList capture_list = proc_->GetCaptureList();
+ ASSERT_EQ(4u, capture_list.size());
+
+ EXPECT_EQ("req0", capture_list[0].hostname);
+ EXPECT_EQ("req2", capture_list[1].hostname);
+ EXPECT_EQ("req6", capture_list[2].hostname);
+ EXPECT_EQ("req3", capture_list[3].hostname);
+}
+
+// Test that when too many requests are enqueued, old ones start to be aborted.
+TEST_F(HostResolverImplTest, QueueOverflow) {
+ CreateSerialResolver();
+
+ // Allow only 3 queued jobs.
+ const size_t kMaxPendingJobs = 3u;
+ resolver_->SetMaxQueuedJobs(kMaxPendingJobs);
+
+ // Note that at this point the MockHostResolverProc is blocked, so any
+ // requests we make will not complete.
+
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("req0", 80, LOWEST)->Resolve());
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("req1", 80, HIGHEST)->Resolve());
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("req2", 80, MEDIUM)->Resolve());
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("req3", 80, MEDIUM)->Resolve());
+
+ // At this point, there are 3 enqueued jobs.
+ // Insertion of subsequent requests will cause evictions
+ // based on priority.
+
+ EXPECT_EQ(ERR_HOST_RESOLVER_QUEUE_TOO_LARGE,
+ CreateRequest("req4", 80, LOW)->Resolve()); // Evicts itself!
+
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("req5", 80, MEDIUM)->Resolve());
+ EXPECT_EQ(ERR_HOST_RESOLVER_QUEUE_TOO_LARGE, requests_[2]->result());
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("req6", 80, HIGHEST)->Resolve());
+ EXPECT_EQ(ERR_HOST_RESOLVER_QUEUE_TOO_LARGE, requests_[3]->result());
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("req7", 80, MEDIUM)->Resolve());
+ EXPECT_EQ(ERR_HOST_RESOLVER_QUEUE_TOO_LARGE, requests_[5]->result());
+
+ // Unblock the resolver thread so the requests can run.
+ proc_->SignalMultiple(4u);
+
+ // The rest should succeed.
+ EXPECT_EQ(OK, requests_[7]->WaitForResult());
+ EXPECT_EQ(OK, requests_[0]->result());
+ EXPECT_EQ(OK, requests_[1]->result());
+ EXPECT_EQ(OK, requests_[6]->result());
+
+ // Verify that they called out the the resolver proc (which runs on the
+ // resolver thread) in the expected order.
+ MockHostResolverProc::CaptureList capture_list = proc_->GetCaptureList();
+ ASSERT_EQ(4u, capture_list.size());
+
+ EXPECT_EQ("req0", capture_list[0].hostname);
+ EXPECT_EQ("req1", capture_list[1].hostname);
+ EXPECT_EQ("req6", capture_list[2].hostname);
+ EXPECT_EQ("req7", capture_list[3].hostname);
+
+ // Verify that the evicted (incomplete) requests were not cached.
+ EXPECT_EQ(4u, resolver_->GetHostCache()->size());
+
+ for (size_t i = 0; i < requests_.size(); ++i) {
+ EXPECT_TRUE(requests_[i]->completed()) << i;
+ }
+}
+
+// Tests that after changing the default AddressFamily to IPV4, requests
+// with UNSPECIFIED address family map to IPV4.
+TEST_F(HostResolverImplTest, SetDefaultAddressFamily_IPv4) {
+ CreateSerialResolver(); // To guarantee order of resolutions.
+
+ proc_->AddRule("h1", ADDRESS_FAMILY_IPV4, "1.0.0.1");
+ proc_->AddRule("h1", ADDRESS_FAMILY_IPV6, "::2");
+
+ resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_IPV4);
+
+ CreateRequest("h1", 80, MEDIUM, ADDRESS_FAMILY_UNSPECIFIED);
+ CreateRequest("h1", 80, MEDIUM, ADDRESS_FAMILY_IPV4);
+ CreateRequest("h1", 80, MEDIUM, ADDRESS_FAMILY_IPV6);
+
+ // Start all of the requests.
+ for (size_t i = 0; i < requests_.size(); ++i) {
+ EXPECT_EQ(ERR_IO_PENDING, requests_[i]->Resolve()) << i;
+ }
+
+ proc_->SignalMultiple(requests_.size());
+
+ // Wait for all the requests to complete.
+ for (size_t i = 0u; i < requests_.size(); ++i) {
+ EXPECT_EQ(OK, requests_[i]->WaitForResult()) << i;
+ }
+
+ // Since the requests all had the same priority and we limited the thread
+ // count to 1, they should have completed in the same order as they were
+ // requested. Moreover, request0 and request1 will have been serviced by
+ // the same job.
+
+ MockHostResolverProc::CaptureList capture_list = proc_->GetCaptureList();
+ ASSERT_EQ(2u, capture_list.size());
+
+ EXPECT_EQ("h1", capture_list[0].hostname);
+ EXPECT_EQ(ADDRESS_FAMILY_IPV4, capture_list[0].address_family);
+
+ EXPECT_EQ("h1", capture_list[1].hostname);
+ EXPECT_EQ(ADDRESS_FAMILY_IPV6, capture_list[1].address_family);
+
+ // Now check that the correct resolved IP addresses were returned.
+ EXPECT_TRUE(requests_[0]->HasOneAddress("1.0.0.1", 80));
+ EXPECT_TRUE(requests_[1]->HasOneAddress("1.0.0.1", 80));
+ EXPECT_TRUE(requests_[2]->HasOneAddress("::2", 80));
+}
+
+// This is the exact same test as SetDefaultAddressFamily_IPv4, except the
+// default family is set to IPv6 and the family of requests is flipped where
+// specified.
+TEST_F(HostResolverImplTest, SetDefaultAddressFamily_IPv6) {
+ CreateSerialResolver(); // To guarantee order of resolutions.
+
+ // Don't use IPv6 replacements here since some systems don't support it.
+ proc_->AddRule("h1", ADDRESS_FAMILY_IPV4, "1.0.0.1");
+ proc_->AddRule("h1", ADDRESS_FAMILY_IPV6, "::2");
+
+ resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_IPV6);
+
+ CreateRequest("h1", 80, MEDIUM, ADDRESS_FAMILY_UNSPECIFIED);
+ CreateRequest("h1", 80, MEDIUM, ADDRESS_FAMILY_IPV6);
+ CreateRequest("h1", 80, MEDIUM, ADDRESS_FAMILY_IPV4);
+
+ // Start all of the requests.
+ for (size_t i = 0; i < requests_.size(); ++i) {
+ EXPECT_EQ(ERR_IO_PENDING, requests_[i]->Resolve()) << i;
+ }
+
+ proc_->SignalMultiple(requests_.size());
+
+ // Wait for all the requests to complete.
+ for (size_t i = 0u; i < requests_.size(); ++i) {
+ EXPECT_EQ(OK, requests_[i]->WaitForResult()) << i;
+ }
+
+ // Since the requests all had the same priority and we limited the thread
+ // count to 1, they should have completed in the same order as they were
+ // requested. Moreover, request0 and request1 will have been serviced by
+ // the same job.
+
+ MockHostResolverProc::CaptureList capture_list = proc_->GetCaptureList();
+ ASSERT_EQ(2u, capture_list.size());
+
+ EXPECT_EQ("h1", capture_list[0].hostname);
+ EXPECT_EQ(ADDRESS_FAMILY_IPV6, capture_list[0].address_family);
+
+ EXPECT_EQ("h1", capture_list[1].hostname);
+ EXPECT_EQ(ADDRESS_FAMILY_IPV4, capture_list[1].address_family);
+
+ // Now check that the correct resolved IP addresses were returned.
+ EXPECT_TRUE(requests_[0]->HasOneAddress("::2", 80));
+ EXPECT_TRUE(requests_[1]->HasOneAddress("::2", 80));
+ EXPECT_TRUE(requests_[2]->HasOneAddress("1.0.0.1", 80));
+}
+
+TEST_F(HostResolverImplTest, ResolveFromCache) {
+ proc_->AddRuleForAllFamilies("just.testing", "192.168.1.42");
+ proc_->SignalMultiple(1u); // Need only one.
+
+ HostResolver::RequestInfo info(HostPortPair("just.testing", 80));
+
+ // First hit will miss the cache.
+ EXPECT_EQ(ERR_DNS_CACHE_MISS, CreateRequest(info)->ResolveFromCache());
+
+ // This time, we fetch normally.
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest(info)->Resolve());
+ EXPECT_EQ(OK, requests_[1]->WaitForResult());
+
+ // Now we should be able to fetch from the cache.
+ EXPECT_EQ(OK, CreateRequest(info)->ResolveFromCache());
+ EXPECT_TRUE(requests_[2]->HasOneAddress("192.168.1.42", 80));
+}
+
+// Test the retry attempts simulating host resolver proc that takes too long.
+TEST_F(HostResolverImplTest, MultipleAttempts) {
+ // Total number of attempts would be 3 and we want the 3rd attempt to resolve
+ // the host. First and second attempt will be forced to sleep until they get
+ // word that a resolution has completed. The 3rd resolution attempt will try
+ // to get done ASAP, and won't sleep..
+ int kAttemptNumberToResolve = 3;
+ int kTotalAttempts = 3;
+
+ scoped_refptr<LookupAttemptHostResolverProc> resolver_proc(
+ new LookupAttemptHostResolverProc(
+ NULL, kAttemptNumberToResolve, kTotalAttempts));
+
+ HostResolverImpl::ProcTaskParams params = DefaultParams(resolver_proc.get());
+
+ // Specify smaller interval for unresponsive_delay_ for HostResolverImpl so
+ // that unit test runs faster. For example, this test finishes in 1.5 secs
+ // (500ms * 3).
+ params.unresponsive_delay = base::TimeDelta::FromMilliseconds(500);
+
+ resolver_.reset(
+ new HostResolverImpl(HostCache::CreateDefaultCache(),
+ DefaultLimits(),
+ params,
+ NULL));
+
+ // Resolve "host1".
+ HostResolver::RequestInfo info(HostPortPair("host1", 70));
+ Request* req = CreateRequest(info);
+ EXPECT_EQ(ERR_IO_PENDING, req->Resolve());
+
+ // Resolve returns -4 to indicate that 3rd attempt has resolved the host.
+ EXPECT_EQ(-4, req->WaitForResult());
+
+ resolver_proc->WaitForAllAttemptsToFinish(
+ base::TimeDelta::FromMilliseconds(60000));
+ MessageLoop::current()->RunUntilIdle();
+
+ EXPECT_EQ(resolver_proc->total_attempts_resolved(), kTotalAttempts);
+ EXPECT_EQ(resolver_proc->resolved_attempt_number(), kAttemptNumberToResolve);
+}
+
+DnsConfig CreateValidDnsConfig() {
+ IPAddressNumber dns_ip;
+ bool rv = ParseIPLiteralToNumber("192.168.1.0", &dns_ip);
+ EXPECT_TRUE(rv);
+
+ DnsConfig config;
+ config.nameservers.push_back(IPEndPoint(dns_ip, dns_protocol::kDefaultPort));
+ EXPECT_TRUE(config.IsValid());
+ return config;
+}
+
+// Specialized fixture for tests of DnsTask.
+class HostResolverImplDnsTest : public HostResolverImplTest {
+ protected:
+ virtual void SetUp() OVERRIDE {
+ AddDnsRule("er", dns_protocol::kTypeA, MockDnsClientRule::FAIL_SYNC);
+ AddDnsRule("er", dns_protocol::kTypeAAAA, MockDnsClientRule::FAIL_SYNC);
+ AddDnsRule("nx", dns_protocol::kTypeA, MockDnsClientRule::FAIL_ASYNC);
+ AddDnsRule("nx", dns_protocol::kTypeAAAA, MockDnsClientRule::FAIL_ASYNC);
+ AddDnsRule("ok", dns_protocol::kTypeA, MockDnsClientRule::OK);
+ AddDnsRule("ok", dns_protocol::kTypeAAAA, MockDnsClientRule::OK);
+ AddDnsRule("4ok", dns_protocol::kTypeA, MockDnsClientRule::OK);
+ AddDnsRule("4ok", dns_protocol::kTypeAAAA, MockDnsClientRule::EMPTY);
+ AddDnsRule("6ok", dns_protocol::kTypeA, MockDnsClientRule::EMPTY);
+ AddDnsRule("6ok", dns_protocol::kTypeAAAA, MockDnsClientRule::OK);
+ AddDnsRule("4nx", dns_protocol::kTypeA, MockDnsClientRule::OK);
+ AddDnsRule("4nx", dns_protocol::kTypeAAAA, MockDnsClientRule::FAIL_ASYNC);
+ CreateResolver();
+ }
+
+ void CreateResolver() {
+ resolver_.reset(new HostResolverImpl(
+ HostCache::CreateDefaultCache(),
+ DefaultLimits(),
+ DefaultParams(proc_),
+ NULL));
+ resolver_->SetDnsClient(CreateMockDnsClient(DnsConfig(), dns_rules_));
+ }
+
+ // Adds a rule to |dns_rules_|. Must be followed by |CreateResolver| to apply.
+ void AddDnsRule(const std::string& prefix,
+ uint16 qtype,
+ MockDnsClientRule::Result result) {
+ dns_rules_.push_back(MockDnsClientRule(prefix, qtype, result));
+ }
+
+ void ChangeDnsConfig(const DnsConfig& config) {
+ NetworkChangeNotifier::SetDnsConfig(config);
+ // Notification is delivered asynchronously.
+ MessageLoop::current()->RunUntilIdle();
+ }
+
+ MockDnsClientRuleList dns_rules_;
+};
+
+// TODO(szym): Test AbortAllInProgressJobs due to DnsConfig change.
+
+// TODO(cbentzel): Test a mix of requests with different HostResolverFlags.
+
+// Test successful and fallback resolutions in HostResolverImpl::DnsTask.
+TEST_F(HostResolverImplDnsTest, DnsTask) {
+ resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_IPV4);
+
+ proc_->AddRuleForAllFamilies("er_succeed", "192.168.1.101");
+ proc_->AddRuleForAllFamilies("nx_succeed", "192.168.1.102");
+ // All other hostnames will fail in proc_.
+
+ // Initially there is no config, so client should not be invoked.
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("ok_fail", 80)->Resolve());
+ proc_->SignalMultiple(requests_.size());
+
+ EXPECT_EQ(ERR_NAME_NOT_RESOLVED, requests_[0]->WaitForResult());
+
+ ChangeDnsConfig(CreateValidDnsConfig());
+
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("ok_fail", 80)->Resolve());
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("er_fail", 80)->Resolve());
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("nx_fail", 80)->Resolve());
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("er_succeed", 80)->Resolve());
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("nx_succeed", 80)->Resolve());
+
+ proc_->SignalMultiple(requests_.size());
+
+ for (size_t i = 1; i < requests_.size(); ++i)
+ EXPECT_NE(ERR_UNEXPECTED, requests_[i]->WaitForResult()) << i;
+
+ EXPECT_EQ(OK, requests_[1]->result());
+ // Resolved by MockDnsClient.
+ EXPECT_TRUE(requests_[1]->HasOneAddress("127.0.0.1", 80));
+ // Fallback to ProcTask.
+ EXPECT_EQ(ERR_NAME_NOT_RESOLVED, requests_[2]->result());
+ EXPECT_EQ(ERR_NAME_NOT_RESOLVED, requests_[3]->result());
+ EXPECT_EQ(OK, requests_[4]->result());
+ EXPECT_TRUE(requests_[4]->HasOneAddress("192.168.1.101", 80));
+ EXPECT_EQ(OK, requests_[5]->result());
+ EXPECT_TRUE(requests_[5]->HasOneAddress("192.168.1.102", 80));
+}
+
+TEST_F(HostResolverImplDnsTest, DnsTaskUnspec) {
+ ChangeDnsConfig(CreateValidDnsConfig());
+
+ proc_->AddRuleForAllFamilies("4nx", "192.168.1.101");
+ // All other hostnames will fail in proc_.
+
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("ok", 80)->Resolve());
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("4ok", 80)->Resolve());
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("6ok", 80)->Resolve());
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("4nx", 80)->Resolve());
+
+ proc_->SignalMultiple(requests_.size());
+
+ for (size_t i = 0; i < requests_.size(); ++i)
+ EXPECT_EQ(OK, requests_[i]->WaitForResult()) << i;
+
+ EXPECT_EQ(2u, requests_[0]->NumberOfAddresses());
+ EXPECT_TRUE(requests_[0]->HasAddress("127.0.0.1", 80));
+ EXPECT_TRUE(requests_[0]->HasAddress("::1", 80));
+ EXPECT_EQ(1u, requests_[1]->NumberOfAddresses());
+ EXPECT_TRUE(requests_[1]->HasAddress("127.0.0.1", 80));
+ EXPECT_EQ(1u, requests_[2]->NumberOfAddresses());
+ EXPECT_TRUE(requests_[2]->HasAddress("::1", 80));
+ EXPECT_EQ(1u, requests_[3]->NumberOfAddresses());
+ EXPECT_TRUE(requests_[3]->HasAddress("192.168.1.101", 80));
+}
+
+TEST_F(HostResolverImplDnsTest, ServeFromHosts) {
+ // Initially, use empty HOSTS file.
+ DnsConfig config = CreateValidDnsConfig();
+ ChangeDnsConfig(config);
+
+ proc_->AddRuleForAllFamilies("", ""); // Default to failures.
+ proc_->SignalMultiple(1u); // For the first request which misses.
+
+ Request* req0 = CreateRequest("er_ipv4", 80);
+ EXPECT_EQ(ERR_IO_PENDING, req0->Resolve());
+ EXPECT_EQ(ERR_NAME_NOT_RESOLVED, req0->WaitForResult());
+
+ IPAddressNumber local_ipv4, local_ipv6;
+ ASSERT_TRUE(ParseIPLiteralToNumber("127.0.0.1", &local_ipv4));
+ ASSERT_TRUE(ParseIPLiteralToNumber("::1", &local_ipv6));
+
+ DnsHosts hosts;
+ hosts[DnsHostsKey("er_ipv4", ADDRESS_FAMILY_IPV4)] = local_ipv4;
+ hosts[DnsHostsKey("er_ipv6", ADDRESS_FAMILY_IPV6)] = local_ipv6;
+ hosts[DnsHostsKey("er_both", ADDRESS_FAMILY_IPV4)] = local_ipv4;
+ hosts[DnsHostsKey("er_both", ADDRESS_FAMILY_IPV6)] = local_ipv6;
+
+ // Update HOSTS file.
+ config.hosts = hosts;
+ ChangeDnsConfig(config);
+
+ Request* req1 = CreateRequest("er_ipv4", 80);
+ EXPECT_EQ(OK, req1->Resolve());
+ EXPECT_TRUE(req1->HasOneAddress("127.0.0.1", 80));
+
+ Request* req2 = CreateRequest("er_ipv6", 80);
+ EXPECT_EQ(OK, req2->Resolve());
+ EXPECT_TRUE(req2->HasOneAddress("::1", 80));
+
+ Request* req3 = CreateRequest("er_both", 80);
+ EXPECT_EQ(OK, req3->Resolve());
+ EXPECT_TRUE(req3->HasOneAddress("127.0.0.1", 80) ||
+ req3->HasOneAddress("::1", 80));
+
+ // Requests with specified AddressFamily.
+ Request* req4 = CreateRequest("er_ipv4", 80, MEDIUM, ADDRESS_FAMILY_IPV4);
+ EXPECT_EQ(OK, req4->Resolve());
+ EXPECT_TRUE(req4->HasOneAddress("127.0.0.1", 80));
+
+ Request* req5 = CreateRequest("er_ipv6", 80, MEDIUM, ADDRESS_FAMILY_IPV6);
+ EXPECT_EQ(OK, req5->Resolve());
+ EXPECT_TRUE(req5->HasOneAddress("::1", 80));
+
+ // Request with upper case.
+ Request* req6 = CreateRequest("er_IPV4", 80);
+ EXPECT_EQ(OK, req6->Resolve());
+ EXPECT_TRUE(req6->HasOneAddress("127.0.0.1", 80));
+}
+
+TEST_F(HostResolverImplDnsTest, BypassDnsTask) {
+ ChangeDnsConfig(CreateValidDnsConfig());
+
+ proc_->AddRuleForAllFamilies("", ""); // Default to failures.
+
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("ok.local", 80)->Resolve());
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("ok.local.", 80)->Resolve());
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("oklocal", 80)->Resolve());
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("oklocal.", 80)->Resolve());
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest("ok", 80)->Resolve());
+
+ proc_->SignalMultiple(requests_.size());
+
+ for (size_t i = 0; i < 2; ++i)
+ EXPECT_EQ(ERR_NAME_NOT_RESOLVED, requests_[i]->WaitForResult()) << i;
+
+ for (size_t i = 2; i < requests_.size(); ++i)
+ EXPECT_EQ(OK, requests_[i]->WaitForResult()) << i;
+}
+
+TEST_F(HostResolverImplDnsTest, DisableDnsClientOnPersistentFailure) {
+ ChangeDnsConfig(CreateValidDnsConfig());
+
+ proc_->AddRuleForAllFamilies("", ""); // Default to failures.
+
+ // Check that DnsTask works.
+ Request* req = CreateRequest("ok_1", 80);
+ EXPECT_EQ(ERR_IO_PENDING, req->Resolve());
+ EXPECT_EQ(OK, req->WaitForResult());
+
+ for (unsigned i = 0; i < 20; ++i) {
+ // Use custom names to require separate Jobs.
+ std::string hostname = base::StringPrintf("err_%u", i);
+ // Ensure fallback to ProcTask succeeds.
+ proc_->AddRuleForAllFamilies(hostname, "192.168.1.101");
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest(hostname, 80)->Resolve()) << i;
+ }
+
+ proc_->SignalMultiple(requests_.size());
+
+ for (size_t i = 0; i < requests_.size(); ++i)
+ EXPECT_EQ(OK, requests_[i]->WaitForResult()) << i;
+
+ ASSERT_FALSE(proc_->HasBlockedRequests());
+
+ // DnsTask should be disabled by now.
+ req = CreateRequest("ok_2", 80);
+ EXPECT_EQ(ERR_IO_PENDING, req->Resolve());
+ proc_->SignalMultiple(1u);
+ EXPECT_EQ(ERR_NAME_NOT_RESOLVED, req->WaitForResult());
+
+ // Check that it is re-enabled after DNS change.
+ ChangeDnsConfig(CreateValidDnsConfig());
+ req = CreateRequest("ok_3", 80);
+ EXPECT_EQ(ERR_IO_PENDING, req->Resolve());
+ EXPECT_EQ(OK, req->WaitForResult());
+}
+
+TEST_F(HostResolverImplDnsTest, DontDisableDnsClientOnSporadicFailure) {
+ ChangeDnsConfig(CreateValidDnsConfig());
+
+ // |proc_| defaults to successes.
+
+ // 20 failures interleaved with 20 successes.
+ for (unsigned i = 0; i < 40; ++i) {
+ // Use custom names to require separate Jobs.
+ std::string hostname = (i % 2) == 0 ? base::StringPrintf("err_%u", i)
+ : base::StringPrintf("ok_%u", i);
+ EXPECT_EQ(ERR_IO_PENDING, CreateRequest(hostname, 80)->Resolve()) << i;
+ }
+
+ proc_->SignalMultiple(requests_.size());
+
+ for (size_t i = 0; i < requests_.size(); ++i)
+ EXPECT_EQ(OK, requests_[i]->WaitForResult()) << i;
+
+ // Make |proc_| default to failures.
+ proc_->AddRuleForAllFamilies("", "");
+
+ // DnsTask should still be enabled.
+ Request* req = CreateRequest("ok_last", 80);
+ EXPECT_EQ(ERR_IO_PENDING, req->Resolve());
+ EXPECT_EQ(OK, req->WaitForResult());
+}
+
+} // namespace net
diff --git a/net/dns/host_resolver_proc.cc b/net/dns/host_resolver_proc.cc
new file mode 100644
index 0000000..6c786a1
--- /dev/null
+++ b/net/dns/host_resolver_proc.cc
@@ -0,0 +1,251 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/dns/host_resolver_proc.h"
+
+#include "build/build_config.h"
+
+#include "base/logging.h"
+#include "base/sys_byteorder.h"
+#include "net/base/address_list.h"
+#include "net/base/dns_reloader.h"
+#include "net/base/net_errors.h"
+#include "net/base/sys_addrinfo.h"
+
+#if defined(OS_OPENBSD)
+#define AI_ADDRCONFIG 0
+#endif
+
+namespace net {
+
+namespace {
+
+bool IsAllLocalhostOfOneFamily(const struct addrinfo* ai) {
+ bool saw_v4_localhost = false;
+ bool saw_v6_localhost = false;
+ for (; ai != NULL; ai = ai->ai_next) {
+ switch (ai->ai_family) {
+ case AF_INET: {
+ const struct sockaddr_in* addr_in =
+ reinterpret_cast<struct sockaddr_in*>(ai->ai_addr);
+ if ((base::NetToHost32(addr_in->sin_addr.s_addr) & 0xff000000) ==
+ 0x7f000000)
+ saw_v4_localhost = true;
+ else
+ return false;
+ break;
+ }
+ case AF_INET6: {
+ const struct sockaddr_in6* addr_in6 =
+ reinterpret_cast<struct sockaddr_in6*>(ai->ai_addr);
+ if (IN6_IS_ADDR_LOOPBACK(&addr_in6->sin6_addr))
+ saw_v6_localhost = true;
+ else
+ return false;
+ break;
+ }
+ default:
+ NOTREACHED();
+ return false;
+ }
+ }
+
+ return saw_v4_localhost != saw_v6_localhost;
+}
+
+} // namespace
+
+HostResolverProc* HostResolverProc::default_proc_ = NULL;
+
+HostResolverProc::HostResolverProc(HostResolverProc* previous) {
+ SetPreviousProc(previous);
+
+ // Implicitly fall-back to the global default procedure.
+ if (!previous)
+ SetPreviousProc(default_proc_);
+}
+
+HostResolverProc::~HostResolverProc() {
+}
+
+int HostResolverProc::ResolveUsingPrevious(
+ const std::string& host,
+ AddressFamily address_family,
+ HostResolverFlags host_resolver_flags,
+ AddressList* addrlist,
+ int* os_error) {
+ if (previous_proc_) {
+ return previous_proc_->Resolve(host, address_family, host_resolver_flags,
+ addrlist, os_error);
+ }
+
+ // Final fallback is the system resolver.
+ return SystemHostResolverProc(host, address_family, host_resolver_flags,
+ addrlist, os_error);
+}
+
+void HostResolverProc::SetPreviousProc(HostResolverProc* proc) {
+ HostResolverProc* current_previous = previous_proc_;
+ previous_proc_ = NULL;
+ // Now that we've guaranteed |this| is the last proc in a chain, we can
+ // detect potential cycles using GetLastProc().
+ previous_proc_ = (GetLastProc(proc) == this) ? current_previous : proc;
+}
+
+void HostResolverProc::SetLastProc(HostResolverProc* proc) {
+ GetLastProc(this)->SetPreviousProc(proc);
+}
+
+// static
+HostResolverProc* HostResolverProc::GetLastProc(HostResolverProc* proc) {
+ if (proc == NULL)
+ return NULL;
+ HostResolverProc* last_proc = proc;
+ while (last_proc->previous_proc_ != NULL)
+ last_proc = last_proc->previous_proc_;
+ return last_proc;
+}
+
+// static
+HostResolverProc* HostResolverProc::SetDefault(HostResolverProc* proc) {
+ HostResolverProc* old = default_proc_;
+ default_proc_ = proc;
+ return old;
+}
+
+// static
+HostResolverProc* HostResolverProc::GetDefault() {
+ return default_proc_;
+}
+
+int SystemHostResolverProc(const std::string& host,
+ AddressFamily address_family,
+ HostResolverFlags host_resolver_flags,
+ AddressList* addrlist,
+ int* os_error) {
+ if (os_error)
+ *os_error = 0;
+
+ struct addrinfo* ai = NULL;
+ struct addrinfo hints = {0};
+
+ switch (address_family) {
+ case ADDRESS_FAMILY_IPV4:
+ hints.ai_family = AF_INET;
+ break;
+ case ADDRESS_FAMILY_IPV6:
+ hints.ai_family = AF_INET6;
+ break;
+ case ADDRESS_FAMILY_UNSPECIFIED:
+ hints.ai_family = AF_UNSPEC;
+ break;
+ default:
+ NOTREACHED();
+ hints.ai_family = AF_UNSPEC;
+ }
+
+#if defined(OS_WIN)
+ // DO NOT USE AI_ADDRCONFIG ON WINDOWS.
+ //
+ // The following comment in <winsock2.h> is the best documentation I found
+ // on AI_ADDRCONFIG for Windows:
+ // Flags used in "hints" argument to getaddrinfo()
+ // - AI_ADDRCONFIG is supported starting with Vista
+ // - default is AI_ADDRCONFIG ON whether the flag is set or not
+ // because the performance penalty in not having ADDRCONFIG in
+ // the multi-protocol stack environment is severe;
+ // this defaulting may be disabled by specifying the AI_ALL flag,
+ // in that case AI_ADDRCONFIG must be EXPLICITLY specified to
+ // enable ADDRCONFIG behavior
+ //
+ // Not only is AI_ADDRCONFIG unnecessary, but it can be harmful. If the
+ // computer is not connected to a network, AI_ADDRCONFIG causes getaddrinfo
+ // to fail with WSANO_DATA (11004) for "localhost", probably because of the
+ // following note on AI_ADDRCONFIG in the MSDN getaddrinfo page:
+ // The IPv4 or IPv6 loopback address is not considered a valid global
+ // address.
+ // See http://crbug.com/5234.
+ //
+ // OpenBSD does not support it, either.
+ hints.ai_flags = 0;
+#else
+ hints.ai_flags = AI_ADDRCONFIG;
+#endif
+
+ // On Linux AI_ADDRCONFIG doesn't consider loopback addreses, even if only
+ // loopback addresses are configured. So don't use it when there are only
+ // loopback addresses.
+ if (host_resolver_flags & HOST_RESOLVER_LOOPBACK_ONLY)
+ hints.ai_flags &= ~AI_ADDRCONFIG;
+
+ if (host_resolver_flags & HOST_RESOLVER_CANONNAME)
+ hints.ai_flags |= AI_CANONNAME;
+
+ // Restrict result set to only this socket type to avoid duplicates.
+ hints.ai_socktype = SOCK_STREAM;
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD) && \
+ !defined(OS_ANDROID)
+ DnsReloaderMaybeReload();
+#endif
+ int err = getaddrinfo(host.c_str(), NULL, &hints, &ai);
+ bool should_retry = false;
+ // If the lookup was restricted (either by address family, or address
+ // detection), and the results where all localhost of a single family,
+ // maybe we should retry. There were several bugs related to these
+ // issues, for example http://crbug.com/42058 and http://crbug.com/49024
+ if ((hints.ai_family != AF_UNSPEC || hints.ai_flags & AI_ADDRCONFIG) &&
+ err == 0 && IsAllLocalhostOfOneFamily(ai)) {
+ if (host_resolver_flags & HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6) {
+ hints.ai_family = AF_UNSPEC;
+ should_retry = true;
+ }
+ if (hints.ai_flags & AI_ADDRCONFIG) {
+ hints.ai_flags &= ~AI_ADDRCONFIG;
+ should_retry = true;
+ }
+ }
+ if (should_retry) {
+ if (ai != NULL) {
+ freeaddrinfo(ai);
+ ai = NULL;
+ }
+ err = getaddrinfo(host.c_str(), NULL, &hints, &ai);
+ }
+
+ if (err) {
+#if defined(OS_WIN)
+ err = WSAGetLastError();
+#endif
+
+ // Return the OS error to the caller.
+ if (os_error)
+ *os_error = err;
+
+ // If the call to getaddrinfo() failed because of a system error, report
+ // it separately from ERR_NAME_NOT_RESOLVED.
+#if defined(OS_WIN)
+ if (err != WSAHOST_NOT_FOUND && err != WSANO_DATA)
+ return ERR_NAME_RESOLUTION_FAILED;
+#elif defined(OS_POSIX) && !defined(OS_FREEBSD)
+ if (err != EAI_NONAME && err != EAI_NODATA)
+ return ERR_NAME_RESOLUTION_FAILED;
+#endif
+
+ return ERR_NAME_NOT_RESOLVED;
+ }
+
+#if defined(OS_ANDROID)
+ // Workaround for Android's getaddrinfo leaving ai==NULL without an error.
+ // http://crbug.com/134142
+ if (ai == NULL)
+ return ERR_NAME_NOT_RESOLVED;
+#endif
+
+ *addrlist = AddressList::CreateFromAddrinfo(ai);
+ freeaddrinfo(ai);
+ return OK;
+}
+
+} // namespace net
diff --git a/net/dns/host_resolver_proc.h b/net/dns/host_resolver_proc.h
new file mode 100644
index 0000000..b4fc0fa
--- /dev/null
+++ b/net/dns/host_resolver_proc.h
@@ -0,0 +1,96 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_DNS_HOST_RESOLVER_PROC_H_
+#define NET_DNS_HOST_RESOLVER_PROC_H_
+
+#include <string>
+
+#include "base/memory/ref_counted.h"
+#include "net/base/address_family.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+class AddressList;
+
+// Interface for a getaddrinfo()-like procedure. This is used by unit-tests
+// to control the underlying resolutions in HostResolverImpl. HostResolverProcs
+// can be chained together; they fallback to the next procedure in the chain
+// by calling ResolveUsingPrevious().
+//
+// Note that implementations of HostResolverProc *MUST BE THREADSAFE*, since
+// the HostResolver implementation using them can be multi-threaded.
+class NET_EXPORT HostResolverProc
+ : public base::RefCountedThreadSafe<HostResolverProc> {
+ public:
+ explicit HostResolverProc(HostResolverProc* previous);
+
+ // Resolves |host| to an address list, restricting the results to addresses
+ // in |address_family|. If successful returns OK and fills |addrlist| with
+ // a list of socket addresses. Otherwise returns a network error code, and
+ // fills |os_error| with a more specific error if it was non-NULL.
+ virtual int Resolve(const std::string& host,
+ AddressFamily address_family,
+ HostResolverFlags host_resolver_flags,
+ AddressList* addrlist,
+ int* os_error) = 0;
+
+ protected:
+ friend class base::RefCountedThreadSafe<HostResolverProc>;
+
+ virtual ~HostResolverProc();
+
+ // Asks the fallback procedure (if set) to do the resolve.
+ int ResolveUsingPrevious(const std::string& host,
+ AddressFamily address_family,
+ HostResolverFlags host_resolver_flags,
+ AddressList* addrlist,
+ int* os_error);
+
+ private:
+ friend class HostResolverImpl;
+ friend class MockHostResolverBase;
+ friend class ScopedDefaultHostResolverProc;
+
+ // Sets the previous procedure in the chain. Aborts if this would result in a
+ // cycle.
+ void SetPreviousProc(HostResolverProc* proc);
+
+ // Sets the last procedure in the chain, i.e. appends |proc| to the end of the
+ // current chain. Aborts if this would result in a cycle.
+ void SetLastProc(HostResolverProc* proc);
+
+ // Returns the last procedure in the chain starting at |proc|. Will return
+ // NULL iff |proc| is NULL.
+ static HostResolverProc* GetLastProc(HostResolverProc* proc);
+
+ // Sets the default host resolver procedure that is used by HostResolverImpl.
+ // This can be used through ScopedDefaultHostResolverProc to set a catch-all
+ // DNS block in unit-tests (individual tests should use MockHostResolver to
+ // prevent hitting the network).
+ static HostResolverProc* SetDefault(HostResolverProc* proc);
+ static HostResolverProc* GetDefault();
+
+ scoped_refptr<HostResolverProc> previous_proc_;
+ static HostResolverProc* default_proc_;
+
+ DISALLOW_COPY_AND_ASSIGN(HostResolverProc);
+};
+
+// Resolves |host| to an address list, using the system's default host resolver.
+// (i.e. this calls out to getaddrinfo()). If successful returns OK and fills
+// |addrlist| with a list of socket addresses. Otherwise returns a
+// network error code, and fills |os_error| with a more specific error if it
+// was non-NULL.
+NET_EXPORT_PRIVATE int SystemHostResolverProc(
+ const std::string& host,
+ AddressFamily address_family,
+ HostResolverFlags host_resolver_flags,
+ AddressList* addrlist,
+ int* os_error);
+
+} // namespace net
+
+#endif // NET_DNS_HOST_RESOLVER_PROC_H_
diff --git a/net/dns/mapped_host_resolver.cc b/net/dns/mapped_host_resolver.cc
new file mode 100644
index 0000000..c905b34
--- /dev/null
+++ b/net/dns/mapped_host_resolver.cc
@@ -0,0 +1,67 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/dns/mapped_host_resolver.h"
+
+#include "base/string_util.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_util.h"
+
+namespace net {
+
+MappedHostResolver::MappedHostResolver(scoped_ptr<HostResolver> impl)
+ : impl_(impl.Pass()) {
+}
+
+MappedHostResolver::~MappedHostResolver() {
+}
+
+int MappedHostResolver::Resolve(const RequestInfo& original_info,
+ AddressList* addresses,
+ const CompletionCallback& callback,
+ RequestHandle* out_req,
+ const BoundNetLog& net_log) {
+ RequestInfo info = original_info;
+ int rv = ApplyRules(&info);
+ if (rv != OK)
+ return rv;
+
+ return impl_->Resolve(info, addresses, callback, out_req, net_log);
+}
+
+int MappedHostResolver::ResolveFromCache(const RequestInfo& original_info,
+ AddressList* addresses,
+ const BoundNetLog& net_log) {
+ RequestInfo info = original_info;
+ int rv = ApplyRules(&info);
+ if (rv != OK)
+ return rv;
+
+ return impl_->ResolveFromCache(info, addresses, net_log);
+}
+
+void MappedHostResolver::CancelRequest(RequestHandle req) {
+ impl_->CancelRequest(req);
+}
+
+void MappedHostResolver::ProbeIPv6Support() {
+ impl_->ProbeIPv6Support();
+}
+
+HostCache* MappedHostResolver::GetHostCache() {
+ return impl_->GetHostCache();
+}
+
+int MappedHostResolver::ApplyRules(RequestInfo* info) const {
+ HostPortPair host_port(info->host_port_pair());
+ if (rules_.RewriteHost(&host_port)) {
+ if (host_port.host() == "~NOTFOUND")
+ return ERR_NAME_NOT_RESOLVED;
+ info->set_host_port_pair(host_port);
+ }
+ return OK;
+}
+
+} // namespace net
diff --git a/net/dns/mapped_host_resolver.h b/net/dns/mapped_host_resolver.h
new file mode 100644
index 0000000..db64689
--- /dev/null
+++ b/net/dns/mapped_host_resolver.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_DNS_MAPPED_HOST_RESOLVER_H_
+#define NET_DNS_MAPPED_HOST_RESOLVER_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "net/base/host_mapping_rules.h"
+#include "net/base/net_export.h"
+#include "net/dns/host_resolver.h"
+
+namespace net {
+
+// This class wraps an existing HostResolver instance, but modifies the
+// request before passing it off to |impl|. This is different from
+// MockHostResolver which does the remapping at the HostResolverProc
+// layer, so it is able to preserve the effectiveness of the cache.
+class NET_EXPORT MappedHostResolver : public HostResolver {
+ public:
+ // Creates a MappedHostResolver that forwards all of its requests through
+ // |impl|.
+ explicit MappedHostResolver(scoped_ptr<HostResolver> impl);
+ virtual ~MappedHostResolver();
+
+ // Adds a rule to this mapper. The format of the rule can be one of:
+ //
+ // "MAP" <hostname_pattern> <replacement_host> [":" <replacement_port>]
+ // "EXCLUDE" <hostname_pattern>
+ //
+ // The <replacement_host> can be either a hostname, or an IP address literal,
+ // or "~NOTFOUND". If it is "~NOTFOUND" then all matched hostnames will fail
+ // to be resolved with ERR_NAME_NOT_RESOLVED.
+ //
+ // Returns true if the rule was successfully parsed and added.
+ bool AddRuleFromString(const std::string& rule_string) {
+ return rules_.AddRuleFromString(rule_string);
+ }
+
+ // Takes a comma separated list of rules, and assigns them to this resolver.
+ void SetRulesFromString(const std::string& rules_string) {
+ rules_.SetRulesFromString(rules_string);
+ }
+
+ // HostResolver methods:
+ virtual int Resolve(const RequestInfo& info,
+ AddressList* addresses,
+ const CompletionCallback& callback,
+ RequestHandle* out_req,
+ const BoundNetLog& net_log) OVERRIDE;
+ virtual int ResolveFromCache(const RequestInfo& info,
+ AddressList* addresses,
+ const BoundNetLog& net_log) OVERRIDE;
+ virtual void CancelRequest(RequestHandle req) OVERRIDE;
+ virtual void ProbeIPv6Support() OVERRIDE;
+ virtual HostCache* GetHostCache() OVERRIDE;
+
+ private:
+ // Modify the request |info| according to |rules_|. Returns either OK or
+ // the network error code that the hostname's resolution mapped to.
+ int ApplyRules(RequestInfo* info) const;
+
+ scoped_ptr<HostResolver> impl_;
+
+ HostMappingRules rules_;
+};
+
+} // namespace net
+
+#endif // NET_DNS_MAPPED_HOST_RESOLVER_H_
diff --git a/net/dns/mapped_host_resolver_unittest.cc b/net/dns/mapped_host_resolver_unittest.cc
new file mode 100644
index 0000000..8da95e1
--- /dev/null
+++ b/net/dns/mapped_host_resolver_unittest.cc
@@ -0,0 +1,219 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/dns/mapped_host_resolver.h"
+
+#include "net/base/address_list.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_log.h"
+#include "net/base/net_util.h"
+#include "net/base/test_completion_callback.h"
+#include "net/dns/mock_host_resolver.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+std::string FirstAddress(const AddressList& address_list) {
+ if (address_list.empty())
+ return "";
+ return address_list.front().ToString();
+}
+
+TEST(MappedHostResolverTest, Inclusion) {
+ // Create a mock host resolver, with specific hostname to IP mappings.
+ scoped_ptr<MockHostResolver> resolver_impl(new MockHostResolver());
+ resolver_impl->rules()->AddSimulatedFailure("*google.com");
+ resolver_impl->rules()->AddRule("baz.com", "192.168.1.5");
+ resolver_impl->rules()->AddRule("foo.com", "192.168.1.8");
+ resolver_impl->rules()->AddRule("proxy", "192.168.1.11");
+
+ // Create a remapped resolver that uses |resolver_impl|.
+ scoped_ptr<MappedHostResolver> resolver(
+ new MappedHostResolver(resolver_impl.PassAs<HostResolver>()));
+
+ int rv;
+ AddressList address_list;
+
+ // Try resolving "www.google.com:80". There are no mappings yet, so this
+ // hits |resolver_impl| and fails.
+ TestCompletionCallback callback;
+ rv = resolver->Resolve(HostResolver::RequestInfo(
+ HostPortPair("www.google.com", 80)),
+ &address_list, callback.callback(), NULL,
+ BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ rv = callback.WaitForResult();
+ EXPECT_EQ(ERR_NAME_NOT_RESOLVED, rv);
+
+ // Remap *.google.com to baz.com.
+ EXPECT_TRUE(resolver->AddRuleFromString("map *.google.com baz.com"));
+
+ // Try resolving "www.google.com:80". Should be remapped to "baz.com:80".
+ rv = resolver->Resolve(HostResolver::RequestInfo(
+ HostPortPair("www.google.com", 80)),
+ &address_list, callback.callback(), NULL,
+ BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+ EXPECT_EQ("192.168.1.5:80", FirstAddress(address_list));
+
+ // Try resolving "foo.com:77". This will NOT be remapped, so result
+ // is "foo.com:77".
+ rv = resolver->Resolve(HostResolver::RequestInfo(HostPortPair("foo.com", 77)),
+ &address_list, callback.callback(), NULL,
+ BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+ EXPECT_EQ("192.168.1.8:77", FirstAddress(address_list));
+
+ // Remap "*.org" to "proxy:99".
+ EXPECT_TRUE(resolver->AddRuleFromString("Map *.org proxy:99"));
+
+ // Try resolving "chromium.org:61". Should be remapped to "proxy:99".
+ rv = resolver->Resolve(HostResolver::RequestInfo
+ (HostPortPair("chromium.org", 61)),
+ &address_list, callback.callback(), NULL,
+ BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+ EXPECT_EQ("192.168.1.11:99", FirstAddress(address_list));
+}
+
+// Tests that exclusions are respected.
+TEST(MappedHostResolverTest, Exclusion) {
+ // Create a mock host resolver, with specific hostname to IP mappings.
+ scoped_ptr<MockHostResolver> resolver_impl(new MockHostResolver());
+ resolver_impl->rules()->AddRule("baz", "192.168.1.5");
+ resolver_impl->rules()->AddRule("www.google.com", "192.168.1.3");
+
+ // Create a remapped resolver that uses |resolver_impl|.
+ scoped_ptr<MappedHostResolver> resolver(
+ new MappedHostResolver(resolver_impl.PassAs<HostResolver>()));
+
+ int rv;
+ AddressList address_list;
+ TestCompletionCallback callback;
+
+ // Remap "*.com" to "baz".
+ EXPECT_TRUE(resolver->AddRuleFromString("map *.com baz"));
+
+ // Add an exclusion for "*.google.com".
+ EXPECT_TRUE(resolver->AddRuleFromString("EXCLUDE *.google.com"));
+
+ // Try resolving "www.google.com". Should not be remapped due to exclusion).
+ rv = resolver->Resolve(HostResolver::RequestInfo(
+ HostPortPair("www.google.com", 80)),
+ &address_list, callback.callback(), NULL,
+ BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+ EXPECT_EQ("192.168.1.3:80", FirstAddress(address_list));
+
+ // Try resolving "chrome.com:80". Should be remapped to "baz:80".
+ rv = resolver->Resolve(HostResolver::RequestInfo(
+ HostPortPair("chrome.com", 80)),
+ &address_list, callback.callback(), NULL,
+ BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+ EXPECT_EQ("192.168.1.5:80", FirstAddress(address_list));
+}
+
+TEST(MappedHostResolverTest, SetRulesFromString) {
+ // Create a mock host resolver, with specific hostname to IP mappings.
+ scoped_ptr<MockHostResolver> resolver_impl(new MockHostResolver());
+ resolver_impl->rules()->AddRule("baz", "192.168.1.7");
+ resolver_impl->rules()->AddRule("bar", "192.168.1.9");
+
+ // Create a remapped resolver that uses |resolver_impl|.
+ scoped_ptr<MappedHostResolver> resolver(
+ new MappedHostResolver(resolver_impl.PassAs<HostResolver>()));
+
+ int rv;
+ AddressList address_list;
+ TestCompletionCallback callback;
+
+ // Remap "*.com" to "baz", and *.net to "bar:60".
+ resolver->SetRulesFromString("map *.com baz , map *.net bar:60");
+
+ // Try resolving "www.google.com". Should be remapped to "baz".
+ rv = resolver->Resolve(HostResolver::RequestInfo(
+ HostPortPair("www.google.com", 80)),
+ &address_list, callback.callback(), NULL,
+ BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+ EXPECT_EQ("192.168.1.7:80", FirstAddress(address_list));
+
+ // Try resolving "chrome.net:80". Should be remapped to "bar:60".
+ rv = resolver->Resolve(HostResolver::RequestInfo(
+ HostPortPair("chrome.net", 80)),
+ &address_list, callback.callback(), NULL,
+ BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+ EXPECT_EQ("192.168.1.9:60", FirstAddress(address_list));
+}
+
+// Parsing bad rules should silently discard the rule (and never crash).
+TEST(MappedHostResolverTest, ParseInvalidRules) {
+ scoped_ptr<MappedHostResolver> resolver(
+ new MappedHostResolver(scoped_ptr<HostResolver>()));
+
+ EXPECT_FALSE(resolver->AddRuleFromString("xyz"));
+ EXPECT_FALSE(resolver->AddRuleFromString(""));
+ EXPECT_FALSE(resolver->AddRuleFromString(" "));
+ EXPECT_FALSE(resolver->AddRuleFromString("EXCLUDE"));
+ EXPECT_FALSE(resolver->AddRuleFromString("EXCLUDE foo bar"));
+ EXPECT_FALSE(resolver->AddRuleFromString("INCLUDE"));
+ EXPECT_FALSE(resolver->AddRuleFromString("INCLUDE x"));
+ EXPECT_FALSE(resolver->AddRuleFromString("INCLUDE x :10"));
+}
+
+// Test mapping hostnames to resolving failures.
+TEST(MappedHostResolverTest, MapToError) {
+ scoped_ptr<MockHostResolver> resolver_impl(new MockHostResolver());
+ resolver_impl->rules()->AddRule("*", "192.168.1.5");
+
+ scoped_ptr<MappedHostResolver> resolver(
+ new MappedHostResolver(resolver_impl.PassAs<HostResolver>()));
+
+ int rv;
+ AddressList address_list;
+
+ // Remap *.google.com to resolving failures.
+ EXPECT_TRUE(resolver->AddRuleFromString("MAP *.google.com ~NOTFOUND"));
+
+ // Try resolving www.google.com --> Should give an error.
+ TestCompletionCallback callback1;
+ rv = resolver->Resolve(HostResolver::RequestInfo(
+ HostPortPair("www.google.com", 80)),
+ &address_list, callback1.callback(), NULL,
+ BoundNetLog());
+ EXPECT_EQ(ERR_NAME_NOT_RESOLVED, rv);
+
+ // Try resolving www.foo.com --> Should succeed.
+ TestCompletionCallback callback2;
+ rv = resolver->Resolve(HostResolver::RequestInfo(
+ HostPortPair("www.foo.com", 80)),
+ &address_list, callback2.callback(), NULL,
+ BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ rv = callback2.WaitForResult();
+ EXPECT_EQ(OK, rv);
+ EXPECT_EQ("192.168.1.5:80", FirstAddress(address_list));
+}
+
+} // namespace
+
+} // namespace net
diff --git a/net/dns/mock_host_resolver.cc b/net/dns/mock_host_resolver.cc
new file mode 100644
index 0000000..fb03836
--- /dev/null
+++ b/net/dns/mock_host_resolver.cc
@@ -0,0 +1,417 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/dns/mock_host_resolver.h"
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop.h"
+#include "base/stl_util.h"
+#include "base/string_util.h"
+#include "base/strings/string_split.h"
+#include "base/threading/platform_thread.h"
+#include "net/base/host_cache.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_util.h"
+#include "net/base/test_completion_callback.h"
+#if defined(OS_WIN)
+#include "net/base/winsock_init.h"
+#endif
+
+namespace net {
+
+namespace {
+
+// Cache size for the MockCachingHostResolver.
+const unsigned kMaxCacheEntries = 100;
+// TTL for the successful resolutions. Failures are not cached.
+const unsigned kCacheEntryTTLSeconds = 60;
+
+} // namespace
+
+int ParseAddressList(const std::string& host_list,
+ const std::string& canonical_name,
+ AddressList* addrlist) {
+ *addrlist = AddressList();
+ std::vector<std::string> addresses;
+ base::SplitString(host_list, ',', &addresses);
+ addrlist->set_canonical_name(canonical_name);
+ for (size_t index = 0; index < addresses.size(); ++index) {
+ IPAddressNumber ip_number;
+ if (!ParseIPLiteralToNumber(addresses[index], &ip_number)) {
+ LOG(WARNING) << "Not a supported IP literal: " << addresses[index];
+ return ERR_UNEXPECTED;
+ }
+ addrlist->push_back(IPEndPoint(ip_number, -1));
+ }
+ return OK;
+}
+
+struct MockHostResolverBase::Request {
+ Request(const RequestInfo& req_info,
+ AddressList* addr,
+ const CompletionCallback& cb)
+ : info(req_info), addresses(addr), callback(cb) {}
+ RequestInfo info;
+ AddressList* addresses;
+ CompletionCallback callback;
+};
+
+MockHostResolverBase::~MockHostResolverBase() {
+ STLDeleteValues(&requests_);
+}
+
+int MockHostResolverBase::Resolve(const RequestInfo& info,
+ AddressList* addresses,
+ const CompletionCallback& callback,
+ RequestHandle* handle,
+ const BoundNetLog& net_log) {
+ DCHECK(CalledOnValidThread());
+ num_resolve_++;
+ size_t id = next_request_id_++;
+ int rv = ResolveFromIPLiteralOrCache(info, addresses);
+ if (rv != ERR_DNS_CACHE_MISS) {
+ return rv;
+ }
+ if (synchronous_mode_) {
+ return ResolveProc(id, info, addresses);
+ }
+ // Store the request for asynchronous resolution
+ Request* req = new Request(info, addresses, callback);
+ requests_[id] = req;
+ if (handle)
+ *handle = reinterpret_cast<RequestHandle>(id);
+
+ if (!ondemand_mode_) {
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&MockHostResolverBase::ResolveNow, AsWeakPtr(), id));
+ }
+
+ return ERR_IO_PENDING;
+}
+
+int MockHostResolverBase::ResolveFromCache(const RequestInfo& info,
+ AddressList* addresses,
+ const BoundNetLog& net_log) {
+ num_resolve_from_cache_++;
+ DCHECK(CalledOnValidThread());
+ next_request_id_++;
+ int rv = ResolveFromIPLiteralOrCache(info, addresses);
+ return rv;
+}
+
+void MockHostResolverBase::CancelRequest(RequestHandle handle) {
+ DCHECK(CalledOnValidThread());
+ size_t id = reinterpret_cast<size_t>(handle);
+ RequestMap::iterator it = requests_.find(id);
+ if (it != requests_.end()) {
+ scoped_ptr<Request> req(it->second);
+ requests_.erase(it);
+ } else {
+ NOTREACHED() << "CancelRequest must NOT be called after request is "
+ "complete or canceled.";
+ }
+}
+
+HostCache* MockHostResolverBase::GetHostCache() {
+ return cache_.get();
+}
+
+void MockHostResolverBase::ResolveAllPending() {
+ DCHECK(CalledOnValidThread());
+ DCHECK(ondemand_mode_);
+ for (RequestMap::iterator i = requests_.begin(); i != requests_.end(); ++i) {
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&MockHostResolverBase::ResolveNow, AsWeakPtr(), i->first));
+ }
+}
+
+// start id from 1 to distinguish from NULL RequestHandle
+MockHostResolverBase::MockHostResolverBase(bool use_caching)
+ : synchronous_mode_(false),
+ ondemand_mode_(false),
+ next_request_id_(1),
+ num_resolve_(0),
+ num_resolve_from_cache_(0) {
+ rules_ = CreateCatchAllHostResolverProc();
+
+ if (use_caching) {
+ cache_.reset(new HostCache(kMaxCacheEntries));
+ }
+}
+
+int MockHostResolverBase::ResolveFromIPLiteralOrCache(const RequestInfo& info,
+ AddressList* addresses) {
+ IPAddressNumber ip;
+ if (ParseIPLiteralToNumber(info.hostname(), &ip)) {
+ *addresses = AddressList::CreateFromIPAddress(ip, info.port());
+ if (info.host_resolver_flags() & HOST_RESOLVER_CANONNAME)
+ addresses->SetDefaultCanonicalName();
+ return OK;
+ }
+ int rv = ERR_DNS_CACHE_MISS;
+ if (cache_.get() && info.allow_cached_response()) {
+ HostCache::Key key(info.hostname(),
+ info.address_family(),
+ info.host_resolver_flags());
+ const HostCache::Entry* entry = cache_->Lookup(key, base::TimeTicks::Now());
+ if (entry) {
+ rv = entry->error;
+ if (rv == OK)
+ *addresses = AddressList::CopyWithPort(entry->addrlist, info.port());
+ }
+ }
+ return rv;
+}
+
+int MockHostResolverBase::ResolveProc(size_t id,
+ const RequestInfo& info,
+ AddressList* addresses) {
+ AddressList addr;
+ int rv = rules_->Resolve(info.hostname(),
+ info.address_family(),
+ info.host_resolver_flags(),
+ &addr,
+ NULL);
+ if (cache_.get()) {
+ HostCache::Key key(info.hostname(),
+ info.address_family(),
+ info.host_resolver_flags());
+ // Storing a failure with TTL 0 so that it overwrites previous value.
+ base::TimeDelta ttl;
+ if (rv == OK)
+ ttl = base::TimeDelta::FromSeconds(kCacheEntryTTLSeconds);
+ cache_->Set(key, HostCache::Entry(rv, addr), base::TimeTicks::Now(), ttl);
+ }
+ if (rv == OK)
+ *addresses = AddressList::CopyWithPort(addr, info.port());
+ return rv;
+}
+
+void MockHostResolverBase::ResolveNow(size_t id) {
+ RequestMap::iterator it = requests_.find(id);
+ if (it == requests_.end())
+ return; // was canceled
+
+ scoped_ptr<Request> req(it->second);
+ requests_.erase(it);
+ int rv = ResolveProc(id, req->info, req->addresses);
+ if (!req->callback.is_null())
+ req->callback.Run(rv);
+}
+
+//-----------------------------------------------------------------------------
+
+struct RuleBasedHostResolverProc::Rule {
+ enum ResolverType {
+ kResolverTypeFail,
+ kResolverTypeSystem,
+ kResolverTypeIPLiteral,
+ };
+
+ ResolverType resolver_type;
+ std::string host_pattern;
+ AddressFamily address_family;
+ HostResolverFlags host_resolver_flags;
+ std::string replacement;
+ std::string canonical_name;
+ int latency_ms; // In milliseconds.
+
+ Rule(ResolverType resolver_type,
+ const std::string& host_pattern,
+ AddressFamily address_family,
+ HostResolverFlags host_resolver_flags,
+ const std::string& replacement,
+ const std::string& canonical_name,
+ int latency_ms)
+ : resolver_type(resolver_type),
+ host_pattern(host_pattern),
+ address_family(address_family),
+ host_resolver_flags(host_resolver_flags),
+ replacement(replacement),
+ canonical_name(canonical_name),
+ latency_ms(latency_ms) {}
+};
+
+RuleBasedHostResolverProc::RuleBasedHostResolverProc(HostResolverProc* previous)
+ : HostResolverProc(previous) {
+}
+
+void RuleBasedHostResolverProc::AddRule(const std::string& host_pattern,
+ const std::string& replacement) {
+ AddRuleForAddressFamily(host_pattern, ADDRESS_FAMILY_UNSPECIFIED,
+ replacement);
+}
+
+void RuleBasedHostResolverProc::AddRuleForAddressFamily(
+ const std::string& host_pattern,
+ AddressFamily address_family,
+ const std::string& replacement) {
+ DCHECK(!replacement.empty());
+ HostResolverFlags flags = HOST_RESOLVER_LOOPBACK_ONLY |
+ HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6;
+ Rule rule(Rule::kResolverTypeSystem, host_pattern, address_family, flags,
+ replacement, "", 0);
+ rules_.push_back(rule);
+}
+
+void RuleBasedHostResolverProc::AddIPLiteralRule(
+ const std::string& host_pattern,
+ const std::string& ip_literal,
+ const std::string& canonical_name) {
+ // Literals are always resolved to themselves by HostResolverImpl,
+ // consequently we do not support remapping them.
+ IPAddressNumber ip_number;
+ DCHECK(!ParseIPLiteralToNumber(host_pattern, &ip_number));
+ HostResolverFlags flags = HOST_RESOLVER_LOOPBACK_ONLY |
+ HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6;
+ if (!canonical_name.empty())
+ flags |= HOST_RESOLVER_CANONNAME;
+ Rule rule(Rule::kResolverTypeIPLiteral, host_pattern,
+ ADDRESS_FAMILY_UNSPECIFIED, flags, ip_literal, canonical_name,
+ 0);
+ rules_.push_back(rule);
+}
+
+void RuleBasedHostResolverProc::AddRuleWithLatency(
+ const std::string& host_pattern,
+ const std::string& replacement,
+ int latency_ms) {
+ DCHECK(!replacement.empty());
+ HostResolverFlags flags = HOST_RESOLVER_LOOPBACK_ONLY |
+ HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6;
+ Rule rule(Rule::kResolverTypeSystem, host_pattern, ADDRESS_FAMILY_UNSPECIFIED,
+ flags, replacement, "", latency_ms);
+ rules_.push_back(rule);
+}
+
+void RuleBasedHostResolverProc::AllowDirectLookup(
+ const std::string& host_pattern) {
+ HostResolverFlags flags = HOST_RESOLVER_LOOPBACK_ONLY |
+ HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6;
+ Rule rule(Rule::kResolverTypeSystem, host_pattern, ADDRESS_FAMILY_UNSPECIFIED,
+ flags, "", "", 0);
+ rules_.push_back(rule);
+}
+
+void RuleBasedHostResolverProc::AddSimulatedFailure(
+ const std::string& host_pattern) {
+ HostResolverFlags flags = HOST_RESOLVER_LOOPBACK_ONLY |
+ HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6;
+ Rule rule(Rule::kResolverTypeFail, host_pattern, ADDRESS_FAMILY_UNSPECIFIED,
+ flags, "", "", 0);
+ rules_.push_back(rule);
+}
+
+void RuleBasedHostResolverProc::ClearRules() {
+ rules_.clear();
+}
+
+int RuleBasedHostResolverProc::Resolve(const std::string& host,
+ AddressFamily address_family,
+ HostResolverFlags host_resolver_flags,
+ AddressList* addrlist,
+ int* os_error) {
+ RuleList::iterator r;
+ for (r = rules_.begin(); r != rules_.end(); ++r) {
+ bool matches_address_family =
+ r->address_family == ADDRESS_FAMILY_UNSPECIFIED ||
+ r->address_family == address_family;
+ // Flags match if all of the bitflags in host_resolver_flags are enabled
+ // in the rule's host_resolver_flags. However, the rule may have additional
+ // flags specified, in which case the flags should still be considered a
+ // match.
+ bool matches_flags = (r->host_resolver_flags & host_resolver_flags) ==
+ host_resolver_flags;
+ if (matches_flags && matches_address_family &&
+ MatchPattern(host, r->host_pattern)) {
+ if (r->latency_ms != 0) {
+ base::PlatformThread::Sleep(
+ base::TimeDelta::FromMilliseconds(r->latency_ms));
+ }
+
+ // Remap to a new host.
+ const std::string& effective_host =
+ r->replacement.empty() ? host : r->replacement;
+
+ // Apply the resolving function to the remapped hostname.
+ switch (r->resolver_type) {
+ case Rule::kResolverTypeFail:
+ return ERR_NAME_NOT_RESOLVED;
+ case Rule::kResolverTypeSystem:
+#if defined(OS_WIN)
+ net::EnsureWinsockInit();
+#endif
+ return SystemHostResolverProc(effective_host,
+ address_family,
+ host_resolver_flags,
+ addrlist, os_error);
+ case Rule::kResolverTypeIPLiteral:
+ return ParseAddressList(effective_host,
+ r->canonical_name,
+ addrlist);
+ default:
+ NOTREACHED();
+ return ERR_UNEXPECTED;
+ }
+ }
+ }
+ return ResolveUsingPrevious(host, address_family,
+ host_resolver_flags, addrlist, os_error);
+}
+
+RuleBasedHostResolverProc::~RuleBasedHostResolverProc() {
+}
+
+RuleBasedHostResolverProc* CreateCatchAllHostResolverProc() {
+ RuleBasedHostResolverProc* catchall = new RuleBasedHostResolverProc(NULL);
+ catchall->AddIPLiteralRule("*", "127.0.0.1", "localhost");
+
+ // Next add a rules-based layer the use controls.
+ return new RuleBasedHostResolverProc(catchall);
+}
+
+//-----------------------------------------------------------------------------
+
+int HangingHostResolver::Resolve(const RequestInfo& info,
+ AddressList* addresses,
+ const CompletionCallback& callback,
+ RequestHandle* out_req,
+ const BoundNetLog& net_log) {
+ return ERR_IO_PENDING;
+}
+
+int HangingHostResolver::ResolveFromCache(const RequestInfo& info,
+ AddressList* addresses,
+ const BoundNetLog& net_log) {
+ return ERR_DNS_CACHE_MISS;
+}
+
+//-----------------------------------------------------------------------------
+
+ScopedDefaultHostResolverProc::ScopedDefaultHostResolverProc() {}
+
+ScopedDefaultHostResolverProc::ScopedDefaultHostResolverProc(
+ HostResolverProc* proc) {
+ Init(proc);
+}
+
+ScopedDefaultHostResolverProc::~ScopedDefaultHostResolverProc() {
+ HostResolverProc* old_proc = HostResolverProc::SetDefault(previous_proc_);
+ // The lifetimes of multiple instances must be nested.
+ CHECK_EQ(old_proc, current_proc_);
+}
+
+void ScopedDefaultHostResolverProc::Init(HostResolverProc* proc) {
+ current_proc_ = proc;
+ previous_proc_ = HostResolverProc::SetDefault(current_proc_);
+ current_proc_->SetLastProc(previous_proc_);
+}
+
+} // namespace net
diff --git a/net/dns/mock_host_resolver.h b/net/dns/mock_host_resolver.h
new file mode 100644
index 0000000..d22e800c
--- /dev/null
+++ b/net/dns/mock_host_resolver.h
@@ -0,0 +1,254 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_DNS_MOCK_HOST_RESOLVER_H_
+#define NET_DNS_MOCK_HOST_RESOLVER_H_
+
+#include <list>
+#include <map>
+
+#include "base/memory/weak_ptr.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/non_thread_safe.h"
+#include "net/dns/host_resolver.h"
+#include "net/dns/host_resolver_proc.h"
+
+namespace net {
+
+class HostCache;
+class RuleBasedHostResolverProc;
+
+// Fills |*addrlist| with a socket address for |host_list| which should be a
+// comma-separated list of IPv4 or IPv6 literal(s) without enclosing brackets.
+// If |canonical_name| is non-empty it is used as the DNS canonical name for
+// the host. Returns OK on success, ERR_UNEXPECTED otherwise.
+int ParseAddressList(const std::string& host_list,
+ const std::string& canonical_name,
+ AddressList* addrlist);
+
+// In most cases, it is important that unit tests avoid relying on making actual
+// DNS queries since the resulting tests can be flaky, especially if the network
+// is unreliable for some reason. To simplify writing tests that avoid making
+// actual DNS queries, pass a MockHostResolver as the HostResolver dependency.
+// The socket addresses returned can be configured using the
+// RuleBasedHostResolverProc:
+//
+// host_resolver->rules()->AddRule("foo.com", "1.2.3.4");
+// host_resolver->rules()->AddRule("bar.com", "2.3.4.5");
+//
+// The above rules define a static mapping from hostnames to IP address
+// literals. The first parameter to AddRule specifies a host pattern to match
+// against, and the second parameter indicates what value should be used to
+// replace the given hostname. So, the following is also supported:
+//
+// host_mapper->AddRule("*.com", "127.0.0.1");
+//
+// Replacement doesn't have to be string representing an IP address. It can
+// re-map one hostname to another as well.
+//
+// By default, MockHostResolvers include a single rule that maps all hosts to
+// 127.0.0.1.
+
+// Base class shared by MockHostResolver and MockCachingHostResolver.
+class MockHostResolverBase : public HostResolver,
+ public base::SupportsWeakPtr<MockHostResolverBase>,
+ public base::NonThreadSafe {
+ public:
+ virtual ~MockHostResolverBase();
+
+ RuleBasedHostResolverProc* rules() { return rules_; }
+ void set_rules(RuleBasedHostResolverProc* rules) { rules_ = rules; }
+
+ // Controls whether resolutions complete synchronously or asynchronously.
+ void set_synchronous_mode(bool is_synchronous) {
+ synchronous_mode_ = is_synchronous;
+ }
+
+ // Asynchronous requests are automatically resolved by default.
+ // If set_ondemand_mode() is set then Resolve() returns IO_PENDING and
+ // ResolveAllPending() must be explicitly invoked to resolve all requests
+ // that are pending.
+ void set_ondemand_mode(bool is_ondemand) {
+ ondemand_mode_ = is_ondemand;
+ }
+
+ // HostResolver methods:
+ virtual int Resolve(const RequestInfo& info,
+ AddressList* addresses,
+ const CompletionCallback& callback,
+ RequestHandle* out_req,
+ const BoundNetLog& net_log) OVERRIDE;
+ virtual int ResolveFromCache(const RequestInfo& info,
+ AddressList* addresses,
+ const BoundNetLog& net_log) OVERRIDE;
+ virtual void CancelRequest(RequestHandle req) OVERRIDE;
+ virtual HostCache* GetHostCache() OVERRIDE;
+
+ // Resolves all pending requests. It is only valid to invoke this if
+ // set_ondemand_mode was set before. The requests are resolved asynchronously,
+ // after this call returns.
+ void ResolveAllPending();
+
+ // Returns true if there are pending requests that can be resolved by invoking
+ // ResolveAllPending().
+ bool has_pending_requests() const { return !requests_.empty(); }
+
+ // The number of times that Resolve() has been called.
+ size_t num_resolve() const {
+ return num_resolve_;
+ }
+
+ // The number of times that ResolveFromCache() has been called.
+ size_t num_resolve_from_cache() const {
+ return num_resolve_from_cache_;
+ }
+
+ protected:
+ explicit MockHostResolverBase(bool use_caching);
+
+ private:
+ struct Request;
+ typedef std::map<size_t, Request*> RequestMap;
+
+ // Resolve as IP or from |cache_| return cached error or
+ // DNS_CACHE_MISS if failed.
+ int ResolveFromIPLiteralOrCache(const RequestInfo& info,
+ AddressList* addresses);
+ // Resolve via |proc_|.
+ int ResolveProc(size_t id, const RequestInfo& info, AddressList* addresses);
+ // Resolve request stored in |requests_|. Pass rv to callback.
+ void ResolveNow(size_t id);
+
+ bool synchronous_mode_;
+ bool ondemand_mode_;
+ scoped_refptr<RuleBasedHostResolverProc> rules_;
+ scoped_ptr<HostCache> cache_;
+ RequestMap requests_;
+ size_t next_request_id_;
+
+ size_t num_resolve_;
+ size_t num_resolve_from_cache_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockHostResolverBase);
+};
+
+class MockHostResolver : public MockHostResolverBase {
+ public:
+ MockHostResolver() : MockHostResolverBase(false /*use_caching*/) {}
+ virtual ~MockHostResolver() {}
+};
+
+// Same as MockHostResolver, except internally it uses a host-cache.
+//
+// Note that tests are advised to use MockHostResolver instead, since it is
+// more predictable. (MockHostResolver also can be put into synchronous
+// operation mode in case that is what you needed from the caching version).
+class MockCachingHostResolver : public MockHostResolverBase {
+ public:
+ MockCachingHostResolver() : MockHostResolverBase(true /*use_caching*/) {}
+ virtual ~MockCachingHostResolver() {}
+};
+
+// RuleBasedHostResolverProc applies a set of rules to map a host string to
+// a replacement host string. It then uses the system host resolver to return
+// a socket address. Generally the replacement should be an IPv4 literal so
+// there is no network dependency.
+class RuleBasedHostResolverProc : public HostResolverProc {
+ public:
+ explicit RuleBasedHostResolverProc(HostResolverProc* previous);
+
+ // Any hostname matching the given pattern will be replaced with the given
+ // replacement value. Usually, replacement should be an IP address literal.
+ void AddRule(const std::string& host_pattern,
+ const std::string& replacement);
+
+ // Same as AddRule(), but further restricts to |address_family|.
+ void AddRuleForAddressFamily(const std::string& host_pattern,
+ AddressFamily address_family,
+ const std::string& replacement);
+
+ // Same as AddRule(), but the replacement is expected to be an IPv4 or IPv6
+ // literal. This can be used in place of AddRule() to bypass the system's
+ // host resolver (the address list will be constructed manually).
+ // If |canonical_name| is non-empty, it is copied to the resulting AddressList
+ // but does not impact DNS resolution.
+ // |ip_literal| can be a single IP address like "192.168.1.1" or a comma
+ // separated list of IP addresses, like "::1,192:168.1.2".
+ void AddIPLiteralRule(const std::string& host_pattern,
+ const std::string& ip_literal,
+ const std::string& canonical_name);
+
+ void AddRuleWithLatency(const std::string& host_pattern,
+ const std::string& replacement,
+ int latency_ms);
+
+ // Make sure that |host| will not be re-mapped or even processed by underlying
+ // host resolver procedures. It can also be a pattern.
+ void AllowDirectLookup(const std::string& host);
+
+ // Simulate a lookup failure for |host| (it also can be a pattern).
+ void AddSimulatedFailure(const std::string& host);
+
+ // Deletes all the rules that have been added.
+ void ClearRules();
+
+ // HostResolverProc methods:
+ virtual int Resolve(const std::string& host,
+ AddressFamily address_family,
+ HostResolverFlags host_resolver_flags,
+ AddressList* addrlist,
+ int* os_error) OVERRIDE;
+
+ private:
+ struct Rule;
+ typedef std::list<Rule> RuleList;
+
+ virtual ~RuleBasedHostResolverProc();
+
+ RuleList rules_;
+};
+
+// Create rules that map all requests to localhost.
+RuleBasedHostResolverProc* CreateCatchAllHostResolverProc();
+
+// HangingHostResolver never completes its |Resolve| request.
+class HangingHostResolver : public HostResolver {
+ public:
+ virtual int Resolve(const RequestInfo& info,
+ AddressList* addresses,
+ const CompletionCallback& callback,
+ RequestHandle* out_req,
+ const BoundNetLog& net_log) OVERRIDE;
+ virtual int ResolveFromCache(const RequestInfo& info,
+ AddressList* addresses,
+ const BoundNetLog& net_log) OVERRIDE;
+ virtual void CancelRequest(RequestHandle req) OVERRIDE {}
+};
+
+// This class sets the default HostResolverProc for a particular scope. The
+// chain of resolver procs starting at |proc| is placed in front of any existing
+// default resolver proc(s). This means that if multiple
+// ScopedDefaultHostResolverProcs are declared, then resolving will start with
+// the procs given to the last-allocated one, then fall back to the procs given
+// to the previously-allocated one, and so forth.
+//
+// NOTE: Only use this as a catch-all safety net. Individual tests should use
+// MockHostResolver.
+class ScopedDefaultHostResolverProc {
+ public:
+ ScopedDefaultHostResolverProc();
+ explicit ScopedDefaultHostResolverProc(HostResolverProc* proc);
+
+ ~ScopedDefaultHostResolverProc();
+
+ void Init(HostResolverProc* proc);
+
+ private:
+ scoped_refptr<HostResolverProc> current_proc_;
+ scoped_refptr<HostResolverProc> previous_proc_;
+};
+
+} // namespace net
+
+#endif // NET_DNS_MOCK_HOST_RESOLVER_H_
diff --git a/net/dns/single_request_host_resolver.cc b/net/dns/single_request_host_resolver.cc
new file mode 100644
index 0000000..e926e86
--- /dev/null
+++ b/net/dns/single_request_host_resolver.cc
@@ -0,0 +1,77 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/dns/single_request_host_resolver.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "net/base/net_errors.h"
+
+namespace net {
+
+SingleRequestHostResolver::SingleRequestHostResolver(HostResolver* resolver)
+ : resolver_(resolver),
+ cur_request_(NULL),
+ ALLOW_THIS_IN_INITIALIZER_LIST(callback_(
+ base::Bind(&SingleRequestHostResolver::OnResolveCompletion,
+ base::Unretained(this)))) {
+ DCHECK(resolver_ != NULL);
+}
+
+SingleRequestHostResolver::~SingleRequestHostResolver() {
+ Cancel();
+}
+
+int SingleRequestHostResolver::Resolve(
+ const HostResolver::RequestInfo& info, AddressList* addresses,
+ const CompletionCallback& callback, const BoundNetLog& net_log) {
+ DCHECK(addresses);
+ DCHECK_EQ(false, callback.is_null());
+ DCHECK(cur_request_callback_.is_null()) << "resolver already in use";
+
+ HostResolver::RequestHandle request = NULL;
+
+ // We need to be notified of completion before |callback| is called, so that
+ // we can clear out |cur_request_*|.
+ CompletionCallback transient_callback =
+ callback.is_null() ? CompletionCallback() : callback_;
+
+ int rv = resolver_->Resolve(
+ info, addresses, transient_callback, &request, net_log);
+
+ if (rv == ERR_IO_PENDING) {
+ DCHECK_EQ(false, callback.is_null());
+ // Cleared in OnResolveCompletion().
+ cur_request_ = request;
+ cur_request_callback_ = callback;
+ }
+
+ return rv;
+}
+
+void SingleRequestHostResolver::Cancel() {
+ if (!cur_request_callback_.is_null()) {
+ resolver_->CancelRequest(cur_request_);
+ cur_request_ = NULL;
+ cur_request_callback_.Reset();
+ }
+}
+
+void SingleRequestHostResolver::OnResolveCompletion(int result) {
+ DCHECK(cur_request_);
+ DCHECK_EQ(false, cur_request_callback_.is_null());
+
+ CompletionCallback callback = cur_request_callback_;
+
+ // Clear the outstanding request information.
+ cur_request_ = NULL;
+ cur_request_callback_.Reset();
+
+ // Call the user's original callback.
+ callback.Run(result);
+}
+
+} // namespace net
diff --git a/net/dns/single_request_host_resolver.h b/net/dns/single_request_host_resolver.h
new file mode 100644
index 0000000..52d0132
--- /dev/null
+++ b/net/dns/single_request_host_resolver.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_DNS_SINGLE_REQUEST_HOST_RESOLVER_H_
+#define NET_DNS_SINGLE_REQUEST_HOST_RESOLVER_H_
+
+#include "net/dns/host_resolver.h"
+
+namespace net {
+
+// This class represents the task of resolving a hostname (or IP address
+// literal) to an AddressList object. It wraps HostResolver to resolve only a
+// single hostname at a time and cancels this request when going out of scope.
+class NET_EXPORT SingleRequestHostResolver {
+ public:
+ // |resolver| must remain valid for the lifetime of |this|.
+ explicit SingleRequestHostResolver(HostResolver* resolver);
+
+ // If a completion callback is pending when the resolver is destroyed, the
+ // host resolution is cancelled, and the completion callback will not be
+ // called.
+ ~SingleRequestHostResolver();
+
+ // Resolves the given hostname (or IP address literal), filling out the
+ // |addresses| object upon success. See HostResolver::Resolve() for details.
+ int Resolve(const HostResolver::RequestInfo& info,
+ AddressList* addresses,
+ const CompletionCallback& callback,
+ const BoundNetLog& net_log);
+
+ // Cancels the in-progress request, if any. This prevents the callback
+ // from being invoked. Resolve() can be called again after cancelling.
+ void Cancel();
+
+ private:
+ // Callback for when the request to |resolver_| completes, so we dispatch
+ // to the user's callback.
+ void OnResolveCompletion(int result);
+
+ // The actual host resolver that will handle the request.
+ HostResolver* const resolver_;
+
+ // The current request (if any).
+ HostResolver::RequestHandle cur_request_;
+ CompletionCallback cur_request_callback_;
+
+ // Completion callback for when request to |resolver_| completes.
+ CompletionCallback callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(SingleRequestHostResolver);
+};
+
+} // namespace net
+
+#endif // NET_DNS_SINGLE_REQUEST_HOST_RESOLVER_H_
diff --git a/net/dns/single_request_host_resolver_unittest.cc b/net/dns/single_request_host_resolver_unittest.cc
new file mode 100644
index 0000000..3e2ea1e
--- /dev/null
+++ b/net/dns/single_request_host_resolver_unittest.cc
@@ -0,0 +1,124 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/dns/single_request_host_resolver.h"
+
+#include "net/base/address_list.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_log.h"
+#include "net/base/test_completion_callback.h"
+#include "net/dns/mock_host_resolver.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+// Helper class used by SingleRequestHostResolverTest.Cancel test.
+// It checks that only one request is outstanding at a time, and that
+// it is cancelled before the class is destroyed.
+class HangingHostResolver : public HostResolver {
+ public:
+ HangingHostResolver() : outstanding_request_(NULL) {}
+
+ virtual ~HangingHostResolver() {
+ EXPECT_TRUE(!has_outstanding_request());
+ }
+
+ bool has_outstanding_request() const {
+ return outstanding_request_ != NULL;
+ }
+
+ virtual int Resolve(const RequestInfo& info,
+ AddressList* addresses,
+ const CompletionCallback& callback,
+ RequestHandle* out_req,
+ const BoundNetLog& net_log) OVERRIDE {
+ EXPECT_FALSE(has_outstanding_request());
+ outstanding_request_ = reinterpret_cast<RequestHandle>(0x1234);
+ *out_req = outstanding_request_;
+
+ // Never complete this request! Caller is expected to cancel it
+ // before destroying the resolver.
+ return ERR_IO_PENDING;
+ }
+
+ virtual int ResolveFromCache(const RequestInfo& info,
+ AddressList* addresses,
+ const BoundNetLog& net_log) OVERRIDE {
+ NOTIMPLEMENTED();
+ return ERR_UNEXPECTED;
+ }
+
+ virtual void CancelRequest(RequestHandle req) OVERRIDE {
+ EXPECT_TRUE(has_outstanding_request());
+ EXPECT_EQ(req, outstanding_request_);
+ outstanding_request_ = NULL;
+ }
+
+ private:
+ RequestHandle outstanding_request_;
+
+ DISALLOW_COPY_AND_ASSIGN(HangingHostResolver);
+};
+
+// Test that a regular end-to-end lookup returns the expected result.
+TEST(SingleRequestHostResolverTest, NormalResolve) {
+ // Create a host resolver dependency that returns address "199.188.1.166"
+ // for resolutions of "watsup".
+ MockHostResolver resolver;
+ resolver.rules()->AddIPLiteralRule("watsup", "199.188.1.166", "");
+
+ SingleRequestHostResolver single_request_resolver(&resolver);
+
+ // Resolve "watsup:90" using our SingleRequestHostResolver.
+ AddressList addrlist;
+ TestCompletionCallback callback;
+ HostResolver::RequestInfo request(HostPortPair("watsup", 90));
+ int rv = single_request_resolver.Resolve(
+ request, &addrlist, callback.callback(), BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ EXPECT_EQ(OK, callback.WaitForResult());
+
+ // Verify that the result is what we specified in the MockHostResolver.
+ ASSERT_FALSE(addrlist.empty());
+ EXPECT_EQ("199.188.1.166", addrlist.front().ToStringWithoutPort());
+}
+
+// Test that the Cancel() method cancels any outstanding request.
+TEST(SingleRequestHostResolverTest, Cancel) {
+ HangingHostResolver resolver;
+
+ {
+ SingleRequestHostResolver single_request_resolver(&resolver);
+
+ // Resolve "watsup:90" using our SingleRequestHostResolver.
+ AddressList addrlist;
+ TestCompletionCallback callback;
+ HostResolver::RequestInfo request(HostPortPair("watsup", 90));
+ int rv = single_request_resolver.Resolve(
+ request, &addrlist, callback.callback(), BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ EXPECT_TRUE(resolver.has_outstanding_request());
+ }
+
+ // Now that the SingleRequestHostResolver has been destroyed, the
+ // in-progress request should have been aborted.
+ EXPECT_FALSE(resolver.has_outstanding_request());
+}
+
+// Test that the Cancel() method is a no-op when there is no outstanding
+// request.
+TEST(SingleRequestHostResolverTest, CancelWhileNoPendingRequest) {
+ HangingHostResolver resolver;
+ SingleRequestHostResolver single_request_resolver(&resolver);
+ single_request_resolver.Cancel();
+
+ // To pass, HangingHostResolver should not have received a cancellation
+ // request (since there is nothing to cancel). If it does, it will crash.
+}
+
+} // namespace
+
+} // namespace net