diff options
38 files changed, 1503 insertions, 1071 deletions
diff --git a/chrome/browser/browser_unittest.cc b/chrome/browser/browser_unittest.cc index 1ef86da..702d1dce 100644 --- a/chrome/browser/browser_unittest.cc +++ b/chrome/browser/browser_unittest.cc @@ -4,21 +4,21 @@ #include "chrome/browser/browser.h" #include "chrome/test/in_process_browser_test.h" -#include "net/base/host_resolver_unittest.h" +#include "net/base/mock_host_resolver.h" class BrowserTest : public InProcessBrowserTest { public: BrowserTest() { - host_mapper_ = new net::RuleBasedHostMapper(); + host_resolver_proc_ = new net::RuleBasedHostResolverProc(NULL); // Avoid making external DNS lookups. In this test we don't need this // to succeed. - host_mapper_->AddSimulatedFailure("*.google.com"); - scoped_host_mapper_.Init(host_mapper_.get()); + host_resolver_proc_->AddSimulatedFailure("*.google.com"); + scoped_host_resolver_proc_.Init(host_resolver_proc_.get()); } private: - scoped_refptr<net::RuleBasedHostMapper> host_mapper_; - net::ScopedHostMapper scoped_host_mapper_; + scoped_refptr<net::RuleBasedHostResolverProc> host_resolver_proc_; + net::ScopedDefaultHostResolverProc scoped_host_resolver_proc_; }; /* @@ -38,7 +38,7 @@ IN_PROC_BROWSER_TEST_F(BrowserTest, NoTabsInPopups) { // Now try opening another tab in the popup browser. popup_browser->AddTabWithURL(GURL("about:blank"), GURL(), PageTransition::TYPED, true, -1, NULL); - + // The popup should still only have one tab. EXPECT_EQ(1, popup_browser->tab_count()); @@ -54,7 +54,7 @@ IN_PROC_BROWSER_TEST_F(BrowserTest, NoTabsInPopups) { // Now try opening another tab in the app browser. app_browser->AddTabWithURL(GURL("about:blank"), GURL(), PageTransition::TYPED, true, -1, NULL); - + // The popup should still only have one tab. EXPECT_EQ(1, app_browser->tab_count()); @@ -70,7 +70,7 @@ IN_PROC_BROWSER_TEST_F(BrowserTest, NoTabsInPopups) { // Now try opening another tab in the app popup browser. app_popup_browser->AddTabWithURL(GURL("about:blank"), GURL(), PageTransition::TYPED, true, -1, NULL); - + // The popup should still only have one tab. EXPECT_EQ(1, app_popup_browser->tab_count()); @@ -82,4 +82,4 @@ IN_PROC_BROWSER_TEST_F(BrowserTest, NoTabsInPopups) { app_browser->CloseAllTabs(); app_popup_browser->CloseAllTabs(); } -*/
\ No newline at end of file +*/ diff --git a/chrome/browser/net/dns_global.cc b/chrome/browser/net/dns_global.cc index d18ea45..25829e7 100644 --- a/chrome/browser/net/dns_global.cc +++ b/chrome/browser/net/dns_global.cc @@ -476,11 +476,7 @@ static void DiscardAllPrefetchState() { net::HostResolver* GetGlobalHostResolver() { // Called from UI thread. if (!global_host_resolver) { - static const size_t kMaxHostCacheEntries = 100; - static const size_t kHostCacheExpirationSeconds = 60; // 1 minute. - - global_host_resolver = new net::HostResolver( - kMaxHostCacheEntries, kHostCacheExpirationSeconds * 1000); + global_host_resolver = net::CreateSystemHostResolver(); } return global_host_resolver; } diff --git a/chrome/browser/net/dns_master_unittest.cc b/chrome/browser/net/dns_master_unittest.cc index 61ffd68..c3746bb 100644 --- a/chrome/browser/net/dns_master_unittest.cc +++ b/chrome/browser/net/dns_master_unittest.cc @@ -10,13 +10,13 @@ #include "base/message_loop.h" #include "base/scoped_ptr.h" +#include "base/string_util.h" #include "base/timer.h" #include "chrome/browser/net/dns_global.h" #include "chrome/browser/net/dns_host_info.h" #include "chrome/common/net/dns.h" #include "net/base/address_list.h" -#include "net/base/host_resolver.h" -#include "net/base/host_resolver_unittest.h" +#include "net/base/mock_host_resolver.h" #include "net/base/winsock_init.h" #include "testing/gtest/include/gtest/gtest.h" @@ -59,10 +59,9 @@ class WaitForResolutionHelper { class DnsMasterTest : public testing::Test { public: DnsMasterTest() - : mapper_(new net::RuleBasedHostMapper()), + : host_resolver_(new net::MockHostResolver()), default_max_queueing_delay_(TimeDelta::FromMilliseconds( - DnsPrefetcherInit::kMaxQueueingDelayMs)), - scoped_mapper_(mapper_.get()) { + DnsPrefetcherInit::kMaxQueueingDelayMs)) { } protected: @@ -70,10 +69,11 @@ class DnsMasterTest : public testing::Test { #if defined(OS_WIN) net::EnsureWinsockInit(); #endif - mapper_->AddRuleWithLatency("www.google.com", "127.0.0.1", 50); - mapper_->AddRuleWithLatency("gmail.google.com.com", "127.0.0.1", 70); - mapper_->AddRuleWithLatency("mail.google.com", "127.0.0.1", 44); - mapper_->AddRuleWithLatency("gmail.com", "127.0.0.1", 63); + net::RuleBasedHostResolverProc* rules = host_resolver_->rules(); + rules->AddRuleWithLatency("www.google.com", "127.0.0.1", 50); + rules->AddRuleWithLatency("gmail.google.com.com", "127.0.0.1", 70); + rules->AddRuleWithLatency("mail.google.com", "127.0.0.1", 44); + rules->AddRuleWithLatency("gmail.com", "127.0.0.1", 63); } void WaitForResolution(DnsMaster* master, const NameList& hosts) { @@ -84,15 +84,18 @@ class DnsMasterTest : public testing::Test { MessageLoop::current()->Run(); } - scoped_refptr<net::RuleBasedHostMapper> mapper_; + private: + // IMPORTANT: do not move this below |host_resolver_|; the host resolver + // must not outlive the message loop, otherwise bad things can happen + // (like posting to a deleted message loop). + MessageLoop loop; + + protected: + scoped_refptr<net::MockHostResolver> host_resolver_; // Shorthand to access TimeDelta of DnsPrefetcherInit::kMaxQueueingDelayMs. // (It would be a static constant... except style rules preclude that :-/ ). const TimeDelta default_max_queueing_delay_; - - private: - MessageLoop loop; - net::ScopedHostMapper scoped_mapper_; }; //------------------------------------------------------------------------------ @@ -113,15 +116,15 @@ static std::string GetNonexistantDomain() { //------------------------------------------------------------------------------ // Use a blocking function to contrast results we get via async services. //------------------------------------------------------------------------------ -TimeDelta BlockingDnsLookup(const std::string& hostname) { - Time start = Time::Now(); +TimeDelta BlockingDnsLookup(net::HostResolver* resolver, + const std::string& hostname) { + Time start = Time::Now(); - scoped_refptr<net::HostResolver> resolver(new net::HostResolver); - net::AddressList addresses; - net::HostResolver::RequestInfo info(hostname, 80); - resolver->Resolve(info, &addresses, NULL, NULL); + net::AddressList addresses; + net::HostResolver::RequestInfo info(hostname, 80); + resolver->Resolve(info, &addresses, NULL, NULL); - return Time::Now() - start; + return Time::Now() - start; } //------------------------------------------------------------------------------ @@ -129,7 +132,9 @@ TimeDelta BlockingDnsLookup(const std::string& hostname) { // First test to be sure the OS is caching lookups, which is the whole premise // of DNS prefetching. TEST_F(DnsMasterTest, OsCachesLookupsTest) { - mapper_->AllowDirectLookup("*.google.com"); + // Make sure caching is disabled in the mock host resolver. + host_resolver_->Reset(NULL, 0, 0); + host_resolver_->rules()->AllowDirectLookup("*.google.com"); const Time start = Time::Now(); int all_lookups = 0; @@ -140,13 +145,13 @@ TEST_F(DnsMasterTest, OsCachesLookupsTest) { std::string badname; badname = GetNonexistantDomain(); - TimeDelta duration = BlockingDnsLookup(badname); + TimeDelta duration = BlockingDnsLookup(host_resolver_, badname); // Produce more than one result and remove the largest one // to reduce flakiness. std::vector<TimeDelta> cached_results; for (int j = 0; j < 3; j++) - cached_results.push_back(BlockingDnsLookup(badname)); + cached_results.push_back(BlockingDnsLookup(host_resolver_, badname)); std::sort(cached_results.begin(), cached_results.end()); cached_results.pop_back(); @@ -168,14 +173,14 @@ TEST_F(DnsMasterTest, OsCachesLookupsTest) { } TEST_F(DnsMasterTest, StartupShutdownTest) { - scoped_refptr<DnsMaster> testing_master = new DnsMaster(new net::HostResolver, + scoped_refptr<DnsMaster> testing_master = new DnsMaster(host_resolver_, MessageLoop::current(), default_max_queueing_delay_, DnsPrefetcherInit::kMaxConcurrentLookups); testing_master->Shutdown(); } TEST_F(DnsMasterTest, BenefitLookupTest) { - scoped_refptr<DnsMaster> testing_master = new DnsMaster(new net::HostResolver, + scoped_refptr<DnsMaster> testing_master = new DnsMaster(host_resolver_, MessageLoop::current(), default_max_queueing_delay_, DnsPrefetcherInit::kMaxConcurrentLookups); @@ -236,10 +241,11 @@ TEST_F(DnsMasterTest, BenefitLookupTest) { } TEST_F(DnsMasterTest, ShutdownWhenResolutionIsPendingTest) { - scoped_refptr<net::WaitingHostMapper> mapper = new net::WaitingHostMapper(); - net::ScopedHostMapper scoped_mapper(mapper.get()); + scoped_refptr<net::WaitingHostResolverProc> resolver_proc = + new net::WaitingHostResolverProc(NULL); + host_resolver_->Reset(resolver_proc, 0, 0); - scoped_refptr<DnsMaster> testing_master = new DnsMaster(new net::HostResolver, + scoped_refptr<DnsMaster> testing_master = new DnsMaster(host_resolver_, MessageLoop::current(), default_max_queueing_delay_, DnsPrefetcherInit::kMaxConcurrentLookups); @@ -258,12 +264,12 @@ TEST_F(DnsMasterTest, ShutdownWhenResolutionIsPendingTest) { testing_master->Shutdown(); // Clean up after ourselves. - mapper->Signal(); + resolver_proc->Signal(); MessageLoop::current()->RunAllPending(); } TEST_F(DnsMasterTest, SingleLookupTest) { - scoped_refptr<DnsMaster> testing_master = new DnsMaster(new net::HostResolver, + scoped_refptr<DnsMaster> testing_master = new DnsMaster(host_resolver_, MessageLoop::current(), default_max_queueing_delay_, DnsPrefetcherInit::kMaxConcurrentLookups); @@ -291,9 +297,9 @@ TEST_F(DnsMasterTest, SingleLookupTest) { } TEST_F(DnsMasterTest, ConcurrentLookupTest) { - mapper_->AddSimulatedFailure("*.notfound"); + host_resolver_->rules()->AddSimulatedFailure("*.notfound"); - scoped_refptr<DnsMaster> testing_master = new DnsMaster(new net::HostResolver, + scoped_refptr<DnsMaster> testing_master = new DnsMaster(host_resolver_, MessageLoop::current(), default_max_queueing_delay_, DnsPrefetcherInit::kMaxConcurrentLookups); @@ -313,12 +319,6 @@ TEST_F(DnsMasterTest, ConcurrentLookupTest) { names.insert(names.end(), goog4); names.insert(names.end(), goog); - // Warm up the *OS* cache for all the goog domains. - BlockingDnsLookup(goog); - BlockingDnsLookup(goog2); - BlockingDnsLookup(goog3); - BlockingDnsLookup(goog4); - // Try to flood the master with many concurrent requests. for (int i = 0; i < 10; i++) testing_master->ResolveList(names, DnsHostInfo::PAGE_SCAN_MOTIVATED); @@ -346,9 +346,9 @@ TEST_F(DnsMasterTest, ConcurrentLookupTest) { } TEST_F(DnsMasterTest, DISABLED_MassiveConcurrentLookupTest) { - mapper_->AddSimulatedFailure("*.notfound"); + host_resolver_->rules()->AddSimulatedFailure("*.notfound"); - scoped_refptr<DnsMaster> testing_master = new DnsMaster(new net::HostResolver, + scoped_refptr<DnsMaster> testing_master = new DnsMaster(host_resolver_, MessageLoop::current(), default_max_queueing_delay_, DnsPrefetcherInit::kMaxConcurrentLookups); @@ -453,7 +453,7 @@ int GetLatencyFromSerialization(const std::string& motivation, // Make sure nil referral lists really have no entries, and no latency listed. TEST_F(DnsMasterTest, ReferrerSerializationNilTest) { - scoped_refptr<DnsMaster> master = new DnsMaster(new net::HostResolver, + scoped_refptr<DnsMaster> master = new DnsMaster(host_resolver_, MessageLoop::current(), default_max_queueing_delay_, DnsPrefetcherInit::kMaxConcurrentLookups); ListValue referral_list; @@ -469,7 +469,7 @@ TEST_F(DnsMasterTest, ReferrerSerializationNilTest) { // deserialized into the database, and can be extracted back out via // serialization without being changed. TEST_F(DnsMasterTest, ReferrerSerializationSingleReferrerTest) { - scoped_refptr<DnsMaster> master = new DnsMaster(new net::HostResolver, + scoped_refptr<DnsMaster> master = new DnsMaster(host_resolver_, MessageLoop::current(), default_max_queueing_delay_, DnsPrefetcherInit::kMaxConcurrentLookups); std::string motivation_hostname = "www.google.com"; @@ -494,7 +494,7 @@ TEST_F(DnsMasterTest, ReferrerSerializationSingleReferrerTest) { // Make sure the Trim() functionality works as expected. TEST_F(DnsMasterTest, ReferrerSerializationTrimTest) { - scoped_refptr<DnsMaster> master = new DnsMaster(new net::HostResolver, + scoped_refptr<DnsMaster> master = new DnsMaster(host_resolver_, MessageLoop::current(), default_max_queueing_delay_, DnsPrefetcherInit::kMaxConcurrentLookups); std::string motivation_hostname = "www.google.com"; diff --git a/chrome/browser/search_engines/template_url_scraper_unittest.cc b/chrome/browser/search_engines/template_url_scraper_unittest.cc index 71aace7..a93790f 100644 --- a/chrome/browser/search_engines/template_url_scraper_unittest.cc +++ b/chrome/browser/search_engines/template_url_scraper_unittest.cc @@ -11,7 +11,7 @@ #include "chrome/common/notification_type.h" #include "chrome/test/in_process_browser_test.h" #include "chrome/test/ui_test_utils.h" -#include "net/base/host_resolver_unittest.h" +#include "net/base/mock_host_resolver.h" #include "net/base/net_util.h" namespace { @@ -21,10 +21,10 @@ class TemplateURLScraperTest : public InProcessBrowserTest { } protected: - virtual void ConfigureHostMapper(net::RuleBasedHostMapper* host_mapper) { - InProcessBrowserTest::ConfigureHostMapper(host_mapper); + virtual void ConfigureHostResolverProc(net::RuleBasedHostResolverProc* proc) { + InProcessBrowserTest::ConfigureHostResolverProc(proc); // We use foo.com in our tests. - host_mapper->AddRule("*.foo.com", "localhost"); + proc->AddRule("*.foo.com", "localhost"); } private: diff --git a/chrome/test/in_process_browser_test.cc b/chrome/test/in_process_browser_test.cc index b43edd2..7544b49 100644 --- a/chrome/test/in_process_browser_test.cc +++ b/chrome/test/in_process_browser_test.cc @@ -26,7 +26,7 @@ #include "chrome/common/main_function_params.h" #include "chrome/test/testing_browser_process.h" #include "chrome/test/ui_test_utils.h" -#include "net/base/host_resolver_unittest.h" +#include "net/base/mock_host_resolver.h" #include "sandbox/src/dep.h" extern int BrowserMain(const MainFunctionParams&); @@ -140,10 +140,11 @@ void InProcessBrowserTest::SetUp() { params.ui_task = NewRunnableMethod(this, &InProcessBrowserTest::RunTestOnMainThreadLoop); - scoped_refptr<net::RuleBasedHostMapper> host_mapper( - new net::RuleBasedHostMapper()); - ConfigureHostMapper(host_mapper.get()); - net::ScopedHostMapper scoped_host_mapper(host_mapper.get()); + scoped_refptr<net::RuleBasedHostResolverProc> host_resolver_proc( + new net::RuleBasedHostResolverProc(NULL)); + ConfigureHostResolverProc(host_resolver_proc); + net::ScopedDefaultHostResolverProc scoped_host_resolver_proc( + host_resolver_proc); BrowserMain(params); } @@ -241,12 +242,12 @@ void InProcessBrowserTest::RunTestOnMainThreadLoop() { MessageLoopForUI::current()->Quit(); } -void InProcessBrowserTest::ConfigureHostMapper( - net::RuleBasedHostMapper* host_mapper) { - host_mapper->AllowDirectLookup("*.google.com"); +void InProcessBrowserTest::ConfigureHostResolverProc( + net::RuleBasedHostResolverProc* host_resolver_proc) { + host_resolver_proc->AllowDirectLookup("*.google.com"); // See http://en.wikipedia.org/wiki/Web_Proxy_Autodiscovery_Protocol // We don't want the test code to use it. - host_mapper->AddSimulatedFailure("wpad"); + host_resolver_proc->AddSimulatedFailure("wpad"); } void InProcessBrowserTest::TimedOut() { diff --git a/chrome/test/in_process_browser_test.h b/chrome/test/in_process_browser_test.h index bec71d9..11a2880 100644 --- a/chrome/test/in_process_browser_test.h +++ b/chrome/test/in_process_browser_test.h @@ -11,7 +11,7 @@ class Browser; class Profile; namespace net { -class RuleBasedHostMapper; +class RuleBasedHostResolverProc; } // Base class for tests wanting to bring up a browser in the unit test process. @@ -66,10 +66,10 @@ class InProcessBrowserTest : public testing::Test { // main thread before the browser is torn down. virtual void CleanUpOnMainThread() {} - // Allows subclasses to configure the host mapper. By default this blocks - // requests to google.com as Chrome pings that on startup and we don't want to - // do that during testing. - virtual void ConfigureHostMapper(net::RuleBasedHostMapper* host_mapper); + // Allows subclasses to configure the host resolver procedure. By default + // this blocks requests to google.com as Chrome pings that on startup and we + // don't want to do that during testing. + virtual void ConfigureHostResolverProc(net::RuleBasedHostResolverProc* proc); // Invoked when a test is not finishing in a timely manner. void TimedOut(); diff --git a/chrome/test/unit/chrome_test_suite.h b/chrome/test/unit/chrome_test_suite.h index 7c49d72..7f8733b 100644 --- a/chrome/test/unit/chrome_test_suite.h +++ b/chrome/test/unit/chrome_test_suite.h @@ -28,15 +28,17 @@ #include "chrome/common/mac_app_names.h" #endif #include "chrome/test/testing_browser_process.h" -#include "net/base/host_resolver_unittest.h" +#include "net/base/mock_host_resolver.h" #include "net/base/net_util.h" // In many cases it may be not obvious that a test makes a real DNS lookup. // We generally don't want to rely on external DNS servers for our tests, -// so this mapper catches external queries. -class WarningHostMapper : public net::HostMapper { +// so this host resolver procedure catches external queries. +class WarningHostResolverProc : public net::HostResolverProc { public: - virtual std::string Map(const std::string& host) { + WarningHostResolverProc() : HostResolverProc(NULL) {} + + virtual int Resolve(const std::string& host, net::AddressList* addrlist) { const char* kLocalHostNames[] = {"localhost", "127.0.0.1"}; bool local = false; @@ -51,11 +53,11 @@ class WarningHostMapper : public net::HostMapper { } // Make the test fail so it's harder to ignore. - // If you really need to make real DNS query, use net::RuleBasedHostMapper - // and its AllowDirectLookup method. + // If you really need to make real DNS query, use + // net::RuleBasedHostResolverProc and its AllowDirectLookup method. EXPECT_TRUE(local) << "Making external DNS lookup of " << host; - return MapUsingPrevious(host); + return ResolveUsingPrevious(host, addrlist); } }; @@ -71,8 +73,8 @@ class ChromeTestSuite : public TestSuite { TestSuite::Initialize(); - host_mapper_ = new WarningHostMapper(); - scoped_host_mapper_.Init(host_mapper_.get()); + host_resolver_proc_ = new WarningHostResolverProc(); + scoped_host_resolver_proc_.Init(host_resolver_proc_.get()); chrome::RegisterPathProvider(); app::RegisterPathProvider(); @@ -140,8 +142,8 @@ class ChromeTestSuite : public TestSuite { StatsTable* stats_table_; ScopedOleInitializer ole_initializer_; - scoped_refptr<WarningHostMapper> host_mapper_; - net::ScopedHostMapper scoped_host_mapper_; + scoped_refptr<WarningHostResolverProc> host_resolver_proc_; + net::ScopedDefaultHostResolverProc scoped_host_resolver_proc_; }; #endif // CHROME_TEST_UNIT_CHROME_TEST_SUITE_H_ 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) { diff --git a/net/ftp/ftp_network_transaction_unittest.cc b/net/ftp/ftp_network_transaction_unittest.cc index c81fc0f..d549b2f 100644 --- a/net/ftp/ftp_network_transaction_unittest.cc +++ b/net/ftp/ftp_network_transaction_unittest.cc @@ -5,9 +5,8 @@ #include "net/ftp/ftp_network_transaction.h" #include "base/ref_counted.h" -#include "net/base/host_resolver.h" -#include "net/base/host_resolver_unittest.h" #include "net/base/io_buffer.h" +#include "net/base/mock_host_resolver.h" #include "net/base/test_completion_callback.h" #include "net/ftp/ftp_network_session.h" #include "net/ftp/ftp_request_info.h" @@ -237,7 +236,8 @@ class FtpMockControlSocketFileDownloadRetrFail class FtpNetworkTransactionTest : public PlatformTest { public: FtpNetworkTransactionTest() - : session_(new FtpNetworkSession(new HostResolver)), + : host_resolver_(new MockHostResolver), + session_(new FtpNetworkSession(host_resolver_)), transaction_(session_.get(), &mock_socket_factory_) { } @@ -286,6 +286,7 @@ class FtpNetworkTransactionTest : public PlatformTest { ExecuteTransaction(ctrl_socket, request, expected_result); } + scoped_refptr<MockHostResolver> host_resolver_; scoped_refptr<FtpNetworkSession> session_; MockClientSocketFactory mock_socket_factory_; FtpNetworkTransaction transaction_; @@ -294,9 +295,7 @@ class FtpNetworkTransactionTest : public PlatformTest { TEST_F(FtpNetworkTransactionTest, FailedLookup) { FtpRequestInfo request_info = GetRequestInfo("ftp://badhost"); - scoped_refptr<RuleBasedHostMapper> mapper(new RuleBasedHostMapper()); - mapper->AddSimulatedFailure("badhost"); - ScopedHostMapper scoped_mapper(mapper.get()); + host_resolver_->rules()->AddSimulatedFailure("badhost"); ASSERT_EQ(ERR_IO_PENDING, transaction_.Start(&request_info, &callback_)); EXPECT_EQ(ERR_FAILED, callback_.WaitForResult()); } diff --git a/net/http/http_network_layer_unittest.cc b/net/http/http_network_layer_unittest.cc index 9fd35ea..b61ca3d 100644 --- a/net/http/http_network_layer_unittest.cc +++ b/net/http/http_network_layer_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/mock_host_resolver.h" #include "net/http/http_network_layer.h" #include "net/http/http_transaction_unittest.h" #include "net/proxy/proxy_service.h" @@ -16,7 +16,7 @@ class HttpNetworkLayerTest : public PlatformTest { TEST_F(HttpNetworkLayerTest, CreateAndDestroy) { scoped_ptr<net::ProxyService> proxy_service(net::ProxyService::CreateNull()); net::HttpNetworkLayer factory( - NULL, new net::HostResolver, proxy_service.get()); + NULL, new net::MockHostResolver, proxy_service.get()); scoped_ptr<net::HttpTransaction> trans(factory.CreateTransaction()); } @@ -24,7 +24,7 @@ TEST_F(HttpNetworkLayerTest, CreateAndDestroy) { TEST_F(HttpNetworkLayerTest, Suspend) { scoped_ptr<net::ProxyService> proxy_service(net::ProxyService::CreateNull()); net::HttpNetworkLayer factory( - NULL, new net::HostResolver, proxy_service.get()); + NULL, new net::MockHostResolver, proxy_service.get()); scoped_ptr<net::HttpTransaction> trans(factory.CreateTransaction()); trans.reset(); @@ -56,7 +56,7 @@ TEST_F(HttpNetworkLayerTest, GET) { mock_socket_factory.AddMockSocket(&data); scoped_ptr<net::ProxyService> proxy_service(net::ProxyService::CreateNull()); - net::HttpNetworkLayer factory(&mock_socket_factory, new net::HostResolver, + net::HttpNetworkLayer factory(&mock_socket_factory, new net::MockHostResolver, proxy_service.get()); TestCompletionCallback callback; diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc index 74764a5..c1510a1 100644 --- a/net/http/http_network_transaction_unittest.cc +++ b/net/http/http_network_transaction_unittest.cc @@ -6,7 +6,7 @@ #include "base/compiler_specific.h" #include "net/base/completion_callback.h" -#include "net/base/host_resolver_unittest.h" +#include "net/base/mock_host_resolver.h" #include "net/base/ssl_info.h" #include "net/base/test_completion_callback.h" #include "net/base/upload_data.h" @@ -35,14 +35,14 @@ ProxyService* CreateNullProxyService() { class SessionDependencies { public: // Default set of dependencies -- "null" proxy service. - SessionDependencies() : host_resolver(new HostResolver), + SessionDependencies() : host_resolver(new MockHostResolver), proxy_service(CreateNullProxyService()) {} // Custom proxy service dependency. explicit SessionDependencies(ProxyService* proxy_service) - : host_resolver(new HostResolver), proxy_service(proxy_service) {} + : host_resolver(new MockHostResolver), proxy_service(proxy_service) {} - scoped_refptr<HostResolver> host_resolver; + scoped_refptr<MockHostResolver> host_resolver; scoped_ptr<ProxyService> proxy_service; MockClientSocketFactory socket_factory; }; @@ -3351,12 +3351,11 @@ TEST_F(HttpNetworkTransactionTest, GroupNameForProxyConnections) { } TEST_F(HttpNetworkTransactionTest, ReconsiderProxyAfterFailedConnection) { - scoped_refptr<RuleBasedHostMapper> host_mapper(new RuleBasedHostMapper()); - ScopedHostMapper scoped_host_mapper(host_mapper.get()); - host_mapper->AddSimulatedFailure("*"); - SessionDependencies session_deps( CreateFixedProxyService("myproxy:70;foobar:80")); + + session_deps.host_resolver->rules()->AddSimulatedFailure("*"); + scoped_ptr<HttpTransaction> trans( new HttpNetworkTransaction( CreateSession(&session_deps), @@ -3457,6 +3456,10 @@ TEST_F(HttpNetworkTransactionTest, ResolveMadeWithReferrer) { // host cache is bypassed. TEST_F(HttpNetworkTransactionTest, BypassHostCacheOnRefresh) { SessionDependencies session_deps; + + // Enable caching in the mock host resolver (it is off by default). + session_deps.host_resolver->Reset(NULL, 100, 60000); + scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction( CreateSession(&session_deps), &session_deps.socket_factory)); @@ -3473,14 +3476,12 @@ TEST_F(HttpNetworkTransactionTest, BypassHostCacheOnRefresh) { rv = session_deps.host_resolver->Resolve( HostResolver::RequestInfo("www.google.com", 80), &addrlist, &resolve_callback, NULL); - EXPECT_EQ(OK, rv); + ASSERT_EQ(OK, rv); // Inject a failure the next time that "www.google.com" is resolved. This way // we can tell if the next lookup hit the cache, or the "network". // (cache --> success, "network" --> failure). - scoped_refptr<RuleBasedHostMapper> host_mapper(new RuleBasedHostMapper()); - ScopedHostMapper scoped_host_mapper(host_mapper.get()); - host_mapper->AddSimulatedFailure("www.google.com"); + session_deps.host_resolver->rules()->AddSimulatedFailure("www.google.com"); // Connect up a mock socket which will fail with ERR_UNEXPECTED during the // first read -- this won't be reached as the host resolution will fail first. diff --git a/net/net.gyp b/net/net.gyp index 82b2e63..4f97ec9 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -75,6 +75,10 @@ 'base/host_cache.h', 'base/host_resolver.cc', 'base/host_resolver.h', + 'base/host_resolver_impl.cc', + 'base/host_resolver_impl.h', + 'base/host_resolver_proc.cc', + 'base/host_resolver_proc.h', 'base/io_buffer.cc', 'base/io_buffer.h', 'base/listen_socket.cc', @@ -85,6 +89,9 @@ 'base/mime_sniffer.h', 'base/mime_util.cc', 'base/mime_util.h', + # TODO(eroman): move this into its own test-support target. + 'base/mock_host_resolver.cc', + 'base/mock_host_resolver.h', 'base/net_error_list.h', 'base/net_errors.cc', 'base/net_errors.h', @@ -434,8 +441,7 @@ 'base/force_tls_state_unittest.cc', 'base/gzip_filter_unittest.cc', 'base/host_cache_unittest.cc', - 'base/host_resolver_unittest.cc', - 'base/host_resolver_unittest.h', + 'base/host_resolver_impl_unittest.cc', 'base/listen_socket_unittest.cc', 'base/listen_socket_unittest.h', 'base/mime_sniffer_unittest.cc', diff --git a/net/proxy/proxy_resolver_perftest.cc b/net/proxy/proxy_resolver_perftest.cc index 22f2350..7c74e72 100644 --- a/net/proxy/proxy_resolver_perftest.cc +++ b/net/proxy/proxy_resolver_perftest.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "base/perftimer.h" +#include "net/base/mock_host_resolver.h" #include "net/proxy/proxy_resolver_v8.h" #include "net/url_request/url_request_unittest.h" #include "testing/gtest/include/gtest/gtest.h" @@ -186,7 +187,8 @@ TEST(ProxyResolverPerfTest, ProxyResolverMac) { TEST(ProxyResolverPerfTest, ProxyResolverV8) { net::ProxyResolverV8::JSBindings* js_bindings = - net::ProxyResolverV8::CreateDefaultBindings(new net::HostResolver, NULL); + net::ProxyResolverV8::CreateDefaultBindings( + new net::MockHostResolver, NULL); net::ProxyResolverV8 resolver(js_bindings); PacPerfSuiteRunner runner(&resolver, "ProxyResolverV8"); diff --git a/net/proxy/proxy_resolver_v8.h b/net/proxy/proxy_resolver_v8.h index 3bf2bec..6fbc968 100644 --- a/net/proxy/proxy_resolver_v8.h +++ b/net/proxy/proxy_resolver_v8.h @@ -56,7 +56,7 @@ class ProxyResolverV8 : public ProxyResolver { // Creates a default javascript bindings implementation that will: // - Send script error messages to LOG(INFO) // - Send script alert()s to LOG(INFO) - // - Use the provided host mapper to service dnsResolve(). + // - Use the provided host resolver to service dnsResolve(). // // For clients that need more control (for example, sending the script output // to a UI widget), use the ProxyResolverV8(JSBindings*) and specify your diff --git a/net/proxy/proxy_resolver_v8_unittest.cc b/net/proxy/proxy_resolver_v8_unittest.cc index 7fab7d7..86544d5 100644 --- a/net/proxy/proxy_resolver_v8_unittest.cc +++ b/net/proxy/proxy_resolver_v8_unittest.cc @@ -6,7 +6,7 @@ #include "base/string_util.h" #include "base/path_service.h" #include "googleurl/src/gurl.h" -#include "net/base/host_resolver.h" +#include "net/base/mock_host_resolver.h" #include "net/base/net_errors.h" #include "net/proxy/proxy_resolver_v8.h" #include "net/proxy/proxy_info.h" @@ -379,7 +379,8 @@ TEST(ProxyResolverV8Test, V8Bindings) { TEST(ProxyResolverV8DefaultBindingsTest, DnsResolve) { // Get a hold of a DefaultJSBindings* (it is a hidden impl class). scoped_ptr<net::ProxyResolverV8::JSBindings> bindings( - net::ProxyResolverV8::CreateDefaultBindings(new net::HostResolver, NULL)); + net::ProxyResolverV8::CreateDefaultBindings( + new net::MockHostResolver, NULL)); // Considered an error. EXPECT_EQ("", bindings->DnsResolve("")); @@ -411,7 +412,7 @@ TEST(ProxyResolverV8DefaultBindingsTest, DnsResolve) { // THIS TEST IS CURRENTLY FLAWED. // // Since we are running in unit-test mode, the HostResolve is using a - // mock HostMapper, which will always return 127.0.0.1, without going + // mock HostResolverProc, which will always return 127.0.0.1, without going // through the real codepaths. // // It is important that these tests be run with the real thing, since we @@ -430,10 +431,11 @@ TEST(ProxyResolverV8DefaultBindingsTest, DnsResolve) { TEST(ProxyResolverV8DefaultBindingsTest, MyIpAddress) { // Get a hold of a DefaultJSBindings* (it is a hidden impl class). scoped_ptr<net::ProxyResolverV8::JSBindings> bindings( - net::ProxyResolverV8::CreateDefaultBindings(new net::HostResolver, NULL)); + net::ProxyResolverV8::CreateDefaultBindings( + new net::MockHostResolver, NULL)); - // Our ip address is always going to be 127.0.0.1, since we are using a - // mock host mapper when running in unit-test mode. + // Our IP address is always going to be 127.0.0.1, since we are using a + // mock host resolver. std::string my_ip_address = bindings->MyIpAddress(); EXPECT_EQ("127.0.0.1", my_ip_address); diff --git a/net/proxy/proxy_script_fetcher_unittest.cc b/net/proxy/proxy_script_fetcher_unittest.cc index 8a7cfb5..8a8030b 100644 --- a/net/proxy/proxy_script_fetcher_unittest.cc +++ b/net/proxy/proxy_script_fetcher_unittest.cc @@ -30,7 +30,7 @@ class RequestContext : public URLRequestContext { public: RequestContext() { net::ProxyConfig no_proxy; - host_resolver_ = new net::HostResolver; + host_resolver_ = net::CreateSystemHostResolver(); proxy_service_ = net::ProxyService::CreateFixed(no_proxy); http_transaction_factory_ = diff --git a/net/socket/client_socket_pool_base_unittest.cc b/net/socket/client_socket_pool_base_unittest.cc index 8874686..e4faf5b 100644 --- a/net/socket/client_socket_pool_base_unittest.cc +++ b/net/socket/client_socket_pool_base_unittest.cc @@ -7,7 +7,6 @@ #include "base/compiler_specific.h" #include "base/message_loop.h" #include "base/scoped_vector.h" -#include "net/base/host_resolver_unittest.h" #include "net/base/net_errors.h" #include "net/base/test_completion_callback.h" #include "net/socket/client_socket.h" @@ -793,7 +792,7 @@ TEST_F(ClientSocketPoolBaseTest, ReleaseSockets) { ClientSocketPoolBase::EnableLateBindingOfSockets(false); // Start job 1 (async OK) - connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob); + connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob); TestSocketRequest req1(pool_.get(), &request_order_); int rv = req1.handle.Init("a", ignored_request_info_, 5, &req1); @@ -1135,7 +1134,7 @@ TEST_F(ClientSocketPoolBaseTest_LateBinding, ReleaseSockets) { CreatePool(kDefaultMaxSocketsPerGroup); // Start job 1 (async OK) - connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob); + connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob); TestSocketRequest req1(pool_.get(), &request_order_); int rv = req1.handle.Init("a", ignored_request_info_, 5, &req1); @@ -1178,7 +1177,7 @@ TEST_F(ClientSocketPoolBaseTest_LateBinding, ReleaseSockets) { TEST_F(ClientSocketPoolBaseTest_LateBinding, PendingJobCompletionOrder) { CreatePool(kDefaultMaxSocketsPerGroup); // First two jobs are async. - connect_job_factory_->set_job_type(TestConnectJob::kMockPendingFailingJob); + connect_job_factory_->set_job_type(TestConnectJob::kMockPendingFailingJob); TestSocketRequest req1(pool_.get(), &request_order_); int rv = req1.handle.Init("a", ignored_request_info_, 5, &req1); @@ -1189,7 +1188,7 @@ TEST_F(ClientSocketPoolBaseTest_LateBinding, PendingJobCompletionOrder) { EXPECT_EQ(ERR_IO_PENDING, rv); // The pending job is sync. - connect_job_factory_->set_job_type(TestConnectJob::kMockJob); + connect_job_factory_->set_job_type(TestConnectJob::kMockJob); TestSocketRequest req3(pool_.get(), &request_order_); rv = req3.handle.Init("a", ignored_request_info_, 5, &req3); @@ -1208,7 +1207,7 @@ TEST_F(ClientSocketPoolBaseTest_LateBinding, PendingJobCompletionOrder) { TEST_F(ClientSocketPoolBaseTest_LateBinding, LoadState) { CreatePool(kDefaultMaxSocketsPerGroup); connect_job_factory_->set_job_type( - TestConnectJob::kMockAdvancingLoadStateJob); + TestConnectJob::kMockAdvancingLoadStateJob); TestSocketRequest req1(pool_.get(), &request_order_); int rv = req1.handle.Init("a", ignored_request_info_, 5, &req1); diff --git a/net/socket/socks5_client_socket_unittest.cc b/net/socket/socks5_client_socket_unittest.cc index 8f763c4..b225887 100644 --- a/net/socket/socks5_client_socket_unittest.cc +++ b/net/socket/socks5_client_socket_unittest.cc @@ -12,7 +12,7 @@ #include <netdb.h> #endif #include "net/base/address_list.h" -#include "net/base/host_resolver_unittest.h" +#include "net/base/mock_host_resolver.h" #include "net/base/test_completion_callback.h" #include "net/base/winsock_init.h" #include "net/socket/client_socket_factory.h" @@ -41,10 +41,8 @@ class SOCKS5ClientSocketTest : public PlatformTest { scoped_ptr<SOCKS5ClientSocket> user_sock_; AddressList address_list_; ClientSocket* tcp_sock_; - ScopedHostMapper host_mapper_; TestCompletionCallback callback_; - scoped_refptr<RuleBasedHostMapper> mapper_; - scoped_refptr<HostResolver> host_resolver_; + scoped_refptr<MockHostResolver> host_resolver_; scoped_ptr<MockSocket> mock_socket_; private: @@ -52,7 +50,7 @@ class SOCKS5ClientSocketTest : public PlatformTest { }; SOCKS5ClientSocketTest::SOCKS5ClientSocketTest() - : kNwPort(htons(80)), host_resolver_(new HostResolver(0, 0)) { + : kNwPort(htons(80)), host_resolver_(new MockHostResolver) { } // Set up platform before every test case @@ -60,14 +58,9 @@ void SOCKS5ClientSocketTest::SetUp() { PlatformTest::SetUp(); // Resolve the "localhost" AddressList used by the TCP connection to connect. - scoped_refptr<HostResolver> resolver = new HostResolver(); HostResolver::RequestInfo info("www.socks-proxy.com", 1080); - int rv = resolver->Resolve(info, &address_list_, NULL, NULL); + int rv = host_resolver_->Resolve(info, &address_list_, NULL, NULL); ASSERT_EQ(OK, rv); - - // Create a new host mapping for the duration of this test case only. - mapper_ = new RuleBasedHostMapper(); - host_mapper_.Init(mapper_); } SOCKS5ClientSocket* SOCKS5ClientSocketTest::BuildMockSocket( @@ -153,7 +146,7 @@ TEST_F(SOCKS5ClientSocketTest, FailedDNS) { const std::string hostname = "unresolved.ipv4.address"; const char kSOCKS5DomainRequest[] = { 0x05, 0x01, 0x00, 0x03 }; - mapper_->AddSimulatedFailure(hostname.c_str()); + host_resolver_->rules()->AddSimulatedFailure(hostname.c_str()); std::string request(kSOCKS5DomainRequest, arraysize(kSOCKS5DomainRequest)); @@ -186,12 +179,11 @@ TEST_F(SOCKS5ClientSocketTest, IPv6Domain) { const uint8 ipv6_addr[] = { 0x20, 0x01, 0x0d, 0xb8, 0x87, 0x14, 0x3a, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x000, 0x00, 0x12 }; - mapper_->AddRule(hostname.c_str(), "2001:db8:8714:3a90::12"); + host_resolver_->rules()->AddRule(hostname, "2001:db8:8714:3a90::12"); AddressList address_list; - scoped_refptr<HostResolver> resolver = new HostResolver(); HostResolver::RequestInfo info(hostname, 80); - int rv = resolver->Resolve(info, &address_list, NULL, NULL); + int rv = host_resolver_->Resolve(info, &address_list, NULL, NULL); if (rv != OK || !address_list.head()) { // This machine does not support IPv6. We skip this test altogether. // TODO(arindam): create a MockIPv6HostResolver to manually diff --git a/net/socket/socks_client_socket_unittest.cc b/net/socket/socks_client_socket_unittest.cc index b2e6627..3d7fa85 100644 --- a/net/socket/socks_client_socket_unittest.cc +++ b/net/socket/socks_client_socket_unittest.cc @@ -5,8 +5,8 @@ #include "net/socket/socks_client_socket.h" #include "net/base/address_list.h" -#include "net/base/host_resolver_unittest.h" #include "net/base/listen_socket.h" +#include "net/base/mock_host_resolver.h" #include "net/base/test_completion_callback.h" #include "net/base/winsock_init.h" #include "net/socket/client_socket_factory.h" @@ -36,10 +36,8 @@ class SOCKSClientSocketTest : public PlatformTest { scoped_ptr<SOCKSClientSocket> user_sock_; AddressList address_list_; ClientSocket* tcp_sock_; - ScopedHostMapper host_mapper_; TestCompletionCallback callback_; - scoped_refptr<RuleBasedHostMapper> mapper_; - scoped_refptr<HostResolver> host_resolver_; + scoped_refptr<MockHostResolver> host_resolver_; scoped_ptr<MockSocket> mock_socket_; private: @@ -47,23 +45,12 @@ class SOCKSClientSocketTest : public PlatformTest { }; SOCKSClientSocketTest::SOCKSClientSocketTest() - : host_resolver_(new HostResolver(0, 0)) { + : host_resolver_(new MockHostResolver) { } // Set up platform before every test case void SOCKSClientSocketTest::SetUp() { PlatformTest::SetUp(); - - // Resolve the "localhost" AddressList used by the tcp_connection to connect. - scoped_refptr<HostResolver> resolver = new HostResolver(); - HostResolver::RequestInfo info("localhost", 1080); - int rv = resolver->Resolve(info, &address_list_, NULL, NULL); - ASSERT_EQ(OK, rv); - - // Create a new host mapping for the duration of this test case only. - mapper_ = new RuleBasedHostMapper(); - host_mapper_.Init(mapper_); - mapper_->AddRule("www.google.com", "127.0.0.1"); } SOCKSClientSocket* SOCKSClientSocketTest::BuildMockSocket( @@ -240,7 +227,7 @@ TEST_F(SOCKSClientSocketTest, FailedSocketRead) { TEST_F(SOCKSClientSocketTest, SOCKS4AFailedDNS) { const char hostname[] = "unresolved.ipv4.address"; - mapper_->AddSimulatedFailure(hostname); + host_resolver_->rules()->AddSimulatedFailure(hostname); std::string request(kSOCKS4aInitialRequest, arraysize(kSOCKS4aInitialRequest)); @@ -266,7 +253,7 @@ TEST_F(SOCKSClientSocketTest, SOCKS4AFailedDNS) { TEST_F(SOCKSClientSocketTest, SOCKS4AIfDomainInIPv6) { const char hostname[] = "an.ipv6.address"; - mapper_->AddRule(hostname, "2001:db8:8714:3a90::12"); + host_resolver_->rules()->AddRule(hostname, "2001:db8:8714:3a90::12"); std::string request(kSOCKS4aInitialRequest, arraysize(kSOCKS4aInitialRequest)); diff --git a/net/socket/ssl_client_socket_unittest.cc b/net/socket/ssl_client_socket_unittest.cc index 1d68bd1..e0520e5 100644 --- a/net/socket/ssl_client_socket_unittest.cc +++ b/net/socket/ssl_client_socket_unittest.cc @@ -23,7 +23,8 @@ const net::SSLConfig kDefaultSSLConfig; class SSLClientSocketTest : public PlatformTest { public: SSLClientSocketTest() - : socket_factory_(net::ClientSocketFactory::GetDefaultFactory()) { + : resolver_(net::CreateSystemHostResolver()), + socket_factory_(net::ClientSocketFactory::GetDefaultFactory()) { } void StartOKServer() { @@ -48,6 +49,7 @@ class SSLClientSocketTest : public PlatformTest { } protected: + scoped_refptr<net::HostResolver> resolver_; net::ClientSocketFactory* socket_factory_; net::TestServerLauncher server_; }; @@ -86,11 +88,10 @@ TEST_F(SSLClientSocketTest, MAYBE_Connect) { StartOKServer(); net::AddressList addr; - scoped_refptr<net::HostResolver> resolver(new net::HostResolver); TestCompletionCallback callback; net::HostResolver::RequestInfo info(server_.kHostName, server_.kOKHTTPSPort); - int rv = resolver->Resolve(info, &addr, NULL, NULL); + int rv = resolver_->Resolve(info, &addr, NULL, NULL); EXPECT_EQ(net::OK, rv); net::ClientSocket *transport = new net::TCPClientSocket(addr); @@ -124,11 +125,10 @@ TEST_F(SSLClientSocketTest, MAYBE_ConnectExpired) { StartExpiredServer(); net::AddressList addr; - scoped_refptr<net::HostResolver> resolver(new net::HostResolver); TestCompletionCallback callback; net::HostResolver::RequestInfo info(server_.kHostName, server_.kBadHTTPSPort); - int rv = resolver->Resolve(info, &addr, NULL, NULL); + int rv = resolver_->Resolve(info, &addr, NULL, NULL); EXPECT_EQ(net::OK, rv); net::ClientSocket *transport = new net::TCPClientSocket(addr); @@ -161,12 +161,11 @@ TEST_F(SSLClientSocketTest, MAYBE_ConnectMismatched) { StartMismatchedServer(); net::AddressList addr; - scoped_refptr<net::HostResolver> resolver(new net::HostResolver); TestCompletionCallback callback; net::HostResolver::RequestInfo info(server_.kMismatchedHostName, server_.kOKHTTPSPort); - int rv = resolver->Resolve(info, &addr, NULL, NULL); + int rv = resolver_->Resolve(info, &addr, NULL, NULL); EXPECT_EQ(net::OK, rv); net::ClientSocket *transport = new net::TCPClientSocket(addr); @@ -204,11 +203,10 @@ TEST_F(SSLClientSocketTest, MAYBE_Read) { StartOKServer(); net::AddressList addr; - scoped_refptr<net::HostResolver> resolver(new net::HostResolver); TestCompletionCallback callback; net::HostResolver::RequestInfo info(server_.kHostName, server_.kOKHTTPSPort); - int rv = resolver->Resolve(info, &addr, &callback, NULL); + int rv = resolver_->Resolve(info, &addr, &callback, NULL); EXPECT_EQ(net::ERR_IO_PENDING, rv); rv = callback.WaitForResult(); @@ -265,11 +263,10 @@ TEST_F(SSLClientSocketTest, MAYBE_Read_SmallChunks) { StartOKServer(); net::AddressList addr; - scoped_refptr<net::HostResolver> resolver(new net::HostResolver); TestCompletionCallback callback; net::HostResolver::RequestInfo info(server_.kHostName, server_.kOKHTTPSPort); - int rv = resolver->Resolve(info, &addr, NULL, NULL); + int rv = resolver_->Resolve(info, &addr, NULL, NULL); EXPECT_EQ(net::OK, rv); net::ClientSocket *transport = new net::TCPClientSocket(addr); @@ -321,11 +318,10 @@ TEST_F(SSLClientSocketTest, MAYBE_Read_Interrupted) { StartOKServer(); net::AddressList addr; - scoped_refptr<net::HostResolver> resolver(new net::HostResolver); TestCompletionCallback callback; net::HostResolver::RequestInfo info(server_.kHostName, server_.kOKHTTPSPort); - int rv = resolver->Resolve(info, &addr, NULL, NULL); + int rv = resolver_->Resolve(info, &addr, NULL, NULL); EXPECT_EQ(net::OK, rv); net::ClientSocket *transport = new net::TCPClientSocket(addr); diff --git a/net/socket/ssl_test_util.cc b/net/socket/ssl_test_util.cc index 0f012f5..1acc0b5 100644 --- a/net/socket/ssl_test_util.cc +++ b/net/socket/ssl_test_util.cc @@ -246,7 +246,7 @@ bool TestServerLauncher::WaitToStart(const std::string& host_name, int port) { // Verify that the webserver is actually started. // Otherwise tests can fail if they run faster than Python can start. net::AddressList addr; - scoped_refptr<net::HostResolver> resolver(new net::HostResolver); + scoped_refptr<net::HostResolver> resolver(net::CreateSystemHostResolver()); net::HostResolver::RequestInfo info(host_name, port); int rv = resolver->Resolve(info, &addr, NULL, NULL); if (rv != net::OK) diff --git a/net/socket/tcp_client_socket_pool_unittest.cc b/net/socket/tcp_client_socket_pool_unittest.cc index 5d9e630..b5cce2c 100644 --- a/net/socket/tcp_client_socket_pool_unittest.cc +++ b/net/socket/tcp_client_socket_pool_unittest.cc @@ -6,7 +6,7 @@ #include "base/compiler_specific.h" #include "base/message_loop.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 "net/socket/client_socket.h" @@ -224,14 +224,16 @@ int TestSocketRequest::completion_count = 0; class TCPClientSocketPoolTest : public testing::Test { protected: TCPClientSocketPoolTest() - : pool_(new TCPClientSocketPool(kMaxSocketsPerGroup, - new HostResolver, - &client_socket_factory_)) {} + : host_resolver_(new MockHostResolver), + pool_(new TCPClientSocketPool(kMaxSocketsPerGroup, + host_resolver_, + &client_socket_factory_)) { + // We enable caching on the mock host-resolver (it is off by default), + // because some of the tests in this file expect it. + host_resolver_->Reset(NULL, 100, 60000); + } virtual void SetUp() { - RuleBasedHostMapper *host_mapper = new RuleBasedHostMapper(); - host_mapper->AddRule("*", "127.0.0.1"); - scoped_host_mapper_.Init(host_mapper); TestSocketRequest::completion_count = 0; } @@ -241,7 +243,7 @@ class TCPClientSocketPoolTest : public testing::Test { MessageLoop::current()->RunAllPending(); } - ScopedHostMapper scoped_host_mapper_; + scoped_refptr<MockHostResolver> host_resolver_; MockClientSocketFactory client_socket_factory_; scoped_refptr<ClientSocketPool> pool_; std::vector<TestSocketRequest*> request_order_; @@ -264,9 +266,7 @@ TEST_F(TCPClientSocketPoolTest, Basic) { } TEST_F(TCPClientSocketPoolTest, InitHostResolutionFailure) { - RuleBasedHostMapper* host_mapper = new RuleBasedHostMapper; - host_mapper->AddSimulatedFailure("unresolvable.host.name"); - ScopedHostMapper scoped_host_mapper(host_mapper); + host_resolver_->rules()->AddSimulatedFailure("unresolvable.host.name"); TestSocketRequest req(pool_.get(), &request_order_); HostResolver::RequestInfo info("unresolvable.host.name", 80); EXPECT_EQ(ERR_IO_PENDING, req.handle.Init("a", info, 5, &req)); diff --git a/net/socket/tcp_client_socket_unittest.cc b/net/socket/tcp_client_socket_unittest.cc index 91b0e57..6d14694 100644 --- a/net/socket/tcp_client_socket_unittest.cc +++ b/net/socket/tcp_client_socket_unittest.cc @@ -86,7 +86,7 @@ void TCPClientSocketTest::SetUp() { listen_port_ = port; AddressList addr; - scoped_refptr<HostResolver> resolver(new HostResolver); + scoped_refptr<HostResolver> resolver(CreateSystemHostResolver()); HostResolver::RequestInfo info("localhost", listen_port_); int rv = resolver->Resolve(info, &addr, NULL, NULL); CHECK(rv == OK); diff --git a/net/socket/tcp_pinger_unittest.cc b/net/socket/tcp_pinger_unittest.cc index c807007..b1e678f 100644 --- a/net/socket/tcp_pinger_unittest.cc +++ b/net/socket/tcp_pinger_unittest.cc @@ -65,7 +65,7 @@ void TCPPingerTest::SetUp() { TEST_F(TCPPingerTest, Ping) { net::AddressList addr; - scoped_refptr<net::HostResolver> resolver(new net::HostResolver); + scoped_refptr<net::HostResolver> resolver(net::CreateSystemHostResolver()); net::HostResolver::RequestInfo info("localhost", listen_port_); int rv = resolver->Resolve(info, &addr, NULL, NULL); @@ -78,7 +78,7 @@ TEST_F(TCPPingerTest, Ping) { TEST_F(TCPPingerTest, PingFail) { net::AddressList addr; - scoped_refptr<net::HostResolver> resolver(new net::HostResolver); + scoped_refptr<net::HostResolver> resolver(net::CreateSystemHostResolver()); // "Kill" "server" listen_sock_ = NULL; diff --git a/net/tools/fetch/fetch_client.cc b/net/tools/fetch/fetch_client.cc index 783c91d..63083c4 100644 --- a/net/tools/fetch/fetch_client.cc +++ b/net/tools/fetch/fetch_client.cc @@ -127,7 +127,9 @@ int main(int argc, char**argv) { // Do work here. MessageLoop loop; - scoped_refptr<net::HostResolver> host_resolver(new net::HostResolver); + scoped_refptr<net::HostResolver> host_resolver( + net::CreateSystemHostResolver()); + scoped_ptr<net::ProxyService> proxy_service(net::ProxyService::CreateNull()); net::HttpTransactionFactory* factory = NULL; if (use_cache) { diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc index 220d7e0..2aaac0a 100644 --- a/net/url_request/url_request_unittest.cc +++ b/net/url_request/url_request_unittest.cc @@ -45,7 +45,7 @@ namespace { class URLRequestHttpCacheContext : public URLRequestContext { public: URLRequestHttpCacheContext() { - host_resolver_ = new net::HostResolver; + host_resolver_ = net::CreateSystemHostResolver(); proxy_service_ = net::ProxyService::CreateNull(); http_transaction_factory_ = new net::HttpCache( diff --git a/net/url_request/url_request_unittest.h b/net/url_request/url_request_unittest.h index a08d776..61a1712 100644 --- a/net/url_request/url_request_unittest.h +++ b/net/url_request/url_request_unittest.h @@ -43,7 +43,7 @@ using base::TimeDelta; class TestURLRequestContext : public URLRequestContext { public: TestURLRequestContext() { - host_resolver_ = new net::HostResolver; + host_resolver_ = net::CreateSystemHostResolver(); proxy_service_ = net::ProxyService::CreateNull(); http_transaction_factory_ = net::HttpNetworkLayer::CreateFactory(host_resolver_, @@ -51,7 +51,7 @@ class TestURLRequestContext : public URLRequestContext { } explicit TestURLRequestContext(const std::string& proxy) { - host_resolver_ = new net::HostResolver; + host_resolver_ = net::CreateSystemHostResolver(); net::ProxyConfig proxy_config; proxy_config.proxy_rules.ParseFromString(proxy); proxy_service_ = net::ProxyService::CreateFixed(proxy_config); diff --git a/webkit/tools/test_shell/test_shell_request_context.cc b/webkit/tools/test_shell/test_shell_request_context.cc index cd0f31a..1d1c11c 100644 --- a/webkit/tools/test_shell/test_shell_request_context.cc +++ b/webkit/tools/test_shell/test_shell_request_context.cc @@ -45,7 +45,7 @@ void TestShellRequestContext::Init( // issues. no_proxy = true; #endif - host_resolver_ = new net::HostResolver(); + host_resolver_ = net::CreateSystemHostResolver(); proxy_service_ = net::ProxyService::Create(no_proxy ? &proxy_config : NULL, false, NULL, NULL); |