| Index: net/base/host_resolver_impl.cc
|
| ===================================================================
|
| --- net/base/host_resolver_impl.cc (revision 20435)
|
| +++ net/base/host_resolver_impl.cc (working copy)
|
| @@ -2,7 +2,7 @@
|
| // Use of this source code is governed by a BSD-style license that can be
|
| // found in the LICENSE file.
|
|
|
| -#include "net/base/host_resolver.h"
|
| +#include "net/base/host_resolver_impl.h"
|
|
|
| #if defined(OS_WIN)
|
| #include <ws2tcpip.h>
|
| @@ -22,161 +22,36 @@
|
| #include "base/time.h"
|
| #include "base/worker_pool.h"
|
| #include "net/base/address_list.h"
|
| +#include "net/base/host_resolver_proc.h"
|
| #include "net/base/net_errors.h"
|
|
|
| -#if defined(OS_LINUX)
|
| -#include "base/singleton.h"
|
| -#include "base/thread_local_storage.h"
|
| -#endif
|
| -
|
| #if defined(OS_WIN)
|
| #include "net/base/winsock_init.h"
|
| #endif
|
|
|
| namespace net {
|
|
|
| -//-----------------------------------------------------------------------------
|
| -
|
| -static HostMapper* host_mapper;
|
| -
|
| -std::string HostMapper::MapUsingPrevious(const std::string& host) {
|
| - return previous_mapper_.get() ? previous_mapper_->Map(host) : host;
|
| +HostResolver* CreateSystemHostResolver() {
|
| + static const size_t kMaxHostCacheEntries = 100;
|
| + static const size_t kHostCacheExpirationMs = 60000; // 1 minute.
|
| + return new HostResolverImpl(
|
| + NULL, kMaxHostCacheEntries, kHostCacheExpirationMs);
|
| }
|
|
|
| -HostMapper* SetHostMapper(HostMapper* value) {
|
| - std::swap(host_mapper, value);
|
| - return value;
|
| -}
|
| -
|
| -#if defined(OS_LINUX)
|
| -// On Linux changes to /etc/resolv.conf can go unnoticed thus resulting in
|
| -// DNS queries failing either because nameservers are unknown on startup
|
| -// or because nameserver info has changed as a result of e.g. connecting to
|
| -// a new network. Some distributions patch glibc to stat /etc/resolv.conf
|
| -// to try to automatically detect such changes but these patches are not
|
| -// universal and even patched systems such as Jaunty appear to need calls
|
| -// to res_ninit to reload the nameserver information in different threads.
|
| -//
|
| -// We adopt the Mozilla solution here which is to call res_ninit when
|
| -// lookups fail and to rate limit the reloading to once per second per
|
| -// thread.
|
| -
|
| -// Keep a timer per calling thread to rate limit the calling of res_ninit.
|
| -class DnsReloadTimer {
|
| - public:
|
| - DnsReloadTimer() {
|
| - tls_index_.Initialize(SlotReturnFunction);
|
| - }
|
| -
|
| - ~DnsReloadTimer() { }
|
| -
|
| - // Check if the timer for the calling thread has expired. When no
|
| - // timer exists for the calling thread, create one.
|
| - bool Expired() {
|
| - const base::TimeDelta kRetryTime = base::TimeDelta::FromSeconds(1);
|
| - base::TimeTicks now = base::TimeTicks::Now();
|
| - base::TimeTicks* timer_ptr =
|
| - static_cast<base::TimeTicks*>(tls_index_.Get());
|
| -
|
| - if (!timer_ptr) {
|
| - timer_ptr = new base::TimeTicks();
|
| - *timer_ptr = base::TimeTicks::Now();
|
| - tls_index_.Set(timer_ptr);
|
| - // Return true to reload dns info on the first call for each thread.
|
| - return true;
|
| - } else if (now - *timer_ptr > kRetryTime) {
|
| - *timer_ptr = now;
|
| - return true;
|
| - } else {
|
| - return false;
|
| - }
|
| - }
|
| -
|
| - // Free the allocated timer.
|
| - static void SlotReturnFunction(void* data) {
|
| - base::TimeTicks* tls_data = static_cast<base::TimeTicks*>(data);
|
| - delete tls_data;
|
| - }
|
| -
|
| - private:
|
| - // We use thread local storage to identify which base::TimeTicks to
|
| - // interact with.
|
| - static ThreadLocalStorage::Slot tls_index_ ;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(DnsReloadTimer);
|
| -};
|
| -
|
| -// A TLS slot to the TimeTicks for the current thread.
|
| -// static
|
| -ThreadLocalStorage::Slot DnsReloadTimer::tls_index_(base::LINKER_INITIALIZED);
|
| -
|
| -#endif // defined(OS_LINUX)
|
| -
|
| -static int HostResolverProc(const std::string& host, struct addrinfo** out) {
|
| - struct addrinfo hints = {0};
|
| - hints.ai_family = AF_UNSPEC;
|
| -
|
| -#if defined(OS_WIN)
|
| - // DO NOT USE AI_ADDRCONFIG ON WINDOWS.
|
| - //
|
| - // The following comment in <winsock2.h> is the best documentation I found
|
| - // on AI_ADDRCONFIG for Windows:
|
| - // Flags used in "hints" argument to getaddrinfo()
|
| - // - AI_ADDRCONFIG is supported starting with Vista
|
| - // - default is AI_ADDRCONFIG ON whether the flag is set or not
|
| - // because the performance penalty in not having ADDRCONFIG in
|
| - // the multi-protocol stack environment is severe;
|
| - // this defaulting may be disabled by specifying the AI_ALL flag,
|
| - // in that case AI_ADDRCONFIG must be EXPLICITLY specified to
|
| - // enable ADDRCONFIG behavior
|
| - //
|
| - // Not only is AI_ADDRCONFIG unnecessary, but it can be harmful. If the
|
| - // computer is not connected to a network, AI_ADDRCONFIG causes getaddrinfo
|
| - // to fail with WSANO_DATA (11004) for "localhost", probably because of the
|
| - // following note on AI_ADDRCONFIG in the MSDN getaddrinfo page:
|
| - // The IPv4 or IPv6 loopback address is not considered a valid global
|
| - // address.
|
| - // See http://crbug.com/5234.
|
| - hints.ai_flags = 0;
|
| -#else
|
| - hints.ai_flags = AI_ADDRCONFIG;
|
| -#endif
|
| -
|
| - // Restrict result set to only this socket type to avoid duplicates.
|
| - hints.ai_socktype = SOCK_STREAM;
|
| -
|
| - int err = getaddrinfo(host.c_str(), NULL, &hints, out);
|
| -#if defined(OS_LINUX)
|
| - net::DnsReloadTimer* dns_timer = Singleton<net::DnsReloadTimer>::get();
|
| - // If we fail, re-initialise the resolver just in case there have been any
|
| - // changes to /etc/resolv.conf and retry. See http://crbug.com/11380 for info.
|
| - if (err && dns_timer->Expired()) {
|
| - res_nclose(&_res);
|
| - if (!res_ninit(&_res))
|
| - err = getaddrinfo(host.c_str(), NULL, &hints, out);
|
| - }
|
| -#endif
|
| -
|
| - return err ? ERR_NAME_NOT_RESOLVED : OK;
|
| -}
|
| -
|
| -static int ResolveAddrInfo(HostMapper* mapper, const std::string& host,
|
| - struct addrinfo** out) {
|
| - if (mapper) {
|
| - std::string mapped_host = mapper->Map(host);
|
| -
|
| - if (mapped_host.empty())
|
| - return ERR_NAME_NOT_RESOLVED;
|
| -
|
| - return HostResolverProc(mapped_host, out);
|
| +static int ResolveAddrInfo(HostResolverProc* resolver_proc,
|
| + const std::string& host, AddressList* out) {
|
| + if (resolver_proc) {
|
| + // Use the custom procedure.
|
| + return resolver_proc->Resolve(host, out);
|
| } else {
|
| - return HostResolverProc(host, out);
|
| + // Use the system procedure (getaddrinfo).
|
| + return SystemHostResolverProc(host, out);
|
| }
|
| }
|
|
|
| //-----------------------------------------------------------------------------
|
|
|
| -class HostResolver::Request {
|
| +class HostResolverImpl::Request {
|
| public:
|
| Request(int id, const RequestInfo& info, CompletionCallback* callback,
|
| AddressList* addresses)
|
| @@ -245,28 +120,25 @@
|
|
|
| // This class represents a request to the worker pool for a "getaddrinfo()"
|
| // call.
|
| -class HostResolver::Job : public base::RefCountedThreadSafe<HostResolver::Job> {
|
| +class HostResolverImpl::Job
|
| + : public base::RefCountedThreadSafe<HostResolverImpl::Job> {
|
| public:
|
| - Job(HostResolver* resolver, const std::string& host)
|
| + Job(HostResolverImpl* resolver, const std::string& host)
|
| : host_(host),
|
| resolver_(resolver),
|
| origin_loop_(MessageLoop::current()),
|
| - host_mapper_(host_mapper),
|
| - error_(OK),
|
| - results_(NULL) {
|
| + resolver_proc_(resolver->effective_resolver_proc()),
|
| + error_(OK) {
|
| }
|
|
|
| ~Job() {
|
| - if (results_)
|
| - freeaddrinfo(results_);
|
| -
|
| // Free the requests attached to this job.
|
| STLDeleteElements(&requests_);
|
| }
|
|
|
| // Attaches a request to this job. The job takes ownership of |req| and will
|
| // take care to delete it.
|
| - void AddRequest(HostResolver::Request* req) {
|
| + void AddRequest(Request* req) {
|
| req->set_job(this);
|
| requests_.push_back(req);
|
| }
|
| @@ -301,11 +173,11 @@
|
|
|
| // We don't have to do anything further to actually cancel the requests
|
| // that were attached to this job (since they are unreachable now).
|
| - // But we will call HostResolver::CancelRequest(Request*) on each one
|
| + // But we will call HostResolverImpl::CancelRequest(Request*) on each one
|
| // in order to notify any observers.
|
| for (RequestsList::const_iterator it = requests_.begin();
|
| it != requests_.end(); ++it) {
|
| - HostResolver::Request* req = *it;
|
| + HostResolverImpl::Request* req = *it;
|
| if (!req->was_cancelled())
|
| resolver->CancelRequest(req);
|
| }
|
| @@ -329,7 +201,7 @@
|
| private:
|
| void DoLookup() {
|
| // Running on the worker thread
|
| - error_ = ResolveAddrInfo(host_mapper_, host_, &results_);
|
| + error_ = ResolveAddrInfo(resolver_proc_, host_, &results_);
|
|
|
| Task* reply = NewRunnableMethod(this, &Job::OnLookupComplete);
|
|
|
| @@ -353,59 +225,57 @@
|
| // TODO(eroman): this is being hit by URLRequestTest.CancelTest*,
|
| // because MessageLoop::current() == NULL.
|
| //DCHECK_EQ(origin_loop_, MessageLoop::current());
|
| - DCHECK(error_ || results_);
|
| + DCHECK(error_ || results_.head());
|
|
|
| if (was_cancelled())
|
| return;
|
|
|
| DCHECK(!requests_.empty());
|
|
|
| - // Adopt the address list using the port number of the first request.
|
| - AddressList addrlist;
|
| - if (error_ == OK) {
|
| - addrlist.Adopt(results_);
|
| - addrlist.SetPort(requests_[0]->port());
|
| - results_ = NULL;
|
| - }
|
| + // Use the port number of the first request.
|
| + if (error_ == OK)
|
| + results_.SetPort(requests_[0]->port());
|
|
|
| - resolver_->OnJobComplete(this, error_, addrlist);
|
| + resolver_->OnJobComplete(this, error_, results_);
|
| }
|
|
|
| // Set on the origin thread, read on the worker thread.
|
| std::string host_;
|
|
|
| // Only used on the origin thread (where Resolve was called).
|
| - HostResolver* resolver_;
|
| + HostResolverImpl* resolver_;
|
| RequestsList requests_; // The requests waiting on this job.
|
|
|
| // Used to post ourselves onto the origin thread.
|
| Lock origin_loop_lock_;
|
| MessageLoop* origin_loop_;
|
|
|
| - // Hold an owning reference to the host mapper that we are going to use.
|
| - // This may not be the current host mapper by the time we call
|
| + // Hold an owning reference to the HostResolverProc that we are going to use.
|
| + // This may not be the current resolver procedure by the time we call
|
| // ResolveAddrInfo, but that's OK... we'll use it anyways, and the owning
|
| // reference ensures that it remains valid until we are done.
|
| - scoped_refptr<HostMapper> host_mapper_;
|
| + scoped_refptr<HostResolverProc> resolver_proc_;
|
|
|
| // Assigned on the worker thread, read on the origin thread.
|
| int error_;
|
| - struct addrinfo* results_;
|
| + AddressList results_;
|
|
|
| DISALLOW_COPY_AND_ASSIGN(Job);
|
| };
|
|
|
| //-----------------------------------------------------------------------------
|
|
|
| -HostResolver::HostResolver(int max_cache_entries, int cache_duration_ms)
|
| +HostResolverImpl::HostResolverImpl(HostResolverProc* resolver_proc,
|
| + int max_cache_entries,
|
| + int cache_duration_ms)
|
| : cache_(max_cache_entries, cache_duration_ms), next_request_id_(0),
|
| - shutdown_(false) {
|
| + resolver_proc_(resolver_proc), shutdown_(false) {
|
| #if defined(OS_WIN)
|
| EnsureWinsockInit();
|
| #endif
|
| }
|
|
|
| -HostResolver::~HostResolver() {
|
| +HostResolverImpl::~HostResolverImpl() {
|
| // Cancel the outstanding jobs. Those jobs may contain several attached
|
| // requests, which will also be cancelled.
|
| for (JobMap::iterator it = jobs_.begin(); it != jobs_.end(); ++it)
|
| @@ -418,10 +288,10 @@
|
|
|
| // TODO(eroman): Don't create cache entries for hostnames which are simply IP
|
| // address literals.
|
| -int HostResolver::Resolve(const RequestInfo& info,
|
| - AddressList* addresses,
|
| - CompletionCallback* callback,
|
| - Request** out_req) {
|
| +int HostResolverImpl::Resolve(const RequestInfo& info,
|
| + AddressList* addresses,
|
| + CompletionCallback* callback,
|
| + RequestHandle* out_req) {
|
| if (shutdown_)
|
| return ERR_UNEXPECTED;
|
|
|
| @@ -437,7 +307,7 @@
|
| info.hostname(), base::TimeTicks::Now());
|
| if (cache_entry) {
|
| addresses->SetFrom(cache_entry->addrlist, info.port());
|
| - int error = OK;
|
| + int error = cache_entry->error;
|
|
|
| // Notify registered observers.
|
| NotifyObserversFinishRequest(request_id, info, error);
|
| @@ -448,13 +318,10 @@
|
|
|
| // If no callback was specified, do a synchronous resolution.
|
| if (!callback) {
|
| - struct addrinfo* results;
|
| - int error = ResolveAddrInfo(host_mapper, info.hostname(), &results);
|
| -
|
| - // Adopt the address list.
|
| AddressList addrlist;
|
| + int error = ResolveAddrInfo(
|
| + effective_resolver_proc(), info.hostname(), &addrlist);
|
| if (error == OK) {
|
| - addrlist.Adopt(results);
|
| addrlist.SetPort(info.port());
|
| *addresses = addrlist;
|
| }
|
| @@ -472,7 +339,7 @@
|
| // asked for it (out_req != NULL).
|
| Request* req = new Request(request_id, info, callback, addresses);
|
| if (out_req)
|
| - *out_req = req;
|
| + *out_req = reinterpret_cast<RequestHandle>(req);
|
|
|
| // Next we need to attach our request to a "job". This job is responsible for
|
| // calling "getaddrinfo(hostname)" on a worker thread.
|
| @@ -499,7 +366,8 @@
|
|
|
| // See OnJobComplete(Job*) for why it is important not to clean out
|
| // cancelled requests from Job::requests_.
|
| -void HostResolver::CancelRequest(Request* req) {
|
| +void HostResolverImpl::CancelRequest(RequestHandle req_handle) {
|
| + Request* req = reinterpret_cast<Request*>(req_handle);
|
| DCHECK(req);
|
| DCHECK(req->job());
|
| // NULL out the fields of req, to mark it as cancelled.
|
| @@ -507,11 +375,11 @@
|
| NotifyObserversCancelRequest(req->id(), req->info());
|
| }
|
|
|
| -void HostResolver::AddObserver(Observer* observer) {
|
| +void HostResolverImpl::AddObserver(Observer* observer) {
|
| observers_.push_back(observer);
|
| }
|
|
|
| -void HostResolver::RemoveObserver(Observer* observer) {
|
| +void HostResolverImpl::RemoveObserver(Observer* observer) {
|
| ObserversList::iterator it =
|
| std::find(observers_.begin(), observers_.end(), observer);
|
|
|
| @@ -521,7 +389,7 @@
|
| observers_.erase(it);
|
| }
|
|
|
| -void HostResolver::Shutdown() {
|
| +void HostResolverImpl::Shutdown() {
|
| shutdown_ = true;
|
|
|
| // Cancel the outstanding jobs.
|
| @@ -530,13 +398,13 @@
|
| jobs_.clear();
|
| }
|
|
|
| -void HostResolver::AddOutstandingJob(Job* job) {
|
| +void HostResolverImpl::AddOutstandingJob(Job* job) {
|
| scoped_refptr<Job>& found_job = jobs_[job->host()];
|
| DCHECK(!found_job);
|
| found_job = job;
|
| }
|
|
|
| -HostResolver::Job* HostResolver::FindOutstandingJob(
|
| +HostResolverImpl::Job* HostResolverImpl::FindOutstandingJob(
|
| const std::string& hostname) {
|
| JobMap::iterator it = jobs_.find(hostname);
|
| if (it != jobs_.end())
|
| @@ -544,14 +412,14 @@
|
| return NULL;
|
| }
|
|
|
| -void HostResolver::RemoveOutstandingJob(Job* job) {
|
| +void HostResolverImpl::RemoveOutstandingJob(Job* job) {
|
| JobMap::iterator it = jobs_.find(job->host());
|
| DCHECK(it != jobs_.end());
|
| DCHECK_EQ(it->second.get(), job);
|
| jobs_.erase(it);
|
| }
|
|
|
| -void HostResolver::OnJobComplete(Job* job,
|
| +void HostResolverImpl::OnJobComplete(Job* job,
|
| int error,
|
| const AddressList& addrlist) {
|
| RemoveOutstandingJob(job);
|
| @@ -586,7 +454,7 @@
|
| cur_completing_job_ = NULL;
|
| }
|
|
|
| -void HostResolver::NotifyObserversStartRequest(int request_id,
|
| +void HostResolverImpl::NotifyObserversStartRequest(int request_id,
|
| const RequestInfo& info) {
|
| for (ObserversList::iterator it = observers_.begin();
|
| it != observers_.end(); ++it) {
|
| @@ -594,7 +462,7 @@
|
| }
|
| }
|
|
|
| -void HostResolver::NotifyObserversFinishRequest(int request_id,
|
| +void HostResolverImpl::NotifyObserversFinishRequest(int request_id,
|
| const RequestInfo& info,
|
| int error) {
|
| bool was_resolved = error == OK;
|
| @@ -604,7 +472,7 @@
|
| }
|
| }
|
|
|
| -void HostResolver::NotifyObserversCancelRequest(int request_id,
|
| +void HostResolverImpl::NotifyObserversCancelRequest(int request_id,
|
| const RequestInfo& info) {
|
| for (ObserversList::iterator it = observers_.begin();
|
| it != observers_.end(); ++it) {
|
| @@ -612,56 +480,4 @@
|
| }
|
| }
|
|
|
| -//-----------------------------------------------------------------------------
|
| -
|
| -SingleRequestHostResolver::SingleRequestHostResolver(HostResolver* resolver)
|
| - : resolver_(resolver),
|
| - cur_request_(NULL),
|
| - cur_request_callback_(NULL),
|
| - ALLOW_THIS_IN_INITIALIZER_LIST(
|
| - callback_(this, &SingleRequestHostResolver::OnResolveCompletion)) {
|
| - DCHECK(resolver_ != NULL);
|
| -}
|
| -
|
| -SingleRequestHostResolver::~SingleRequestHostResolver() {
|
| - if (cur_request_) {
|
| - resolver_->CancelRequest(cur_request_);
|
| - }
|
| -}
|
| -
|
| -int SingleRequestHostResolver::Resolve(const HostResolver::RequestInfo& info,
|
| - AddressList* addresses,
|
| - CompletionCallback* callback) {
|
| - DCHECK(!cur_request_ && !cur_request_callback_) << "resolver already in use";
|
| -
|
| - HostResolver::Request* request = NULL;
|
| -
|
| - // We need to be notified of completion before |callback| is called, so that
|
| - // we can clear out |cur_request_*|.
|
| - CompletionCallback* transient_callback = callback ? &callback_ : NULL;
|
| -
|
| - int rv = resolver_->Resolve(info, addresses, transient_callback, &request);
|
| -
|
| - if (rv == ERR_IO_PENDING) {
|
| - // Cleared in OnResolveCompletion().
|
| - cur_request_ = request;
|
| - cur_request_callback_ = callback;
|
| - }
|
| -
|
| - return rv;
|
| -}
|
| -
|
| -void SingleRequestHostResolver::OnResolveCompletion(int result) {
|
| - DCHECK(cur_request_ && cur_request_callback_);
|
| -
|
| - CompletionCallback* callback = cur_request_callback_;
|
| -
|
| - // Clear the outstanding request information.
|
| - cur_request_ = NULL;
|
| - cur_request_callback_ = NULL;
|
| -
|
| - // Call the user's original callback.
|
| - callback->Run(result);
|
| -}
|
| -
|
| } // namespace net
|
|
|