| Index: net/base/host_resolver_impl.cc
|
| diff --git a/net/base/host_resolver_impl.cc b/net/base/host_resolver_impl.cc
|
| index a1ee8f8cb7184ded40202fbfb0cb7460c3aa1d0e..ce6f1264f40da6914f6ecbc9a48d997d665ffe2a 100644
|
| --- a/net/base/host_resolver_impl.cc
|
| +++ b/net/base/host_resolver_impl.cc
|
| @@ -38,6 +38,7 @@
|
| #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"
|
| @@ -609,7 +610,7 @@ class HostResolverImpl::ProcTask
|
| void Cancel() {
|
| DCHECK(origin_loop_->BelongsToCurrentThread());
|
|
|
| - if (was_canceled())
|
| + if (was_canceled() || was_completed())
|
| return;
|
|
|
| callback_.Reset();
|
| @@ -1042,32 +1043,33 @@ class HostResolverImpl::IPv6ProbeJob
|
|
|
| // Resolves the hostname using DnsTransaction.
|
| // TODO(szym): This could be moved to separate source file as well.
|
| -class HostResolverImpl::DnsTask {
|
| +class HostResolverImpl::DnsTask : public base::SupportsWeakPtr<DnsTask> {
|
| public:
|
| typedef base::Callback<void(int net_error,
|
| const AddressList& addr_list,
|
| base::TimeDelta ttl)> Callback;
|
|
|
| - DnsTask(DnsTransactionFactory* factory,
|
| + DnsTask(DnsClient* client,
|
| const Key& key,
|
| const Callback& callback,
|
| const BoundNetLog& job_net_log)
|
| - : callback_(callback), net_log_(job_net_log) {
|
| - DCHECK(factory);
|
| + : client_(client),
|
| + family_(key.address_family),
|
| + callback_(callback),
|
| + net_log_(job_net_log) {
|
| + DCHECK(client);
|
| DCHECK(!callback.is_null());
|
|
|
| - // For now we treat ADDRESS_FAMILY_UNSPEC as if it was IPV4.
|
| - uint16 qtype = (key.address_family == ADDRESS_FAMILY_IPV6)
|
| - ? dns_protocol::kTypeAAAA
|
| - : dns_protocol::kTypeA;
|
| - // TODO(szym): Implement "happy eyeballs".
|
| - transaction_ = factory->CreateTransaction(
|
| + // 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),
|
| - base::TimeTicks::Now()),
|
| + true /* first_query */, base::TimeTicks::Now()),
|
| net_log_);
|
| - DCHECK(transaction_.get());
|
| }
|
|
|
| int Start() {
|
| @@ -1075,47 +1077,138 @@ class HostResolverImpl::DnsTask {
|
| return transaction_->Start();
|
| }
|
|
|
| - void OnTransactionComplete(const base::TimeTicks& start_time,
|
| + private:
|
| + void OnTransactionComplete(bool first_query,
|
| + const base::TimeTicks& start_time,
|
| DnsTransaction* transaction,
|
| int net_error,
|
| const DnsResponse* response) {
|
| DCHECK(transaction);
|
| // Run |callback_| last since the owning Job will then delete this DnsTask.
|
| - DnsResponse::Result result = DnsResponse::DNS_SUCCESS;
|
| - if (net_error == OK) {
|
| - CHECK(response);
|
| - DNS_HISTOGRAM("AsyncDNS.TransactionSuccess",
|
| + if (net_error != OK) {
|
| + DNS_HISTOGRAM("AsyncDNS.TransactionFailure",
|
| base::TimeTicks::Now() - start_time);
|
| - AddressList addr_list;
|
| - base::TimeDelta ttl;
|
| - result = response->ParseToAddressList(&addr_list, &ttl);
|
| - UMA_HISTOGRAM_ENUMERATION("AsyncDNS.ParseToAddressList",
|
| - result,
|
| - DnsResponse::DNS_PARSE_RESULT_MAX);
|
| - if (result == DnsResponse::DNS_SUCCESS) {
|
| - net_log_.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_DNS_TASK,
|
| - addr_list.CreateNetLogCallback());
|
| - callback_.Run(net_error, addr_list, ttl);
|
| + OnFailure(net_error, DnsResponse::DNS_PARSE_OK);
|
| + return;
|
| + }
|
| +
|
| + CHECK(response);
|
| + DNS_HISTOGRAM("AsyncDNS.TransactionSuccess",
|
| + base::TimeTicks::Now() - start_time);
|
| + 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;
|
| }
|
| - net_error = ERR_DNS_MALFORMED_RESPONSE;
|
| } else {
|
| - DNS_HISTOGRAM("AsyncDNS.TransactionFailure",
|
| + 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());
|
| }
|
|
|
| - private:
|
| + 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);
|
| };
|
|
|
| //-----------------------------------------------------------------------------
|
| @@ -1214,7 +1307,7 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job {
|
| }
|
|
|
| // Marks |req| as cancelled. If it was the last active Request, also finishes
|
| - // this Job marking it either as aborted or cancelled, and deletes it.
|
| + // this Job, marking it as cancelled, and deletes it.
|
| void CancelRequest(Request* req) {
|
| DCHECK_EQ(key_.hostname, req->info().hostname());
|
| DCHECK(!req->was_canceled());
|
| @@ -1381,7 +1474,7 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job {
|
| void StartDnsTask() {
|
| DCHECK(resolver_->HaveDnsConfig());
|
| dns_task_.reset(new DnsTask(
|
| - resolver_->dns_client_->GetTransactionFactory(),
|
| + resolver_->dns_client_.get(),
|
| key_,
|
| base::Bind(&Job::OnDnsTaskComplete, base::Unretained(this)),
|
| net_log_));
|
| @@ -1415,6 +1508,7 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job {
|
| }
|
|
|
| UmaAsyncDnsResolveStatus(RESOLVE_STATUS_DNS_SUCCESS);
|
| +
|
| CompleteRequests(net_error, addr_list, ttl);
|
| }
|
|
|
|
|