Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(206)

Unified Diff: net/base/host_resolver_impl.cc

Issue 9369045: [net] HostResolverImpl + DnsTransaction + DnsConfigService = Asynchronous DNS ready for experiments. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Ready for test-drive. Created 8 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: net/base/host_resolver_impl.cc
diff --git a/net/base/host_resolver_impl.cc b/net/base/host_resolver_impl.cc
index d9330e213cebb1405002629e2fda5e6042329fac..cdf04536e5ee41c6d52f97f9c116172ea6dcf92f 100644
--- a/net/base/host_resolver_impl.cc
+++ b/net/base/host_resolver_impl.cc
@@ -24,12 +24,14 @@
#include "base/message_loop_proxy.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram.h"
+#include "base/rand_util.h"
#include "base/stl_util.h"
#include "base/string_util.h"
#include "base/threading/worker_pool.h"
#include "base/time.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
+#include "net/base/address_family.h"
#include "net/base/address_list.h"
#include "net/base/address_list_net_log_param.h"
#include "net/base/dns_reloader.h"
@@ -38,6 +40,12 @@
#include "net/base/net_errors.h"
#include "net/base/net_log.h"
#include "net/base/net_util.h"
+#include "net/dns/dns_config_service.h"
+#include "net/dns/dns_protocol.h"
+#include "net/dns/dns_response.h"
+#include "net/dns/dns_session.h"
+#include "net/dns/dns_transaction.h"
+#include "net/socket/client_socket_factory.h"
#if defined(OS_WIN)
#include "net/base/winsock_init.h"
@@ -315,8 +323,8 @@ void LogCancelRequest(const BoundNetLog& source_net_log,
// Keeps track of the highest priority.
class PriorityTracker {
public:
- PriorityTracker()
- : highest_priority_(IDLE), total_count_(0) {
+ PriorityTracker(RequestPriority priority)
+ : highest_priority_(priority), total_count_(0) {
memset(counts_, 0, sizeof(counts_));
}
@@ -359,22 +367,72 @@ class PriorityTracker {
//-----------------------------------------------------------------------------
+// Convenience wrapper for PrioritizedDispatcher::Handle so that we don't forget
+// to update it when cancelling or changing the priority.
+// TODO(szym): consider making PrioritizedDispatcher::Handle into this.
+class DispatcherHandle {
+ public:
+ DispatcherHandle() : dispatcher_(NULL) {}
+ DispatcherHandle(PrioritizedDispatcher* dispatcher,
+ const PrioritizedDispatcher::Handle& handle)
+ : dispatcher_(dispatcher), handle_(handle) {
+ if (handle_.is_null())
+ dispatcher_ = NULL;
+ }
+
+ bool is_null() const {
+ return dispatcher_ == NULL;
+ }
+
+ void Reset() {
+ dispatcher_ = NULL;
+ handle_ = PrioritizedDispatcher::Handle();
+ }
+
+ void ChangePriority(RequestPriority priority) {
+ DCHECK(!is_null());
+ handle_ = dispatcher_->ChangePriority(handle_, priority);
+ }
+
+ void Cancel() {
+ DCHECK(!is_null());
+ dispatcher_->Cancel(handle_);
+ Reset();
+ }
+
+ private:
+ PrioritizedDispatcher* dispatcher_;
+ PrioritizedDispatcher::Handle handle_;
+};
+
+//-----------------------------------------------------------------------------
+
HostResolver* CreateHostResolver(size_t max_concurrent_resolves,
size_t max_retry_attempts,
bool use_cache,
+ bool use_async,
NetLog* net_log) {
if (max_concurrent_resolves == HostResolver::kDefaultParallelism)
max_concurrent_resolves = kDefaultMaxProcTasks;
// TODO(szym): Add experiments with reserved slots for higher priority
// requests.
+ // TODO(szym): Add tighter limits for proc_dispatcher_
PrioritizedDispatcher::Limits limits(NUM_PRIORITIES, max_concurrent_resolves);
+ scoped_ptr<DnsConfigService> config_service;
+ if (use_async) {
+ config_service = DnsConfigService::CreateSystemService();
+ config_service->Watch();
+ }
+
HostResolverImpl* resolver = new HostResolverImpl(
use_cache ? HostCache::CreateDefaultCache() : NULL,
limits,
+ limits,
HostResolverImpl::ProcTaskParams(NULL, max_retry_attempts),
+ config_service.Pass(),
net_log);
return resolver;
@@ -390,6 +448,7 @@ HostResolver* CreateSystemHostResolver(size_t max_concurrent_resolves,
return CreateHostResolver(max_concurrent_resolves,
max_retry_attempts,
true /* use_cache */,
+ false /* use_async */,
net_log);
}
@@ -399,6 +458,17 @@ HostResolver* CreateNonCachingSystemHostResolver(size_t max_concurrent_resolves,
return CreateHostResolver(max_concurrent_resolves,
max_retry_attempts,
false /* use_cache */,
+ false /* use_async */,
+ net_log);
+}
+
+HostResolver* CreateAsyncHostResolver(size_t max_concurrent_resolves,
+ size_t max_retry_attempts,
+ NetLog* net_log) {
+ return CreateHostResolver(max_concurrent_resolves,
+ max_retry_attempts,
+ true /* use_cache */,
cbentzel 2012/02/10 19:51:08 I don't like these comments - would rather have a
szym 2012/02/10 21:49:36 Not a fan of bit flag or enum in this case. If we
+ true /* use_async */,
net_log);
}
@@ -505,7 +575,9 @@ class HostResolverImpl::Request {
class HostResolverImpl::ProcTask
: public base::RefCountedThreadSafe<HostResolverImpl::ProcTask> {
public:
- typedef base::Callback<void(int, int, const AddressList&)> Callback;
+ typedef base::Callback<void(int net_error,
+ int os_error,
+ const AddressList& addr_list)> Callback;
ProcTask(const Key& key,
const ProcTaskParams& params,
@@ -891,6 +963,8 @@ class HostResolverImpl::ProcTask
//-----------------------------------------------------------------------------
// Represents a request to the worker pool for a "probe for IPv6 support" call.
+//
+// TODO(szym): This could also be replaced with PostTaskAndReply and Callbacks.
class HostResolverImpl::IPv6ProbeJob
: public base::RefCountedThreadSafe<HostResolverImpl::IPv6ProbeJob> {
public:
@@ -958,6 +1032,63 @@ class HostResolverImpl::IPv6ProbeJob
//-----------------------------------------------------------------------------
+// Resolves the hostname using DnsTransaction.
+// TODO(szym): This could be moved to separate source file as well.
+class HostResolverImpl::DnsTask {
+ public:
+ typedef base::Callback<void(int net_error,
+ const AddressList& addr_list,
+ base::TimeDelta ttl)> Callback;
+
+ DnsTask(DnsTransactionFactory* factory,
+ const Key& key,
+ const Callback& callback,
+ const BoundNetLog& source_net_log) : callback_(callback) {
+ DCHECK(factory);
+ DCHECK(!callback.is_null());
+
+ // For now we treat ADDRESS_FAMILY_UNSPEC as if it was IPV4.
+ uint16 qtype = (key.address_family == ADDRESS_FAMILY_IPV6)
+ ? dns_protocol::kTypeAAAA
+ : dns_protocol::kTypeA;
+ // TODO(szym): Implement "happy eyeballs".
+ transaction_ = factory->CreateTransaction(
+ key.hostname,
+ qtype,
+ base::Bind(&DnsTask::OnTransactionComplete, base::Unretained(this)),
+ source_net_log);
+ DCHECK(transaction_.get());
+ }
+
+ int Start() {
+ return transaction_->Start();
+ }
+
+ void OnTransactionComplete(DnsTransaction* transaction,
+ int net_error,
+ const DnsResponse* response) {
+ // TODO(szym): Record performance histograms.
+ if (net_error == OK) {
+ AddressList addr_list;
+ base::TimeDelta ttl;
+ if (DnsResponseToAddressList(response, &addr_list, &ttl)) {
+ callback_.Run(net_error, addr_list, ttl);
+ return;
+ }
+ net_error = ERR_DNS_MALFORMED_RESPONSE;
+ }
+ callback_.Run(net_error, AddressList(), base::TimeDelta());
+ }
+
+ private:
+ // The listener to the results of this DnsTask.
+ Callback callback_;
+
+ scoped_ptr<DnsTransaction> transaction_;
+};
+
+//-----------------------------------------------------------------------------
+
// Aggregates all Requests for the same Key. Dispatched via PriorityDispatch.
// Spawns ProcTask when started.
class HostResolverImpl::Job : public PrioritizedDispatcher::Job {
@@ -966,9 +1097,11 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job {
// request that spawned it.
Job(HostResolverImpl* resolver,
const Key& key,
- const BoundNetLog& request_net_log)
+ const BoundNetLog& request_net_log,
+ RequestPriority priority)
: resolver_(resolver->AsWeakPtr()),
key_(key),
+ priority_tracker_(priority),
had_non_speculative_request_(false),
net_log_(BoundNetLog::Make(request_net_log.net_log(),
NetLog::SOURCE_HOST_RESOLVER_IMPL_JOB)),
@@ -1009,10 +1142,6 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job {
STLDeleteElements(&requests_);
}
- HostResolverImpl* resolver() const {
- return resolver_;
- }
-
RequestPriority priority() const {
return priority_tracker_.highest_priority();
}
@@ -1030,13 +1159,13 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job {
return net_error_;
}
- // Used by HostResolverImpl with |dispatcher_|.
- const PrioritizedDispatcher::Handle& handle() const {
- return handle_;
+ void AddToDispatcher(PrioritizedDispatcher* dispatcher) {
+ DCHECK(handle_.is_null());
+ handle_ = DispatcherHandle(dispatcher, dispatcher->Add(this, priority()));
}
- void set_handle(const PrioritizedDispatcher::Handle& handle) {
- handle_ = handle;
+ bool IsWaitingInDispatch() const {
+ return !handle_.is_null();
}
// The Job will own |req| and destroy it in ~Job.
@@ -1064,6 +1193,9 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job {
if (proc_task_)
proc_task_->set_had_non_speculative_request();
}
+
+ if (!handle_.is_null())
+ handle_.ChangePriority(priority());
}
void CancelRequest(Request* req) {
@@ -1078,44 +1210,72 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job {
NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_REQUEST_DETACH,
make_scoped_refptr(new JobAttachParameters(
req->request_net_log().source(), priority())));
+
+ if (!handle_.is_null()) {
+ if (num_active_requests() > 0) {
+ handle_.ChangePriority(priority());
+ } else {
+ handle_.Cancel();
+ }
+ }
}
// Aborts and destroys the job, completes all requests as aborted.
void Abort() {
// Job should only be aborted if it's running.
DCHECK(is_running());
- proc_task_->Cancel();
- proc_task_ = NULL;
+ if (proc_task_.get()) {
+ proc_task_->Cancel();
+ proc_task_ = NULL;
+ }
+ dns_task_.reset();
net_error_ = ERR_ABORTED;
os_error_ = 0;
- CompleteRequests(AddressList());
+ CompleteRequests(AddressList(), base::TimeDelta());
}
- bool is_running() const {
+ bool is_dns_running() const {
+ return dns_task_.get() != NULL;
+ }
+
+ bool is_proc_running() const {
return proc_task_.get() != NULL;
}
+ bool is_running() const {
+ return is_dns_running() || is_proc_running();
+ }
+
// Called by HostResolverImpl when this job is evicted due to queue overflow.
void OnEvicted() {
// Must not be running.
DCHECK(!is_running());
- handle_ = PrioritizedDispatcher::Handle();
+ handle_.Reset();
net_log_.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_EVICTED, NULL);
// This signals to CompleteRequests that this job never ran.
net_error_ = ERR_HOST_RESOLVER_QUEUE_TOO_LARGE;
os_error_ = 0;
- CompleteRequests(AddressList());
+ CompleteRequests(AddressList(), base::TimeDelta());
}
// PriorityDispatch::Job interface.
virtual void Start() OVERRIDE {
DCHECK(!is_running());
- handle_ = PrioritizedDispatcher::Handle();
+ handle_.Reset();
net_log_.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_JOB_STARTED, NULL);
+ if (resolver_->dns_transaction_factory_.get()) {
+ StartDnsTask();
+ } else {
+ StartProcTask();
+ }
+ }
+
+ private:
+ void StartProcTask() {
proc_task_ = new ProcTask(
key_,
resolver_->proc_params_,
@@ -1129,33 +1289,70 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job {
proc_task_->Start();
}
- private:
+ void StartDnsTask() {
+ dns_task_.reset(new DnsTask(
+ resolver_->dns_transaction_factory_.get(),
+ key_,
+ base::Bind(&Job::OnDnsTaskComplete, base::Unretained(this)),
+ net_log_));
+
+ int rv = dns_task_->Start();
+ if (rv != ERR_IO_PENDING) {
+ DCHECK_NE(OK, rv);
+ // TODO(szym): AddToDispatcher(resolver_->proc_dispatcher_);
+ StartProcTask();
+ }
+ }
+
// Called by ProcTask when it completes.
void OnProcTaskComplete(int net_error, int os_error,
- const AddressList& addrlist) {
- DCHECK(is_running());
+ const AddressList& addr_list) {
+ DCHECK(is_proc_running());
proc_task_ = NULL;
net_error_ = net_error;
os_error_ = os_error;
- // We are the only consumer of |addrlist|, so we can safely change the port
- // without copy-on-write. This pays off, when job has only one request.
- AddressList list = addrlist;
- if (net_error == OK && !requests_.empty())
- MutableSetPort(requests_.front()->info().port(), &list);
- CompleteRequests(list);
+ base::TimeDelta ttl = base::TimeDelta::FromSeconds(0);
+ if (net_error == OK)
+ ttl = base::TimeDelta::FromSeconds(kCacheEntryTTLSeconds);
+ CompleteRequests(addr_list, ttl);
+ }
+
+ // Called by DnsTask when it completes.
+ void OnDnsTaskComplete(int net_error,
+ const AddressList& addr_list,
+ base::TimeDelta ttl) {
+ DCHECK(is_dns_running());
+ dns_task_.reset();
+
+ if (net_error != OK) {
+ // TODO(szym): Some net errors indicate lack of connectivity. Starting
+ // ProcTask in that case is a waste of time.
+ StartProcTask();
+ return;
+ }
+
+ net_error_ = OK;
+ os_error_ = 0;
+ CompleteRequests(addr_list, ttl);
}
// Completes all Requests. Calls OnJobFinished and deletes self.
- void CompleteRequests(const AddressList& addrlist) {
+ void CompleteRequests(const AddressList& addr_list, base::TimeDelta ttl) {
CHECK(resolver_);
+ // We are the only consumer of |addr_list|, so we can safely change the port
+ // without copy-on-write. This pays off, when job has only one request.
+ AddressList list = addr_list;
+ if (net_error_ == OK && !requests_.empty())
+ MutableSetPort(requests_.front()->info().port(), &list);
+
// This job must be removed from resolver's |jobs_| now to make room for a
// new job with the same key in case one of the OnComplete callbacks decides
// to spawn one. Consequently, the job deletes itself when CompleteRequests
// is done.
scoped_ptr<Job> self_deleter(this);
- resolver_->OnJobFinished(this, addrlist);
+ resolver_->OnJobFinished(this, addr_list, ttl);
// Complete all of the requests that were attached to the job.
for (RequestsList::const_iterator it = requests_.begin();
@@ -1170,7 +1367,7 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job {
LogFinishRequest(req->source_net_log(), req->request_net_log(),
req->info(), net_error_, os_error_);
- req->OnComplete(net_error_, addrlist);
+ req->OnComplete(net_error_, addr_list);
// Check if the resolver was destroyed as a result of running the
// callback. If it was, we could continue, but we choose to bail.
@@ -1179,7 +1376,7 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job {
}
}
- // Used to call OnJobFinished and RemoveJob.
+ // Used to call OnJobFinished.
base::WeakPtr<HostResolverImpl> resolver_;
Key key_;
@@ -1195,14 +1392,17 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job {
int net_error_;
int os_error_;
- // A ProcTask created and started when this Job is dispatched..
+ // Resolves the host using a HostResolverProc.
scoped_refptr<ProcTask> proc_task_;
+ // Resolves the host using a DnsTransaction.
+ scoped_ptr<DnsTask> dns_task_;
+
// All Requests waiting for the result of this Job. Some can be canceled.
RequestsList requests_;
// A handle used by HostResolverImpl in |dispatcher_|.
- PrioritizedDispatcher::Handle handle_;
+ DispatcherHandle handle_;
};
//-----------------------------------------------------------------------------
@@ -1221,18 +1421,23 @@ HostResolverImpl::ProcTaskParams::~ProcTaskParams() {}
HostResolverImpl::HostResolverImpl(
HostCache* cache,
const PrioritizedDispatcher::Limits& job_limits,
+ const PrioritizedDispatcher::Limits& proc_limits,
const ProcTaskParams& proc_params,
+ scoped_ptr<DnsConfigService> dns_config_service,
NetLog* net_log)
: cache_(cache),
dispatcher_(job_limits),
+ proc_dispatcher_(proc_limits),
max_queued_jobs_(job_limits.total_jobs * 100u),
proc_params_(proc_params),
default_address_family_(ADDRESS_FAMILY_UNSPECIFIED),
+ dns_config_service_(dns_config_service.Pass()),
ipv6_probe_monitoring_(false),
additional_resolver_flags_(0),
net_log_(net_log) {
DCHECK_GE(dispatcher_.num_priorities(), static_cast<size_t>(NUM_PRIORITIES));
+ DCHECK_EQ(proc_dispatcher_.num_priorities(), dispatcher_.num_priorities());
// Maximum of 4 retry attempts for host resolution.
static const size_t kDefaultMaxRetryAttempts = 4u;
@@ -1254,6 +1459,9 @@ HostResolverImpl::HostResolverImpl(
#endif
NetworkChangeNotifier::AddDNSObserver(this);
#endif
+
+ if (dns_config_service_.get())
+ dns_config_service_->AddObserver(this);
}
HostResolverImpl::~HostResolverImpl() {
@@ -1307,8 +1515,8 @@ int HostResolverImpl::Resolve(const RequestInfo& info,
Job* job;
if (jobit == jobs_.end()) {
// Create new Job.
- job = new Job(this, key, request_net_log);
- job->set_handle(dispatcher_.Add(job, info.priority()));
+ job = new Job(this, key, request_net_log, info.priority());
+ job->AddToDispatcher(&dispatcher_);
// Check for queue overflow.
if (dispatcher_.num_queued_jobs() > max_queued_jobs_) {
@@ -1332,8 +1540,6 @@ int HostResolverImpl::Resolve(const RequestInfo& info,
Request* req = new Request(source_net_log, request_net_log, info, callback,
addresses);
job->AddRequest(req);
- if (!job->handle().is_null())
- job->set_handle(dispatcher_.ChangePriority(job->handle(), job->priority()));
if (out_req)
*out_req = reinterpret_cast<RequestHandle>(req);
@@ -1391,23 +1597,16 @@ void HostResolverImpl::CancelRequest(RequestHandle req_handle) {
job->CancelRequest(req);
- if (!job->handle().is_null()) {
- // Still in queue.
- if (job->num_active_requests()) {
- job->set_handle(dispatcher_.ChangePriority(job->handle(),
- job->priority()));
+ if (job->num_active_requests() == 0) {
+ if (job->is_running()) {
+ // Job is running (and could be in CompleteRequests right now).
+ // But to be in Request::OnComplete we would have to have a non-canceled
+ // request. So it is safe to Abort it if it has no more active requests.
+ job->Abort();
} else {
- dispatcher_.Cancel(job->handle());
RemoveJob(job);
delete job;
}
- } else {
- // Job is running (and could be in CompleteRequests right now).
- // But to be in Request::OnComplete we would have to have a non-canceled
- // request. So it is safe to Abort it if it has no more active requests.
- if (!job->num_active_requests()) {
- job->Abort();
- }
}
}
@@ -1482,9 +1681,11 @@ bool HostResolverImpl::ServeFromCache(const Key& key,
return true;
}
-void HostResolverImpl::OnJobFinished(Job* job, const AddressList& addrlist) {
+void HostResolverImpl::OnJobFinished(Job* job,
+ const AddressList& addrlist,
+ base::TimeDelta ttl) {
DCHECK(job);
- DCHECK(job->handle().is_null());
+ DCHECK(!job->IsWaitingInDispatch());
RemoveJob(job);
if (job->net_error() == ERR_HOST_RESOLVER_QUEUE_TOO_LARGE)
return;
@@ -1495,9 +1696,6 @@ void HostResolverImpl::OnJobFinished(Job* job, const AddressList& addrlist) {
return;
// Write result to the cache.
if (cache_.get()) {
- base::TimeDelta ttl = base::TimeDelta::FromSeconds(0);
- if (job->net_error() == OK)
- ttl = base::TimeDelta::FromSeconds(kCacheEntryTTLSeconds);
cache_->Set(job->key(), job->net_error(), addrlist,
base::TimeTicks::Now(), ttl);
}
@@ -1557,7 +1755,7 @@ void HostResolverImpl::AbortAllInProgressJobs() {
return;
} else {
// Keep it in |dispatch_|.
- DCHECK(!job->handle().is_null());
+ DCHECK(job->IsWaitingInDispatch());
}
}
}
@@ -1594,4 +1792,22 @@ void HostResolverImpl::OnDNSChanged() {
// |this| may be deleted inside AbortAllInProgressJobs().
}
+void HostResolverImpl::OnConfigChanged(const DnsConfig& dns_config) {
+ // We want a new factory in place, before we Abort running Jobs, so that the
+ // newly started jobs use the new factory.
+ bool had_factory = (dns_transaction_factory_.get() != NULL);
+ if (dns_config.IsValid()) {
+ dns_transaction_factory_ = DnsTransactionFactory::CreateFactory(
+ new DnsSession(dns_config,
+ ClientSocketFactory::GetDefaultFactory(),
+ base::Bind(&base::RandInt),
+ net_log_));
+ } else {
+ dns_transaction_factory_.reset();
+ }
+ if (had_factory) {
+ OnDNSChanged();
+ }
+}
+
} // namespace net

Powered by Google App Engine
This is Rietveld 408576698