diff options
author | agayev@chromium.org <agayev@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-22 15:58:20 +0000 |
---|---|---|
committer | agayev@chromium.org <agayev@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-22 15:58:20 +0000 |
commit | 4b0112ab742b96585b897269dd27cc7818b3f129 (patch) | |
tree | 0ea379258252b88f5e323b9c6f547f8b3c2500fa /net/dns/async_host_resolver.cc | |
parent | 17bf5d37a85eff17c372db57ef37cc4b57f28b53 (diff) | |
download | chromium_src-4b0112ab742b96585b897269dd27cc7818b3f129.zip chromium_src-4b0112ab742b96585b897269dd27cc7818b3f129.tar.gz chromium_src-4b0112ab742b96585b897269dd27cc7818b3f129.tar.bz2 |
File reorganization: move AsyncHostResolver files to net/dns.
BUG=60149
TEST=net_unittest --gtest_filter="AsyncHostResolver*"
Review URL: http://codereview.chromium.org/7484012
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@93644 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/dns/async_host_resolver.cc')
-rw-r--r-- | net/dns/async_host_resolver.cc | 484 |
1 files changed, 484 insertions, 0 deletions
diff --git a/net/dns/async_host_resolver.cc b/net/dns/async_host_resolver.cc new file mode 100644 index 0000000..272e2b2 --- /dev/null +++ b/net/dns/async_host_resolver.cc @@ -0,0 +1,484 @@ +// 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/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), + HostCache::CreateDefaultCache(), + 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_; } + void set_addresses(const AddressList& addresses) { *addresses_ = 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, + HostCache* cache, + 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), + cache_(cache), + factory_(factory), + next_request_id_(0), + net_log_(net_log) { +} + +AsyncHostResolver::~AsyncHostResolver() { + // Destroy request lists. + for (KeyRequestListMap::iterator it = requestlist_map_.begin(); + it != requestlist_map_.end(); ++it) + STLDeleteElements(&it->second); + + // Destroy transactions. + STLDeleteElements(&transactions_); + + // Destroy pending requests. + for (size_t i = 0; i < arraysize(pending_requests_); ++i) + STLDeleteElements(&pending_requests_[i]); +} + +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; + + Request* request = CreateNewRequest( + info, dns_name, callback, addresses, source_net_log); + + OnStart(request); + if (rv == ERR_UNEXPECTED) { + if (ServeFromCache(request)) + rv = OK; + else if (info.only_use_cached_response()) + rv = ERR_NAME_NOT_RESOLVED; + } + + if (rv != ERR_UNEXPECTED) { + OnFinish(request, rv); + delete request; + return rv; // Synchronous resolution ends here. + } + + 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); + } + + // It is possible that the requests that caused |transaction| to be + // created are cancelled by the time |transaction| completes. In that + // case |requests| would be empty. We are knowingly throwing away the + // result of a DNS resolution in that case, because (a) if there are no + // requests, we do not have info to obtain a key from, (b) DnsTransaction + // does not have info(), adding one into it just temporarily doesn't make + // sense, since HostCache will be replaced with RR cache soon, (c) + // recreating info from DnsTransaction::Key adds a lot of temporary + // code/functions (like converting back from qtype to AddressFamily.) + // Also, we only cache positive results. All of this will change when RR + // cache is added. + if (result == OK && cache_.get() && !requests.empty()) { + Request* request = requests.front(); + HostResolver::RequestInfo info = request->info(); + HostCache::Key key( + info.hostname(), info.address_family(), info.host_resolver_flags()); + AddressList addrlist = + AddressList::CreateFromIPAddressList(ip_addresses, info.port()); + cache_->Set(key, result, addrlist, base::TimeTicks::Now()); + } + + // 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(); +} + +bool AsyncHostResolver::ServeFromCache(Request* request) const { + // Sanity check -- it shouldn't be the case that allow_cached_response is + // false while only_use_cached_response is true. + DCHECK(request->info().allow_cached_response() || + !request->info().only_use_cached_response()); + + if (!cache_.get() || !request->info().allow_cached_response()) + return false; + + HostResolver::RequestInfo info = request->info(); + HostCache::Key key(info.hostname(), info.address_family(), + info.host_resolver_flags()); + const HostCache::Entry* cache_entry = cache_->Lookup( + key, base::TimeTicks::Now()); + if (cache_entry) { + request->request_net_log().AddEvent( + NetLog::TYPE_ASYNC_HOST_RESOLVER_CACHE_HIT, NULL); + DCHECK_EQ(OK, cache_entry->error); + request->set_addresses( + CreateAddressListUsingPort(cache_entry->addrlist, info.port())); + return true; + } + return false; +} + +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_++; + return new Request( + source_net_log, request_net_log, id, info, key, callback, addresses); +} + +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() const { + 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 |