diff options
author | agayev@chromium.org <agayev@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-21 12:54:37 +0000 |
---|---|---|
committer | agayev@chromium.org <agayev@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-21 12:54:37 +0000 |
commit | d987cca5eac5c840027698fa49a4712d84057e68 (patch) | |
tree | d92552852fdb7e6c387c9d2f46357beb753ee6d5 | |
parent | 357b5202da8b573ef3239e722bc18d93fc90fbd9 (diff) | |
download | chromium_src-d987cca5eac5c840027698fa49a4712d84057e68.zip chromium_src-d987cca5eac5c840027698fa49a4712d84057e68.tar.gz chromium_src-d987cca5eac5c840027698fa49a4712d84057e68.tar.bz2 |
Implemented AsyncHostResolver -- asynchronous HostResolver!
BUG=60149
TEST=net_unittests --gtest_filter="AsyncHostResolverTest*"
Review URL: http://codereview.chromium.org/7472025
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@93380 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/io_thread.cc | 19 | ||||
-rw-r--r-- | chrome/browser/net/passive_log_collector.cc | 29 | ||||
-rw-r--r-- | chrome/browser/net/passive_log_collector.h | 16 | ||||
-rw-r--r-- | chrome/browser/resources/net_internals/sourceentry.js | 1 | ||||
-rw-r--r-- | net/base/async_host_resolver.cc | 427 | ||||
-rw-r--r-- | net/base/async_host_resolver.h | 157 | ||||
-rw-r--r-- | net/base/async_host_resolver_unittest.cc | 574 | ||||
-rw-r--r-- | net/base/dns_test_util.cc | 35 | ||||
-rw-r--r-- | net/base/dns_test_util.h | 181 | ||||
-rw-r--r-- | net/base/dns_transaction_unittest.cc | 211 | ||||
-rw-r--r-- | net/base/host_resolver.h | 6 | ||||
-rw-r--r-- | net/base/host_resolver_impl_unittest.cc | 94 | ||||
-rw-r--r-- | net/base/net_log_event_type_list.h | 40 | ||||
-rw-r--r-- | net/base/net_log_source_type_list.h | 3 | ||||
-rw-r--r-- | net/net.gyp | 3 |
15 files changed, 1584 insertions, 212 deletions
diff --git a/chrome/browser/io_thread.cc b/chrome/browser/io_thread.cc index 11d8827..35070e19 100644 --- a/chrome/browser/io_thread.cc +++ b/chrome/browser/io_thread.cc @@ -34,6 +34,7 @@ #include "content/browser/gpu/gpu_process_host.h" #include "content/browser/in_process_webkit/indexed_db_key_utility_client.h" #include "content/common/url_fetcher.h" +#include "net/base/async_host_resolver.h" #include "net/base/cert_verifier.h" #include "net/base/cookie_monster.h" #include "net/base/dnsrr_resolver.h" @@ -160,8 +161,24 @@ net::HostResolver* CreateGlobalHostResolver(net::NetLog* net_log) { } } - net::HostResolver* global_host_resolver = + net::HostResolver* global_host_resolver = NULL; + if (command_line.HasSwitch(switches::kDnsServer)) { + std::string dns_ip_string = + command_line.GetSwitchValueASCII(switches::kDnsServer); + net::IPAddressNumber dns_ip_number; + if (net::ParseIPLiteralToNumber(dns_ip_string, &dns_ip_number)) { + global_host_resolver = + net::CreateAsyncHostResolver(parallelism, dns_ip_number, net_log); + } else { + LOG(ERROR) << "Invalid IP address specified for --dns-server: " + << dns_ip_string; + } + } + + if (!global_host_resolver) { + global_host_resolver = net::CreateSystemHostResolver(parallelism, retry_attempts, net_log); + } // Determine if we should disable IPv6 support. if (!command_line.HasSwitch(switches::kEnableIPv6)) { diff --git a/chrome/browser/net/passive_log_collector.cc b/chrome/browser/net/passive_log_collector.cc index 6e522a7..ca321dd 100644 --- a/chrome/browser/net/passive_log_collector.cc +++ b/chrome/browser/net/passive_log_collector.cc @@ -78,6 +78,8 @@ PassiveLogCollector::PassiveLogCollector() trackers_[net::NetLog::SOURCE_EXPONENTIAL_BACKOFF_THROTTLING] = &exponential_backoff_throttling_tracker_; trackers_[net::NetLog::SOURCE_DNS_TRANSACTION] = &dns_transaction_tracker_; + trackers_[net::NetLog::SOURCE_ASYNC_HOST_RESOLVER_REQUEST] = + &async_host_resolver_request_tracker_; // Make sure our mapping is up-to-date. for (size_t i = 0; i < arraysize(trackers_); ++i) DCHECK(trackers_[i]) << "Unhandled SourceType: " << i; @@ -695,3 +697,30 @@ PassiveLogCollector::DnsTransactionTracker::DoAddEntry( } return ACTION_NONE; } + +//---------------------------------------------------------------------------- +// AsyncHostResolverRequestTracker +//---------------------------------------------------------------------------- + +const size_t +PassiveLogCollector::AsyncHostResolverRequestTracker::kMaxNumSources = 100; + +const size_t +PassiveLogCollector::AsyncHostResolverRequestTracker::kMaxGraveyardSize = 15; + +PassiveLogCollector:: + AsyncHostResolverRequestTracker::AsyncHostResolverRequestTracker() + : SourceTracker(kMaxNumSources, kMaxGraveyardSize, NULL) { +} + +PassiveLogCollector::SourceTracker::Action +PassiveLogCollector::AsyncHostResolverRequestTracker::DoAddEntry( + const ChromeNetLog::Entry& entry, + SourceInfo* out_info) { + AddEntryToSourceInfo(entry, out_info); + if (entry.type == net::NetLog::TYPE_ASYNC_HOST_RESOLVER_REQUEST && + entry.phase == net::NetLog::PHASE_END) { + return ACTION_MOVE_TO_GRAVEYARD; + } + return ACTION_NONE; +} diff --git a/chrome/browser/net/passive_log_collector.h b/chrome/browser/net/passive_log_collector.h index 56c60c7..28598b2 100644 --- a/chrome/browser/net/passive_log_collector.h +++ b/chrome/browser/net/passive_log_collector.h @@ -369,6 +369,21 @@ class PassiveLogCollector : public ChromeNetLog::ThreadSafeObserver { DISALLOW_COPY_AND_ASSIGN(DnsTransactionTracker); }; + // Tracks the log entries for the last seen SOURCE_ASYNC_HOST_RESOLVER_REQUEST + class AsyncHostResolverRequestTracker : public SourceTracker { + public: + static const size_t kMaxNumSources; + static const size_t kMaxGraveyardSize; + + AsyncHostResolverRequestTracker(); + + private: + virtual Action DoAddEntry(const ChromeNetLog::Entry& entry, + SourceInfo* out_info); + + DISALLOW_COPY_AND_ASSIGN(AsyncHostResolverRequestTracker); + }; + PassiveLogCollector(); virtual ~PassiveLogCollector(); @@ -410,6 +425,7 @@ class PassiveLogCollector : public ChromeNetLog::ThreadSafeObserver { HttpStreamJobTracker http_stream_job_tracker_; ExponentialBackoffThrottlingTracker exponential_backoff_throttling_tracker_; DnsTransactionTracker dns_transaction_tracker_; + AsyncHostResolverRequestTracker async_host_resolver_request_tracker_; // This array maps each NetLog::SourceType to one of the tracker instances // defined above. Use of this array avoid duplicating the list of trackers diff --git a/chrome/browser/resources/net_internals/sourceentry.js b/chrome/browser/resources/net_internals/sourceentry.js index 3cb5fd0..1a901ca 100644 --- a/chrome/browser/resources/net_internals/sourceentry.js +++ b/chrome/browser/resources/net_internals/sourceentry.js @@ -321,6 +321,7 @@ SourceEntry.prototype.getDescription = function() { description = connectJobSourceEntry.getDescription(); } break; + case LogSourceType.ASYNC_HOST_RESOLVER_REQUEST: case LogSourceType.DNS_TRANSACTION: description = e.params.hostname; break; diff --git a/net/base/async_host_resolver.cc b/net/base/async_host_resolver.cc new file mode 100644 index 0000000..8aa9818 --- /dev/null +++ b/net/base/async_host_resolver.cc @@ -0,0 +1,427 @@ +// 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/base/async_host_resolver.h" + +#include <algorithm> + +#include "base/bind.h" +#include "base/logging.h" +#include "base/rand_util.h" +#include "base/stl_util.h" +#include "base/values.h" +#include "net/base/address_list.h" +#include "net/base/dns_util.h" +#include "net/base/net_errors.h" +#include "net/socket/client_socket_factory.h" + +namespace net { + +namespace { + +// TODO(agayev): fix this when IPv6 support is added. +uint16 QueryTypeFromAddressFamily(AddressFamily address_family) { + return kDNS_A; +} + +int ResolveAsIp(const HostResolver::RequestInfo& info, + const IPAddressNumber& ip_number, + AddressList* addresses) { + if (ip_number.size() != kIPv4AddressSize) + return ERR_NAME_NOT_RESOLVED; + + *addresses = AddressList::CreateFromIPAddressWithCname( + ip_number, + info.port(), + info.host_resolver_flags() & HOST_RESOLVER_CANONNAME); + return OK; +} + +class RequestParameters : public NetLog::EventParameters { + public: + RequestParameters(const HostResolver::RequestInfo& info, + const NetLog::Source& source) + : info_(info), source_(source) {} + + virtual Value* ToValue() const { + DictionaryValue* dict = new DictionaryValue(); + dict->SetString("hostname", 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("only_use_cached_response", + info_.only_use_cached_response()); + dict->SetBoolean("is_speculative", info_.is_speculative()); + dict->SetInteger("priority", info_.priority()); + + if (source_.is_valid()) + dict->Set("source_dependency", source_.ToValue()); + + return dict; + } + + private: + const HostResolver::RequestInfo info_; + const NetLog::Source source_; +}; + +} // namespace + +HostResolver* CreateAsyncHostResolver(size_t max_concurrent_resolves, + const IPAddressNumber& dns_ip, + NetLog* net_log) { + size_t max_transactions = max_concurrent_resolves; + if (max_transactions == 0) + max_transactions = 20; + size_t max_pending_requests = max_transactions * 100; + HostResolver* resolver = new AsyncHostResolver( + IPEndPoint(dns_ip, 53), + max_transactions, + max_pending_requests, + base::Bind(&base::RandInt), + NULL, + net_log); + return resolver; +} + +//----------------------------------------------------------------------------- +class AsyncHostResolver::Request { + public: + Request(const BoundNetLog& source_net_log, + const BoundNetLog& request_net_log, + int id, + const HostResolver::RequestInfo& info, + const Key& key, + CompletionCallback* callback, + AddressList* addresses) + : source_net_log_(source_net_log), + request_net_log_(request_net_log), + id_(id), + info_(info), + key_(key), + callback_(callback), + addresses_(addresses) { + DCHECK(addresses_); + } + + int id() const { return id_; } + const HostResolver::RequestInfo& info() const { return info_; } + const Key& key() const { return key_; } + RequestPriority priority() const { return info_.priority(); } + const BoundNetLog& source_net_log() const { return source_net_log_; } + const BoundNetLog& request_net_log() const { return request_net_log_; } + const AddressList* addresses() const { return addresses_; } + + void OnComplete(int result, const IPAddressList& ip_addresses) { + DCHECK(callback_); + if (result == OK) + *addresses_ = + AddressList::CreateFromIPAddressList(ip_addresses, info_.port()); + callback_->Run(result); + } + + private: + BoundNetLog source_net_log_; + BoundNetLog request_net_log_; + const int id_; + const HostResolver::RequestInfo info_; + const Key key_; + CompletionCallback* callback_; + AddressList* addresses_; +}; + +//----------------------------------------------------------------------------- +AsyncHostResolver::AsyncHostResolver(const IPEndPoint& dns_server, + size_t max_transactions, + size_t max_pending_requests, + const RandIntCallback& rand_int_cb, + ClientSocketFactory* factory, + NetLog* net_log) + : max_transactions_(max_transactions), + max_pending_requests_(max_pending_requests), + dns_server_(dns_server), + rand_int_cb_(rand_int_cb), + factory_(factory), + next_request_id_(0), + net_log_(net_log) { +} + +AsyncHostResolver::~AsyncHostResolver() { + for (KeyRequestListMap::iterator it = requestlist_map_.begin(); + it != requestlist_map_.end(); ++it) + STLDeleteElements(&it->second); + STLDeleteElements(&transactions_); +} + +int AsyncHostResolver::Resolve(const RequestInfo& info, + AddressList* addresses, + CompletionCallback* callback, + RequestHandle* out_req, + const BoundNetLog& source_net_log) { + DCHECK(addresses); + + IPAddressNumber ip_number; + std::string dns_name; + int rv = ERR_UNEXPECTED; + if (info.hostname().empty()) + rv = ERR_NAME_NOT_RESOLVED; + else if (ParseIPLiteralToNumber(info.hostname(), &ip_number)) + rv = ResolveAsIp(info, ip_number, addresses); + else if (!DNSDomainFromDot(info.hostname(), &dns_name)) + rv = ERR_NAME_NOT_RESOLVED; + else if (info.only_use_cached_response()) // TODO(agayev): support caching + rv = ERR_NAME_NOT_RESOLVED; + + Request* request = CreateNewRequest( + info, dns_name, callback, addresses, source_net_log); + + OnStart(request); + if (rv != ERR_UNEXPECTED) { + OnFinish(request, rv); + delete request; + return rv; + } + + if (out_req) + *out_req = reinterpret_cast<RequestHandle>(request); + + if (AttachToRequestList(request)) + return ERR_IO_PENDING; + if (transactions_.size() < max_transactions_) + return StartNewTransactionFor(request); + return Enqueue(request); +} + +void AsyncHostResolver::OnStart(Request* request) { + DCHECK(request); + + request->source_net_log().BeginEvent( + NetLog::TYPE_ASYNC_HOST_RESOLVER, + make_scoped_refptr(new NetLogSourceParameter( + "source_dependency", request->request_net_log().source()))); + + request->request_net_log().BeginEvent( + NetLog::TYPE_ASYNC_HOST_RESOLVER_REQUEST, + make_scoped_refptr(new RequestParameters( + request->info(), request->source_net_log().source()))); + + FOR_EACH_OBSERVER( + HostResolver::Observer, observers_, + OnStartResolution(request->id(), request->info())); +} + +void AsyncHostResolver::OnFinish(Request* request, int result) { + DCHECK(request); + bool was_resolved = result == OK; + + FOR_EACH_OBSERVER( + HostResolver::Observer, observers_, + OnFinishResolutionWithStatus( + request->id(), was_resolved, request->info())); + + request->request_net_log().EndEventWithNetErrorCode( + NetLog::TYPE_ASYNC_HOST_RESOLVER_REQUEST, result); + request->source_net_log().EndEvent( + NetLog::TYPE_ASYNC_HOST_RESOLVER, NULL); +} + +void AsyncHostResolver::OnCancel(Request* request) { + DCHECK(request); + + FOR_EACH_OBSERVER( + HostResolver::Observer, observers_, + OnCancelResolution(request->id(), request->info())); + + request->request_net_log().AddEvent( + NetLog::TYPE_CANCELLED, NULL); + request->request_net_log().EndEvent( + NetLog::TYPE_ASYNC_HOST_RESOLVER_REQUEST, NULL); + request->source_net_log().EndEvent( + NetLog::TYPE_ASYNC_HOST_RESOLVER, NULL); +} + +void AsyncHostResolver::CancelRequest(RequestHandle req_handle) { + scoped_ptr<Request> request(reinterpret_cast<Request*>(req_handle)); + DCHECK(request.get()); + + KeyRequestListMap::iterator it = requestlist_map_.find(request->key()); + if (it != requestlist_map_.end()) + it->second.remove(request.get()); + else + pending_requests_[request->priority()].remove(request.get()); + + OnCancel(request.get()); +} + +void AsyncHostResolver::AddObserver(HostResolver::Observer* observer) { + observers_.AddObserver(observer); +} + +void AsyncHostResolver::RemoveObserver(HostResolver::Observer* observer) { + observers_.RemoveObserver(observer); +} + +void AsyncHostResolver::SetDefaultAddressFamily( + AddressFamily address_family) { + NOTIMPLEMENTED(); +} + +AddressFamily AsyncHostResolver::GetDefaultAddressFamily() const { + return ADDRESS_FAMILY_IPV4; +} + +HostResolverImpl* AsyncHostResolver::GetAsHostResolverImpl() { + return NULL; +} + +void AsyncHostResolver::OnTransactionComplete( + int result, + const DnsTransaction* transaction, + const IPAddressList& ip_addresses) { + + DCHECK(std::find(transactions_.begin(), transactions_.end(), transaction) + != transactions_.end()); + DCHECK(requestlist_map_.find(transaction->key()) != requestlist_map_.end()); + + // Run callback of every request that was depending on this transaction, + // also notify observers. + RequestList& requests = requestlist_map_[transaction->key()]; + for (RequestList::iterator it = requests.begin(); it != requests.end(); + ++it) { + Request* request = *it; + OnFinish(request, result); + request->OnComplete(result, ip_addresses); + } + + // Cleanup requests. + STLDeleteElements(&requests); + requestlist_map_.erase(transaction->key()); + + // Cleanup transaction and start a new one if there are pending requests. + delete transaction; + transactions_.remove(transaction); + ProcessPending(); +} + +AsyncHostResolver::Request* AsyncHostResolver::CreateNewRequest( + const RequestInfo& info, + const std::string& dns_name, + CompletionCallback* callback, + AddressList* addresses, + const BoundNetLog& source_net_log) { + + uint16 query_type = QueryTypeFromAddressFamily(info.address_family()); + Key key(dns_name, query_type); + + BoundNetLog request_net_log = BoundNetLog::Make(net_log_, + NetLog::SOURCE_ASYNC_HOST_RESOLVER_REQUEST); + + int id = next_request_id_++; + Request* request = new Request( + source_net_log, request_net_log, id, info, key, callback, addresses); + return request; +} + +bool AsyncHostResolver::AttachToRequestList(Request* request) { + KeyRequestListMap::iterator it = requestlist_map_.find(request->key()); + if (it == requestlist_map_.end()) + return false; + it->second.push_back(request); + return true; +} + +int AsyncHostResolver::StartNewTransactionFor(Request* request) { + DCHECK(requestlist_map_.find(request->key()) == requestlist_map_.end()); + DCHECK(transactions_.size() < max_transactions_); + + request->request_net_log().AddEvent( + NetLog::TYPE_ASYNC_HOST_RESOLVER_CREATE_DNS_TRANSACTION, NULL); + + requestlist_map_[request->key()].push_back(request); + DnsTransaction* transaction = new DnsTransaction( + dns_server_, + request->key().first, + request->key().second, + rand_int_cb_, + factory_, + request->request_net_log(), + net_log_); + transaction->SetDelegate(this); + transactions_.push_back(transaction); + return transaction->Start(); +} + +int AsyncHostResolver::Enqueue(Request* request) { + scoped_ptr<Request> evicted_request(Insert(request)); + if (evicted_request != NULL) { + int rv = ERR_HOST_RESOLVER_QUEUE_TOO_LARGE; + if (evicted_request.get() == request) + return rv; + evicted_request->OnComplete(rv, IPAddressList()); + } + return ERR_IO_PENDING; +} + +AsyncHostResolver::Request* AsyncHostResolver::Insert(Request* request) { + pending_requests_[request->priority()].push_back(request); + if (GetNumPending() > max_pending_requests_) { + Request* req = RemoveLowest(); + DCHECK(req); + return req; + } + return NULL; +} + +size_t AsyncHostResolver::GetNumPending() { + size_t num_pending = 0; + for (size_t i = 0; i < arraysize(pending_requests_); ++i) + num_pending += pending_requests_[i].size(); + return num_pending; +} + +AsyncHostResolver::Request* AsyncHostResolver::RemoveLowest() { + for (int i = static_cast<int>(arraysize(pending_requests_)) - 1; + i >= 0; --i) { + RequestList& requests = pending_requests_[i]; + if (!requests.empty()) { + Request* request = requests.front(); + requests.pop_front(); + return request; + } + } + return NULL; +} + +AsyncHostResolver::Request* AsyncHostResolver::RemoveHighest() { + for (size_t i = 0; i < arraysize(pending_requests_) - 1; ++i) { + RequestList& requests = pending_requests_[i]; + if (!requests.empty()) { + Request* request = requests.front(); + requests.pop_front(); + return request; + } + } + return NULL; +} + +void AsyncHostResolver::ProcessPending() { + Request* request = RemoveHighest(); + if (!request) + return; + for (size_t i = 0; i < arraysize(pending_requests_); ++i) { + RequestList& requests = pending_requests_[i]; + RequestList::iterator it = requests.begin(); + while (it != requests.end()) { + if (request->key() == (*it)->key()) { + requestlist_map_[request->key()].push_back(*it); + it = requests.erase(it); + } else { + ++it; + } + } + } + StartNewTransactionFor(request); +} + +} // namespace net diff --git a/net/base/async_host_resolver.h b/net/base/async_host_resolver.h new file mode 100644 index 0000000..2d55717 --- /dev/null +++ b/net/base/async_host_resolver.h @@ -0,0 +1,157 @@ +// 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_BASE_ASYNC_HOST_RESOLVER_H_ +#define NET_BASE_ASYNC_HOST_RESOLVER_H_ +#pragma once + +#include <list> +#include <map> +#include <vector> + +#include "base/observer_list.h" +#include "base/threading/non_thread_safe.h" +#include "net/base/address_family.h" +#include "net/base/dns_transaction.h" +#include "net/base/host_resolver.h" +#include "net/base/ip_endpoint.h" +#include "net/base/net_log.h" +#include "net/base/rand_callback.h" + +namespace net { + +class AddressesList; +class ClientSocketFactory; + +class NET_API AsyncHostResolver + : public HostResolver, + public DnsTransaction::Delegate, + NON_EXPORTED_BASE(public base::NonThreadSafe) { + public: + AsyncHostResolver(const IPEndPoint& dns_server, + size_t max_transactions, + size_t max_pending_requests_, + const RandIntCallback& rand_int, + ClientSocketFactory* factory, + NetLog* net_log); + virtual ~AsyncHostResolver(); + + // HostResolver interface + virtual int Resolve(const RequestInfo& info, + AddressList* addresses, + CompletionCallback* callback, + RequestHandle* out_req, + const BoundNetLog& source_net_log) OVERRIDE; + virtual void CancelRequest(RequestHandle req_handle) OVERRIDE; + virtual void AddObserver(HostResolver::Observer* observer) OVERRIDE; + virtual void RemoveObserver(HostResolver::Observer* observer) OVERRIDE; + virtual void SetDefaultAddressFamily(AddressFamily address_family) OVERRIDE; + virtual AddressFamily GetDefaultAddressFamily() const OVERRIDE; + virtual HostResolverImpl* GetAsHostResolverImpl() OVERRIDE; + + // DnsTransaction::Delegate interface + virtual void OnTransactionComplete( + int result, + const DnsTransaction* transaction, + const IPAddressList& ip_addresses) OVERRIDE; + + private: + FRIEND_TEST_ALL_PREFIXES(AsyncHostResolverTest, QueuedLookup); + FRIEND_TEST_ALL_PREFIXES(AsyncHostResolverTest, CancelPendingLookup); + FRIEND_TEST_ALL_PREFIXES(AsyncHostResolverTest, + ResolverDestructionCancelsLookups); + FRIEND_TEST_ALL_PREFIXES(AsyncHostResolverTest, + OverflowQueueWithLowPriorityLookup); + FRIEND_TEST_ALL_PREFIXES(AsyncHostResolverTest, + OverflowQueueWithHighPriorityLookup); + + class Request; + + typedef DnsTransaction::Key Key; + typedef std::list<Request*> RequestList; + typedef std::list<const DnsTransaction*> TransactionList; + typedef std::map<Key, RequestList> KeyRequestListMap; + + // Create a new request for the incoming Resolve() call. + Request* CreateNewRequest(const RequestInfo& info, + const std::string& dns_name, + CompletionCallback* callback, + AddressList* addresses, + const BoundNetLog& source_net_log); + + // Called when a request has just been started. + void OnStart(Request* request); + + // Called when a request has just completed (before its callback is run). + void OnFinish(Request* request, int result); + + // Called when a request has been cancelled. + void OnCancel(Request* request); + + // If there is an in-progress transaction for Request->key(), this will + // attach |request| to the respective list. + bool AttachToRequestList(Request* request); + + // Will start a new transaction for |request|, will insert a new key in + // |requestlist_map_| and append |request| to the respective list. + int StartNewTransactionFor(Request* request); + + // Will enqueue |request| in |pending_requests_|. + int Enqueue(Request* request); + + // A helper used by Enqueue to insert |request| into |pending_requests_|. + Request* Insert(Request* request); + + // Returns the number of pending requests. + size_t GetNumPending(); + + // Removes and returns a pointer to the lowest/highest priority request + // from |pending_requests_|. + Request* RemoveLowest(); + Request* RemoveHighest(); + + // Once a transaction has completed, called to start a new transaction if + // there are pending requests. + void ProcessPending(); + + // Maximum number of concurrent transactions. + size_t max_transactions_; + + // List of current transactions. + TransactionList transactions_; + + // A map from Key to a list of requests waiting for the Key to resolve. + KeyRequestListMap requestlist_map_; + + // Maximum number of pending requests. + size_t max_pending_requests_; + + // Queues based on priority for putting pending requests. + RequestList pending_requests_[NUM_PRIORITIES]; + + // DNS server to which queries will be setn. + IPEndPoint dns_server_; + + // Callback to be passed to DnsTransaction for generating DNS query ids. + RandIntCallback rand_int_cb_; + + // Also passed to DnsTransaction; it's a dependency injection to aid + // testing, outside of unit tests, its value is always NULL. + ClientSocketFactory* factory_; + + // The observers to notify when a request starts/ends. + ObserverList<HostResolver::Observer> observers_; + + // Monotonically increasing ID number to assign to the next request. + // Observers are the only consumers of this ID number. + int next_request_id_; + + NetLog* net_log_; + + DISALLOW_COPY_AND_ASSIGN(AsyncHostResolver); +}; + +} // namespace net + +#endif // NET_BASE_ASYNC_HOST_RESOLVER_H_ diff --git a/net/base/async_host_resolver_unittest.cc b/net/base/async_host_resolver_unittest.cc new file mode 100644 index 0000000..0e92981 --- /dev/null +++ b/net/base/async_host_resolver_unittest.cc @@ -0,0 +1,574 @@ +// 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/base/async_host_resolver.h" + +#include "base/bind.h" +#include "base/scoped_ptr.h" +#include "net/base/dns_test_util.h" +#include "net/base/net_log.h" +#include "net/base/rand_callback.h" +#include "net/base/sys_addrinfo.h" +#include "net/socket/socket_test_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { + +namespace { + +void VerifyAddressList(const std::vector<const char*>& ip_addresses, + int port, + const AddressList& addrlist) { + ASSERT_LT(0u, ip_addresses.size()); + ASSERT_NE(static_cast<addrinfo*>(NULL), addrlist.head()); + + IPAddressNumber ip_number; + const struct addrinfo* ainfo = addrlist.head(); + for (std::vector<const char*>::const_iterator i = ip_addresses.begin(); + i != ip_addresses.end(); ++i, ainfo = ainfo->ai_next) { + ASSERT_NE(static_cast<addrinfo*>(NULL), ainfo); + EXPECT_EQ(sizeof(struct sockaddr_in), ainfo->ai_addrlen); + + const struct sockaddr* sa = ainfo->ai_addr; + const struct sockaddr_in* sa_in = (const struct sockaddr_in*) sa; + EXPECT_TRUE(htons(port) == sa_in->sin_port); + EXPECT_STREQ(*i, NetAddressToString(sa, ainfo->ai_addrlen).c_str()); + } + ASSERT_EQ(static_cast<addrinfo*>(NULL), ainfo); +} + +} // namespace + +static const int kPortNum = 80; +static const size_t kMaxTransactions = 2; +static const size_t kMaxPendingRequests = 1; +static int transaction_ids[] = {0, 1, 2, 3}; + +// The following fixture sets up an environment for four different lookups +// with their data defined in dns_test_util.h. All tests make use of these +// predefined variables instead of each defining their own, to avoid +// boilerplate code in every test. Assuming every coming query is for a +// distinct hostname, as |kMaxTransactions| is set to 2 and +// |kMaxPendingRequests| is set to 1, first two queries start immediately +// and the next one is sent to pending queue; as a result, the next query +// should either fail itself or cause the pending query to fail depending +// on its priority. +class AsyncHostResolverTest : public testing::Test { + public: + AsyncHostResolverTest() + : info0_(HostPortPair(kT0HostName, kPortNum)), + info1_(HostPortPair(kT1HostName, kPortNum)), + info2_(HostPortPair(kT2HostName, kPortNum)), + info3_(HostPortPair(kT3HostName, kPortNum)), + ip_addresses0_(kT0IpAddresses, + kT0IpAddresses + arraysize(kT0IpAddresses)), + ip_addresses1_(kT1IpAddresses, + kT1IpAddresses + arraysize(kT1IpAddresses)), + ip_addresses2_(kT2IpAddresses, + kT2IpAddresses + arraysize(kT2IpAddresses)), + ip_addresses3_(kT3IpAddresses, + kT3IpAddresses + arraysize(kT3IpAddresses)), + test_prng_(std::deque<int>( + transaction_ids, transaction_ids + arraysize(transaction_ids))) { + + rand_int_cb_ = base::Bind(&TestPrng::GetNext, + base::Unretained(&test_prng_)); + // AF_INET only for now. + info0_.set_address_family(ADDRESS_FAMILY_IPV4); + info1_.set_address_family(ADDRESS_FAMILY_IPV4); + info2_.set_address_family(ADDRESS_FAMILY_IPV4); + info3_.set_address_family(ADDRESS_FAMILY_IPV4); + + // Setup socket read/writes for transaction 0. + writes0_.push_back( + MockWrite(true, reinterpret_cast<const char*>(kT0QueryDatagram), + arraysize(kT0QueryDatagram))); + reads0_.push_back( + MockRead(true, reinterpret_cast<const char*>(kT0ResponseDatagram), + arraysize(kT0ResponseDatagram))); + data0_.reset(new StaticSocketDataProvider(&reads0_[0], reads0_.size(), + &writes0_[0], writes0_.size())); + + // Setup socket read/writes for transaction 1. + writes1_.push_back( + MockWrite(true, reinterpret_cast<const char*>(kT1QueryDatagram), + arraysize(kT1QueryDatagram))); + reads1_.push_back( + MockRead(true, reinterpret_cast<const char*>(kT1ResponseDatagram), + arraysize(kT1ResponseDatagram))); + data1_.reset(new StaticSocketDataProvider(&reads1_[0], reads1_.size(), + &writes1_[0], writes1_.size())); + + // Setup socket read/writes for transaction 2. + writes2_.push_back( + MockWrite(true, reinterpret_cast<const char*>(kT2QueryDatagram), + arraysize(kT2QueryDatagram))); + reads2_.push_back( + MockRead(true, reinterpret_cast<const char*>(kT2ResponseDatagram), + arraysize(kT2ResponseDatagram))); + data2_.reset(new StaticSocketDataProvider(&reads2_[0], reads2_.size(), + &writes2_[0], writes2_.size())); + + // Setup socket read/writes for transaction 3. + writes3_.push_back( + MockWrite(true, reinterpret_cast<const char*>(kT3QueryDatagram), + arraysize(kT3QueryDatagram))); + reads3_.push_back( + MockRead(true, reinterpret_cast<const char*>(kT3ResponseDatagram), + arraysize(kT3ResponseDatagram))); + data3_.reset(new StaticSocketDataProvider(&reads3_[0], reads3_.size(), + &writes3_[0], writes3_.size())); + + factory_.AddSocketDataProvider(data0_.get()); + factory_.AddSocketDataProvider(data1_.get()); + factory_.AddSocketDataProvider(data2_.get()); + factory_.AddSocketDataProvider(data3_.get()); + + IPEndPoint dns_server; + bool rv0 = CreateDnsAddress(kDnsIp, kDnsPort, &dns_server); + DCHECK(rv0); + + resolver_.reset( + new AsyncHostResolver( + dns_server, kMaxTransactions, kMaxPendingRequests, rand_int_cb_, + &factory_, NULL)); + } + + protected: + AddressList addrlist0_, addrlist1_, addrlist2_, addrlist3_; + HostResolver::RequestInfo info0_, info1_, info2_, info3_; + std::vector<MockWrite> writes0_, writes1_, writes2_, writes3_; + std::vector<MockRead> reads0_, reads1_, reads2_, reads3_; + scoped_ptr<StaticSocketDataProvider> data0_, data1_, data2_, data3_; + std::vector<const char*> ip_addresses0_, ip_addresses1_, + ip_addresses2_, ip_addresses3_; + MockClientSocketFactory factory_; + TestPrng test_prng_; + RandIntCallback rand_int_cb_; + scoped_ptr<HostResolver> resolver_; + TestCompletionCallback callback0_, callback1_, callback2_, callback3_; +}; + +TEST_F(AsyncHostResolverTest, EmptyHostLookup) { + info0_.set_host_port_pair(HostPortPair("", kPortNum)); + int rv = resolver_->Resolve(info0_, &addrlist0_, &callback0_, NULL, + BoundNetLog()); + EXPECT_EQ(ERR_NAME_NOT_RESOLVED, rv); +} + +TEST_F(AsyncHostResolverTest, IPv4LiteralLookup) { + const char* kIPLiteral = "192.168.1.2"; + info0_.set_host_port_pair(HostPortPair(kIPLiteral, kPortNum)); + info0_.set_host_resolver_flags(HOST_RESOLVER_CANONNAME); + int rv = resolver_->Resolve(info0_, &addrlist0_, &callback0_, NULL, + BoundNetLog()); + EXPECT_EQ(OK, rv); + std::vector<const char*> ip_addresses(1, kIPLiteral); + VerifyAddressList(ip_addresses, kPortNum, addrlist0_); + EXPECT_STREQ(kIPLiteral, addrlist0_.head()->ai_canonname); +} + +TEST_F(AsyncHostResolverTest, IPv6LiteralLookup) { + info0_.set_host_port_pair(HostPortPair("2001:db8:0::42", kPortNum)); + int rv = resolver_->Resolve(info0_, &addrlist0_, &callback0_, NULL, + BoundNetLog()); + // When support for IPv6 is added, this should succeed. + EXPECT_EQ(ERR_NAME_NOT_RESOLVED, rv); +} + +TEST_F(AsyncHostResolverTest, CachedOnlyLookup) { + info0_.set_only_use_cached_response(true); + int rv = resolver_->Resolve(info0_, &addrlist0_, &callback0_, NULL, + BoundNetLog()); + // When caching is added, this should succeed. + EXPECT_EQ(ERR_NAME_NOT_RESOLVED, rv); +} + +TEST_F(AsyncHostResolverTest, InvalidHostNameLookup) { + const std::string kHostName1(64, 'a'); + info0_.set_host_port_pair(HostPortPair(kHostName1, kPortNum)); + int rv = resolver_->Resolve(info0_, &addrlist0_, &callback0_, NULL, + BoundNetLog()); + EXPECT_EQ(ERR_NAME_NOT_RESOLVED, rv); + + const std::string kHostName2(4097, 'b'); + info0_.set_host_port_pair(HostPortPair(kHostName2, kPortNum)); + rv = resolver_->Resolve(info0_, &addrlist0_, &callback0_, NULL, + BoundNetLog()); + EXPECT_EQ(ERR_NAME_NOT_RESOLVED, rv); +} + +TEST_F(AsyncHostResolverTest, Lookup) { + int rv = resolver_->Resolve(info0_, &addrlist0_, &callback0_, NULL, + BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + rv = callback0_.WaitForResult(); + EXPECT_EQ(OK, rv); + VerifyAddressList(ip_addresses0_, kPortNum, addrlist0_); +} + +TEST_F(AsyncHostResolverTest, ConcurrentLookup) { + int rv0 = resolver_->Resolve(info0_, &addrlist0_, &callback0_, NULL, + BoundNetLog()); + int rv1 = resolver_->Resolve(info1_, &addrlist1_, &callback1_, NULL, + BoundNetLog()); + int rv2 = resolver_->Resolve(info2_, &addrlist2_, &callback2_, NULL, + BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv0); + EXPECT_EQ(ERR_IO_PENDING, rv1); + EXPECT_EQ(ERR_IO_PENDING, rv2); + + rv0 = callback0_.WaitForResult(); + EXPECT_EQ(OK, rv0); + VerifyAddressList(ip_addresses0_, kPortNum, addrlist0_); + + rv1 = callback1_.WaitForResult(); + EXPECT_EQ(OK, rv1); + VerifyAddressList(ip_addresses1_, kPortNum, addrlist1_); + + rv2 = callback2_.WaitForResult(); + EXPECT_EQ(OK, rv2); + VerifyAddressList(ip_addresses2_, kPortNum, addrlist2_); + + EXPECT_EQ(3u, factory_.udp_client_sockets().size()); +} + +TEST_F(AsyncHostResolverTest, SameHostLookupsConsumeSingleTransaction) { + // We pass the info0_ to all requests. + int rv0 = resolver_->Resolve(info0_, &addrlist0_, &callback0_, NULL, + BoundNetLog()); + int rv1 = resolver_->Resolve(info0_, &addrlist1_, &callback1_, NULL, + BoundNetLog()); + int rv2 = resolver_->Resolve(info0_, &addrlist2_, &callback2_, NULL, + BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv0); + EXPECT_EQ(ERR_IO_PENDING, rv1); + EXPECT_EQ(ERR_IO_PENDING, rv2); + + rv0 = callback0_.WaitForResult(); + EXPECT_EQ(OK, rv0); + VerifyAddressList(ip_addresses0_, kPortNum, addrlist0_); + + rv1 = callback1_.WaitForResult(); + EXPECT_EQ(OK, rv1); + VerifyAddressList(ip_addresses0_, kPortNum, addrlist1_); + + rv2 = callback2_.WaitForResult(); + EXPECT_EQ(OK, rv2); + VerifyAddressList(ip_addresses0_, kPortNum, addrlist2_); + + // Although we have three lookups, a single UDP socket was used. + EXPECT_EQ(1u, factory_.udp_client_sockets().size()); +} + +TEST_F(AsyncHostResolverTest, CancelLookup) { + HostResolver::RequestHandle req0 = NULL, req2 = NULL; + int rv0 = resolver_->Resolve(info0_, &addrlist0_, &callback0_, &req0, + BoundNetLog()); + int rv1 = resolver_->Resolve(info1_, &addrlist1_, &callback1_, NULL, + BoundNetLog()); + int rv2 = resolver_->Resolve(info2_, &addrlist2_, &callback2_, &req2, + BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv0); + EXPECT_EQ(ERR_IO_PENDING, rv1); + EXPECT_EQ(ERR_IO_PENDING, rv2); + + resolver_->CancelRequest(req0); + resolver_->CancelRequest(req2); + + MessageLoop::current()->RunAllPending(); + + EXPECT_FALSE(callback0_.have_result()); + EXPECT_FALSE(callback2_.have_result()); + + rv1 = callback1_.WaitForResult(); + EXPECT_EQ(OK, rv1); + VerifyAddressList(ip_addresses1_, kPortNum, addrlist1_); +} + +// Tests the following scenario: start two resolutions for the same host, +// cancel one of them, make sure that the other one completes. +TEST_F(AsyncHostResolverTest, CancelSameHostLookup) { + HostResolver::RequestHandle req0 = NULL; + + // Pass the info0_ to both requests. + int rv0 = resolver_->Resolve(info0_, &addrlist0_, &callback0_, &req0, + BoundNetLog()); + int rv1 = resolver_->Resolve(info0_, &addrlist1_, &callback1_, NULL, + BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv0); + EXPECT_EQ(ERR_IO_PENDING, rv1); + + resolver_->CancelRequest(req0); + MessageLoop::current()->RunAllPending(); + EXPECT_FALSE(callback0_.have_result()); + + rv1 = callback1_.WaitForResult(); + EXPECT_EQ(OK, rv1); + VerifyAddressList(ip_addresses0_, kPortNum, addrlist1_); + + EXPECT_EQ(1u, factory_.udp_client_sockets().size()); +} + +// Test that a queued lookup completes. +TEST_F(AsyncHostResolverTest, QueuedLookup) { + // kMaxTransactions is 2, thus the following requests consume all + // available transactions. + int rv0 = resolver_->Resolve(info0_, &addrlist0_, &callback0_, NULL, + BoundNetLog()); + int rv1 = resolver_->Resolve(info1_, &addrlist1_, &callback1_, NULL, + BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv0); + EXPECT_EQ(ERR_IO_PENDING, rv1); + + // The following request will end up in queue. + int rv2 = resolver_->Resolve(info2_, &addrlist2_, &callback2_, NULL, + BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv2); + EXPECT_EQ(1u, + static_cast<AsyncHostResolver*>(resolver_.get())->GetNumPending()); + + // Make sure all requests complete. + rv0 = callback0_.WaitForResult(); + EXPECT_EQ(OK, rv0); + VerifyAddressList(ip_addresses0_, kPortNum, addrlist0_); + + rv1 = callback1_.WaitForResult(); + EXPECT_EQ(OK, rv1); + VerifyAddressList(ip_addresses1_, kPortNum, addrlist1_); + + rv2 = callback2_.WaitForResult(); + EXPECT_EQ(OK, rv2); + VerifyAddressList(ip_addresses2_, kPortNum, addrlist2_); +} + +// Test that cancelling a queued lookup works. +TEST_F(AsyncHostResolverTest, CancelPendingLookup) { + // kMaxTransactions is 2, thus the following requests consume all + // available transactions. + int rv0 = resolver_->Resolve(info0_, &addrlist0_, &callback0_, NULL, + BoundNetLog()); + int rv1 = resolver_->Resolve(info1_, &addrlist1_, &callback1_, NULL, + BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv0); + EXPECT_EQ(ERR_IO_PENDING, rv1); + + // The following request will end up in queue. + HostResolver::RequestHandle req2 = NULL; + int rv2 = resolver_->Resolve(info2_, &addrlist2_, &callback2_, &req2, + BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv2); + EXPECT_EQ(1u, + static_cast<AsyncHostResolver*>(resolver_.get())->GetNumPending()); + + resolver_->CancelRequest(req2); + + // Make sure first two requests complete while the cancelled one doesn't. + MessageLoop::current()->RunAllPending(); + EXPECT_FALSE(callback2_.have_result()); + + rv0 = callback0_.WaitForResult(); + EXPECT_EQ(OK, rv0); + VerifyAddressList(ip_addresses0_, kPortNum, addrlist0_); + + rv1 = callback1_.WaitForResult(); + EXPECT_EQ(OK, rv1); + VerifyAddressList(ip_addresses1_, kPortNum, addrlist1_); +} + +TEST_F(AsyncHostResolverTest, ResolverDestructionCancelsLookups) { + int rv0 = resolver_->Resolve(info0_, &addrlist0_, &callback0_, NULL, + BoundNetLog()); + int rv1 = resolver_->Resolve(info1_, &addrlist1_, &callback1_, NULL, + BoundNetLog()); + // This one is queued. + int rv2 = resolver_->Resolve(info2_, &addrlist2_, &callback2_, NULL, + BoundNetLog()); + EXPECT_EQ(1u, + static_cast<AsyncHostResolver*>(resolver_.get())->GetNumPending()); + + EXPECT_EQ(ERR_IO_PENDING, rv0); + EXPECT_EQ(ERR_IO_PENDING, rv1); + EXPECT_EQ(ERR_IO_PENDING, rv2); + + resolver_.reset(); + + MessageLoop::current()->RunAllPending(); + + EXPECT_FALSE(callback0_.have_result()); + EXPECT_FALSE(callback1_.have_result()); + EXPECT_FALSE(callback2_.have_result()); +} + +// Test that when the number of pending lookups is at max, a new lookup +// with a priority lower than all of those in the queue fails. +TEST_F(AsyncHostResolverTest, OverflowQueueWithLowPriorityLookup) { + int rv0 = resolver_->Resolve(info0_, &addrlist0_, &callback0_, NULL, + BoundNetLog()); + int rv1 = resolver_->Resolve(info1_, &addrlist1_, &callback1_, NULL, + BoundNetLog()); + // This one is queued and fills up the queue since its size is 1. + int rv2 = resolver_->Resolve(info2_, &addrlist2_, &callback2_, NULL, + BoundNetLog()); + EXPECT_EQ(1u, + static_cast<AsyncHostResolver*>(resolver_.get())->GetNumPending()); + + EXPECT_EQ(ERR_IO_PENDING, rv0); + EXPECT_EQ(ERR_IO_PENDING, rv1); + EXPECT_EQ(ERR_IO_PENDING, rv2); + + // This one fails. + info3_.set_priority(LOWEST); + int rv3 = resolver_->Resolve(info3_, &addrlist3_, &callback3_, NULL, + BoundNetLog()); + EXPECT_EQ(ERR_HOST_RESOLVER_QUEUE_TOO_LARGE, rv3); + + MessageLoop::current()->RunAllPending(); + EXPECT_FALSE(callback3_.have_result()); +} + +// Test that when the number of pending lookups is at max, a new lookup +// with a priority higher than any of those in the queue succeeds and +// causes the lowest priority lookup in the queue to fail. +TEST_F(AsyncHostResolverTest, OverflowQueueWithHighPriorityLookup) { + int rv0 = resolver_->Resolve(info0_, &addrlist0_, &callback0_, NULL, + BoundNetLog()); + int rv1 = resolver_->Resolve(info1_, &addrlist1_, &callback1_, NULL, + BoundNetLog()); + + // Next lookup is queued. Since this will be ejected from the queue and + // will not consume a socket from our factory, we are not passing it + // predefined members. + HostResolver::RequestInfo info(HostPortPair("cnn.com", 80)); + info.set_address_family(ADDRESS_FAMILY_IPV4); + AddressList addrlist_fail; + TestCompletionCallback callback_fail; + int rv_fail = resolver_->Resolve(info, &addrlist_fail, &callback_fail, NULL, + BoundNetLog()); + EXPECT_EQ(1u, + static_cast<AsyncHostResolver*>(resolver_.get())->GetNumPending()); + + EXPECT_EQ(ERR_IO_PENDING, rv0); + EXPECT_EQ(ERR_IO_PENDING, rv1); + EXPECT_EQ(ERR_IO_PENDING, rv_fail); + + // Lookup 2 causes the above to fail, but itself should succeed. + info2_.set_priority(HIGHEST); + int rv2 = resolver_->Resolve(info2_, &addrlist2_, &callback2_, NULL, + BoundNetLog()); + + rv0 = callback0_.WaitForResult(); + EXPECT_EQ(OK, rv0); + VerifyAddressList(ip_addresses0_, kPortNum, addrlist0_); + + rv1 = callback1_.WaitForResult(); + EXPECT_EQ(OK, rv1); + VerifyAddressList(ip_addresses1_, kPortNum, addrlist1_); + + rv_fail = callback_fail.WaitForResult(); + EXPECT_EQ(ERR_HOST_RESOLVER_QUEUE_TOO_LARGE, rv_fail); + EXPECT_EQ(static_cast<addrinfo*>(NULL), addrlist_fail.head()); + + rv2 = callback2_.WaitForResult(); + EXPECT_EQ(OK, rv2); + VerifyAddressList(ip_addresses2_, kPortNum, addrlist2_); +} + +// Test that registering, unregistering, and notifying of observers of +// resolution start, completion and cancellation (both due to CancelRequest +// and resolver destruction) work. +TEST_F(AsyncHostResolverTest, Observers) { + TestHostResolverObserver observer; + resolver_->AddObserver(&observer); + + int rv0 = resolver_->Resolve(info0_, &addrlist0_, &callback0_, NULL, + BoundNetLog()); + int rv1 = resolver_->Resolve(info1_, &addrlist1_, &callback1_, NULL, + BoundNetLog()); + // We will cancel this one. + HostResolver::RequestHandle req2 = NULL; + int rv2 = resolver_->Resolve(info2_, &addrlist2_, &callback2_, &req2, + BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv0); + EXPECT_EQ(ERR_IO_PENDING, rv1); + EXPECT_EQ(ERR_IO_PENDING, rv2); + + // Cancel lookup 2. + resolver_->CancelRequest(req2); + + // Lookup 0 and 1 should succeed. + rv0 = callback0_.WaitForResult(); + EXPECT_EQ(OK, rv0); + VerifyAddressList(ip_addresses0_, kPortNum, addrlist0_); + + rv1 = callback1_.WaitForResult(); + EXPECT_EQ(OK, rv1); + VerifyAddressList(ip_addresses1_, kPortNum, addrlist1_); + + // Next lookup should not have finished. + MessageLoop::current()->RunAllPending(); + EXPECT_FALSE(callback2_.have_result()); + + // Verify observer calls. + EXPECT_EQ(3u, observer.start_log.size()); + EXPECT_EQ(2u, observer.finish_log.size()); + EXPECT_EQ(1u, observer.cancel_log.size()); + + // Lookup 0 started and finished. + EXPECT_TRUE(observer.start_log[0] == + TestHostResolverObserver::StartOrCancelEntry(0, info0_)); + EXPECT_TRUE(observer.finish_log[0] == + TestHostResolverObserver::FinishEntry(0, true, info0_)); + + // Ditto for lookup 1. + EXPECT_TRUE(observer.start_log[1] == + TestHostResolverObserver::StartOrCancelEntry(1, info1_)); + EXPECT_TRUE(observer.finish_log[1] == + TestHostResolverObserver::FinishEntry(1, true, info1_)); + + // Lookup 2 was cancelled, hence, failed to finish. + EXPECT_TRUE(observer.start_log[2] == + TestHostResolverObserver::StartOrCancelEntry(2, info2_)); + EXPECT_TRUE(observer.cancel_log[0] == + TestHostResolverObserver::StartOrCancelEntry(2, info2_)); + + // Unregister observer. + resolver_->RemoveObserver(&observer); + + // We will do lookup 2 again but will not be cancel it this time. + rv2 = resolver_->Resolve(info2_, &addrlist2_, &callback2_, NULL, + BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv2); + + // Run lookup 2 to completion. + rv2 = callback2_.WaitForResult(); + EXPECT_EQ(OK, rv2); + VerifyAddressList(ip_addresses2_, kPortNum, addrlist2_); + + // Observer log should stay the same. + EXPECT_EQ(3u, observer.start_log.size()); + EXPECT_EQ(2u, observer.finish_log.size()); + EXPECT_EQ(1u, observer.cancel_log.size()); + + // Re-register observer. + resolver_->AddObserver(&observer); + + // Start lookup 3. + int rv3 = resolver_->Resolve(info3_, &addrlist3_, &callback3_, NULL, + BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv3); + + // Destroy the resolver and make sure that observer was notified of just + // the resolution start. + resolver_.reset(); + + EXPECT_EQ(4u, observer.start_log.size()); // Was incremented by 1. + EXPECT_EQ(2u, observer.finish_log.size()); + EXPECT_EQ(1u, observer.cancel_log.size()); + + EXPECT_TRUE(observer.start_log[3] == + TestHostResolverObserver::StartOrCancelEntry(4, info3_)); +} + +} // namespace net diff --git a/net/base/dns_test_util.cc b/net/base/dns_test_util.cc index f448b6f..d7f6803 100644 --- a/net/base/dns_test_util.cc +++ b/net/base/dns_test_util.cc @@ -22,6 +22,41 @@ int TestPrng::GetNext(int min, int max) { return rv; } +bool operator==(const HostResolver::RequestInfo& a, + const HostResolver::RequestInfo& b) { + return a.hostname() == b.hostname() && + a.port() == b.port() && + a.allow_cached_response() == b.allow_cached_response() && + a.priority() == b.priority() && + a.is_speculative() == b.is_speculative() && + a.referrer() == b.referrer(); +} + +TestHostResolverObserver::TestHostResolverObserver() { +} + +TestHostResolverObserver::~TestHostResolverObserver() { +} + +void TestHostResolverObserver::OnStartResolution( + int id, + const HostResolver::RequestInfo& info) { + start_log.push_back(StartOrCancelEntry(id, info)); +} + +void TestHostResolverObserver::OnFinishResolutionWithStatus( + int id, + bool was_resolved, + const HostResolver::RequestInfo& info) { + finish_log.push_back(FinishEntry(id, was_resolved, info)); +} + +void TestHostResolverObserver::OnCancelResolution( + int id, + const HostResolver::RequestInfo& info) { + cancel_log.push_back(StartOrCancelEntry(id, info)); +} + bool ConvertStringsToIPAddressList( const char* const ip_strings[], size_t size, IPAddressList* address_list) { DCHECK(address_list); diff --git a/net/base/dns_test_util.h b/net/base/dns_test_util.h index 96ce38a..04d8652 100644 --- a/net/base/dns_test_util.h +++ b/net/base/dns_test_util.h @@ -12,6 +12,7 @@ #include "base/logging.h" #include "net/base/dns_transaction.h" #include "net/base/dns_util.h" +#include "net/base/host_resolver.h" #include "net/base/ip_endpoint.h" #include "net/base/net_util.h" @@ -34,6 +35,62 @@ class TestPrng { DISALLOW_COPY_AND_ASSIGN(TestPrng); }; +bool operator==(const HostResolver::RequestInfo& a, + const HostResolver::RequestInfo& b); + +// Observer that just makes note of how it was called. The test code can then +// inspect to make sure it was called with the right parameters. Used by +// HostResolverImpl and AsyncHostResolver unit tests. +class TestHostResolverObserver : public HostResolver::Observer { + public: + TestHostResolverObserver(); + virtual ~TestHostResolverObserver(); + + // HostResolver::Observer methods: + virtual void OnStartResolution(int id, const HostResolver::RequestInfo& info); + virtual void OnFinishResolutionWithStatus( + int id, + bool was_resolved, + const HostResolver::RequestInfo& info); + virtual void OnCancelResolution( + int id, + const HostResolver::RequestInfo& info); + + // Tuple (id, info). + struct StartOrCancelEntry { + StartOrCancelEntry(int id, const HostResolver::RequestInfo& info) + : id(id), info(info) {} + + bool operator==(const StartOrCancelEntry& other) const { + return id == other.id && info == other.info; + } + + int id; + HostResolver::RequestInfo info; + }; + + // Tuple (id, was_resolved, info). + struct FinishEntry { + FinishEntry(int id, bool was_resolved, + const HostResolver::RequestInfo& info) + : id(id), was_resolved(was_resolved), info(info) {} + + bool operator==(const FinishEntry& other) const { + return id == other.id && + was_resolved == other.was_resolved && + info == other.info; + } + + int id; + bool was_resolved; + HostResolver::RequestInfo info; + }; + + std::vector<StartOrCancelEntry> start_log; + std::vector<FinishEntry> finish_log; + std::vector<StartOrCancelEntry> cancel_log; +}; + // A utility function for tests that given an array of IP literals, // converts it to an IPAddressList. bool ConvertStringsToIPAddressList( @@ -47,29 +104,26 @@ static const char kDnsIp[] = "192.168.1.1"; static const uint16 kDnsPort = 53; //----------------------------------------------------------------------------- -// Query/response set for www.google.com, ID is fixed to 1. - -static const uint16 kT1Qtype = kDNS_A; - -static const char kT1DnsName[] = { +// Query/response set for www.google.com, ID is fixed to 0. +static const char kT0HostName[] = "www.google.com"; +static const uint16 kT0Qtype = kDNS_A; +static const char kT0DnsName[] = { 0x03, 'w', 'w', 'w', 0x06, 'g', 'o', 'o', 'g', 'l', 'e', 0x03, 'c', 'o', 'm', 0x00 }; - -static const uint8 kT1QueryDatagram[] = { +static const uint8 kT0QueryDatagram[] = { // query for www.google.com, type A. - 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01 }; - -static const uint8 kT1ResponseDatagram[] = { +static const uint8 kT0ResponseDatagram[] = { // response contains one CNAME for www.l.google.com and the following // IP addresses: 74.125.226.{179,180,176,177,178} - 0x00, 0x01, 0x81, 0x80, 0x00, 0x01, 0x00, 0x06, + 0x00, 0x00, 0x81, 0x80, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, @@ -87,37 +141,34 @@ static const uint8 kT1ResponseDatagram[] = { 0x00, 0x01, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x04, 0x4a, 0x7d, 0xe2, 0xb2 }; - -static const char* const kT1IpAddresses[] = { +static const char* const kT0IpAddresses[] = { "74.125.226.179", "74.125.226.180", "74.125.226.176", "74.125.226.177", "74.125.226.178" }; //----------------------------------------------------------------------------- -// Query/response set for codereview.chromium.org, ID is fixed to 2. -static const uint16 kT2Qtype = kDNS_A; - -static const char kT2DnsName[] = { +// Query/response set for codereview.chromium.org, ID is fixed to 1. +static const char kT1HostName[] = "codereview.chromium.org"; +static const uint16 kT1Qtype = kDNS_A; +static const char kT1DnsName[] = { 0x12, 'c', 'o', 'd', 'e', 'r', 'e', 'v', 'i', 'e', 'w', 0x10, 'c', 'h', 'r', 'o', 'm', 'i', 'u', 'm', 0x03, 'o', 'r', 'g', 0x00 }; - -static const uint8 kT2QueryDatagram[] = { +static const uint8 kT1QueryDatagram[] = { // query for codereview.chromium.org, type A. - 0x00, 0x02, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x08, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x69, 0x75, 0x6d, 0x03, 0x6f, 0x72, 0x67, 0x00, 0x00, 0x01, 0x00, 0x01 }; - -static const uint8 kT2ResponseDatagram[] = { +static const uint8 kT1ResponseDatagram[] = { // response contains one CNAME for ghs.l.google.com and the following // IP address: 64.233.169.121 - 0x00, 0x02, 0x81, 0x80, 0x00, 0x01, 0x00, 0x02, + 0x00, 0x01, 0x81, 0x80, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x08, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x69, 0x75, 0x6d, @@ -129,8 +180,92 @@ static const uint8 kT2ResponseDatagram[] = { 0x35, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x0b, 0x00, 0x04, 0x40, 0xe9, 0xa9, 0x79 }; +static const char* const kT1IpAddresses[] = { + "64.233.169.121" +}; //----------------------------------------------------------------------------- +// Query/response set for www.ccs.neu.edu, ID is fixed to 2. +static const char kT2HostName[] = "www.ccs.neu.edu"; +static const uint16 kT2Qtype = kDNS_A; +static const char kT2DnsName[] = { + 0x03, 'w', 'w', 'w', + 0x03, 'c', 'c', 'c', + 0x03, 'n', 'e', 'u', + 0x03, 'e', 'd', 'u', + 0x00 +}; +static const uint8 kT2QueryDatagram[] = { + // query for www.ccs.neu.edu, type A. + 0x00, 0x02, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x77, 0x77, 0x77, + 0x03, 0x63, 0x63, 0x73, 0x03, 0x6e, 0x65, 0x75, + 0x03, 0x65, 0x64, 0x75, 0x00, 0x00, 0x01, 0x00, + 0x01 +}; +static const uint8 kT2ResponseDatagram[] = { + // response contains one CNAME for vulcan.ccs.neu.edu and the following + // IP address: 129.10.116.81 + 0x00, 0x02, 0x81, 0x80, 0x00, 0x01, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x77, 0x77, 0x77, + 0x03, 0x63, 0x63, 0x73, 0x03, 0x6e, 0x65, 0x75, + 0x03, 0x65, 0x64, 0x75, 0x00, 0x00, 0x01, 0x00, + 0x01, 0xc0, 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x2c, 0x00, 0x09, 0x06, 0x76, 0x75, + 0x6c, 0x63, 0x61, 0x6e, 0xc0, 0x10, 0xc0, 0x2d, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x2c, + 0x00, 0x04, 0x81, 0x0a, 0x74, 0x51 +}; +static const char* const kT2IpAddresses[] = { + "129.10.116.81" +}; + +//----------------------------------------------------------------------------- +// Query/response set for www.google.az, ID is fixed to 3. +static const char kT3HostName[] = "www.google.az"; +static const uint16 kT3Qtype = kDNS_A; +static const char kT3DnsName[] = { + 0x03, 'w', 'w', 'w', + 0x06, 'g', 'o', 'o', 'g', 'l', 'e', + 0x02, 'a', 'z', + 0x00 +}; +static const uint8 kT3QueryDatagram[] = { + // query for www.google.az, type A. + 0x00, 0x03, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x77, 0x77, 0x77, + 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x02, + 0x61, 0x7a, 0x00, 0x00, 0x01, 0x00, 0x01 +}; +static const uint8 kT3ResponseDatagram[] = { + // response contains www.google.com as CNAME for www.google.az and + // www.l.google.com as CNAME for www.google.com and the following + // IP addresses: 74.125.226.{178,179,180,176,177} + 0x00, 0x03, 0x81, 0x80, 0x00, 0x01, 0x00, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x77, 0x77, 0x77, + 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x02, + 0x61, 0x7a, 0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, + 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00, 0x01, 0x50, + 0x99, 0x00, 0x10, 0x03, 0x77, 0x77, 0x77, 0x06, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, + 0x6f, 0x6d, 0x00, 0xc0, 0x2b, 0x00, 0x05, 0x00, + 0x01, 0x00, 0x01, 0x50, 0x99, 0x00, 0x08, 0x03, + 0x77, 0x77, 0x77, 0x01, 0x6c, 0xc0, 0x2f, 0xc0, + 0x47, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x04, 0x4a, 0x7d, 0xe2, 0xb2, 0xc0, + 0x47, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x04, 0x4a, 0x7d, 0xe2, 0xb3, 0xc0, + 0x47, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x04, 0x4a, 0x7d, 0xe2, 0xb4, 0xc0, + 0x47, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x04, 0x4a, 0x7d, 0xe2, 0xb0, 0xc0, + 0x47, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x04, 0x4a, 0x7d, 0xe2, 0xb1 +}; +static const char* const kT3IpAddresses[] = { + "74.125.226.178", "74.125.226.179", "74.125.226.180", + "74.125.226.176", "74.125.226.177" +}; } // namespace net diff --git a/net/base/dns_transaction_unittest.cc b/net/base/dns_transaction_unittest.cc index 6c532f2..2e557e9 100644 --- a/net/base/dns_transaction_unittest.cc +++ b/net/base/dns_transaction_unittest.cc @@ -53,48 +53,48 @@ class TestDelegate : public DnsTransaction::Delegate { TEST(DnsTransactionTest, NormalQueryResponseTest) { - MockWrite writes1[] = { - MockWrite(true, reinterpret_cast<const char*>(kT1QueryDatagram), - arraysize(kT1QueryDatagram)) + MockWrite writes0[] = { + MockWrite(true, reinterpret_cast<const char*>(kT0QueryDatagram), + arraysize(kT0QueryDatagram)) }; - MockRead reads1[] = { - MockRead(true, reinterpret_cast<const char*>(kT1ResponseDatagram), - arraysize(kT1ResponseDatagram)) + MockRead reads0[] = { + MockRead(true, reinterpret_cast<const char*>(kT0ResponseDatagram), + arraysize(kT0ResponseDatagram)) }; - StaticSocketDataProvider data(reads1, arraysize(reads1), - writes1, arraysize(writes1)); + StaticSocketDataProvider data(reads0, arraysize(reads0), + writes0, arraysize(writes0)); MockClientSocketFactory factory; factory.AddSocketDataProvider(&data); - TestPrng test_prng(std::deque<int>(1, 1)); + TestPrng test_prng(std::deque<int>(1, 0)); RandIntCallback rand_int_cb = base::Bind(&TestPrng::GetNext, base::Unretained(&test_prng)); - std::string t1_dns_name(kT1DnsName, arraysize(kT1DnsName)); + std::string t0_dns_name(kT0DnsName, arraysize(kT0DnsName)); IPEndPoint dns_server; - bool rv0 = CreateDnsAddress(kDnsIp, kDnsPort, &dns_server); - ASSERT_TRUE(rv0); + bool rv = CreateDnsAddress(kDnsIp, kDnsPort, &dns_server); + ASSERT_TRUE(rv); - DnsTransaction t(dns_server, t1_dns_name, kT1Qtype, rand_int_cb, &factory, + DnsTransaction t(dns_server, t0_dns_name, kT1Qtype, rand_int_cb, &factory, BoundNetLog(), NULL); TestDelegate delegate; t.SetDelegate(&delegate); IPAddressList expected_ip_addresses; - rv0 = ConvertStringsToIPAddressList(kT1IpAddresses, - arraysize(kT1IpAddresses), - &expected_ip_addresses); - ASSERT_TRUE(rv0); + rv = ConvertStringsToIPAddressList(kT0IpAddresses, + arraysize(kT0IpAddresses), + &expected_ip_addresses); + ASSERT_TRUE(rv); - int rv = t.Start(); - EXPECT_EQ(ERR_IO_PENDING, rv); + int rv0 = t.Start(); + EXPECT_EQ(ERR_IO_PENDING, rv0); MessageLoop::current()->Run(); - EXPECT_TRUE(DnsTransaction::Key(t1_dns_name, kT1Qtype) == t.key()); + EXPECT_TRUE(DnsTransaction::Key(t0_dns_name, kT0Qtype) == t.key()); EXPECT_EQ(OK, delegate.result()); EXPECT_EQ(&t, delegate.transaction()); EXPECT_TRUE(expected_ip_addresses == delegate.ip_addresses()); @@ -104,42 +104,42 @@ TEST(DnsTransactionTest, NormalQueryResponseTest) { } TEST(DnsTransactionTest, MismatchedQueryResponseTest) { - MockWrite writes1[] = { - MockWrite(true, reinterpret_cast<const char*>(kT1QueryDatagram), - arraysize(kT1QueryDatagram)) + MockWrite writes0[] = { + MockWrite(true, reinterpret_cast<const char*>(kT0QueryDatagram), + arraysize(kT0QueryDatagram)) }; - MockRead reads2[] = { - MockRead(true, reinterpret_cast<const char*>(kT2ResponseDatagram), - arraysize(kT2ResponseDatagram)) + MockRead reads1[] = { + MockRead(true, reinterpret_cast<const char*>(kT1ResponseDatagram), + arraysize(kT1ResponseDatagram)) }; - StaticSocketDataProvider data(reads2, arraysize(reads2), - writes1, arraysize(writes1)); + StaticSocketDataProvider data(reads1, arraysize(reads1), + writes0, arraysize(writes0)); MockClientSocketFactory factory; factory.AddSocketDataProvider(&data); - TestPrng test_prng(std::deque<int>(1, 1)); + TestPrng test_prng(std::deque<int>(1, 0)); RandIntCallback rand_int_cb = base::Bind(&TestPrng::GetNext, base::Unretained(&test_prng)); - std::string t1_dns_name(kT1DnsName, arraysize(kT1DnsName)); + std::string t0_dns_name(kT0DnsName, arraysize(kT0DnsName)); IPEndPoint dns_server; - bool rv0 = CreateDnsAddress(kDnsIp, kDnsPort, &dns_server); - ASSERT_TRUE(rv0); + bool rv = CreateDnsAddress(kDnsIp, kDnsPort, &dns_server); + ASSERT_TRUE(rv); - DnsTransaction t(dns_server, t1_dns_name, kT1Qtype, rand_int_cb, &factory, + DnsTransaction t(dns_server, t0_dns_name, kT1Qtype, rand_int_cb, &factory, BoundNetLog(), NULL); TestDelegate delegate; t.SetDelegate(&delegate); - int rv = t.Start(); - EXPECT_EQ(ERR_IO_PENDING, rv); + int rv0 = t.Start(); + EXPECT_EQ(ERR_IO_PENDING, rv0); MessageLoop::current()->Run(); - EXPECT_TRUE(DnsTransaction::Key(t1_dns_name, kT1Qtype) == t.key()); + EXPECT_TRUE(DnsTransaction::Key(t0_dns_name, kT0Qtype) == t.key()); EXPECT_EQ(ERR_DNS_MALFORMED_RESPONSE, delegate.result()); EXPECT_EQ(0u, delegate.ip_addresses().size()); EXPECT_EQ(&t, delegate.transaction()); @@ -150,35 +150,35 @@ TEST(DnsTransactionTest, MismatchedQueryResponseTest) { // Test that after the first timeout we do a fresh connection and if we get // a response on the new connection, we return it. TEST(DnsTransactionTest, FirstTimeoutTest) { - MockWrite writes1[] = { - MockWrite(true, reinterpret_cast<const char*>(kT1QueryDatagram), - arraysize(kT1QueryDatagram)) + MockWrite writes0[] = { + MockWrite(true, reinterpret_cast<const char*>(kT0QueryDatagram), + arraysize(kT0QueryDatagram)) }; - MockRead reads1[] = { - MockRead(true, reinterpret_cast<const char*>(kT1ResponseDatagram), - arraysize(kT1ResponseDatagram)) + MockRead reads0[] = { + MockRead(true, reinterpret_cast<const char*>(kT0ResponseDatagram), + arraysize(kT0ResponseDatagram)) }; + scoped_refptr<DelayedSocketData> socket0_data( + new DelayedSocketData(2, NULL, 0, writes0, arraysize(writes0))); scoped_refptr<DelayedSocketData> socket1_data( - new DelayedSocketData(2, NULL, 0, writes1, arraysize(writes1))); - scoped_refptr<DelayedSocketData> socket2_data( - new DelayedSocketData(0, reads1, arraysize(reads1), - writes1, arraysize(writes1))); + new DelayedSocketData(0, reads0, arraysize(reads0), + writes0, arraysize(writes0))); MockClientSocketFactory factory; + factory.AddSocketDataProvider(socket0_data.get()); factory.AddSocketDataProvider(socket1_data.get()); - factory.AddSocketDataProvider(socket2_data.get()); - TestPrng test_prng(std::deque<int>(2, 1)); + TestPrng test_prng(std::deque<int>(2, 0)); RandIntCallback rand_int_cb = base::Bind(&TestPrng::GetNext, base::Unretained(&test_prng)); - std::string t1_dns_name(kT1DnsName, arraysize(kT1DnsName)); + std::string t0_dns_name(kT0DnsName, arraysize(kT0DnsName)); IPEndPoint dns_server; - bool rv0 = CreateDnsAddress(kDnsIp, kDnsPort, &dns_server); - ASSERT_TRUE(rv0); + bool rv = CreateDnsAddress(kDnsIp, kDnsPort, &dns_server); + ASSERT_TRUE(rv); - DnsTransaction t(dns_server, t1_dns_name, kT1Qtype, rand_int_cb, &factory, + DnsTransaction t(dns_server, t0_dns_name, kT1Qtype, rand_int_cb, &factory, BoundNetLog(), NULL); TestDelegate delegate; @@ -189,25 +189,26 @@ TEST(DnsTransactionTest, FirstTimeoutTest) { kTimeoutsMs + arraysize(kTimeoutsMs))); IPAddressList expected_ip_addresses; - rv0 = ConvertStringsToIPAddressList(kT1IpAddresses, - arraysize(kT1IpAddresses), - &expected_ip_addresses); - ASSERT_TRUE(rv0); + rv = ConvertStringsToIPAddressList(kT0IpAddresses, + arraysize(kT0IpAddresses), + &expected_ip_addresses); + ASSERT_TRUE(rv); + + int rv0 = t.Start(); + EXPECT_EQ(ERR_IO_PENDING, rv0); - int rv = t.Start(); - EXPECT_EQ(ERR_IO_PENDING, rv); MessageLoop::current()->Run(); - EXPECT_TRUE(DnsTransaction::Key(t1_dns_name, kT1Qtype) == t.key()); + EXPECT_TRUE(DnsTransaction::Key(t0_dns_name, kT0Qtype) == t.key()); EXPECT_EQ(OK, delegate.result()); EXPECT_EQ(&t, delegate.transaction()); EXPECT_TRUE(expected_ip_addresses == delegate.ip_addresses()); + EXPECT_TRUE(socket0_data->at_read_eof()); + EXPECT_TRUE(socket0_data->at_write_eof()); EXPECT_TRUE(socket1_data->at_read_eof()); EXPECT_TRUE(socket1_data->at_write_eof()); - EXPECT_TRUE(socket2_data->at_read_eof()); - EXPECT_TRUE(socket2_data->at_write_eof()); EXPECT_EQ(2u, factory.udp_client_sockets().size()); } @@ -215,38 +216,38 @@ TEST(DnsTransactionTest, FirstTimeoutTest) { // the second timeout we do another fresh connection, and if we get a // response on the second connection, we return it. TEST(DnsTransactionTest, SecondTimeoutTest) { - MockWrite writes1[] = { - MockWrite(true, reinterpret_cast<const char*>(kT1QueryDatagram), - arraysize(kT1QueryDatagram)) + MockWrite writes0[] = { + MockWrite(true, reinterpret_cast<const char*>(kT0QueryDatagram), + arraysize(kT0QueryDatagram)) }; - MockRead reads1[] = { - MockRead(true, reinterpret_cast<const char*>(kT1ResponseDatagram), - arraysize(kT1ResponseDatagram)) + MockRead reads0[] = { + MockRead(true, reinterpret_cast<const char*>(kT0ResponseDatagram), + arraysize(kT0ResponseDatagram)) }; + scoped_refptr<DelayedSocketData> socket0_data( + new DelayedSocketData(2, NULL, 0, writes0, arraysize(writes0))); scoped_refptr<DelayedSocketData> socket1_data( - new DelayedSocketData(2, NULL, 0, writes1, arraysize(writes1))); + new DelayedSocketData(2, NULL, 0, writes0, arraysize(writes0))); scoped_refptr<DelayedSocketData> socket2_data( - new DelayedSocketData(2, NULL, 0, writes1, arraysize(writes1))); - scoped_refptr<DelayedSocketData> socket3_data( - new DelayedSocketData(0, reads1, arraysize(reads1), - writes1, arraysize(writes1))); + new DelayedSocketData(0, reads0, arraysize(reads0), + writes0, arraysize(writes0))); MockClientSocketFactory factory; + factory.AddSocketDataProvider(socket0_data.get()); factory.AddSocketDataProvider(socket1_data.get()); factory.AddSocketDataProvider(socket2_data.get()); - factory.AddSocketDataProvider(socket3_data.get()); - TestPrng test_prng(std::deque<int>(3, 1)); + TestPrng test_prng(std::deque<int>(3, 0)); RandIntCallback rand_int_cb = base::Bind(&TestPrng::GetNext, base::Unretained(&test_prng)); - std::string t1_dns_name(kT1DnsName, arraysize(kT1DnsName)); + std::string t0_dns_name(kT0DnsName, arraysize(kT0DnsName)); IPEndPoint dns_server; - bool rv0 = CreateDnsAddress(kDnsIp, kDnsPort, &dns_server); - ASSERT_TRUE(rv0); + bool rv = CreateDnsAddress(kDnsIp, kDnsPort, &dns_server); + ASSERT_TRUE(rv); - DnsTransaction t(dns_server, t1_dns_name, kT1Qtype, rand_int_cb, &factory, + DnsTransaction t(dns_server, t0_dns_name, kT1Qtype, rand_int_cb, &factory, BoundNetLog(), NULL); TestDelegate delegate; @@ -257,27 +258,27 @@ TEST(DnsTransactionTest, SecondTimeoutTest) { kTimeoutsMs + arraysize(kTimeoutsMs))); IPAddressList expected_ip_addresses; - rv0 = ConvertStringsToIPAddressList(kT1IpAddresses, - arraysize(kT1IpAddresses), - &expected_ip_addresses); - ASSERT_TRUE(rv0); + rv = ConvertStringsToIPAddressList(kT0IpAddresses, + arraysize(kT0IpAddresses), + &expected_ip_addresses); + ASSERT_TRUE(rv); - int rv = t.Start(); - EXPECT_EQ(ERR_IO_PENDING, rv); + int rv0 = t.Start(); + EXPECT_EQ(ERR_IO_PENDING, rv0); MessageLoop::current()->Run(); - EXPECT_TRUE(DnsTransaction::Key(t1_dns_name, kT1Qtype) == t.key()); + EXPECT_TRUE(DnsTransaction::Key(t0_dns_name, kT1Qtype) == t.key()); EXPECT_EQ(OK, delegate.result()); EXPECT_EQ(&t, delegate.transaction()); EXPECT_TRUE(expected_ip_addresses == delegate.ip_addresses()); + EXPECT_TRUE(socket0_data->at_read_eof()); + EXPECT_TRUE(socket0_data->at_write_eof()); EXPECT_TRUE(socket1_data->at_read_eof()); EXPECT_TRUE(socket1_data->at_write_eof()); EXPECT_TRUE(socket2_data->at_read_eof()); EXPECT_TRUE(socket2_data->at_write_eof()); - EXPECT_TRUE(socket3_data->at_read_eof()); - EXPECT_TRUE(socket3_data->at_write_eof()); EXPECT_EQ(3u, factory.udp_client_sockets().size()); } @@ -285,32 +286,32 @@ TEST(DnsTransactionTest, SecondTimeoutTest) { // the second timeout we do another fresh connection and after the third // timeout we give up and return a timeout error. TEST(DnsTransactionTest, ThirdTimeoutTest) { - MockWrite writes1[] = { - MockWrite(true, reinterpret_cast<const char*>(kT1QueryDatagram), - arraysize(kT1QueryDatagram)) + MockWrite writes0[] = { + MockWrite(true, reinterpret_cast<const char*>(kT0QueryDatagram), + arraysize(kT0QueryDatagram)) }; + scoped_refptr<DelayedSocketData> socket0_data( + new DelayedSocketData(2, NULL, 0, writes0, arraysize(writes0))); scoped_refptr<DelayedSocketData> socket1_data( - new DelayedSocketData(2, NULL, 0, writes1, arraysize(writes1))); + new DelayedSocketData(2, NULL, 0, writes0, arraysize(writes0))); scoped_refptr<DelayedSocketData> socket2_data( - new DelayedSocketData(2, NULL, 0, writes1, arraysize(writes1))); - scoped_refptr<DelayedSocketData> socket3_data( - new DelayedSocketData(2, NULL, 0, writes1, arraysize(writes1))); + new DelayedSocketData(2, NULL, 0, writes0, arraysize(writes0))); MockClientSocketFactory factory; + factory.AddSocketDataProvider(socket0_data.get()); factory.AddSocketDataProvider(socket1_data.get()); factory.AddSocketDataProvider(socket2_data.get()); - factory.AddSocketDataProvider(socket3_data.get()); - TestPrng test_prng(std::deque<int>(3, 1)); + TestPrng test_prng(std::deque<int>(3, 0)); RandIntCallback rand_int_cb = base::Bind(&TestPrng::GetNext, base::Unretained(&test_prng)); - std::string t1_dns_name(kT1DnsName, arraysize(kT1DnsName)); + std::string t0_dns_name(kT0DnsName, arraysize(kT0DnsName)); IPEndPoint dns_server; - bool rv0 = CreateDnsAddress(kDnsIp, kDnsPort, &dns_server); - ASSERT_TRUE(rv0); + bool rv = CreateDnsAddress(kDnsIp, kDnsPort, &dns_server); + ASSERT_TRUE(rv); - DnsTransaction t(dns_server, t1_dns_name, kT1Qtype, rand_int_cb, &factory, + DnsTransaction t(dns_server, t0_dns_name, kT1Qtype, rand_int_cb, &factory, BoundNetLog(), NULL); TestDelegate delegate; @@ -320,21 +321,21 @@ TEST(DnsTransactionTest, ThirdTimeoutTest) { std::vector<base::TimeDelta>(kTimeoutsMs, kTimeoutsMs + arraysize(kTimeoutsMs))); - int rv = t.Start(); - EXPECT_EQ(ERR_IO_PENDING, rv); + int rv0 = t.Start(); + EXPECT_EQ(ERR_IO_PENDING, rv0); MessageLoop::current()->Run(); - EXPECT_TRUE(DnsTransaction::Key(t1_dns_name, kT1Qtype) == t.key()); + EXPECT_TRUE(DnsTransaction::Key(t0_dns_name, kT0Qtype) == t.key()); EXPECT_EQ(ERR_DNS_TIMED_OUT, delegate.result()); EXPECT_EQ(&t, delegate.transaction()); + EXPECT_TRUE(socket0_data->at_read_eof()); + EXPECT_TRUE(socket0_data->at_write_eof()); EXPECT_TRUE(socket1_data->at_read_eof()); EXPECT_TRUE(socket1_data->at_write_eof()); EXPECT_TRUE(socket2_data->at_read_eof()); EXPECT_TRUE(socket2_data->at_write_eof()); - EXPECT_TRUE(socket3_data->at_read_eof()); - EXPECT_TRUE(socket3_data->at_write_eof()); EXPECT_EQ(3u, factory.udp_client_sockets().size()); } diff --git a/net/base/host_resolver.h b/net/base/host_resolver.h index d46fcc3..77977c1 100644 --- a/net/base/host_resolver.h +++ b/net/base/host_resolver.h @@ -14,6 +14,7 @@ #include "net/base/completion_callback.h" #include "net/base/host_port_pair.h" #include "net/base/net_api.h" +#include "net/base/net_util.h" #include "net/base/request_priority.h" namespace net { @@ -209,6 +210,11 @@ NET_API HostResolver* CreateSystemHostResolver(size_t max_concurrent_resolves, size_t max_retry_attempts, NetLog* net_log); +// Creates a HostResolver implementation that sends actual DNS queries to +// the specified DNS server and parses response and returns results. +NET_API HostResolver* CreateAsyncHostResolver(size_t max_concurrent_resolves, + const IPAddressNumber& dns_ip, + NetLog* net_log); } // namespace net #endif // NET_BASE_HOST_RESOLVER_H_ diff --git a/net/base/host_resolver_impl_unittest.cc b/net/base/host_resolver_impl_unittest.cc index 52b7f89..a833599 100644 --- a/net/base/host_resolver_impl_unittest.cc +++ b/net/base/host_resolver_impl_unittest.cc @@ -16,6 +16,7 @@ #include "base/time.h" #include "net/base/address_list.h" #include "net/base/completion_callback.h" +#include "net/base/dns_test_util.h" #include "net/base/mock_host_resolver.h" #include "net/base/net_errors.h" #include "net/base/net_log_unittest.h" @@ -979,73 +980,6 @@ TEST_F(HostResolverImplTest, BypassCache) { MessageLoop::current()->Run(); } -bool operator==(const HostResolver::RequestInfo& a, - const HostResolver::RequestInfo& b) { - return a.hostname() == b.hostname() && - a.port() == b.port() && - a.allow_cached_response() == b.allow_cached_response() && - a.priority() == b.priority() && - a.is_speculative() == b.is_speculative() && - a.referrer() == b.referrer(); -} - -// Observer that just makes note of how it was called. The test code can then -// inspect to make sure it was called with the right parameters. -class CapturingObserver : public HostResolver::Observer { - public: - // DnsResolutionObserver methods: - virtual void OnStartResolution(int id, - const HostResolver::RequestInfo& info) { - start_log.push_back(StartOrCancelEntry(id, info)); - } - - virtual void OnFinishResolutionWithStatus( - int id, - bool was_resolved, - const HostResolver::RequestInfo& info) { - finish_log.push_back(FinishEntry(id, was_resolved, info)); - } - - virtual void OnCancelResolution(int id, - const HostResolver::RequestInfo& info) { - cancel_log.push_back(StartOrCancelEntry(id, info)); - } - - // Tuple (id, info). - struct StartOrCancelEntry { - StartOrCancelEntry(int id, const HostResolver::RequestInfo& info) - : id(id), info(info) {} - - bool operator==(const StartOrCancelEntry& other) const { - return id == other.id && info == other.info; - } - - int id; - HostResolver::RequestInfo info; - }; - - // Tuple (id, was_resolved, info). - struct FinishEntry { - FinishEntry(int id, bool was_resolved, - const HostResolver::RequestInfo& info) - : id(id), was_resolved(was_resolved), info(info) {} - - bool operator==(const FinishEntry& other) const { - return id == other.id && - was_resolved == other.was_resolved && - info == other.info; - } - - int id; - bool was_resolved; - HostResolver::RequestInfo info; - }; - - std::vector<StartOrCancelEntry> start_log; - std::vector<FinishEntry> finish_log; - std::vector<StartOrCancelEntry> cancel_log; -}; - // Test that registering, unregistering, and notifying of observers works. // Does not test the cancellation notification since all resolves are // synchronous. @@ -1053,7 +987,7 @@ TEST_F(HostResolverImplTest, Observers) { scoped_ptr<HostResolver> host_resolver( CreateHostResolverImpl(NULL)); - CapturingObserver observer; + TestHostResolverObserver observer; host_resolver->AddObserver(&observer); @@ -1078,9 +1012,9 @@ TEST_F(HostResolverImplTest, Observers) { EXPECT_EQ(1U, observer.finish_log.size()); EXPECT_EQ(0U, observer.cancel_log.size()); EXPECT_TRUE(observer.start_log[0] == - CapturingObserver::StartOrCancelEntry(0, info1)); + TestHostResolverObserver::StartOrCancelEntry(0, info1)); EXPECT_TRUE(observer.finish_log[0] == - CapturingObserver::FinishEntry(0, true, info1)); + TestHostResolverObserver::FinishEntry(0, true, info1)); // Resolve "host1" again -- this time it will be served from cache, but it // should still notify of completion. @@ -1092,9 +1026,9 @@ TEST_F(HostResolverImplTest, Observers) { EXPECT_EQ(2U, observer.finish_log.size()); EXPECT_EQ(0U, observer.cancel_log.size()); EXPECT_TRUE(observer.start_log[1] == - CapturingObserver::StartOrCancelEntry(1, info1)); + TestHostResolverObserver::StartOrCancelEntry(1, info1)); EXPECT_TRUE(observer.finish_log[1] == - CapturingObserver::FinishEntry(1, true, info1)); + TestHostResolverObserver::FinishEntry(1, true, info1)); // Resolve "host2", setting referrer to "http://foobar.com" HostResolver::RequestInfo info2(HostPortPair("host2", 70)); @@ -1106,9 +1040,9 @@ TEST_F(HostResolverImplTest, Observers) { EXPECT_EQ(3U, observer.finish_log.size()); EXPECT_EQ(0U, observer.cancel_log.size()); EXPECT_TRUE(observer.start_log[2] == - CapturingObserver::StartOrCancelEntry(2, info2)); + TestHostResolverObserver::StartOrCancelEntry(2, info2)); EXPECT_TRUE(observer.finish_log[2] == - CapturingObserver::FinishEntry(2, true, info2)); + TestHostResolverObserver::FinishEntry(2, true, info2)); // Unregister the observer. host_resolver->RemoveObserver(&observer); @@ -1128,7 +1062,7 @@ TEST_F(HostResolverImplTest, Observers) { // (1) Delete the HostResolver while job is outstanding. // (2) Call HostResolver::CancelRequest() while a request is outstanding. TEST_F(HostResolverImplTest, CancellationObserver) { - CapturingObserver observer; + TestHostResolverObserver observer; { // Create a host resolver and attach an observer. scoped_ptr<HostResolver> host_resolver( @@ -1155,7 +1089,7 @@ TEST_F(HostResolverImplTest, CancellationObserver) { EXPECT_EQ(0U, observer.cancel_log.size()); EXPECT_TRUE(observer.start_log[0] == - CapturingObserver::StartOrCancelEntry(0, info1)); + TestHostResolverObserver::StartOrCancelEntry(0, info1)); // Cancel the request. host_resolver->CancelRequest(req); @@ -1165,7 +1099,7 @@ TEST_F(HostResolverImplTest, CancellationObserver) { EXPECT_EQ(1U, observer.cancel_log.size()); EXPECT_TRUE(observer.cancel_log[0] == - CapturingObserver::StartOrCancelEntry(0, info1)); + TestHostResolverObserver::StartOrCancelEntry(0, info1)); // Start an async request for (host2:60) HostResolver::RequestInfo info2(HostPortPair("host2", 60)); @@ -1179,7 +1113,7 @@ TEST_F(HostResolverImplTest, CancellationObserver) { EXPECT_EQ(1U, observer.cancel_log.size()); EXPECT_TRUE(observer.start_log[1] == - CapturingObserver::StartOrCancelEntry(1, info2)); + TestHostResolverObserver::StartOrCancelEntry(1, info2)); // Upon exiting this scope, HostResolver is destroyed, so all requests are // implicitly cancelled. @@ -1194,7 +1128,7 @@ TEST_F(HostResolverImplTest, CancellationObserver) { HostResolver::RequestInfo info(HostPortPair("host2", 60)); EXPECT_TRUE(observer.cancel_log[1] == - CapturingObserver::StartOrCancelEntry(1, info)); + TestHostResolverObserver::StartOrCancelEntry(1, info)); } // Test that IP address changes flush the cache. @@ -1364,7 +1298,7 @@ TEST_F(HostResolverImplTest, HigherPriorityRequestsStartedFirst) { new HostResolverImpl(resolver_proc, CreateDefaultCache(), kMaxJobs, kRetryAttempts, NULL)); - CapturingObserver observer; + TestHostResolverObserver observer; host_resolver->AddObserver(&observer); // Note that at this point the CapturingHostResolverProc is blocked, so any diff --git a/net/base/net_log_event_type_list.h b/net/base/net_log_event_type_list.h index f0c2333..8e57c96 100644 --- a/net/base/net_log_event_type_list.h +++ b/net/base/net_log_event_type_list.h @@ -1077,8 +1077,8 @@ EVENT_TYPE(THROTTLING_GOT_CUSTOM_RETRY_AFTER) // // { // "net_error": <The net error code for the failure>, -// "ip_address_list": <The result of the resolution process -- list of IP -// addresses>, +// "ip_address_list": <The result of the resolution process, +// an IPAddressList> // } EVENT_TYPE(DNS_TRANSACTION) @@ -1093,3 +1093,39 @@ EVENT_TYPE(DNS_TRANSACTION) // attempt>, // } EVENT_TYPE(DNS_TRANSACTION_ATTEMPT_STARTED) + +// ------------------------------------------------------------------------ +// AsyncHostResolver +// ------------------------------------------------------------------------ + +// The start/end of waiting on a host resolve (DNS) request. +// The BEGIN phase contains the following parameters: +// +// { +// "source_dependency": <Source id of the request being waited on>, +// } +EVENT_TYPE(ASYNC_HOST_RESOLVER) + +// The start/end of a host resolve (DNS) request. +// +// The BEGIN phase contains the following parameters: +// +// { +// "hostname": <Hostname associated with the request>, +// "address_family": <Address family of the request>, +// "allow_cached_response": <Whether to allow cached response>, +// "only_use_cached_response": <Use cached results only>, +// "is_speculative": <Whether the lookup is speculative>, +// "priority": <Priority of the request>, +// "source_dependency": <Source id, if any, of what created the request>, +// } +// +// The END phase will contain this parameter: +// { +// "net_error": <The net error code integer>, +// } +EVENT_TYPE(ASYNC_HOST_RESOLVER_REQUEST) + +// This event is created when a new DnsTransaction is about to be created +// for a request. +EVENT_TYPE(ASYNC_HOST_RESOLVER_CREATE_DNS_TRANSACTION) diff --git a/net/base/net_log_source_type_list.h b/net/base/net_log_source_type_list.h index 2fcd534..de80d14 100644 --- a/net/base/net_log_source_type_list.h +++ b/net/base/net_log_source_type_list.h @@ -21,5 +21,6 @@ SOURCE_TYPE(MEMORY_CACHE_ENTRY, 10) SOURCE_TYPE(HTTP_STREAM_JOB, 11) SOURCE_TYPE(EXPONENTIAL_BACKOFF_THROTTLING, 12) SOURCE_TYPE(DNS_TRANSACTION, 13) +SOURCE_TYPE(ASYNC_HOST_RESOLVER_REQUEST, 14) -SOURCE_TYPE(COUNT, 14) // Always keep this as the last entry. +SOURCE_TYPE(COUNT, 15) // Always keep this as the last entry. diff --git a/net/net.gyp b/net/net.gyp index f83cc76..12cfe91 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -31,6 +31,8 @@ 'base/address_list_net_log_param.h', 'base/asn1_util.cc', 'base/asn1_util.h', + 'base/async_host_resolver.cc', + 'base/async_host_resolver.h', 'base/auth.cc', 'base/auth.h', 'base/backoff_entry.cc', @@ -872,6 +874,7 @@ ], 'sources': [ 'base/address_list_unittest.cc', + 'base/async_host_resolver_unittest.cc', 'base/backoff_entry_unittest.cc', 'base/cert_database_nss_unittest.cc', 'base/cert_verifier_unittest.cc', |