| 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..02d20617be9bf20b33461cd2747335fe8f53042c 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"
|
| @@ -54,6 +62,9 @@ const size_t kMaxHostLength = 4096;
|
| // Default TTL for successful resolutions with ProcTask.
|
| const unsigned kCacheEntryTTLSeconds = 60;
|
|
|
| +// Default TTL for unsuccessful resolutions with ProcTask.
|
| +const unsigned kNegativeCacheEntryTTLSeconds = 0;
|
| +
|
| // Maximum of 8 concurrent resolver threads (excluding retries).
|
| // Some routers (or resolvers) appear to start to provide host-not-found if
|
| // too many simultaneous resolutions are pending. This number needs to be
|
| @@ -66,9 +77,9 @@ static const size_t kDefaultMaxProcTasks = 8u;
|
| //
|
| // However since we allocated the AddressList ourselves we can safely
|
| // do this optimization and avoid reallocating the list.
|
| -void MutableSetPort(int port, AddressList* addrlist) {
|
| +void MutableSetPort(int port, AddressList* addr_list) {
|
| struct addrinfo* mutable_head =
|
| - const_cast<struct addrinfo*>(addrlist->head());
|
| + const_cast<struct addrinfo*>(addr_list->head());
|
| SetPortForAllAddrinfos(mutable_head, port);
|
| }
|
|
|
| @@ -144,22 +155,20 @@ class CallSystemHostResolverProc : public HostResolverProc {
|
| virtual int Resolve(const std::string& hostname,
|
| AddressFamily address_family,
|
| HostResolverFlags host_resolver_flags,
|
| - AddressList* addrlist,
|
| + AddressList* addr_list,
|
| int* os_error) OVERRIDE {
|
| return SystemHostResolverProc(hostname,
|
| address_family,
|
| host_resolver_flags,
|
| - addrlist,
|
| + addr_list,
|
| os_error);
|
| }
|
| };
|
|
|
| // Extra parameters to attach to the NetLog when the resolve failed.
|
| -class HostResolveFailedParams : public NetLog::EventParameters {
|
| +class ProcTaskFailedParams : public NetLog::EventParameters {
|
| public:
|
| - HostResolveFailedParams(uint32 attempt_number,
|
| - int net_error,
|
| - int os_error)
|
| + ProcTaskFailedParams(uint32 attempt_number, int net_error, int os_error)
|
| : attempt_number_(attempt_number),
|
| net_error_(net_error),
|
| os_error_(os_error) {
|
| @@ -201,6 +210,26 @@ class HostResolveFailedParams : public NetLog::EventParameters {
|
| const int os_error_;
|
| };
|
|
|
| +// Extra parameters to attach to the NetLog when the DnsTask failed.
|
| +class DnsTaskFailedParams : public NetLog::EventParameters {
|
| + public:
|
| + DnsTaskFailedParams(int net_error, int dns_error)
|
| + : net_error_(net_error), dns_error_(dns_error) {
|
| + }
|
| +
|
| + virtual Value* ToValue() const OVERRIDE {
|
| + DictionaryValue* dict = new DictionaryValue();
|
| + dict->SetInteger("net_error", net_error_);
|
| + if (dns_error_)
|
| + dict->SetInteger("dns_error", dns_error_);
|
| + return dict;
|
| + }
|
| +
|
| + private:
|
| + const int net_error_;
|
| + const int dns_error_;
|
| +};
|
| +
|
| // Parameters representing the information in a RequestInfo object, along with
|
| // the associated NetLog::Source.
|
| class RequestInfoParameters : public NetLog::EventParameters {
|
| @@ -229,8 +258,7 @@ class RequestInfoParameters : public NetLog::EventParameters {
|
| const NetLog::Source source_;
|
| };
|
|
|
| -// Parameters associated with the creation of a HostResolverImpl::Job
|
| -// or a HostResolverImpl::ProcTask.
|
| +// Parameters associated with the creation of a HostResolverImpl::Job.
|
| class JobCreationParameters : public NetLog::EventParameters {
|
| public:
|
| JobCreationParameters(const std::string& host,
|
| @@ -290,14 +318,9 @@ void LogStartRequest(const BoundNetLog& source_net_log,
|
| void LogFinishRequest(const BoundNetLog& source_net_log,
|
| const BoundNetLog& request_net_log,
|
| const HostResolver::RequestInfo& info,
|
| - int net_error,
|
| - int os_error) {
|
| - scoped_refptr<NetLog::EventParameters> params;
|
| - if (net_error != OK) {
|
| - params = new HostResolveFailedParams(0, net_error, os_error);
|
| - }
|
| -
|
| - request_net_log.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_REQUEST, params);
|
| + int net_error) {
|
| + request_net_log.EndEventWithNetErrorCode(
|
| + NetLog::TYPE_HOST_RESOLVER_IMPL_REQUEST, net_error);
|
| source_net_log.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL, NULL);
|
| }
|
|
|
| @@ -315,8 +338,8 @@ void LogCancelRequest(const BoundNetLog& source_net_log,
|
| // Keeps track of the highest priority.
|
| class PriorityTracker {
|
| public:
|
| - PriorityTracker()
|
| - : highest_priority_(IDLE), total_count_(0) {
|
| + explicit PriorityTracker(RequestPriority priority)
|
| + : highest_priority_(priority), total_count_(0) {
|
| memset(counts_, 0, sizeof(counts_));
|
| }
|
|
|
| @@ -361,7 +384,8 @@ class PriorityTracker {
|
|
|
| HostResolver* CreateHostResolver(size_t max_concurrent_resolves,
|
| size_t max_retry_attempts,
|
| - bool use_cache,
|
| + HostCache* cache,
|
| + scoped_ptr<DnsConfigService> config_service,
|
| NetLog* net_log) {
|
| if (max_concurrent_resolves == HostResolver::kDefaultParallelism)
|
| max_concurrent_resolves = kDefaultMaxProcTasks;
|
| @@ -372,9 +396,10 @@ HostResolver* CreateHostResolver(size_t max_concurrent_resolves,
|
| PrioritizedDispatcher::Limits limits(NUM_PRIORITIES, max_concurrent_resolves);
|
|
|
| HostResolverImpl* resolver = new HostResolverImpl(
|
| - use_cache ? HostCache::CreateDefaultCache() : NULL,
|
| + cache,
|
| limits,
|
| HostResolverImpl::ProcTaskParams(NULL, max_retry_attempts),
|
| + config_service.Pass(),
|
| net_log);
|
|
|
| return resolver;
|
| @@ -389,7 +414,8 @@ HostResolver* CreateSystemHostResolver(size_t max_concurrent_resolves,
|
| NetLog* net_log) {
|
| return CreateHostResolver(max_concurrent_resolves,
|
| max_retry_attempts,
|
| - true /* use_cache */,
|
| + HostCache::CreateDefaultCache(),
|
| + scoped_ptr<DnsConfigService>(NULL),
|
| net_log);
|
| }
|
|
|
| @@ -398,7 +424,21 @@ HostResolver* CreateNonCachingSystemHostResolver(size_t max_concurrent_resolves,
|
| NetLog* net_log) {
|
| return CreateHostResolver(max_concurrent_resolves,
|
| max_retry_attempts,
|
| - false /* use_cache */,
|
| + NULL,
|
| + scoped_ptr<DnsConfigService>(NULL),
|
| + net_log);
|
| +}
|
| +
|
| +HostResolver* CreateAsyncHostResolver(size_t max_concurrent_resolves,
|
| + size_t max_retry_attempts,
|
| + NetLog* net_log) {
|
| + scoped_ptr<DnsConfigService> config_service =
|
| + DnsConfigService::CreateSystemService();
|
| + config_service->Watch();
|
| + return CreateHostResolver(max_concurrent_resolves,
|
| + max_retry_attempts,
|
| + HostCache::CreateDefaultCache(),
|
| + config_service.Pass(),
|
| net_log);
|
| }
|
|
|
| @@ -440,9 +480,9 @@ class HostResolverImpl::Request {
|
| }
|
|
|
| // Prepare final AddressList and call completion callback.
|
| - void OnComplete(int error, const AddressList& addrlist) {
|
| + void OnComplete(int error, const AddressList& addr_list) {
|
| if (error == OK)
|
| - *addresses_ = CreateAddressListUsingPort(addrlist, info_.port());
|
| + *addresses_ = CreateAddressListUsingPort(addr_list, info_.port());
|
| CompletionCallback callback = callback_;
|
| MarkAsCanceled();
|
| callback.Run(error);
|
| @@ -505,7 +545,8 @@ 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,
|
| + const AddressList& addr_list)> Callback;
|
|
|
| ProcTask(const Key& key,
|
| const ProcTaskParams& params,
|
| @@ -519,22 +560,14 @@ class HostResolverImpl::ProcTask
|
| completed_attempt_number_(0),
|
| completed_attempt_error_(ERR_UNEXPECTED),
|
| had_non_speculative_request_(false),
|
| - net_log_(BoundNetLog::Make(
|
| - job_net_log.net_log(),
|
| - NetLog::SOURCE_HOST_RESOLVER_IMPL_PROC_TASK)) {
|
| + net_log_(job_net_log) {
|
| if (!params_.resolver_proc)
|
| params_.resolver_proc = HostResolverProc::GetDefault();
|
| // If default is unset, use the system proc.
|
| if (!params_.resolver_proc)
|
| params_.resolver_proc = new CallSystemHostResolverProc();
|
|
|
| - job_net_log.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_CREATE_PROC_TASK,
|
| - new NetLogSourceParameter("source_dependency",
|
| - net_log_.source()));
|
| -
|
| - net_log_.BeginEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_PROC_TASK,
|
| - new JobCreationParameters(key_.hostname,
|
| - job_net_log.source()));
|
| + net_log_.BeginEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_PROC_TASK, NULL);
|
| }
|
|
|
| void Start() {
|
| @@ -551,8 +584,6 @@ class HostResolverImpl::ProcTask
|
| if (was_canceled())
|
| return;
|
|
|
| - net_log_.AddEvent(NetLog::TYPE_CANCELLED, NULL);
|
| -
|
| callback_.Reset();
|
|
|
| net_log_.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_PROC_TASK, NULL);
|
| @@ -621,7 +652,6 @@ class HostResolverImpl::ProcTask
|
| AddressList results;
|
| int os_error = 0;
|
| // Running on the worker thread
|
| -
|
| int error = params_.resolver_proc->Resolve(key_.hostname,
|
| key_.address_family,
|
| key_.host_resolver_flags,
|
| @@ -657,14 +687,13 @@ class HostResolverImpl::ProcTask
|
| bool was_retry_attempt = attempt_number > 1;
|
|
|
| // Ideally the following code would be part of host_resolver_proc.cc,
|
| - // however it isn't safe to call NetworkChangeNotifier from worker
|
| - // threads. So we do it here on the IO thread instead.
|
| + // however it isn't safe to call NetworkChangeNotifier from worker threads.
|
| + // So we do it here on the IO thread instead.
|
| if (error != OK && NetworkChangeNotifier::IsOffline())
|
| error = ERR_INTERNET_DISCONNECTED;
|
|
|
| - // If this is the first attempt that is finishing later, then record
|
| - // data for the first attempt. Won't contaminate with retry attempt's
|
| - // data.
|
| + // If this is the first attempt that is finishing later, then record data
|
| + // for the first attempt. Won't contaminate with retry attempt's data.
|
| if (!was_retry_attempt)
|
| RecordPerformanceHistograms(start_time, error, os_error);
|
|
|
| @@ -675,7 +704,7 @@ class HostResolverImpl::ProcTask
|
|
|
| scoped_refptr<NetLog::EventParameters> params;
|
| if (error != OK) {
|
| - params = new HostResolveFailedParams(attempt_number, error, os_error);
|
| + params = new ProcTaskFailedParams(attempt_number, error, os_error);
|
| } else {
|
| params = new NetLogIntegerParameter("attempt_number", attempt_number);
|
| }
|
| @@ -696,13 +725,13 @@ class HostResolverImpl::ProcTask
|
| }
|
|
|
| if (error != OK) {
|
| - params = new HostResolveFailedParams(0, error, os_error);
|
| + params = new ProcTaskFailedParams(0, error, os_error);
|
| } else {
|
| params = new AddressListNetLogParam(results_);
|
| }
|
| net_log_.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_PROC_TASK, params);
|
|
|
| - callback_.Run(error, os_error, results_);
|
| + callback_.Run(error, results_);
|
| }
|
|
|
| void RecordPerformanceHistograms(const base::TimeTicks& start_time,
|
| @@ -891,6 +920,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,61 +989,130 @@ 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& job_net_log)
|
| + : callback_(callback), net_log_(job_net_log) {
|
| + 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)),
|
| + net_log_);
|
| + DCHECK(transaction_.get());
|
| + }
|
| +
|
| + int Start() {
|
| + net_log_.BeginEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_DNS_TASK, NULL);
|
| + return transaction_->Start();
|
| + }
|
| +
|
| + void OnTransactionComplete(DnsTransaction* transaction,
|
| + int net_error,
|
| + const DnsResponse* response) {
|
| + // TODO(szym): Record performance histograms.
|
| + // Run |callback_| last since the owning Job will then delete this DnsTask.
|
| + DnsResponse::Result result = DnsResponse::DNS_SUCCESS;
|
| + if (net_error == OK) {
|
| + AddressList addr_list;
|
| + base::TimeDelta ttl;
|
| + result = response->ParseToAddressList(&addr_list, &ttl);
|
| + if (result == DnsResponse::DNS_SUCCESS) {
|
| + net_log_.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_DNS_TASK,
|
| + new AddressListNetLogParam(addr_list));
|
| + callback_.Run(net_error, addr_list, ttl);
|
| + return;
|
| + }
|
| + net_error = ERR_DNS_MALFORMED_RESPONSE;
|
| + }
|
| + net_log_.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_DNS_TASK,
|
| + new DnsTaskFailedParams(net_error, result));
|
| + callback_.Run(net_error, AddressList(), base::TimeDelta());
|
| + }
|
| +
|
| + private:
|
| + // The listener to the results of this DnsTask.
|
| + Callback callback_;
|
| +
|
| + const BoundNetLog net_log_;
|
| +
|
| + scoped_ptr<DnsTransaction> transaction_;
|
| +};
|
| +
|
| +//-----------------------------------------------------------------------------
|
| +
|
| // Aggregates all Requests for the same Key. Dispatched via PriorityDispatch.
|
| -// Spawns ProcTask when started.
|
| class HostResolverImpl::Job : public PrioritizedDispatcher::Job {
|
| public:
|
| // Creates new job for |key| where |request_net_log| is bound to the
|
| - // request that spawned it.
|
| + // request that spawned it and |priority| is the initial priority.
|
| 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)),
|
| - net_error_(ERR_IO_PENDING),
|
| - os_error_(0) {
|
| + NetLog::SOURCE_HOST_RESOLVER_IMPL_JOB)) {
|
| request_net_log.AddEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_CREATE_JOB, NULL);
|
|
|
| net_log_.BeginEvent(
|
| NetLog::TYPE_HOST_RESOLVER_IMPL_JOB,
|
| make_scoped_refptr(new JobCreationParameters(
|
| key_.hostname, request_net_log.source())));
|
| +
|
| + handle_ = resolver_->dispatcher_.Add(this, priority);
|
| }
|
|
|
| virtual ~Job() {
|
| - if (net_error_ == ERR_IO_PENDING) {
|
| - if (is_running()) {
|
| - DCHECK_EQ(ERR_IO_PENDING, net_error_);
|
| + if (is_running()) {
|
| + // |resolver_| was destroyed with this Job still in flight.
|
| + // Clean-up, record in the log, but don't run any callbacks.
|
| + if (is_proc_running()) {
|
| proc_task_->Cancel();
|
| proc_task_ = NULL;
|
| - net_error_ = ERR_ABORTED;
|
| - } else {
|
| - net_log_.AddEvent(NetLog::TYPE_CANCELLED, NULL);
|
| - net_error_ = OK; // For NetLog.
|
| }
|
| + net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HOST_RESOLVER_IMPL_JOB,
|
| + ERR_ABORTED);
|
| + } else if (is_queued()) {
|
| + // This Job was cancelled without running.
|
| + // TODO(szym): is there's any benefit in having this distinction?
|
| + net_log_.AddEvent(NetLog::TYPE_CANCELLED, NULL);
|
| + net_log_.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL_JOB, NULL);
|
| + }
|
| + // else CompleteRequests logged EndEvent.
|
|
|
| - for (RequestsList::const_iterator it = requests_.begin();
|
| - it != requests_.end(); ++it) {
|
| - Request* req = *it;
|
| - if (req->was_canceled())
|
| - continue;
|
| - DCHECK_EQ(this, req->job());
|
| - LogCancelRequest(req->source_net_log(), req->request_net_log(),
|
| - req->info());
|
| - }
|
| + // Log any remaining Requests as cancelled.
|
| + for (RequestsList::const_iterator it = requests_.begin();
|
| + it != requests_.end(); ++it) {
|
| + Request* req = *it;
|
| + if (req->was_canceled())
|
| + continue;
|
| + DCHECK_EQ(this, req->job());
|
| + LogCancelRequest(req->source_net_log(), req->request_net_log(),
|
| + req->info());
|
| }
|
| - net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HOST_RESOLVER_IMPL_JOB,
|
| - net_error_);
|
| STLDeleteElements(&requests_);
|
| }
|
|
|
| - HostResolverImpl* resolver() const {
|
| - return resolver_;
|
| - }
|
| -
|
| RequestPriority priority() const {
|
| return priority_tracker_.highest_priority();
|
| }
|
| @@ -1026,26 +1126,14 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job {
|
| return key_;
|
| }
|
|
|
| - int net_error() const {
|
| - return net_error_;
|
| - }
|
| -
|
| - // Used by HostResolverImpl with |dispatcher_|.
|
| - const PrioritizedDispatcher::Handle& handle() const {
|
| - return handle_;
|
| - }
|
| -
|
| - void set_handle(const PrioritizedDispatcher::Handle& handle) {
|
| - handle_ = handle;
|
| + bool is_queued() const {
|
| + return !handle_.is_null();
|
| }
|
|
|
| - // The Job will own |req| and destroy it in ~Job.
|
| - void AddRequest(Request* req) {
|
| + void AddRequest(scoped_ptr<Request> req) {
|
| DCHECK_EQ(key_.hostname, req->info().hostname());
|
|
|
| req->set_job(this);
|
| - requests_.push_back(req);
|
| -
|
| priority_tracker_.Add(req->info().priority());
|
|
|
| req->request_net_log().AddEvent(
|
| @@ -1064,6 +1152,11 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job {
|
| if (proc_task_)
|
| proc_task_->set_had_non_speculative_request();
|
| }
|
| +
|
| + requests_.push_back(req.release());
|
| +
|
| + if (!handle_.is_null())
|
| + handle_ = resolver_->dispatcher_.ChangePriority(handle_, priority());
|
| }
|
|
|
| void CancelRequest(Request* req) {
|
| @@ -1078,44 +1171,80 @@ 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_ = resolver_->dispatcher_.ChangePriority(handle_, priority());
|
| + } else {
|
| + resolver_->dispatcher_.Cancel(handle_);
|
| + handle_.Reset();
|
| + }
|
| + }
|
| }
|
|
|
| // Aborts and destroys the job, completes all requests as aborted.
|
| + // The caller should clean up.
|
| void Abort() {
|
| // Job should only be aborted if it's running.
|
| DCHECK(is_running());
|
| - proc_task_->Cancel();
|
| - proc_task_ = NULL;
|
| - net_error_ = ERR_ABORTED;
|
| - os_error_ = 0;
|
| - CompleteRequests(AddressList());
|
| + if (is_proc_running()) {
|
| + proc_task_->Cancel();
|
| + proc_task_ = NULL;
|
| + }
|
| + dns_task_.reset();
|
| + CompleteRequests(ERR_ABORTED, 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);
|
|
|
| + scoped_ptr<Job> self_deleter(this);
|
| + resolver_->RemoveJob(this);
|
| +
|
| // This signals to CompleteRequests that this job never ran.
|
| - net_error_ = ERR_HOST_RESOLVER_QUEUE_TOO_LARGE;
|
| - os_error_ = 0;
|
| - CompleteRequests(AddressList());
|
| + CompleteRequests(ERR_HOST_RESOLVER_QUEUE_TOO_LARGE,
|
| + 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:
|
| + // TODO(szym): Since DnsTransaction does not consume threads, we can increase
|
| + // the limits on |dispatcher_|. But in order to keep the number of WorkerPool
|
| + // threads low, we will need to use an "inner" PrioritizedDispatcher with
|
| + // tighter limits.
|
| + void StartProcTask() {
|
| + DCHECK(!dns_task_.get());
|
| proc_task_ = new ProcTask(
|
| key_,
|
| resolver_->proc_params_,
|
| @@ -1129,33 +1258,82 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job {
|
| proc_task_->Start();
|
| }
|
|
|
| - private:
|
| // Called by ProcTask when it completes.
|
| - void OnProcTaskComplete(int net_error, int os_error,
|
| - const AddressList& addrlist) {
|
| - DCHECK(is_running());
|
| + void OnProcTaskComplete(int net_error, const AddressList& addr_list) {
|
| + DCHECK(is_proc_running());
|
| + // |addr_list| will be destroyed once we destroy |proc_task_|.
|
| + AddressList list = addr_list;
|
| 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);
|
| - }
|
| -
|
| - // Completes all Requests. Calls OnJobFinished and deletes self.
|
| - void CompleteRequests(const AddressList& addrlist) {
|
| - CHECK(resolver_);
|
| + base::TimeDelta ttl = base::TimeDelta::FromSeconds(
|
| + kNegativeCacheEntryTTLSeconds);
|
| + if (net_error == OK)
|
| + ttl = base::TimeDelta::FromSeconds(kCacheEntryTTLSeconds);
|
|
|
| // 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, net_error, list, ttl);
|
| +
|
| + CompleteRequests(net_error, list, ttl);
|
| + }
|
| +
|
| + 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);
|
| + dns_task_.reset();
|
| + StartProcTask();
|
| + }
|
| + }
|
| +
|
| + // Called by DnsTask when it completes.
|
| + void OnDnsTaskComplete(int net_error,
|
| + const AddressList& addr_list,
|
| + base::TimeDelta ttl) {
|
| + DCHECK(is_dns_running());
|
| + // |addr_list| will be destroyed once we destroy |dns_task_|.
|
| + AddressList list = addr_list;
|
| + 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;
|
| + }
|
| +
|
| + // As in OnProcTaskComplete
|
| + scoped_ptr<Job> self_deleter(this);
|
| + resolver_->OnJobFinished(this, net_error, list, ttl);
|
| +
|
| + CompleteRequests(net_error, list, ttl);
|
| + }
|
| +
|
| + // Completes all Requests. Calls OnJobFinished and deletes self.
|
| + void CompleteRequests(int net_error,
|
| + const AddressList& addr_list,
|
| + base::TimeDelta ttl) {
|
| + CHECK(resolver_);
|
| + DCHECK(!is_running());
|
| + DCHECK(!is_queued());
|
| +
|
| + // 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);
|
| +
|
| + net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HOST_RESOLVER_IMPL_JOB,
|
| + net_error);
|
|
|
| // Complete all of the requests that were attached to the job.
|
| for (RequestsList::const_iterator it = requests_.begin();
|
| @@ -1168,9 +1346,9 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job {
|
| DCHECK_EQ(this, req->job());
|
| // Update the net log and notify registered observers.
|
| LogFinishRequest(req->source_net_log(), req->request_net_log(),
|
| - req->info(), net_error_, os_error_);
|
| + req->info(), net_error);
|
|
|
| - req->OnComplete(net_error_, addrlist);
|
| + req->OnComplete(net_error, 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 +1357,7 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job {
|
| }
|
| }
|
|
|
| - // Used to call OnJobFinished and RemoveJob.
|
| + // Used to call OnJobFinished.
|
| base::WeakPtr<HostResolverImpl> resolver_;
|
|
|
| Key key_;
|
| @@ -1191,17 +1369,16 @@ class HostResolverImpl::Job : public PrioritizedDispatcher::Job {
|
|
|
| BoundNetLog net_log_;
|
|
|
| - // Store result here in case the job fails fast in Resolve().
|
| - 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_|.
|
| + // A handle used by |HostResolverImpl::dispatcher_|.
|
| PrioritizedDispatcher::Handle handle_;
|
| };
|
|
|
| @@ -1222,12 +1399,14 @@ HostResolverImpl::HostResolverImpl(
|
| HostCache* cache,
|
| const PrioritizedDispatcher::Limits& job_limits,
|
| const ProcTaskParams& proc_params,
|
| + scoped_ptr<DnsConfigService> dns_config_service,
|
| NetLog* net_log)
|
| : cache_(cache),
|
| dispatcher_(job_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) {
|
| @@ -1254,6 +1433,9 @@ HostResolverImpl::HostResolverImpl(
|
| #endif
|
| NetworkChangeNotifier::AddDNSObserver(this);
|
| #endif
|
| +
|
| + if (dns_config_service_.get())
|
| + dns_config_service_->AddObserver(this);
|
| }
|
|
|
| HostResolverImpl::~HostResolverImpl() {
|
| @@ -1295,8 +1477,7 @@ int HostResolverImpl::Resolve(const RequestInfo& info,
|
|
|
| int rv = ResolveHelper(key, info, addresses, request_net_log);
|
| if (rv != ERR_DNS_CACHE_MISS) {
|
| - LogFinishRequest(source_net_log, request_net_log, info, rv,
|
| - 0 /* os_error (unknown since from cache) */);
|
| + LogFinishRequest(source_net_log, request_net_log, info, rv);
|
| return rv;
|
| }
|
|
|
| @@ -1307,8 +1488,7 @@ 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());
|
|
|
| // Check for queue overflow.
|
| if (dispatcher_.num_queued_jobs() > max_queued_jobs_) {
|
| @@ -1317,27 +1497,26 @@ int HostResolverImpl::Resolve(const RequestInfo& info,
|
| if (evicted == job) {
|
| delete job;
|
| rv = ERR_HOST_RESOLVER_QUEUE_TOO_LARGE;
|
| - LogFinishRequest(source_net_log, request_net_log, info, rv, 0);
|
| + LogFinishRequest(source_net_log, request_net_log, info, rv);
|
| return rv;
|
| }
|
| evicted->OnEvicted(); // Deletes |evicted|.
|
| }
|
| -
|
| jobs_.insert(jobit, std::make_pair(key, job));
|
| } else {
|
| job = jobit->second;
|
| }
|
|
|
| // Can't complete synchronously. Create and attach request.
|
| - 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()));
|
| + scoped_ptr<Request> req(new Request(source_net_log,
|
| + request_net_log,
|
| + info,
|
| + callback,
|
| + addresses));
|
| if (out_req)
|
| - *out_req = reinterpret_cast<RequestHandle>(req);
|
| + *out_req = reinterpret_cast<RequestHandle>(req.get());
|
|
|
| - DCHECK_EQ(ERR_IO_PENDING, job->net_error());
|
| + job->AddRequest(req.Pass());
|
| // Completion happens during Job::CompleteRequests().
|
| return ERR_IO_PENDING;
|
| }
|
| @@ -1376,8 +1555,7 @@ int HostResolverImpl::ResolveFromCache(const RequestInfo& info,
|
| Key key = GetEffectiveKeyForRequest(info);
|
|
|
| int rv = ResolveHelper(key, info, addresses, request_net_log);
|
| - LogFinishRequest(source_net_log, request_net_log, info, rv,
|
| - 0 /* os_error (unknown since from cache) */);
|
| + LogFinishRequest(source_net_log, request_net_log, info, rv);
|
| return rv;
|
| }
|
|
|
| @@ -1391,23 +1569,14 @@ 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()));
|
| - } 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()) {
|
| + if (job->num_active_requests() == 0) {
|
| + // If we were called from a Requests' callback within Job::CompleteRequests,
|
| + // that Request could not have been cancelled, so job->num_active_requests()
|
| + // could not be 0. Therefore, we are not in Job::CompleteRequests().
|
| + RemoveJob(job);
|
| + if (job->is_running())
|
| job->Abort();
|
| - }
|
| + delete job;
|
| }
|
| }
|
|
|
| @@ -1482,23 +1651,21 @@ bool HostResolverImpl::ServeFromCache(const Key& key,
|
| return true;
|
| }
|
|
|
| -void HostResolverImpl::OnJobFinished(Job* job, const AddressList& addrlist) {
|
| +void HostResolverImpl::OnJobFinished(Job* job,
|
| + int net_error,
|
| + const AddressList& addr_list,
|
| + base::TimeDelta ttl) {
|
| DCHECK(job);
|
| - DCHECK(job->handle().is_null());
|
| - RemoveJob(job);
|
| - if (job->net_error() == ERR_HOST_RESOLVER_QUEUE_TOO_LARGE)
|
| - return;
|
| + DCHECK(!job->is_queued());
|
| + DCHECK_NE(ERR_HOST_RESOLVER_QUEUE_TOO_LARGE, net_error);
|
| + DCHECK_NE(ERR_ABORTED, net_error);
|
|
|
| + RemoveJob(job);
|
| // Signal dispatcher that a slot has opened.
|
| dispatcher_.OnJobFinished();
|
| - if (job->net_error() == ERR_ABORTED)
|
| - 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,
|
| + cache_->Set(job->key(), net_error, addr_list,
|
| base::TimeTicks::Now(), ttl);
|
| }
|
| }
|
| @@ -1545,21 +1712,26 @@ HostResolverImpl::Key HostResolverImpl::GetEffectiveKeyForRequest(
|
|
|
| void HostResolverImpl::AbortAllInProgressJobs() {
|
| base::WeakPtr<HostResolverImpl> self = AsWeakPtr();
|
| - // Scan |jobs_| for running jobs and abort them.
|
| + // In Abort, a Request callback could spawn new Jobs with matching keys, so
|
| + // first collect and remove all running jobs from |jobs_|.
|
| + std::vector<Job*> jobs_to_abort;
|
| for (JobMap::iterator it = jobs_.begin(); it != jobs_.end(); ) {
|
| Job* job = it->second;
|
| - // Advance the iterator before we might erase it.
|
| - ++it;
|
| if (job->is_running()) {
|
| - job->Abort();
|
| - // Check if resolver was deleted in a request callback.
|
| - if (!self)
|
| - return;
|
| + jobs_to_abort.push_back(job);
|
| + jobs_.erase(it++);
|
| } else {
|
| - // Keep it in |dispatch_|.
|
| - DCHECK(!job->handle().is_null());
|
| + DCHECK(job->is_queued());
|
| + ++it;
|
| }
|
| }
|
| +
|
| + // Then Abort them and dispatch new Jobs.
|
| + for (size_t i = 0; i < jobs_to_abort.size(); ++i) {
|
| + jobs_to_abort[i]->Abort();
|
| + dispatcher_.OnJobFinished();
|
| + }
|
| + STLDeleteElements(&jobs_to_abort);
|
| }
|
|
|
| void HostResolverImpl::OnIPAddressChanged() {
|
| @@ -1594,4 +1766,23 @@ 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();
|
| + }
|
| + // Don't Abort running Jobs unless they were running on DnsTransaction.
|
| + // TODO(szym): This will change once http://crbug.com/114827 is fixed.
|
| + if (had_factory)
|
| + OnDNSChanged();
|
| +}
|
| +
|
| } // namespace net
|
|
|