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 |