| 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 | 
|  |