summaryrefslogtreecommitdiffstats
path: root/net/base
diff options
context:
space:
mode:
Diffstat (limited to 'net/base')
-rw-r--r--net/base/address_list_unittest.cc23
-rw-r--r--net/base/host_resolver.cc608
-rw-r--r--net/base/host_resolver.h159
-rw-r--r--net/base/host_resolver_impl.cc483
-rw-r--r--net/base/host_resolver_impl.h136
-rw-r--r--net/base/host_resolver_impl_unittest.cc (renamed from net/base/host_resolver_unittest.cc)236
-rw-r--r--net/base/host_resolver_proc.cc180
-rw-r--r--net/base/host_resolver_proc.h69
-rw-r--r--net/base/mock_host_resolver.cc154
-rw-r--r--net/base/mock_host_resolver.h143
-rw-r--r--net/base/run_all_unittests.cc12
11 files changed, 1325 insertions, 878 deletions
diff --git a/net/base/address_list_unittest.cc b/net/base/address_list_unittest.cc
index d594c85..d8ab3c1 100644
--- a/net/base/address_list_unittest.cc
+++ b/net/base/address_list_unittest.cc
@@ -4,15 +4,8 @@
#include "net/base/address_list.h"
-#if defined(OS_WIN)
-#include <ws2tcpip.h>
-#include <wspiapi.h> // Needed for Win2k compat.
-#elif defined(OS_POSIX)
-#include <netdb.h>
-#include <sys/socket.h>
-#endif
-
#include "base/string_util.h"
+#include "net/base/host_resolver_proc.h"
#include "net/base/net_util.h"
#if defined(OS_WIN)
#include "net/base/winsock_init.h"
@@ -26,17 +19,9 @@ void CreateAddressList(net::AddressList* addrlist, int port) {
#if defined(OS_WIN)
net::EnsureWinsockInit();
#endif
- std::string portstr = IntToString(port);
-
- struct addrinfo* result = NULL;
- struct addrinfo hints = {0};
- hints.ai_family = AF_UNSPEC;
- hints.ai_flags = AI_NUMERICHOST;
- hints.ai_socktype = SOCK_STREAM;
-
- int err = getaddrinfo("192.168.1.1", portstr.c_str(), &hints, &result);
- EXPECT_EQ(0, err);
- addrlist->Adopt(result);
+ int rv = SystemHostResolverProc("192.168.1.1", addrlist);
+ EXPECT_EQ(0, rv);
+ addrlist->SetPort(port);
}
TEST(AddressListTest, GetPort) {
diff --git a/net/base/host_resolver.cc b/net/base/host_resolver.cc
index 4f0ede5..1d17296 100644
--- a/net/base/host_resolver.cc
+++ b/net/base/host_resolver.cc
@@ -4,616 +4,12 @@
#include "net/base/host_resolver.h"
-#if defined(OS_WIN)
-#include <ws2tcpip.h>
-#include <wspiapi.h> // Needed for Win2k compat.
-#elif defined(OS_POSIX)
-#include <netdb.h>
-#include <sys/socket.h>
-#endif
-#if defined(OS_LINUX)
-#include <resolv.h>
-#endif
-
#include "base/compiler_specific.h"
-#include "base/message_loop.h"
-#include "base/stl_util-inl.h"
-#include "base/string_util.h"
-#include "base/time.h"
-#include "base/worker_pool.h"
-#include "net/base/address_list.h"
+#include "base/logging.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;
-}
-
-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);
- } else {
- return HostResolverProc(host, out);
- }
-}
-
-//-----------------------------------------------------------------------------
-
-class HostResolver::Request {
- public:
- Request(int id, const RequestInfo& info, CompletionCallback* callback,
- AddressList* addresses)
- : id_(id), info_(info), job_(NULL), callback_(callback),
- addresses_(addresses) {}
-
- // Mark the request as cancelled.
- void MarkAsCancelled() {
- job_ = NULL;
- callback_ = NULL;
- addresses_ = NULL;
- }
-
- bool was_cancelled() const {
- return callback_ == NULL;
- }
-
- void set_job(Job* job) {
- DCHECK(job != NULL);
- // Identify which job the request is waiting on.
- job_ = job;
- }
-
- void OnComplete(int error, const AddressList& addrlist) {
- if (error == OK)
- addresses_->SetFrom(addrlist, port());
- callback_->Run(error);
- }
-
- int port() const {
- return info_.port();
- }
-
- Job* job() const {
- return job_;
- }
-
- int id() const {
- return id_;
- }
-
- const RequestInfo& info() const {
- return info_;
- }
-
- private:
- // Unique ID for this request. Used by observers to identify requests.
- int id_;
-
- // The request info that started the request.
- RequestInfo info_;
-
- // The resolve job (running in worker pool) that this request is dependent on.
- Job* job_;
-
- // The user's callback to invoke when the request completes.
- CompletionCallback* callback_;
-
- // The address list to save result into.
- AddressList* addresses_;
-
- DISALLOW_COPY_AND_ASSIGN(Request);
-};
-
-//-----------------------------------------------------------------------------
-
-// This class represents a request to the worker pool for a "getaddrinfo()"
-// call.
-class HostResolver::Job : public base::RefCountedThreadSafe<HostResolver::Job> {
- public:
- Job(HostResolver* resolver, const std::string& host)
- : host_(host),
- resolver_(resolver),
- origin_loop_(MessageLoop::current()),
- host_mapper_(host_mapper),
- error_(OK),
- results_(NULL) {
- }
-
- ~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) {
- req->set_job(this);
- requests_.push_back(req);
- }
-
- // Called from origin loop.
- void Start() {
- // Dispatch the job to a worker thread.
- if (!WorkerPool::PostTask(FROM_HERE,
- NewRunnableMethod(this, &Job::DoLookup), true)) {
- NOTREACHED();
-
- // Since we could be running within Resolve() right now, we can't just
- // call OnLookupComplete(). Instead we must wait until Resolve() has
- // returned (IO_PENDING).
- error_ = ERR_UNEXPECTED;
- MessageLoop::current()->PostTask(
- FROM_HERE, NewRunnableMethod(this, &Job::OnLookupComplete));
- }
- }
-
- // Cancels the current job. Callable from origin thread.
- void Cancel() {
- HostResolver* resolver = resolver_;
- resolver_ = NULL;
-
- // Mark the job as cancelled, so when worker thread completes it will
- // not try to post completion to origin loop.
- {
- AutoLock locked(origin_loop_lock_);
- origin_loop_ = NULL;
- }
-
- // 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
- // in order to notify any observers.
- for (RequestsList::const_iterator it = requests_.begin();
- it != requests_.end(); ++it) {
- HostResolver::Request* req = *it;
- if (!req->was_cancelled())
- resolver->CancelRequest(req);
- }
- }
-
- // Called from origin thread.
- bool was_cancelled() const {
- return resolver_ == NULL;
- }
-
- // Called from origin thread.
- const std::string& host() const {
- return host_;
- }
-
- // Called from origin thread.
- const RequestsList& requests() const {
- return requests_;
- }
-
- private:
- void DoLookup() {
- // Running on the worker thread
- error_ = ResolveAddrInfo(host_mapper_, host_, &results_);
-
- Task* reply = NewRunnableMethod(this, &Job::OnLookupComplete);
-
- // The origin loop could go away while we are trying to post to it, so we
- // need to call its PostTask method inside a lock. See ~HostResolver.
- {
- AutoLock locked(origin_loop_lock_);
- if (origin_loop_) {
- origin_loop_->PostTask(FROM_HERE, reply);
- reply = NULL;
- }
- }
-
- // Does nothing if it got posted.
- delete reply;
- }
-
- // Callback for when DoLookup() completes (runs on origin thread).
- void OnLookupComplete() {
- // Should be running on origin loop.
- // TODO(eroman): this is being hit by URLRequestTest.CancelTest*,
- // because MessageLoop::current() == NULL.
- //DCHECK_EQ(origin_loop_, MessageLoop::current());
- DCHECK(error_ || results_);
-
- 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;
- }
-
- resolver_->OnJobComplete(this, error_, addrlist);
- }
-
- // 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_;
- 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
- // 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_;
-
- // Assigned on the worker thread, read on the origin thread.
- int error_;
- struct addrinfo* results_;
-
- DISALLOW_COPY_AND_ASSIGN(Job);
-};
-
-//-----------------------------------------------------------------------------
-
-HostResolver::HostResolver(int max_cache_entries, int cache_duration_ms)
- : cache_(max_cache_entries, cache_duration_ms), next_request_id_(0),
- shutdown_(false) {
-#if defined(OS_WIN)
- EnsureWinsockInit();
-#endif
-}
-
-HostResolver::~HostResolver() {
- // 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)
- it->second->Cancel();
-
- // In case we are being deleted during the processing of a callback.
- if (cur_completing_job_)
- cur_completing_job_->Cancel();
-}
-
-// 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) {
- if (shutdown_)
- return ERR_UNEXPECTED;
-
- // Choose a unique ID number for observers to see.
- int request_id = next_request_id_++;
-
- // Notify registered observers.
- NotifyObserversStartRequest(request_id, info);
-
- // If we have an unexpired cache entry, use it.
- if (info.allow_cached_response()) {
- const HostCache::Entry* cache_entry = cache_.Lookup(
- info.hostname(), base::TimeTicks::Now());
- if (cache_entry) {
- addresses->SetFrom(cache_entry->addrlist, info.port());
- int error = OK;
-
- // Notify registered observers.
- NotifyObserversFinishRequest(request_id, info, error);
-
- return error;
- }
- }
-
- // 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;
- if (error == OK) {
- addrlist.Adopt(results);
- addrlist.SetPort(info.port());
- *addresses = addrlist;
- }
-
- // Write to cache.
- cache_.Set(info.hostname(), error, addrlist, base::TimeTicks::Now());
-
- // Notify registered observers.
- NotifyObserversFinishRequest(request_id, info, error);
-
- return error;
- }
-
- // Create a handle for this request, and pass it back to the user if they
- // asked for it (out_req != NULL).
- Request* req = new Request(request_id, info, callback, addresses);
- if (out_req)
- *out_req = req;
-
- // Next we need to attach our request to a "job". This job is responsible for
- // calling "getaddrinfo(hostname)" on a worker thread.
- scoped_refptr<Job> job;
-
- // If there is already an outstanding job to resolve |info.hostname()|, use
- // it. This prevents starting concurrent resolves for the same hostname.
- job = FindOutstandingJob(info.hostname());
- if (job) {
- job->AddRequest(req);
- } else {
- // Create a new job for this request.
- job = new Job(this, info.hostname());
- job->AddRequest(req);
- AddOutstandingJob(job);
- // TODO(eroman): Bound the total number of concurrent jobs.
- // http://crbug.com/9598
- job->Start();
- }
-
- // Completion happens during OnJobComplete(Job*).
- return ERR_IO_PENDING;
-}
-
-// See OnJobComplete(Job*) for why it is important not to clean out
-// cancelled requests from Job::requests_.
-void HostResolver::CancelRequest(Request* req) {
- DCHECK(req);
- DCHECK(req->job());
- // NULL out the fields of req, to mark it as cancelled.
- req->MarkAsCancelled();
- NotifyObserversCancelRequest(req->id(), req->info());
-}
-
-void HostResolver::AddObserver(Observer* observer) {
- observers_.push_back(observer);
-}
-
-void HostResolver::RemoveObserver(Observer* observer) {
- ObserversList::iterator it =
- std::find(observers_.begin(), observers_.end(), observer);
-
- // Observer must exist.
- DCHECK(it != observers_.end());
-
- observers_.erase(it);
-}
-
-void HostResolver::Shutdown() {
- shutdown_ = true;
-
- // Cancel the outstanding jobs.
- for (JobMap::iterator it = jobs_.begin(); it != jobs_.end(); ++it)
- it->second->Cancel();
- jobs_.clear();
-}
-
-void HostResolver::AddOutstandingJob(Job* job) {
- scoped_refptr<Job>& found_job = jobs_[job->host()];
- DCHECK(!found_job);
- found_job = job;
-}
-
-HostResolver::Job* HostResolver::FindOutstandingJob(
- const std::string& hostname) {
- JobMap::iterator it = jobs_.find(hostname);
- if (it != jobs_.end())
- return it->second;
- return NULL;
-}
-
-void HostResolver::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,
- int error,
- const AddressList& addrlist) {
- RemoveOutstandingJob(job);
-
- // Write result to the cache.
- cache_.Set(job->host(), error, addrlist, base::TimeTicks::Now());
-
- // Make a note that we are executing within OnJobComplete() in case the
- // HostResolver is deleted by a callback invocation.
- DCHECK(!cur_completing_job_);
- cur_completing_job_ = job;
-
- // Complete all of the requests that were attached to the job.
- for (RequestsList::const_iterator it = job->requests().begin();
- it != job->requests().end(); ++it) {
- Request* req = *it;
- if (!req->was_cancelled()) {
- DCHECK_EQ(job, req->job());
-
- // Notify registered observers.
- NotifyObserversFinishRequest(req->id(), req->info(), error);
-
- req->OnComplete(error, addrlist);
-
- // Check if the job was cancelled as a result of running the callback.
- // (Meaning that |this| was deleted).
- if (job->was_cancelled())
- return;
- }
- }
-
- cur_completing_job_ = NULL;
-}
-
-void HostResolver::NotifyObserversStartRequest(int request_id,
- const RequestInfo& info) {
- for (ObserversList::iterator it = observers_.begin();
- it != observers_.end(); ++it) {
- (*it)->OnStartResolution(request_id, info);
- }
-}
-
-void HostResolver::NotifyObserversFinishRequest(int request_id,
- const RequestInfo& info,
- int error) {
- bool was_resolved = error == OK;
- for (ObserversList::iterator it = observers_.begin();
- it != observers_.end(); ++it) {
- (*it)->OnFinishResolutionWithStatus(request_id, was_resolved, info);
- }
-}
-
-void HostResolver::NotifyObserversCancelRequest(int request_id,
- const RequestInfo& info) {
- for (ObserversList::iterator it = observers_.begin();
- it != observers_.end(); ++it) {
- (*it)->OnCancelResolution(request_id, info);
- }
-}
-
-//-----------------------------------------------------------------------------
-
SingleRequestHostResolver::SingleRequestHostResolver(HostResolver* resolver)
: resolver_(resolver),
cur_request_(NULL),
@@ -634,7 +30,7 @@ int SingleRequestHostResolver::Resolve(const HostResolver::RequestInfo& info,
CompletionCallback* callback) {
DCHECK(!cur_request_ && !cur_request_callback_) << "resolver already in use";
- HostResolver::Request* request = NULL;
+ HostResolver::RequestHandle request = NULL;
// We need to be notified of completion before |callback| is called, so that
// we can clear out |cur_request_*|.
diff --git a/net/base/host_resolver.h b/net/base/host_resolver.h
index 5a6c548..c1ac13f 100644
--- a/net/base/host_resolver.h
+++ b/net/base/host_resolver.h
@@ -6,57 +6,26 @@
#define NET_BASE_HOST_RESOLVER_H_
#include <string>
-#include <vector>
-#include "base/basictypes.h"
-#include "base/lock.h"
#include "base/ref_counted.h"
#include "googleurl/src/gurl.h"
#include "net/base/completion_callback.h"
-#include "net/base/host_cache.h"
class MessageLoop;
namespace net {
class AddressList;
-class HostMapper;
// This class represents the task of resolving hostnames (or IP address
// literal) to an AddressList object.
//
-// HostResolver handles multiple requests at a time, so when cancelling a
-// request the Request* handle that was returned by Resolve() needs to be
+// HostResolver can handle multiple requests at a time, so when cancelling a
+// request the RequestHandle that was returned by Resolve() needs to be
// given. A simpler alternative for consumers that only have 1 outstanding
// request at a time is to create a SingleRequestHostResolver wrapper around
// HostResolver (which will automatically cancel the single request when it
// goes out of scope).
-//
-// For each hostname that is requested, HostResolver creates a
-// HostResolver::Job. This job gets dispatched to a thread in the global
-// WorkerPool, where it runs "getaddrinfo(hostname)". If requests for that same
-// host are made while the job is already outstanding, then they are attached
-// to the existing job rather than creating a new one. This avoids doing
-// parallel resolves for the same host.
-//
-// The way these classes fit together is illustrated by:
-//
-//
-// +------------- HostResolver ---------------+
-// | | |
-// Job Job Job
-// (for host1) (for host2) (for hostX)
-// / | | / | | / | |
-// Request ... Request Request ... Request Request ... Request
-// (port1) (port2) (port3) (port4) (port5) (portX)
-//
-//
-// When a HostResolver::Job finishes its work in the threadpool, the callbacks
-// of each waiting request are run on the origin thread.
-//
-// Thread safety: This class is not threadsafe, and must only be called
-// from one thread!
-//
class HostResolver : public base::RefCounted<HostResolver> {
public:
// The parameters for doing a Resolve(). |hostname| and |port| are required,
@@ -120,20 +89,15 @@ class HostResolver : public base::RefCounted<HostResolver> {
virtual void OnCancelResolution(int id, const RequestInfo& info) = 0;
};
- // Creates a HostResolver that caches up to |max_cache_entries| for
- // |cache_duration_ms| milliseconds.
- //
- // TODO(eroman): Get rid of the default parameters as it violate google
- // style. This is temporary to help with refactoring.
- HostResolver(int max_cache_entries = 100, int cache_duration_ms = 60000);
+ // Opaque type used to cancel a request.
+ typedef void* RequestHandle;
+
+ HostResolver() {}
// If any completion callbacks are pending when the resolver is destroyed,
// the host resolutions are cancelled, and the completion callbacks will not
// be called.
- ~HostResolver();
-
- // Opaque type used to cancel a request.
- class Request;
+ virtual ~HostResolver() {}
// Resolves the given hostname (or IP address literal), filling out the
// |addresses| object upon success. The |info.port| parameter will be set as
@@ -147,74 +111,23 @@ class HostResolver : public base::RefCounted<HostResolver> {
// result code will be passed to the completion callback. If |req| is
// non-NULL, then |*req| will be filled with a handle to the async request.
// This handle is not valid after the request has completed.
- int Resolve(const RequestInfo& info, AddressList* addresses,
- CompletionCallback* callback, Request** req);
+ virtual int Resolve(const RequestInfo& info, AddressList* addresses,
+ CompletionCallback* callback, RequestHandle* out_req) = 0;
// Cancels the specified request. |req| is the handle returned by Resolve().
// After a request is cancelled, its completion callback will not be called.
- void CancelRequest(Request* req);
+ virtual void CancelRequest(RequestHandle req) = 0;
// Adds an observer to this resolver. The observer will be notified of the
// start and completion of all requests (excluding cancellation). |observer|
// must remain valid for the duration of this HostResolver's lifetime.
- void AddObserver(Observer* observer);
+ virtual void AddObserver(Observer* observer) = 0;
// Unregisters an observer previously added by AddObserver().
- void RemoveObserver(Observer* observer);
-
- // TODO(eroman): temp hack for http://crbug.com/15513
- void Shutdown();
-
- private:
- class Job;
- typedef std::vector<Request*> RequestsList;
- typedef base::hash_map<std::string, scoped_refptr<Job> > JobMap;
- typedef std::vector<Observer*> ObserversList;
-
- // Adds a job to outstanding jobs list.
- void AddOutstandingJob(Job* job);
-
- // Returns the outstanding job for |hostname|, or NULL if there is none.
- Job* FindOutstandingJob(const std::string& hostname);
-
- // Removes |job| from the outstanding jobs list.
- void RemoveOutstandingJob(Job* job);
-
- // Callback for when |job| has completed with |error| and |addrlist|.
- void OnJobComplete(Job* job, int error, const AddressList& addrlist);
-
- // Notify all observers of the start of a resolve request.
- void NotifyObserversStartRequest(int request_id,
- const RequestInfo& info);
-
- // Notify all observers of the completion of a resolve request.
- void NotifyObserversFinishRequest(int request_id,
- const RequestInfo& info,
- int error);
-
- // Notify all observers of the cancellation of a resolve request.
- void NotifyObserversCancelRequest(int request_id,
- const RequestInfo& info);
-
- // Cache of host resolution results.
- HostCache cache_;
-
- // Map from hostname to outstanding job.
- JobMap jobs_;
-
- // The job that OnJobComplete() is currently processing (needed in case
- // HostResolver gets deleted from within the callback).
- scoped_refptr<Job> cur_completing_job_;
-
- // The observers to notify when a request starts/ends.
- ObserversList observers_;
-
- // Monotonically increasing ID number to assign to the next request.
- // Observers are the only consumers of this ID number.
- int next_request_id_;
+ virtual void RemoveObserver(Observer* observer) = 0;
// TODO(eroman): temp hack for http://crbug.com/15513
- bool shutdown_;
+ virtual void Shutdown() = 0;
DISALLOW_COPY_AND_ASSIGN(HostResolver);
};
@@ -245,7 +158,7 @@ class SingleRequestHostResolver {
scoped_refptr<HostResolver> resolver_;
// The current request (if any).
- HostResolver::Request* cur_request_;
+ HostResolver::RequestHandle cur_request_;
CompletionCallback* cur_request_callback_;
// Completion callback for when request to |resolver_| completes.
@@ -254,46 +167,10 @@ class SingleRequestHostResolver {
DISALLOW_COPY_AND_ASSIGN(SingleRequestHostResolver);
};
-// A helper class used in unit tests to alter hostname mappings. See
-// SetHostMapper for details.
-class HostMapper : public base::RefCountedThreadSafe<HostMapper> {
- public:
- virtual ~HostMapper() {}
-
- // Returns possibly altered hostname, or empty string to simulate
- // a failed lookup.
- virtual std::string Map(const std::string& host) = 0;
-
- protected:
- // Ask previous host mapper (if set) for mapping of given host.
- std::string MapUsingPrevious(const std::string& host);
-
- private:
- friend class ScopedHostMapper;
-
- // Set mapper to ask when this mapper doesn't want to modify the result.
- void set_previous_mapper(HostMapper* mapper) {
- previous_mapper_ = mapper;
- }
-
- scoped_refptr<HostMapper> previous_mapper_;
-};
-
-#ifdef UNIT_TEST
-// This function is designed to allow unit tests to override the behavior of
-// HostResolver. For example, a HostMapper instance can force all hostnames
-// to map to a fixed IP address such as 127.0.0.1.
-//
-// The previously set HostMapper (or NULL if there was none) is returned.
-//
-// NOTE: This function is not thread-safe, so take care to only call this
-// function while there are no outstanding HostResolver instances.
-//
-// NOTE: In most cases, you should use ScopedHostMapper instead, which is
-// defined in host_resolver_unittest.h
-//
-HostMapper* SetHostMapper(HostMapper* host_mapper);
-#endif
+// Creates a HostResolver implementation that queries the underlying system.
+// (Except if a unit-test has changed the global HostResolverProc using
+// ScopedHostResolverProc to intercept requests to the system).
+HostResolver* CreateSystemHostResolver();
} // namespace net
diff --git a/net/base/host_resolver_impl.cc b/net/base/host_resolver_impl.cc
new file mode 100644
index 0000000..2a0eb0f
--- /dev/null
+++ b/net/base/host_resolver_impl.cc
@@ -0,0 +1,483 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// 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_impl.h"
+
+#if defined(OS_WIN)
+#include <ws2tcpip.h>
+#include <wspiapi.h> // Needed for Win2k compat.
+#elif defined(OS_POSIX)
+#include <netdb.h>
+#include <sys/socket.h>
+#endif
+#if defined(OS_LINUX)
+#include <resolv.h>
+#endif
+
+#include "base/compiler_specific.h"
+#include "base/message_loop.h"
+#include "base/stl_util-inl.h"
+#include "base/string_util.h"
+#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_WIN)
+#include "net/base/winsock_init.h"
+#endif
+
+namespace net {
+
+HostResolver* CreateSystemHostResolver() {
+ static const size_t kMaxHostCacheEntries = 100;
+ static const size_t kHostCacheExpirationMs = 60000; // 1 minute.
+ return new HostResolverImpl(
+ NULL, kMaxHostCacheEntries, kHostCacheExpirationMs);
+}
+
+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 {
+ // Use the system procedure (getaddrinfo).
+ return SystemHostResolverProc(host, out);
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+class HostResolverImpl::Request {
+ public:
+ Request(int id, const RequestInfo& info, CompletionCallback* callback,
+ AddressList* addresses)
+ : id_(id), info_(info), job_(NULL), callback_(callback),
+ addresses_(addresses) {}
+
+ // Mark the request as cancelled.
+ void MarkAsCancelled() {
+ job_ = NULL;
+ callback_ = NULL;
+ addresses_ = NULL;
+ }
+
+ bool was_cancelled() const {
+ return callback_ == NULL;
+ }
+
+ void set_job(Job* job) {
+ DCHECK(job != NULL);
+ // Identify which job the request is waiting on.
+ job_ = job;
+ }
+
+ void OnComplete(int error, const AddressList& addrlist) {
+ if (error == OK)
+ addresses_->SetFrom(addrlist, port());
+ callback_->Run(error);
+ }
+
+ int port() const {
+ return info_.port();
+ }
+
+ Job* job() const {
+ return job_;
+ }
+
+ int id() const {
+ return id_;
+ }
+
+ const RequestInfo& info() const {
+ return info_;
+ }
+
+ private:
+ // Unique ID for this request. Used by observers to identify requests.
+ int id_;
+
+ // The request info that started the request.
+ RequestInfo info_;
+
+ // The resolve job (running in worker pool) that this request is dependent on.
+ Job* job_;
+
+ // The user's callback to invoke when the request completes.
+ CompletionCallback* callback_;
+
+ // The address list to save result into.
+ AddressList* addresses_;
+
+ DISALLOW_COPY_AND_ASSIGN(Request);
+};
+
+//-----------------------------------------------------------------------------
+
+// This class represents a request to the worker pool for a "getaddrinfo()"
+// call.
+class HostResolverImpl::Job
+ : public base::RefCountedThreadSafe<HostResolverImpl::Job> {
+ public:
+ Job(HostResolverImpl* resolver, const std::string& host)
+ : host_(host),
+ resolver_(resolver),
+ origin_loop_(MessageLoop::current()),
+ resolver_proc_(resolver->effective_resolver_proc()),
+ error_(OK) {
+ }
+
+ ~Job() {
+ // 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(Request* req) {
+ req->set_job(this);
+ requests_.push_back(req);
+ }
+
+ // Called from origin loop.
+ void Start() {
+ // Dispatch the job to a worker thread.
+ if (!WorkerPool::PostTask(FROM_HERE,
+ NewRunnableMethod(this, &Job::DoLookup), true)) {
+ NOTREACHED();
+
+ // Since we could be running within Resolve() right now, we can't just
+ // call OnLookupComplete(). Instead we must wait until Resolve() has
+ // returned (IO_PENDING).
+ error_ = ERR_UNEXPECTED;
+ MessageLoop::current()->PostTask(
+ FROM_HERE, NewRunnableMethod(this, &Job::OnLookupComplete));
+ }
+ }
+
+ // Cancels the current job. Callable from origin thread.
+ void Cancel() {
+ HostResolver* resolver = resolver_;
+ resolver_ = NULL;
+
+ // Mark the job as cancelled, so when worker thread completes it will
+ // not try to post completion to origin loop.
+ {
+ AutoLock locked(origin_loop_lock_);
+ origin_loop_ = NULL;
+ }
+
+ // 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 HostResolverImpl::CancelRequest(Request*) on each one
+ // in order to notify any observers.
+ for (RequestsList::const_iterator it = requests_.begin();
+ it != requests_.end(); ++it) {
+ HostResolverImpl::Request* req = *it;
+ if (!req->was_cancelled())
+ resolver->CancelRequest(req);
+ }
+ }
+
+ // Called from origin thread.
+ bool was_cancelled() const {
+ return resolver_ == NULL;
+ }
+
+ // Called from origin thread.
+ const std::string& host() const {
+ return host_;
+ }
+
+ // Called from origin thread.
+ const RequestsList& requests() const {
+ return requests_;
+ }
+
+ private:
+ void DoLookup() {
+ // Running on the worker thread
+ error_ = ResolveAddrInfo(resolver_proc_, host_, &results_);
+
+ Task* reply = NewRunnableMethod(this, &Job::OnLookupComplete);
+
+ // The origin loop could go away while we are trying to post to it, so we
+ // need to call its PostTask method inside a lock. See ~HostResolver.
+ {
+ AutoLock locked(origin_loop_lock_);
+ if (origin_loop_) {
+ origin_loop_->PostTask(FROM_HERE, reply);
+ reply = NULL;
+ }
+ }
+
+ // Does nothing if it got posted.
+ delete reply;
+ }
+
+ // Callback for when DoLookup() completes (runs on origin thread).
+ void OnLookupComplete() {
+ // Should be running on origin loop.
+ // TODO(eroman): this is being hit by URLRequestTest.CancelTest*,
+ // because MessageLoop::current() == NULL.
+ //DCHECK_EQ(origin_loop_, MessageLoop::current());
+ DCHECK(error_ || results_.head());
+
+ if (was_cancelled())
+ return;
+
+ DCHECK(!requests_.empty());
+
+ // Use the port number of the first request.
+ if (error_ == OK)
+ results_.SetPort(requests_[0]->port());
+
+ 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).
+ 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 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<HostResolverProc> resolver_proc_;
+
+ // Assigned on the worker thread, read on the origin thread.
+ int error_;
+ AddressList results_;
+
+ DISALLOW_COPY_AND_ASSIGN(Job);
+};
+
+//-----------------------------------------------------------------------------
+
+HostResolverImpl::HostResolverImpl(HostResolverProc* resolver_proc,
+ int max_cache_entries,
+ int cache_duration_ms)
+ : cache_(max_cache_entries, cache_duration_ms), next_request_id_(0),
+ resolver_proc_(resolver_proc), shutdown_(false) {
+#if defined(OS_WIN)
+ EnsureWinsockInit();
+#endif
+}
+
+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)
+ it->second->Cancel();
+
+ // In case we are being deleted during the processing of a callback.
+ if (cur_completing_job_)
+ cur_completing_job_->Cancel();
+}
+
+// TODO(eroman): Don't create cache entries for hostnames which are simply IP
+// address literals.
+int HostResolverImpl::Resolve(const RequestInfo& info,
+ AddressList* addresses,
+ CompletionCallback* callback,
+ RequestHandle* out_req) {
+ if (shutdown_)
+ return ERR_UNEXPECTED;
+
+ // Choose a unique ID number for observers to see.
+ int request_id = next_request_id_++;
+
+ // Notify registered observers.
+ NotifyObserversStartRequest(request_id, info);
+
+ // If we have an unexpired cache entry, use it.
+ if (info.allow_cached_response()) {
+ const HostCache::Entry* cache_entry = cache_.Lookup(
+ info.hostname(), base::TimeTicks::Now());
+ if (cache_entry) {
+ addresses->SetFrom(cache_entry->addrlist, info.port());
+ int error = cache_entry->error;
+
+ // Notify registered observers.
+ NotifyObserversFinishRequest(request_id, info, error);
+
+ return error;
+ }
+ }
+
+ // If no callback was specified, do a synchronous resolution.
+ if (!callback) {
+ AddressList addrlist;
+ int error = ResolveAddrInfo(
+ effective_resolver_proc(), info.hostname(), &addrlist);
+ if (error == OK) {
+ addrlist.SetPort(info.port());
+ *addresses = addrlist;
+ }
+
+ // Write to cache.
+ cache_.Set(info.hostname(), error, addrlist, base::TimeTicks::Now());
+
+ // Notify registered observers.
+ NotifyObserversFinishRequest(request_id, info, error);
+
+ return error;
+ }
+
+ // Create a handle for this request, and pass it back to the user if they
+ // asked for it (out_req != NULL).
+ Request* req = new Request(request_id, info, callback, addresses);
+ if (out_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.
+ scoped_refptr<Job> job;
+
+ // If there is already an outstanding job to resolve |info.hostname()|, use
+ // it. This prevents starting concurrent resolves for the same hostname.
+ job = FindOutstandingJob(info.hostname());
+ if (job) {
+ job->AddRequest(req);
+ } else {
+ // Create a new job for this request.
+ job = new Job(this, info.hostname());
+ job->AddRequest(req);
+ AddOutstandingJob(job);
+ // TODO(eroman): Bound the total number of concurrent jobs.
+ // http://crbug.com/9598
+ job->Start();
+ }
+
+ // Completion happens during OnJobComplete(Job*).
+ return ERR_IO_PENDING;
+}
+
+// See OnJobComplete(Job*) for why it is important not to clean out
+// cancelled requests from Job::requests_.
+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.
+ req->MarkAsCancelled();
+ NotifyObserversCancelRequest(req->id(), req->info());
+}
+
+void HostResolverImpl::AddObserver(Observer* observer) {
+ observers_.push_back(observer);
+}
+
+void HostResolverImpl::RemoveObserver(Observer* observer) {
+ ObserversList::iterator it =
+ std::find(observers_.begin(), observers_.end(), observer);
+
+ // Observer must exist.
+ DCHECK(it != observers_.end());
+
+ observers_.erase(it);
+}
+
+void HostResolverImpl::Shutdown() {
+ shutdown_ = true;
+
+ // Cancel the outstanding jobs.
+ for (JobMap::iterator it = jobs_.begin(); it != jobs_.end(); ++it)
+ it->second->Cancel();
+ jobs_.clear();
+}
+
+void HostResolverImpl::AddOutstandingJob(Job* job) {
+ scoped_refptr<Job>& found_job = jobs_[job->host()];
+ DCHECK(!found_job);
+ found_job = job;
+}
+
+HostResolverImpl::Job* HostResolverImpl::FindOutstandingJob(
+ const std::string& hostname) {
+ JobMap::iterator it = jobs_.find(hostname);
+ if (it != jobs_.end())
+ return it->second;
+ return NULL;
+}
+
+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 HostResolverImpl::OnJobComplete(Job* job,
+ int error,
+ const AddressList& addrlist) {
+ RemoveOutstandingJob(job);
+
+ // Write result to the cache.
+ cache_.Set(job->host(), error, addrlist, base::TimeTicks::Now());
+
+ // Make a note that we are executing within OnJobComplete() in case the
+ // HostResolver is deleted by a callback invocation.
+ DCHECK(!cur_completing_job_);
+ cur_completing_job_ = job;
+
+ // Complete all of the requests that were attached to the job.
+ for (RequestsList::const_iterator it = job->requests().begin();
+ it != job->requests().end(); ++it) {
+ Request* req = *it;
+ if (!req->was_cancelled()) {
+ DCHECK_EQ(job, req->job());
+
+ // Notify registered observers.
+ NotifyObserversFinishRequest(req->id(), req->info(), error);
+
+ req->OnComplete(error, addrlist);
+
+ // Check if the job was cancelled as a result of running the callback.
+ // (Meaning that |this| was deleted).
+ if (job->was_cancelled())
+ return;
+ }
+ }
+
+ cur_completing_job_ = NULL;
+}
+
+void HostResolverImpl::NotifyObserversStartRequest(int request_id,
+ const RequestInfo& info) {
+ for (ObserversList::iterator it = observers_.begin();
+ it != observers_.end(); ++it) {
+ (*it)->OnStartResolution(request_id, info);
+ }
+}
+
+void HostResolverImpl::NotifyObserversFinishRequest(int request_id,
+ const RequestInfo& info,
+ int error) {
+ bool was_resolved = error == OK;
+ for (ObserversList::iterator it = observers_.begin();
+ it != observers_.end(); ++it) {
+ (*it)->OnFinishResolutionWithStatus(request_id, was_resolved, info);
+ }
+}
+
+void HostResolverImpl::NotifyObserversCancelRequest(int request_id,
+ const RequestInfo& info) {
+ for (ObserversList::iterator it = observers_.begin();
+ it != observers_.end(); ++it) {
+ (*it)->OnCancelResolution(request_id, info);
+ }
+}
+
+} // namespace net
diff --git a/net/base/host_resolver_impl.h b/net/base/host_resolver_impl.h
new file mode 100644
index 0000000..6ece456
--- /dev/null
+++ b/net/base/host_resolver_impl.h
@@ -0,0 +1,136 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_BASE_HOST_RESOLVER_IMPL_H_
+#define NET_BASE_HOST_RESOLVER_IMPL_H_
+
+#include <string>
+#include <vector>
+
+#include "net/base/host_cache.h"
+#include "net/base/host_resolver.h"
+#include "net/base/host_resolver_proc.h"
+
+namespace net {
+
+// For each hostname that is requested, HostResolver creates a
+// HostResolverImpl::Job. This job gets dispatched to a thread in the global
+// WorkerPool, where it runs SystemHostResolverProc(). If requests for that same
+// host are made while the job is already outstanding, then they are attached
+// to the existing job rather than creating a new one. This avoids doing
+// parallel resolves for the same host.
+//
+// The way these classes fit together is illustrated by:
+//
+//
+// +----------- HostResolverImpl -------------+
+// | | |
+// Job Job Job
+// (for host1) (for host2) (for hostX)
+// / | | / | | / | |
+// Request ... Request Request ... Request Request ... Request
+// (port1) (port2) (port3) (port4) (port5) (portX)
+//
+//
+// When a HostResolverImpl::Job finishes its work in the threadpool, the
+// callbacks of each waiting request are run on the origin thread.
+//
+// Thread safety: This class is not threadsafe, and must only be called
+// from one thread!
+//
+class HostResolverImpl : public HostResolver {
+ public:
+ // Creates a HostResolver that caches up to |max_cache_entries| for
+ // |cache_duration_ms| milliseconds. |resolver_proc| is used to perform
+ // the actual resolves; it must be thread-safe since it is run from
+ // multiple worker threads. If |resolver_proc| is NULL then the default
+ // host resolver procedure is used (which is SystemHostResolverProc except
+ // if overridden)
+ HostResolverImpl(HostResolverProc* resolver_proc,
+ int max_cache_entries,
+ int cache_duration_ms);
+
+ // If any completion callbacks are pending when the resolver is destroyed,
+ // the host resolutions are cancelled, and the completion callbacks will not
+ // be called.
+ virtual ~HostResolverImpl();
+
+ // HostResolver methods:
+ virtual int Resolve(const RequestInfo& info, AddressList* addresses,
+ CompletionCallback* callback, RequestHandle* out_req);
+ virtual void CancelRequest(RequestHandle req);
+ virtual void AddObserver(Observer* observer);
+ virtual void RemoveObserver(Observer* observer);
+
+ // TODO(eroman): temp hack for http://crbug.com/15513
+ virtual void Shutdown();
+
+ private:
+ class Job;
+ class Request;
+ typedef std::vector<Request*> RequestsList;
+ typedef base::hash_map<std::string, scoped_refptr<Job> > JobMap;
+ typedef std::vector<Observer*> ObserversList;
+
+ // Returns the HostResolverProc to use for this instance.
+ HostResolverProc* effective_resolver_proc() const {
+ return resolver_proc_ ?
+ resolver_proc_.get() : HostResolverProc::GetDefault();
+ }
+
+ // Adds a job to outstanding jobs list.
+ void AddOutstandingJob(Job* job);
+
+ // Returns the outstanding job for |hostname|, or NULL if there is none.
+ Job* FindOutstandingJob(const std::string& hostname);
+
+ // Removes |job| from the outstanding jobs list.
+ void RemoveOutstandingJob(Job* job);
+
+ // Callback for when |job| has completed with |error| and |addrlist|.
+ void OnJobComplete(Job* job, int error, const AddressList& addrlist);
+
+ // Notify all observers of the start of a resolve request.
+ void NotifyObserversStartRequest(int request_id,
+ const RequestInfo& info);
+
+ // Notify all observers of the completion of a resolve request.
+ void NotifyObserversFinishRequest(int request_id,
+ const RequestInfo& info,
+ int error);
+
+ // Notify all observers of the cancellation of a resolve request.
+ void NotifyObserversCancelRequest(int request_id,
+ const RequestInfo& info);
+
+ // Cache of host resolution results.
+ HostCache cache_;
+
+ // Map from hostname to outstanding job.
+ JobMap jobs_;
+
+ // The job that OnJobComplete() is currently processing (needed in case
+ // HostResolver gets deleted from within the callback).
+ scoped_refptr<Job> cur_completing_job_;
+
+ // The observers to notify when a request starts/ends.
+ ObserversList observers_;
+
+ // Monotonically increasing ID number to assign to the next request.
+ // Observers are the only consumers of this ID number.
+ int next_request_id_;
+
+ // The procedure to use for resolving host names. This will be NULL, except
+ // in the case of unit-tests which inject custom host resolving behaviors.
+ scoped_refptr<HostResolverProc> resolver_proc_;
+
+ // TODO(eroman): temp hack for http://crbug.com/15513
+ bool shutdown_;
+
+ DISALLOW_COPY_AND_ASSIGN(HostResolverImpl);
+};
+
+} // namespace net
+
+#endif // NET_BASE_HOST_RESOLVER_IMPL_H_
diff --git a/net/base/host_resolver_unittest.cc b/net/base/host_resolver_impl_unittest.cc
index 32b6af6..0ff345c 100644
--- a/net/base/host_resolver_unittest.cc
+++ b/net/base/host_resolver_impl_unittest.cc
@@ -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>
@@ -18,39 +18,43 @@
#include "base/ref_counted.h"
#include "net/base/address_list.h"
#include "net/base/completion_callback.h"
-#include "net/base/host_resolver_unittest.h"
+#include "net/base/mock_host_resolver.h"
#include "net/base/net_errors.h"
#include "net/base/test_completion_callback.h"
#include "testing/gtest/include/gtest/gtest.h"
-using net::RuleBasedHostMapper;
-using net::ScopedHostMapper;
-using net::WaitingHostMapper;
+using net::HostResolverImpl;
+using net::RuleBasedHostResolverProc;
+using net::WaitingHostResolverProc;
// TODO(eroman):
// - Test mixing async with sync (in particular how does sync update the
// cache while an async is already pending).
namespace {
+static const int kMaxCacheEntries = 100;
+static const int kMaxCacheAgeMs = 60000;
-// A variant of WaitingHostMapper that pushes each host mapped into a list.
+// A variant of WaitingHostResolverProc that pushes each host mapped into a
+// list.
// (and uses a manual-reset event rather than auto-reset).
-class CapturingHostMapper : public net::HostMapper {
+class CapturingHostResolverProc : public net::HostResolverProc {
public:
- CapturingHostMapper() : event_(true, false) {
+ explicit CapturingHostResolverProc(HostResolverProc* previous)
+ : net::HostResolverProc(previous), event_(true, false) {
}
void Signal() {
event_.Signal();
}
- virtual std::string Map(const std::string& host) {
+ virtual int Resolve(const std::string& host, net::AddressList* addrlist) {
event_.Wait();
{
AutoLock l(lock_);
capture_list_.push_back(host);
}
- return MapUsingPrevious(host);
+ return ResolveUsingPrevious(host, addrlist);
}
std::vector<std::string> GetCaptureList() const {
@@ -134,7 +138,7 @@ class ResolveRequest {
// The request details.
net::HostResolver::RequestInfo info_;
- net::HostResolver::Request* req_;
+ net::HostResolver::RequestHandle req_;
// The result of the resolve.
int result_;
@@ -150,18 +154,18 @@ class ResolveRequest {
DISALLOW_COPY_AND_ASSIGN(ResolveRequest);
};
-class HostResolverTest : public testing::Test {
+class HostResolverImplTest : public testing::Test {
public:
- HostResolverTest()
+ HostResolverImplTest()
: callback_called_(false),
ALLOW_THIS_IN_INITIALIZER_LIST(
- callback_(this, &HostResolverTest::OnLookupFinished)) {
+ callback_(this, &HostResolverImplTest::OnLookupFinished)) {
}
protected:
bool callback_called_;
int callback_result_;
- net::CompletionCallbackImpl<HostResolverTest> callback_;
+ net::CompletionCallbackImpl<HostResolverImplTest> callback_;
private:
void OnLookupFinished(int result) {
@@ -171,14 +175,16 @@ class HostResolverTest : public testing::Test {
}
};
-TEST_F(HostResolverTest, SynchronousLookup) {
- scoped_refptr<net::HostResolver> host_resolver(new net::HostResolver);
+TEST_F(HostResolverImplTest, SynchronousLookup) {
net::AddressList adrlist;
const int kPortnum = 80;
- scoped_refptr<RuleBasedHostMapper> mapper = new RuleBasedHostMapper();
- mapper->AddRule("just.testing", "192.168.1.42");
- ScopedHostMapper scoped_mapper(mapper.get());
+ scoped_refptr<RuleBasedHostResolverProc> resolver_proc =
+ new RuleBasedHostResolverProc(NULL);
+ resolver_proc->AddRule("just.testing", "192.168.1.42");
+
+ scoped_refptr<net::HostResolver> host_resolver(
+ new HostResolverImpl(resolver_proc, kMaxCacheEntries, kMaxCacheAgeMs));
net::HostResolver::RequestInfo info("just.testing", kPortnum);
int err = host_resolver->Resolve(info, &adrlist, NULL, NULL);
@@ -194,14 +200,16 @@ TEST_F(HostResolverTest, SynchronousLookup) {
EXPECT_TRUE(htonl(0xc0a8012a) == sa_in->sin_addr.s_addr);
}
-TEST_F(HostResolverTest, AsynchronousLookup) {
- scoped_refptr<net::HostResolver> host_resolver(new net::HostResolver);
+TEST_F(HostResolverImplTest, AsynchronousLookup) {
net::AddressList adrlist;
const int kPortnum = 80;
- scoped_refptr<RuleBasedHostMapper> mapper = new RuleBasedHostMapper();
- mapper->AddRule("just.testing", "192.168.1.42");
- ScopedHostMapper scoped_mapper(mapper.get());
+ scoped_refptr<RuleBasedHostResolverProc> resolver_proc =
+ new RuleBasedHostResolverProc(NULL);
+ resolver_proc->AddRule("just.testing", "192.168.1.42");
+
+ scoped_refptr<net::HostResolver> host_resolver(
+ new HostResolverImpl(resolver_proc, kMaxCacheEntries, kMaxCacheAgeMs));
net::HostResolver::RequestInfo info("just.testing", kPortnum);
int err = host_resolver->Resolve(info, &adrlist, &callback_, NULL);
@@ -222,12 +230,13 @@ TEST_F(HostResolverTest, AsynchronousLookup) {
EXPECT_TRUE(htonl(0xc0a8012a) == sa_in->sin_addr.s_addr);
}
-TEST_F(HostResolverTest, CanceledAsynchronousLookup) {
- scoped_refptr<WaitingHostMapper> mapper = new WaitingHostMapper();
- ScopedHostMapper scoped_mapper(mapper.get());
+TEST_F(HostResolverImplTest, CanceledAsynchronousLookup) {
+ scoped_refptr<WaitingHostResolverProc> resolver_proc =
+ new WaitingHostResolverProc(NULL);
{
- scoped_refptr<net::HostResolver> host_resolver(new net::HostResolver);
+ scoped_refptr<net::HostResolver> host_resolver(
+ new HostResolverImpl(resolver_proc, kMaxCacheEntries, kMaxCacheAgeMs));
net::AddressList adrlist;
const int kPortnum = 80;
@@ -242,19 +251,20 @@ TEST_F(HostResolverTest, CanceledAsynchronousLookup) {
MessageLoop::current()->Run();
}
- mapper->Signal();
+ resolver_proc->Signal();
EXPECT_FALSE(callback_called_);
}
-TEST_F(HostResolverTest, NumericIPv4Address) {
+TEST_F(HostResolverImplTest, NumericIPv4Address) {
// Stevens says dotted quads with AI_UNSPEC resolve to a single sockaddr_in.
- scoped_refptr<RuleBasedHostMapper> mapper = new RuleBasedHostMapper();
- mapper->AllowDirectLookup("*");
- ScopedHostMapper scoped_mapper(mapper.get());
+ scoped_refptr<RuleBasedHostResolverProc> resolver_proc =
+ new RuleBasedHostResolverProc(NULL);
+ resolver_proc->AllowDirectLookup("*");
- scoped_refptr<net::HostResolver> host_resolver(new net::HostResolver);
+ scoped_refptr<net::HostResolver> host_resolver(
+ new HostResolverImpl(resolver_proc, kMaxCacheEntries, kMaxCacheAgeMs));
net::AddressList adrlist;
const int kPortnum = 5555;
net::HostResolver::RequestInfo info("127.1.2.3", kPortnum);
@@ -271,14 +281,15 @@ TEST_F(HostResolverTest, NumericIPv4Address) {
EXPECT_TRUE(htonl(0x7f010203) == sa_in->sin_addr.s_addr);
}
-TEST_F(HostResolverTest, NumericIPv6Address) {
- scoped_refptr<RuleBasedHostMapper> mapper = new RuleBasedHostMapper();
- mapper->AllowDirectLookup("*");
- ScopedHostMapper scoped_mapper(mapper.get());
+TEST_F(HostResolverImplTest, NumericIPv6Address) {
+ scoped_refptr<RuleBasedHostResolverProc> resolver_proc =
+ new RuleBasedHostResolverProc(NULL);
+ resolver_proc->AllowDirectLookup("*");
// Resolve a plain IPv6 address. Don't worry about [brackets], because
// the caller should have removed them.
- scoped_refptr<net::HostResolver> host_resolver(new net::HostResolver);
+ scoped_refptr<net::HostResolver> host_resolver(
+ new HostResolverImpl(resolver_proc, kMaxCacheEntries, kMaxCacheAgeMs));
net::AddressList adrlist;
const int kPortnum = 5555;
net::HostResolver::RequestInfo info("2001:db8::1", kPortnum);
@@ -307,12 +318,13 @@ TEST_F(HostResolverTest, NumericIPv6Address) {
}
}
-TEST_F(HostResolverTest, EmptyHost) {
- scoped_refptr<RuleBasedHostMapper> mapper = new RuleBasedHostMapper();
- mapper->AllowDirectLookup("*");
- ScopedHostMapper scoped_mapper(mapper.get());
+TEST_F(HostResolverImplTest, EmptyHost) {
+ scoped_refptr<RuleBasedHostResolverProc> resolver_proc =
+ new RuleBasedHostResolverProc(NULL);
+ resolver_proc->AllowDirectLookup("*");
- scoped_refptr<net::HostResolver> host_resolver(new net::HostResolver);
+ scoped_refptr<net::HostResolver> host_resolver(
+ new HostResolverImpl(resolver_proc, kMaxCacheEntries, kMaxCacheAgeMs));
net::AddressList adrlist;
const int kPortnum = 5555;
net::HostResolver::RequestInfo info("", kPortnum);
@@ -320,13 +332,13 @@ TEST_F(HostResolverTest, EmptyHost) {
EXPECT_EQ(net::ERR_NAME_NOT_RESOLVED, err);
}
-// Helper class used by HostResolverTest.DeDupeRequests. It receives request
+// Helper class used by HostResolverImplTest.DeDupeRequests. It receives request
// completion notifications for all the resolves, so it can tally up and
// determine when we are done.
class DeDupeRequestsVerifier : public ResolveRequest::Delegate {
public:
- explicit DeDupeRequestsVerifier(CapturingHostMapper* mapper)
- : count_a_(0), count_b_(0), mapper_(mapper) {}
+ explicit DeDupeRequestsVerifier(CapturingHostResolverProc* resolver_proc)
+ : count_a_(0), count_b_(0), resolver_proc_(resolver_proc) {}
// The test does 5 resolves (which can complete in any order).
virtual void OnCompleted(ResolveRequest* resolve) {
@@ -348,9 +360,9 @@ class DeDupeRequestsVerifier : public ResolveRequest::Delegate {
EXPECT_EQ(2, count_a_);
EXPECT_EQ(3, count_b_);
- // The mapper should have been called only twice -- once with "a", once
- // with "b".
- std::vector<std::string> capture_list = mapper_->GetCaptureList();
+ // The resolver_proc should have been called only twice -- once with "a",
+ // once with "b".
+ std::vector<std::string> capture_list = resolver_proc_->GetCaptureList();
EXPECT_EQ(2U, capture_list.size());
// End this test, we are done.
@@ -361,24 +373,25 @@ class DeDupeRequestsVerifier : public ResolveRequest::Delegate {
private:
int count_a_;
int count_b_;
- CapturingHostMapper* mapper_;
+ CapturingHostResolverProc* resolver_proc_;
DISALLOW_COPY_AND_ASSIGN(DeDupeRequestsVerifier);
};
-TEST_F(HostResolverTest, DeDupeRequests) {
- // Use a capturing mapper, since the verifier needs to know what calls
- // reached Map(). Also, the capturing mapper is initially blocked.
- scoped_refptr<CapturingHostMapper> mapper = new CapturingHostMapper();
- ScopedHostMapper scoped_mapper(mapper.get());
+TEST_F(HostResolverImplTest, DeDupeRequests) {
+ // Use a capturing resolver_proc, since the verifier needs to know what calls
+ // reached Resolve(). Also, the capturing resolver_proc is initially blocked.
+ scoped_refptr<CapturingHostResolverProc> resolver_proc =
+ new CapturingHostResolverProc(NULL);
- scoped_refptr<net::HostResolver> host_resolver(new net::HostResolver);
+ scoped_refptr<net::HostResolver> host_resolver(
+ new HostResolverImpl(resolver_proc, kMaxCacheEntries, kMaxCacheAgeMs));
// The class will receive callbacks for when each resolve completes. It
// checks that the right things happened.
- DeDupeRequestsVerifier verifier(mapper.get());
+ DeDupeRequestsVerifier verifier(resolver_proc.get());
- // Start 5 requests, duplicating hosts "a" and "b". Since the mapper is
+ // Start 5 requests, duplicating hosts "a" and "b". Since the resolver_proc is
// blocked, these should all pile up until we signal it.
ResolveRequest req1(host_resolver, "a", 80, &verifier);
@@ -388,13 +401,13 @@ TEST_F(HostResolverTest, DeDupeRequests) {
ResolveRequest req5(host_resolver, "b", 83, &verifier);
// Ready, Set, GO!!!
- mapper->Signal();
+ resolver_proc->Signal();
// |verifier| will send quit message once all the requests have finished.
MessageLoop::current()->Run();
}
-// Helper class used by HostResolverTest.CancelMultipleRequests.
+// Helper class used by HostResolverImplTest.CancelMultipleRequests.
class CancelMultipleRequestsVerifier : public ResolveRequest::Delegate {
public:
CancelMultipleRequestsVerifier() {}
@@ -415,19 +428,21 @@ class CancelMultipleRequestsVerifier : public ResolveRequest::Delegate {
DISALLOW_COPY_AND_ASSIGN(CancelMultipleRequestsVerifier);
};
-TEST_F(HostResolverTest, CancelMultipleRequests) {
- // Use a capturing mapper, since the verifier needs to know what calls
- // reached Map(). Also, the capturing mapper is initially blocked.
- scoped_refptr<CapturingHostMapper> mapper = new CapturingHostMapper();
- ScopedHostMapper scoped_mapper(mapper.get());
+TEST_F(HostResolverImplTest, CancelMultipleRequests) {
+ // Use a capturing resolver_proc, since the verifier needs to know what calls
+ // reached Resolver(). Also, the capturing resolver_proc is initially
+ // blocked.
+ scoped_refptr<CapturingHostResolverProc> resolver_proc =
+ new CapturingHostResolverProc(NULL);
- scoped_refptr<net::HostResolver> host_resolver(new net::HostResolver);
+ scoped_refptr<net::HostResolver> host_resolver(
+ new HostResolverImpl(resolver_proc, kMaxCacheEntries, kMaxCacheAgeMs));
// The class will receive callbacks for when each resolve completes. It
// checks that the right things happened.
CancelMultipleRequestsVerifier verifier;
- // Start 5 requests, duplicating hosts "a" and "b". Since the mapper is
+ // Start 5 requests, duplicating hosts "a" and "b". Since the resolver_proc is
// blocked, these should all pile up until we signal it.
ResolveRequest req1(host_resolver, "a", 80, &verifier);
@@ -443,13 +458,13 @@ TEST_F(HostResolverTest, CancelMultipleRequests) {
req5.Cancel();
// Ready, Set, GO!!!
- mapper->Signal();
+ resolver_proc->Signal();
// |verifier| will send quit message once all the requests have finished.
MessageLoop::current()->Run();
}
-// Helper class used by HostResolverTest.CancelWithinCallback.
+// Helper class used by HostResolverImplTest.CancelWithinCallback.
class CancelWithinCallbackVerifier : public ResolveRequest::Delegate {
public:
CancelWithinCallbackVerifier()
@@ -500,19 +515,21 @@ class CancelWithinCallbackVerifier : public ResolveRequest::Delegate {
DISALLOW_COPY_AND_ASSIGN(CancelWithinCallbackVerifier);
};
-TEST_F(HostResolverTest, CancelWithinCallback) {
- // Use a capturing mapper, since the verifier needs to know what calls
- // reached Map(). Also, the capturing mapper is initially blocked.
- scoped_refptr<CapturingHostMapper> mapper = new CapturingHostMapper();
- ScopedHostMapper scoped_mapper(mapper.get());
+TEST_F(HostResolverImplTest, CancelWithinCallback) {
+ // Use a capturing resolver_proc, since the verifier needs to know what calls
+ // reached Resolver(). Also, the capturing resolver_proc is initially
+ // blocked.
+ scoped_refptr<CapturingHostResolverProc> resolver_proc =
+ new CapturingHostResolverProc(NULL);
- scoped_refptr<net::HostResolver> host_resolver(new net::HostResolver);
+ scoped_refptr<net::HostResolver> host_resolver(
+ new HostResolverImpl(resolver_proc, kMaxCacheEntries, kMaxCacheAgeMs));
// The class will receive callbacks for when each resolve completes. It
// checks that the right things happened.
CancelWithinCallbackVerifier verifier;
- // Start 4 requests, duplicating hosts "a". Since the mapper is
+ // Start 4 requests, duplicating hosts "a". Since the resolver_proc is
// blocked, these should all pile up until we signal it.
ResolveRequest req1(host_resolver, "a", 80, &verifier);
@@ -524,13 +541,13 @@ TEST_F(HostResolverTest, CancelWithinCallback) {
verifier.SetRequestsToCancel(&req2, &req3);
// Ready, Set, GO!!!
- mapper->Signal();
+ resolver_proc->Signal();
// |verifier| will send quit message once all the requests have finished.
MessageLoop::current()->Run();
}
-// Helper class used by HostResolverTest.DeleteWithinCallback.
+// Helper class used by HostResolverImplTest.DeleteWithinCallback.
class DeleteWithinCallbackVerifier : public ResolveRequest::Delegate {
public:
// |host_resolver| is the resolver that the the resolve requests were started
@@ -556,19 +573,21 @@ class DeleteWithinCallbackVerifier : public ResolveRequest::Delegate {
DISALLOW_COPY_AND_ASSIGN(DeleteWithinCallbackVerifier);
};
-TEST_F(HostResolverTest, DeleteWithinCallback) {
- // Use a capturing mapper, since the verifier needs to know what calls
- // reached Map(). Also, the capturing mapper is initially blocked.
- scoped_refptr<CapturingHostMapper> mapper = new CapturingHostMapper();
- ScopedHostMapper scoped_mapper(mapper.get());
+TEST_F(HostResolverImplTest, DeleteWithinCallback) {
+ // Use a capturing resolver_proc, since the verifier needs to know what calls
+ // reached Resolver(). Also, the capturing resolver_proc is initially
+ // blocked.
+ scoped_refptr<CapturingHostResolverProc> resolver_proc =
+ new CapturingHostResolverProc(NULL);
// The class will receive callbacks for when each resolve completes. It
// checks that the right things happened. Note that the verifier holds the
// only reference to |host_resolver|, so it can delete it within callback.
- net::HostResolver* host_resolver = new net::HostResolver;
+ net::HostResolver* host_resolver =
+ new HostResolverImpl(resolver_proc, kMaxCacheEntries, kMaxCacheAgeMs);
DeleteWithinCallbackVerifier verifier(host_resolver);
- // Start 4 requests, duplicating hosts "a". Since the mapper is
+ // Start 4 requests, duplicating hosts "a". Since the resolver_proc is
// blocked, these should all pile up until we signal it.
ResolveRequest req1(host_resolver, "a", 80, &verifier);
@@ -577,13 +596,13 @@ TEST_F(HostResolverTest, DeleteWithinCallback) {
ResolveRequest req4(host_resolver, "a", 83, &verifier);
// Ready, Set, GO!!!
- mapper->Signal();
+ resolver_proc->Signal();
// |verifier| will send quit message once all the requests have finished.
MessageLoop::current()->Run();
}
-// Helper class used by HostResolverTest.StartWithinCallback.
+// Helper class used by HostResolverImplTest.StartWithinCallback.
class StartWithinCallbackVerifier : public ResolveRequest::Delegate {
public:
StartWithinCallbackVerifier() : num_requests_(0) {}
@@ -609,20 +628,22 @@ class StartWithinCallbackVerifier : public ResolveRequest::Delegate {
DISALLOW_COPY_AND_ASSIGN(StartWithinCallbackVerifier);
};
-TEST_F(HostResolverTest, StartWithinCallback) {
- // Use a capturing mapper, since the verifier needs to know what calls
- // reached Map(). Also, the capturing mapper is initially blocked.
- scoped_refptr<CapturingHostMapper> mapper = new CapturingHostMapper();
- ScopedHostMapper scoped_mapper(mapper.get());
+TEST_F(HostResolverImplTest, StartWithinCallback) {
+ // Use a capturing resolver_proc, since the verifier needs to know what calls
+ // reached Resolver(). Also, the capturing resolver_proc is initially
+ // blocked.
+ scoped_refptr<CapturingHostResolverProc> resolver_proc =
+ new CapturingHostResolverProc(NULL);
// Turn off caching for this host resolver.
- scoped_refptr<net::HostResolver> host_resolver(new net::HostResolver(0, 0));
+ scoped_refptr<net::HostResolver> host_resolver(
+ new HostResolverImpl(resolver_proc, 0, 0));
// The class will receive callbacks for when each resolve completes. It
// checks that the right things happened.
StartWithinCallbackVerifier verifier;
- // Start 4 requests, duplicating hosts "a". Since the mapper is
+ // Start 4 requests, duplicating hosts "a". Since the resolver_proc is
// blocked, these should all pile up until we signal it.
ResolveRequest req1(host_resolver, "a", 80, &verifier);
@@ -631,13 +652,13 @@ TEST_F(HostResolverTest, StartWithinCallback) {
ResolveRequest req4(host_resolver, "a", 83, &verifier);
// Ready, Set, GO!!!
- mapper->Signal();
+ resolver_proc->Signal();
// |verifier| will send quit message once all the requests have finished.
MessageLoop::current()->Run();
}
-// Helper class used by HostResolverTest.BypassCache.
+// Helper class used by HostResolverImplTest.BypassCache.
class BypassCacheVerifier : public ResolveRequest::Delegate {
public:
BypassCacheVerifier() {}
@@ -679,8 +700,9 @@ class BypassCacheVerifier : public ResolveRequest::Delegate {
DISALLOW_COPY_AND_ASSIGN(BypassCacheVerifier);
};
-TEST_F(HostResolverTest, BypassCache) {
- scoped_refptr<net::HostResolver> host_resolver(new net::HostResolver);
+TEST_F(HostResolverImplTest, BypassCache) {
+ scoped_refptr<net::HostResolver> host_resolver(
+ new HostResolverImpl(NULL, kMaxCacheEntries, kMaxCacheAgeMs));
// The class will receive callbacks for when each resolve completes. It
// checks that the right things happened.
@@ -762,8 +784,9 @@ class CapturingObserver : public net::HostResolver::Observer {
// Test that registering, unregistering, and notifying of observers works.
// Does not test the cancellation notification since all resolves are
// synchronous.
-TEST_F(HostResolverTest, Observers) {
- scoped_refptr<net::HostResolver> host_resolver(new net::HostResolver);
+TEST_F(HostResolverImplTest, Observers) {
+ scoped_refptr<net::HostResolver> host_resolver(
+ new HostResolverImpl(NULL, kMaxCacheEntries, kMaxCacheAgeMs));
CapturingObserver observer;
@@ -829,11 +852,12 @@ TEST_F(HostResolverTest, Observers) {
// cancelled. There are two ways to cancel a request:
// (1) Delete the HostResolver while job is outstanding.
// (2) Call HostResolver::CancelRequest() while a request is outstanding.
-TEST_F(HostResolverTest, CancellationObserver) {
+TEST_F(HostResolverImplTest, CancellationObserver) {
CapturingObserver observer;
{
// Create a host resolver and attach an observer.
- scoped_refptr<net::HostResolver> host_resolver(new net::HostResolver);
+ scoped_refptr<net::HostResolver> host_resolver(
+ new HostResolverImpl(NULL, kMaxCacheEntries, kMaxCacheAgeMs));
host_resolver->AddObserver(&observer);
TestCompletionCallback callback;
@@ -844,7 +868,7 @@ TEST_F(HostResolverTest, CancellationObserver) {
// Start an async resolve for (host1:70).
net::HostResolver::RequestInfo info1("host1", 70);
- net::HostResolver::Request* req = NULL;
+ net::HostResolver::RequestHandle req = NULL;
net::AddressList addrlist;
int rv = host_resolver->Resolve(info1, &addrlist, &callback, &req);
EXPECT_EQ(net::ERR_IO_PENDING, rv);
@@ -857,7 +881,7 @@ TEST_F(HostResolverTest, CancellationObserver) {
EXPECT_TRUE(observer.start_log[0] ==
CapturingObserver::StartOrCancelEntry(0, info1));
- // Cancel the request (host mapper is blocked so it cant be finished yet).
+ // Cancel the request.
host_resolver->CancelRequest(req);
EXPECT_EQ(1U, observer.start_log.size());
diff --git a/net/base/host_resolver_proc.cc b/net/base/host_resolver_proc.cc
new file mode 100644
index 0000000..585a33e
--- /dev/null
+++ b/net/base/host_resolver_proc.cc
@@ -0,0 +1,180 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// 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_proc.h"
+
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <ws2tcpip.h>
+#include <wspiapi.h> // Needed for Win2k compat.
+#elif defined(OS_POSIX)
+#include <netdb.h>
+#include <sys/socket.h>
+#endif
+#if defined(OS_LINUX)
+#include <resolv.h>
+#endif
+
+#include "base/logging.h"
+#include "base/time.h"
+#include "net/base/address_list.h"
+#include "net/base/net_errors.h"
+
+#if defined(OS_LINUX)
+#include "base/singleton.h"
+#include "base/thread_local_storage.h"
+#endif
+
+namespace net {
+
+HostResolverProc* HostResolverProc::default_proc_ = NULL;
+
+HostResolverProc::HostResolverProc(HostResolverProc* previous) {
+ set_previous_proc(previous);
+
+ // Implicitly fall-back to the global default procedure.
+ if (!previous)
+ set_previous_proc(default_proc_);
+}
+
+// static
+HostResolverProc* HostResolverProc::SetDefault(HostResolverProc* proc) {
+ HostResolverProc* old = default_proc_;
+ default_proc_ = proc;
+ return old;
+}
+
+// static
+HostResolverProc* HostResolverProc::GetDefault() {
+ return default_proc_;
+}
+
+int HostResolverProc::ResolveUsingPrevious(const std::string& host,
+ AddressList* addrlist) {
+ if (previous_proc_)
+ return previous_proc_->Resolve(host, addrlist);
+
+ // Final fallback is the system resolver.
+ return SystemHostResolverProc(host, addrlist);
+}
+
+#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)
+
+int SystemHostResolverProc(const std::string& host, AddressList* addrlist) {
+ struct addrinfo* ai = NULL;
+ 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, &ai);
+#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, &ai);
+ }
+#endif
+
+ if (err)
+ return ERR_NAME_NOT_RESOLVED;
+
+ addrlist->Adopt(ai);
+ return OK;
+}
+
+} // namespace net
diff --git a/net/base/host_resolver_proc.h b/net/base/host_resolver_proc.h
new file mode 100644
index 0000000..2690987
--- /dev/null
+++ b/net/base/host_resolver_proc.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_BASE_HOST_RESOLVER_PROC_H_
+#define NET_BASE_HOST_RESOLVER_PROC_H_
+
+#include <string>
+
+#include "base/ref_counted.h"
+
+namespace net {
+
+class AddressList;
+
+// Interface for a getaddrinfo()-like procedure. This is used by unit-tests
+// to control the underlying resolutions in HostResolverImpl. HostResolverProcs
+// can be chained together; they fallback to the next procedure in the chain
+// by calling ResolveUsingPrevious().
+//
+// Note that implementations of HostResolverProc *MUST BE THREADSAFE*, since
+// the HostResolver implementation using them can be multi-threaded.
+class HostResolverProc : public base::RefCountedThreadSafe<HostResolverProc> {
+ public:
+ explicit HostResolverProc(HostResolverProc* previous);
+ virtual ~HostResolverProc() {}
+
+ // Resolves |host| to an address list. If successful returns OK and fills
+ // |addrlist| with a list of socket addresses. Otherwise returns a
+ // network error code.
+ virtual int Resolve(const std::string& host, AddressList* addrlist) = 0;
+
+ protected:
+ // Asks the fallback procedure (if set) to do the resolve.
+ int ResolveUsingPrevious(const std::string& host, AddressList* addrlist);
+
+ private:
+ friend class HostResolverImpl;
+ friend class MockHostResolver;
+ friend class ScopedDefaultHostResolverProc;
+
+ // Sets the previous procedure in the chain.
+ void set_previous_proc(HostResolverProc* proc) {
+ previous_proc_ = proc;
+ }
+
+ // Sets the default host resolver procedure that is used by HostResolverImpl.
+ // This can be used through ScopedDefaultHostResolverProc to set a catch-all
+ // DNS block in unit-tests (individual tests should use MockHostResolver to
+ // prevent hitting the network).
+ static HostResolverProc* SetDefault(HostResolverProc* proc);
+ static HostResolverProc* GetDefault();
+
+ private:
+ scoped_refptr<HostResolverProc> previous_proc_;
+ static HostResolverProc* default_proc_;
+
+ DISALLOW_COPY_AND_ASSIGN(HostResolverProc);
+};
+
+// Resolves |host| to an address list, using the system's default host resolver.
+// (i.e. this calls out to getaddrinfo()). If successful returns OK and fills
+// |addrlist| with a list of socket addresses. Otherwise returns a
+// network error code.
+int SystemHostResolverProc(const std::string& host, AddressList* addrlist);
+
+} // namespace net
+
+#endif // NET_BASE_HOST_RESOLVER_PROC_H_
diff --git a/net/base/mock_host_resolver.cc b/net/base/mock_host_resolver.cc
new file mode 100644
index 0000000..58ad552
--- /dev/null
+++ b/net/base/mock_host_resolver.cc
@@ -0,0 +1,154 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/base/mock_host_resolver.h"
+
+#include "base/string_util.h"
+#include "base/platform_thread.h"
+#include "base/ref_counted.h"
+#include "net/base/net_errors.h"
+
+namespace net {
+
+MockHostResolver::MockHostResolver() {
+ Reset(NULL, 0, 0);
+}
+
+int MockHostResolver::Resolve(const RequestInfo& info,
+ AddressList* addresses,
+ CompletionCallback* callback,
+ RequestHandle* out_req) {
+ return impl_->Resolve(info, addresses, callback, out_req);
+}
+
+void MockHostResolver::CancelRequest(RequestHandle req) {
+ impl_->CancelRequest(req);
+}
+
+void MockHostResolver::AddObserver(Observer* observer) {
+ impl_->AddObserver(observer);
+}
+
+void MockHostResolver::RemoveObserver(Observer* observer) {
+ impl_->RemoveObserver(observer);
+}
+
+void MockHostResolver::Shutdown() {
+ impl_->Shutdown();
+}
+
+void MockHostResolver::Reset(HostResolverProc* interceptor,
+ int max_cache_entries,
+ int max_cache_age_ms) {
+ // At the root of the chain, map everything to localhost.
+ scoped_refptr<RuleBasedHostResolverProc> catchall =
+ new RuleBasedHostResolverProc(NULL);
+ catchall->AddRule("*", "127.0.0.1");
+
+ // Next add a rules-based layer the use controls.
+ rules_ = new RuleBasedHostResolverProc(catchall);
+
+ HostResolverProc* proc = rules_;
+
+ // Lastly add the provided interceptor to the front of the chain.
+ if (interceptor) {
+ interceptor->set_previous_proc(proc);
+ proc = interceptor;
+ }
+
+ impl_ = new HostResolverImpl(proc, max_cache_entries, max_cache_age_ms);
+}
+
+//-----------------------------------------------------------------------------
+
+struct RuleBasedHostResolverProc::Rule {
+ std::string host_pattern;
+ std::string replacement;
+ int latency_ms; // In milliseconds.
+ bool direct; // if true, don't mangle hostname and ignore replacement
+ Rule(const std::string& host_pattern, const std::string& replacement)
+ : host_pattern(host_pattern),
+ replacement(replacement),
+ latency_ms(0),
+ direct(false) {}
+ Rule(const std::string& host_pattern, const std::string& replacement,
+ const int latency_ms)
+ : host_pattern(host_pattern),
+ replacement(replacement),
+ latency_ms(latency_ms),
+ direct(false) {}
+ Rule(const std::string& host_pattern, const std::string& replacement,
+ const bool direct)
+ : host_pattern(host_pattern),
+ replacement(replacement),
+ latency_ms(0),
+ direct(direct) {}
+};
+
+RuleBasedHostResolverProc::RuleBasedHostResolverProc(HostResolverProc* previous)
+ : HostResolverProc(previous) {
+}
+
+RuleBasedHostResolverProc::~RuleBasedHostResolverProc() {
+}
+
+void RuleBasedHostResolverProc::AddRule(const std::string& host_pattern,
+ const std::string& replacement) {
+ rules_.push_back(Rule(host_pattern, replacement));
+}
+
+void RuleBasedHostResolverProc::AddRuleWithLatency(
+ const std::string& host_pattern,
+ const std::string& replacement, int latency_ms) {
+ rules_.push_back(Rule(host_pattern, replacement, latency_ms));
+}
+
+void RuleBasedHostResolverProc::AllowDirectLookup(const std::string& host) {
+ rules_.push_back(Rule(host, "", true));
+}
+
+void RuleBasedHostResolverProc::AddSimulatedFailure(const std::string& host) {
+ AddRule(host, "");
+}
+
+int RuleBasedHostResolverProc::Resolve(const std::string& host,
+ AddressList* addrlist) {
+ RuleList::iterator r;
+ for (r = rules_.begin(); r != rules_.end(); ++r) {
+ if (MatchPattern(host, r->host_pattern)) {
+ if (r->latency_ms != 0) {
+ PlatformThread::Sleep(r->latency_ms);
+ // Hmm, this seems unecessary.
+ r->latency_ms = 1;
+ }
+ const std::string& effective_host = r->direct ? host : r->replacement;
+ if (effective_host.empty())
+ return ERR_NAME_NOT_RESOLVED;
+ return SystemHostResolverProc(effective_host, addrlist);
+ }
+ }
+ return ResolveUsingPrevious(host, addrlist);
+}
+
+//-----------------------------------------------------------------------------
+
+ScopedDefaultHostResolverProc::ScopedDefaultHostResolverProc(
+ HostResolverProc* proc) : current_proc_(proc) {
+ previous_proc_ = HostResolverProc::SetDefault(current_proc_);
+ current_proc_->set_previous_proc(previous_proc_);
+}
+
+ScopedDefaultHostResolverProc::~ScopedDefaultHostResolverProc() {
+ HostResolverProc* old_proc = HostResolverProc::SetDefault(previous_proc_);
+ // The lifetimes of multiple instances must be nested.
+ CHECK(old_proc == current_proc_);
+}
+
+void ScopedDefaultHostResolverProc::Init(HostResolverProc* proc) {
+ current_proc_ = proc;
+ previous_proc_ = HostResolverProc::SetDefault(current_proc_);
+ current_proc_->set_previous_proc(previous_proc_);
+}
+
+} // namespace net
diff --git a/net/base/mock_host_resolver.h b/net/base/mock_host_resolver.h
new file mode 100644
index 0000000..ee83105
--- /dev/null
+++ b/net/base/mock_host_resolver.h
@@ -0,0 +1,143 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_BASE_MOCK_HOST_RESOLVER_H_
+#define NET_BASE_MOCK_HOST_RESOLVER_H_
+
+#include <list>
+
+#include "base/waitable_event.h"
+#include "net/base/host_resolver_impl.h"
+#include "net/base/host_resolver_proc.h"
+
+namespace net {
+
+class RuleBasedHostResolverProc;
+
+// In most cases, it is important that unit tests avoid making actual DNS
+// queries since the resulting tests can be flaky, especially if the network is
+// unreliable for some reason. To simplify writing tests that avoid making
+// actual DNS queries, pass a MockHostResolver as the HostResolver dependency.
+// The socket addresses returned can be configured using the
+// RuleBasedHostResolverProc:
+//
+// host_resolver->rules()->AddRule("foo.com", "1.2.3.4");
+// host_resolver->rules()->AddRule("bar.com", "2.3.4.5");
+//
+// The above rules define a static mapping from hostnames to IP address
+// literals. The first parameter to AddRule specifies a host pattern to match
+// against, and the second parameter indicates what value should be used to
+// replace the given hostname. So, the following is also supported:
+//
+// host_mapper->AddRule("*.com", "127.0.0.1");
+//
+// Replacement doesn't have to be string representing an IP address. It can
+// re-map one hostname to another as well.
+class MockHostResolver : public HostResolver {
+ public:
+ // Creates a MockHostResolver that does NOT cache entries
+ // (the HostResolverProc will be called for every lookup). If you need
+ // caching behavior, call Reset() with non-zero cache size.
+ MockHostResolver();
+
+ virtual ~MockHostResolver() {}
+
+ // HostResolver methods:
+ virtual int Resolve(const RequestInfo& info, AddressList* addresses,
+ CompletionCallback* callback, RequestHandle* out_req);
+ virtual void CancelRequest(RequestHandle req);
+ virtual void AddObserver(Observer* observer);
+ virtual void RemoveObserver(Observer* observer);
+ // TODO(eroman): temp hack for http://crbug.com/15513
+ virtual void Shutdown();
+
+ RuleBasedHostResolverProc* rules() { return rules_; }
+
+ // Resets the mock.
+ void Reset(HostResolverProc* interceptor,
+ int max_cache_entries,
+ int max_cache_age_ms);
+
+ private:
+ scoped_refptr<HostResolverImpl> impl_;
+ scoped_refptr<RuleBasedHostResolverProc> rules_;
+};
+
+// RuleBasedHostResolverProc applies a set of rules to map a host string to
+// a replacement host string. It then uses the system host resolver to return
+// a socket address. Generally the replacement should be an IPv4 literal so
+// there is no network dependency.
+class RuleBasedHostResolverProc : public HostResolverProc {
+ public:
+ explicit RuleBasedHostResolverProc(HostResolverProc* previous);
+ ~RuleBasedHostResolverProc();
+
+ // Any hostname matching the given pattern will be replaced with the given
+ // replacement value. Usually, replacement should be an IP address literal.
+ void AddRule(const std::string& host_pattern,
+ const std::string& replacement);
+
+ void AddRuleWithLatency(const std::string& host_pattern,
+ const std::string& replacement,
+ int latency_ms);
+
+ // Make sure that |host| will not be re-mapped or even processed by underlying
+ // host resolver procedures. It can also be a pattern.
+ void AllowDirectLookup(const std::string& host);
+
+ // Simulate a lookup failure for |host| (it also can be a pattern).
+ void AddSimulatedFailure(const std::string& host);
+
+ // HostResolverProc methods:
+ virtual int Resolve(const std::string& host, AddressList* addrlist);
+
+ private:
+ struct Rule;
+ typedef std::list<Rule> RuleList;
+ RuleList rules_;
+};
+
+// Using WaitingHostResolverProc you can simulate very long lookups.
+class WaitingHostResolverProc : public HostResolverProc {
+ public:
+ explicit WaitingHostResolverProc(HostResolverProc* previous)
+ : HostResolverProc(previous), event_(false, false) {}
+
+ void Signal() {
+ event_.Signal();
+ }
+
+ // HostResolverProc methods:
+ virtual int Resolve(const std::string& host, AddressList* addrlist) {
+ event_.Wait();
+ return ResolveUsingPrevious(host, addrlist);
+ }
+
+ base::WaitableEvent event_;
+};
+
+// This class sets the HostResolverProc for a particular scope. If there are
+// multiple ScopedDefaultHostResolverProc in existence, then the last one
+// allocated will be used. However, if it does not provide a matching rule,
+// then it should delegate to the previously set HostResolverProc.
+//
+// NOTE: Only use this as a catch-all safety net. Individual tests should use
+// MockHostResolver.
+class ScopedDefaultHostResolverProc {
+ public:
+ ScopedDefaultHostResolverProc() {}
+ explicit ScopedDefaultHostResolverProc(HostResolverProc* proc);
+
+ ~ScopedDefaultHostResolverProc();
+
+ void Init(HostResolverProc* proc);
+
+ private:
+ scoped_refptr<HostResolverProc> current_proc_;
+ scoped_refptr<HostResolverProc> previous_proc_;
+};
+
+} // namespace net
+
+#endif // NET_BASE_MOCK_HOST_RESOLVER_H_
diff --git a/net/base/run_all_unittests.cc b/net/base/run_all_unittests.cc
index 6c60e2d..c4c4df7 100644
--- a/net/base/run_all_unittests.cc
+++ b/net/base/run_all_unittests.cc
@@ -30,7 +30,7 @@
#include "base/message_loop.h"
#include "base/ref_counted.h"
#include "base/test_suite.h"
-#include "net/base/host_resolver_unittest.h"
+#include "net/base/mock_host_resolver.h"
class NetTestSuite : public TestSuite {
public:
@@ -40,12 +40,12 @@ class NetTestSuite : public TestSuite {
virtual void Initialize() {
TestSuite::Initialize();
- host_mapper_ = new net::RuleBasedHostMapper();
- scoped_host_mapper_.Init(host_mapper_.get());
+ host_resolver_proc_ = new net::RuleBasedHostResolverProc(NULL);
+ scoped_host_resolver_proc_.Init(host_resolver_proc_.get());
// In case any attempts are made to resolve host names, force them all to
// be mapped to localhost. This prevents DNS queries from being sent in
// the process of running these unit tests.
- host_mapper_->AddRule("*", "127.0.0.1");
+ host_resolver_proc_->AddRule("*", "127.0.0.1");
message_loop_.reset(new MessageLoopForIO());
}
@@ -60,8 +60,8 @@ class NetTestSuite : public TestSuite {
private:
scoped_ptr<MessageLoop> message_loop_;
- scoped_refptr<net::RuleBasedHostMapper> host_mapper_;
- net::ScopedHostMapper scoped_host_mapper_;
+ scoped_refptr<net::RuleBasedHostResolverProc> host_resolver_proc_;
+ net::ScopedDefaultHostResolverProc scoped_host_resolver_proc_;
};
int main(int argc, char** argv) {