diff options
author | eroman@chromium.org <eroman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-21 19:12:57 +0000 |
---|---|---|
committer | eroman@chromium.org <eroman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-21 19:12:57 +0000 |
commit | 123ab1e334b44051297c8d4242e81044478c6588 (patch) | |
tree | 7ba32a449e6715f5f4bace667b161b129745b78d /net | |
parent | 57d1ec8fc4b72112bd4d32df76c55395bf33681a (diff) | |
download | chromium_src-123ab1e334b44051297c8d4242e81044478c6588.zip chromium_src-123ab1e334b44051297c8d4242e81044478c6588.tar.gz chromium_src-123ab1e334b44051297c8d4242e81044478c6588.tar.bz2 |
Add a mechanism to disable IPv6.
(1) Adds the ability to specify the address family on a per-request basis.
(2) Exposes a --disable-ipv6 flag to chrome that changes the default address family from AF_UNSPEC to AF_INET (same sort of thing Firefox does).
(3) Changes the backing datastructure for HostCache:EntryMap and HostResolverImpl::JobMap from a "hash_map" to a "std::map". This was for consistency with other code (when I went to add a custom hash trait, I couldn't find any existing code which was using hashmap for custom keys).
(4) Updates about:net-internals to display an address family for the hostcache dump (since it is now a part of the key).
This change is in anticipation of turning off IPv6 host resolving in the PAC utility functions (see bug 24641). But it is also a feature addition.
BUG=24641
TEST=HostCacheTest.AddressFamilyIsPartOfKey
Review URL: http://codereview.chromium.org/302010
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@29686 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/base/address_family.h | 19 | ||||
-rw-r--r-- | net/base/address_list_unittest.cc | 4 | ||||
-rw-r--r-- | net/base/host_cache.cc | 8 | ||||
-rw-r--r-- | net/base/host_cache.h | 32 | ||||
-rw-r--r-- | net/base/host_cache_unittest.cc | 175 | ||||
-rw-r--r-- | net/base/host_resolver.h | 13 | ||||
-rw-r--r-- | net/base/host_resolver_impl.cc | 59 | ||||
-rw-r--r-- | net/base/host_resolver_impl.h | 18 | ||||
-rw-r--r-- | net/base/host_resolver_impl_unittest.cc | 6 | ||||
-rw-r--r-- | net/base/host_resolver_proc.cc | 18 | ||||
-rw-r--r-- | net/base/host_resolver_proc.h | 19 | ||||
-rw-r--r-- | net/base/mock_host_resolver.cc | 7 | ||||
-rw-r--r-- | net/base/mock_host_resolver.h | 10 | ||||
-rw-r--r-- | net/net.gyp | 1 | ||||
-rw-r--r-- | net/url_request/url_request_view_net_internals_job.cc | 26 |
15 files changed, 288 insertions, 127 deletions
diff --git a/net/base/address_family.h b/net/base/address_family.h new file mode 100644 index 0000000..faa9193 --- /dev/null +++ b/net/base/address_family.h @@ -0,0 +1,19 @@ +// 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_ADDRESS_FAMILY_H_ +#define NET_BASE_ADDRESS_FAMILY_H_ + +namespace net { + +// Enum wrapper around the address family types supported by host resolver +// procedures. These correspond with AF_UNSPEC and AF_INET. +enum AddressFamily { + ADDRESS_FAMILY_UNSPECIFIED, + ADDRESS_FAMILY_IPV4_ONLY, +}; + +} // namesapce net + +#endif // NET_BASE_ADDRESS_FAMILY_H_ diff --git a/net/base/address_list_unittest.cc b/net/base/address_list_unittest.cc index d8ab3c1..d440c15 100644 --- a/net/base/address_list_unittest.cc +++ b/net/base/address_list_unittest.cc @@ -19,7 +19,9 @@ void CreateAddressList(net::AddressList* addrlist, int port) { #if defined(OS_WIN) net::EnsureWinsockInit(); #endif - int rv = SystemHostResolverProc("192.168.1.1", addrlist); + int rv = SystemHostResolverProc("192.168.1.1", + net::ADDRESS_FAMILY_UNSPECIFIED, + addrlist); EXPECT_EQ(0, rv); addrlist->SetPort(port); } diff --git a/net/base/host_cache.cc b/net/base/host_cache.cc index 53af5b4..fbe2a7e 100644 --- a/net/base/host_cache.cc +++ b/net/base/host_cache.cc @@ -29,12 +29,12 @@ HostCache::HostCache(size_t max_entries, size_t cache_duration_ms) HostCache::~HostCache() { } -const HostCache::Entry* HostCache::Lookup(const std::string& hostname, +const HostCache::Entry* HostCache::Lookup(const Key& key, base::TimeTicks now) const { if (caching_is_disabled()) return NULL; - EntryMap::const_iterator it = entries_.find(hostname); + EntryMap::const_iterator it = entries_.find(key); if (it == entries_.end()) return NULL; // Not found. @@ -45,7 +45,7 @@ const HostCache::Entry* HostCache::Lookup(const std::string& hostname, return NULL; } -HostCache::Entry* HostCache::Set(const std::string& hostname, +HostCache::Entry* HostCache::Set(const Key& key, int error, const AddressList addrlist, base::TimeTicks now) { @@ -55,7 +55,7 @@ HostCache::Entry* HostCache::Set(const std::string& hostname, base::TimeTicks expiration = now + base::TimeDelta::FromMilliseconds(cache_duration_ms_); - scoped_refptr<Entry>& entry = entries_[hostname]; + scoped_refptr<Entry>& entry = entries_[key]; if (!entry) { // Entry didn't exist, creating one now. Entry* ptr = new Entry(error, addrlist, expiration); diff --git a/net/base/host_cache.h b/net/base/host_cache.h index ab01c86..538f7ef 100644 --- a/net/base/host_cache.h +++ b/net/base/host_cache.h @@ -5,11 +5,12 @@ #ifndef NET_BASE_HOST_CACHE_H_ #define NET_BASE_HOST_CACHE_H_ +#include <map> #include <string> -#include "base/hash_tables.h" #include "base/ref_counted.h" #include "base/time.h" +#include "net/base/address_family.h" #include "net/base/address_list.h" #include "testing/gtest/include/gtest/gtest_prod.h" @@ -31,7 +32,26 @@ class HostCache { base::TimeTicks expiration; }; - typedef base::hash_map<std::string, scoped_refptr<Entry> > EntryMap; + struct Key { + Key(const std::string& hostname, AddressFamily address_family) + : hostname(hostname), address_family(address_family) {} + + bool operator==(const Key& other) const { + return other.hostname == hostname && + other.address_family == address_family; + } + + bool operator<(const Key& other) const { + if (address_family < other.address_family) + return true; + return hostname < other.hostname; + } + + std::string hostname; + AddressFamily address_family; + }; + + typedef std::map<Key, scoped_refptr<Entry> > EntryMap; // Constructs a HostCache whose entries are valid for |cache_duration_ms| // milliseconds. The cache will store up to |max_entries|. @@ -39,15 +59,15 @@ class HostCache { ~HostCache(); - // Returns a pointer to the entry for |hostname|, which is valid at time + // Returns a pointer to the entry for |key|, which is valid at time // |now|. If there is no such entry, returns NULL. - const Entry* Lookup(const std::string& hostname, base::TimeTicks now) const; + const Entry* Lookup(const Key& key, base::TimeTicks now) const; - // Overwrites or creates an entry for |hostname|. Returns the pointer to the + // Overwrites or creates an entry for |key|. Returns the pointer to the // entry, or NULL on failure (fails if caching is disabled). // (|error|, |addrlist|) is the value to set, and |now| is the current // timestamp. - Entry* Set(const std::string& hostname, + Entry* Set(const Key& key, int error, const AddressList addrlist, base::TimeTicks now); diff --git a/net/base/host_cache_unittest.cc b/net/base/host_cache_unittest.cc index 2259185..55742ba 100644 --- a/net/base/host_cache_unittest.cc +++ b/net/base/host_cache_unittest.cc @@ -12,10 +12,16 @@ namespace net { namespace { -static const int kMaxCacheEntries = 10; -static const int kCacheDurationMs = 10000; // 10 seconds. +const int kMaxCacheEntries = 10; +const int kCacheDurationMs = 10000; // 10 seconds. + +// Builds a key for |hostname|, defaulting the address family to unspecified. +HostCache::Key Key(const std::string& hostname) { + return HostCache::Key(hostname, ADDRESS_FAMILY_UNSPECIFIED); } +} // namespace + TEST(HostCacheTest, Basic) { HostCache cache(kMaxCacheEntries, kCacheDurationMs); @@ -28,9 +34,9 @@ TEST(HostCacheTest, Basic) { EXPECT_EQ(0U, cache.size()); // Add an entry for "foobar.com" at t=0. - EXPECT_TRUE(cache.Lookup("foobar.com", base::TimeTicks()) == NULL); - cache.Set("foobar.com", OK, AddressList(), now); - entry1 = cache.Lookup("foobar.com", base::TimeTicks()); + EXPECT_TRUE(cache.Lookup(Key("foobar.com"), base::TimeTicks()) == NULL); + cache.Set(Key("foobar.com"), OK, AddressList(), now); + entry1 = cache.Lookup(Key("foobar.com"), base::TimeTicks()); EXPECT_FALSE(entry1 == NULL); EXPECT_EQ(1U, cache.size()); @@ -38,9 +44,9 @@ TEST(HostCacheTest, Basic) { now += base::TimeDelta::FromSeconds(5); // Add an entry for "foobar2.com" at t=5. - EXPECT_TRUE(cache.Lookup("foobar2.com", base::TimeTicks()) == NULL); - cache.Set("foobar2.com", OK, AddressList(), now); - entry2 = cache.Lookup("foobar2.com", base::TimeTicks()); + EXPECT_TRUE(cache.Lookup(Key("foobar2.com"), base::TimeTicks()) == NULL); + cache.Set(Key("foobar2.com"), OK, AddressList(), now); + entry2 = cache.Lookup(Key("foobar2.com"), base::TimeTicks()); EXPECT_FALSE(NULL == entry1); EXPECT_EQ(2U, cache.size()); @@ -48,30 +54,30 @@ TEST(HostCacheTest, Basic) { now += base::TimeDelta::FromSeconds(4); // Verify that the entries we added are still retrievable, and usable. - EXPECT_EQ(entry1, cache.Lookup("foobar.com", now)); - EXPECT_EQ(entry2, cache.Lookup("foobar2.com", now)); + EXPECT_EQ(entry1, cache.Lookup(Key("foobar.com"), now)); + EXPECT_EQ(entry2, cache.Lookup(Key("foobar2.com"), now)); // Advance to t=10; entry1 is now expired. now += base::TimeDelta::FromSeconds(1); - EXPECT_TRUE(cache.Lookup("foobar.com", now) == NULL); - EXPECT_EQ(entry2, cache.Lookup("foobar2.com", now)); + EXPECT_TRUE(cache.Lookup(Key("foobar.com"), now) == NULL); + EXPECT_EQ(entry2, cache.Lookup(Key("foobar2.com"), now)); // Update entry1, so it is no longer expired. - cache.Set("foobar.com", OK, AddressList(), now); + cache.Set(Key("foobar.com"), OK, AddressList(), now); // Re-uses existing entry storage. - EXPECT_EQ(entry1, cache.Lookup("foobar.com", now)); + EXPECT_EQ(entry1, cache.Lookup(Key("foobar.com"), now)); EXPECT_EQ(2U, cache.size()); // Both entries should still be retrievable and usable. - EXPECT_EQ(entry1, cache.Lookup("foobar.com", now)); - EXPECT_EQ(entry2, cache.Lookup("foobar2.com", now)); + EXPECT_EQ(entry1, cache.Lookup(Key("foobar.com"), now)); + EXPECT_EQ(entry2, cache.Lookup(Key("foobar2.com"), now)); // Advance to t=20; both entries are now expired. now += base::TimeDelta::FromSeconds(10); - EXPECT_TRUE(cache.Lookup("foobar.com", now) == NULL); - EXPECT_TRUE(cache.Lookup("foobar2.com", now) == NULL); + EXPECT_TRUE(cache.Lookup(Key("foobar.com"), now) == NULL); + EXPECT_TRUE(cache.Lookup(Key("foobar2.com"), now) == NULL); } // Try caching entries for a failed resolve attempt. @@ -81,24 +87,24 @@ TEST(HostCacheTest, NegativeEntry) { // Set t=0. base::TimeTicks now; - EXPECT_TRUE(cache.Lookup("foobar.com", base::TimeTicks()) == NULL); - cache.Set("foobar.com", ERR_NAME_NOT_RESOLVED, AddressList(), now); + EXPECT_TRUE(cache.Lookup(Key("foobar.com"), base::TimeTicks()) == NULL); + cache.Set(Key("foobar.com"), ERR_NAME_NOT_RESOLVED, AddressList(), now); EXPECT_EQ(1U, cache.size()); // We disallow use of negative entries. - EXPECT_TRUE(cache.Lookup("foobar.com", now) == NULL); + EXPECT_TRUE(cache.Lookup(Key("foobar.com"), now) == NULL); // Now overwrite with a valid entry, and then overwrite with negative entry // again -- the valid entry should be kicked out. - cache.Set("foobar.com", OK, AddressList(), now); - EXPECT_FALSE(cache.Lookup("foobar.com", now) == NULL); - cache.Set("foobar.com", ERR_NAME_NOT_RESOLVED, AddressList(), now); - EXPECT_TRUE(cache.Lookup("foobar.com", now) == NULL); + cache.Set(Key("foobar.com"), OK, AddressList(), now); + EXPECT_FALSE(cache.Lookup(Key("foobar.com"), now) == NULL); + cache.Set(Key("foobar.com"), ERR_NAME_NOT_RESOLVED, AddressList(), now); + EXPECT_TRUE(cache.Lookup(Key("foobar.com"), now) == NULL); } TEST(HostCacheTest, Compact) { // Initial entries limit is big enough to accomadate everything we add. - net::HostCache cache(kMaxCacheEntries, kCacheDurationMs); + HostCache cache(kMaxCacheEntries, kCacheDurationMs); EXPECT_EQ(0U, cache.size()); @@ -108,7 +114,7 @@ TEST(HostCacheTest, Compact) { // Add five valid entries at t=10. for (int i = 0; i < 5; ++i) { std::string hostname = StringPrintf("valid%d", i); - cache.Set(hostname, OK, AddressList(), now); + cache.Set(Key(hostname), OK, AddressList(), now); } EXPECT_EQ(5U, cache.size()); @@ -116,27 +122,27 @@ TEST(HostCacheTest, Compact) { for (int i = 0; i < 3; ++i) { std::string hostname = StringPrintf("expired%d", i); base::TimeTicks t = now - base::TimeDelta::FromSeconds(10); - cache.Set(hostname, OK, AddressList(), t); + cache.Set(Key(hostname), OK, AddressList(), t); } EXPECT_EQ(8U, cache.size()); // Add 2 negative entries at t=10 for (int i = 0; i < 2; ++i) { std::string hostname = StringPrintf("negative%d", i); - cache.Set(hostname, ERR_NAME_NOT_RESOLVED, AddressList(), now); + cache.Set(Key(hostname), ERR_NAME_NOT_RESOLVED, AddressList(), now); } EXPECT_EQ(10U, cache.size()); - EXPECT_TRUE(ContainsKey(cache.entries_, "valid0")); - EXPECT_TRUE(ContainsKey(cache.entries_, "valid1")); - EXPECT_TRUE(ContainsKey(cache.entries_, "valid2")); - EXPECT_TRUE(ContainsKey(cache.entries_, "valid3")); - EXPECT_TRUE(ContainsKey(cache.entries_, "valid4")); - EXPECT_TRUE(ContainsKey(cache.entries_, "expired0")); - EXPECT_TRUE(ContainsKey(cache.entries_, "expired1")); - EXPECT_TRUE(ContainsKey(cache.entries_, "expired2")); - EXPECT_TRUE(ContainsKey(cache.entries_, "negative0")); - EXPECT_TRUE(ContainsKey(cache.entries_, "negative1")); + EXPECT_TRUE(ContainsKey(cache.entries_, Key("valid0"))); + EXPECT_TRUE(ContainsKey(cache.entries_, Key("valid1"))); + EXPECT_TRUE(ContainsKey(cache.entries_, Key("valid2"))); + EXPECT_TRUE(ContainsKey(cache.entries_, Key("valid3"))); + EXPECT_TRUE(ContainsKey(cache.entries_, Key("valid4"))); + EXPECT_TRUE(ContainsKey(cache.entries_, Key("expired0"))); + EXPECT_TRUE(ContainsKey(cache.entries_, Key("expired1"))); + EXPECT_TRUE(ContainsKey(cache.entries_, Key("expired2"))); + EXPECT_TRUE(ContainsKey(cache.entries_, Key("negative0"))); + EXPECT_TRUE(ContainsKey(cache.entries_, Key("negative1"))); // Shrink the max constraints bound and compact. We expect the "negative" // and "expired" entries to have been dropped. @@ -144,16 +150,16 @@ TEST(HostCacheTest, Compact) { cache.Compact(now, NULL); EXPECT_EQ(5U, cache.entries_.size()); - EXPECT_TRUE(ContainsKey(cache.entries_, "valid0")); - EXPECT_TRUE(ContainsKey(cache.entries_, "valid1")); - EXPECT_TRUE(ContainsKey(cache.entries_, "valid2")); - EXPECT_TRUE(ContainsKey(cache.entries_, "valid3")); - EXPECT_TRUE(ContainsKey(cache.entries_, "valid4")); - EXPECT_FALSE(ContainsKey(cache.entries_, "expired0")); - EXPECT_FALSE(ContainsKey(cache.entries_, "expired1")); - EXPECT_FALSE(ContainsKey(cache.entries_, "expired2")); - EXPECT_FALSE(ContainsKey(cache.entries_, "negative0")); - EXPECT_FALSE(ContainsKey(cache.entries_, "negative1")); + EXPECT_TRUE(ContainsKey(cache.entries_, Key("valid0"))); + EXPECT_TRUE(ContainsKey(cache.entries_, Key("valid1"))); + EXPECT_TRUE(ContainsKey(cache.entries_, Key("valid2"))); + EXPECT_TRUE(ContainsKey(cache.entries_, Key("valid3"))); + EXPECT_TRUE(ContainsKey(cache.entries_, Key("valid4"))); + EXPECT_FALSE(ContainsKey(cache.entries_, Key("expired0"))); + EXPECT_FALSE(ContainsKey(cache.entries_, Key("expired1"))); + EXPECT_FALSE(ContainsKey(cache.entries_, Key("expired2"))); + EXPECT_FALSE(ContainsKey(cache.entries_, Key("negative0"))); + EXPECT_FALSE(ContainsKey(cache.entries_, Key("negative1"))); // Shrink further -- this time the compact will start dropping valid entries // to make space. @@ -164,39 +170,74 @@ TEST(HostCacheTest, Compact) { // Add entries while the cache is at capacity, causing evictions. TEST(HostCacheTest, SetWithCompact) { - net::HostCache cache(3, kCacheDurationMs); + HostCache cache(3, kCacheDurationMs); // t=10 base::TimeTicks now = base::TimeTicks() + base::TimeDelta::FromMilliseconds(kCacheDurationMs); - cache.Set("host1", OK, AddressList(), now); - cache.Set("host2", OK, AddressList(), now); - cache.Set("expired", OK, AddressList(), + cache.Set(Key("host1"), OK, AddressList(), now); + cache.Set(Key("host2"), OK, AddressList(), now); + cache.Set(Key("expired"), OK, AddressList(), now - base::TimeDelta::FromMilliseconds(kCacheDurationMs)); EXPECT_EQ(3U, cache.size()); // Should all be retrievable except "expired". - EXPECT_FALSE(NULL == cache.Lookup("host1", now)); - EXPECT_FALSE(NULL == cache.Lookup("host2", now)); - EXPECT_TRUE(NULL == cache.Lookup("expired", now)); + EXPECT_FALSE(NULL == cache.Lookup(Key("host1"), now)); + EXPECT_FALSE(NULL == cache.Lookup(Key("host2"), now)); + EXPECT_TRUE(NULL == cache.Lookup(Key("expired"), now)); // Adding the fourth entry will cause "expired" to be evicted. - cache.Set("host3", OK, AddressList(), now); + cache.Set(Key("host3"), OK, AddressList(), now); EXPECT_EQ(3U, cache.size()); - EXPECT_TRUE(cache.Lookup("expired", now) == NULL); - EXPECT_FALSE(cache.Lookup("host1", now) == NULL); - EXPECT_FALSE(cache.Lookup("host2", now) == NULL); - EXPECT_FALSE(cache.Lookup("host3", now) == NULL); + EXPECT_TRUE(cache.Lookup(Key("expired"), now) == NULL); + EXPECT_FALSE(cache.Lookup(Key("host1"), now) == NULL); + EXPECT_FALSE(cache.Lookup(Key("host2"), now) == NULL); + EXPECT_FALSE(cache.Lookup(Key("host3"), now) == NULL); // Add two more entries. Something should be evicted, however "host5" // should definitely be in there (since it was last inserted). - cache.Set("host4", OK, AddressList(), now); + cache.Set(Key("host4"), OK, AddressList(), now); EXPECT_EQ(3U, cache.size()); - cache.Set("host5", OK, AddressList(), now); + cache.Set(Key("host5"), OK, AddressList(), now); EXPECT_EQ(3U, cache.size()); - EXPECT_FALSE(cache.Lookup("host5", now) == NULL); + EXPECT_FALSE(cache.Lookup(Key("host5"), now) == NULL); +} + +// Tests that the same hostname can be duplicated in the cache, so long as +// the address family differs. +TEST(HostCacheTest, AddressFamilyIsPartOfKey) { + HostCache cache(kMaxCacheEntries, kCacheDurationMs); + + // t=0. + base::TimeTicks now; + + HostCache::Key key1("foobar.com", ADDRESS_FAMILY_UNSPECIFIED); + HostCache::Key key2("foobar.com", ADDRESS_FAMILY_IPV4_ONLY); + + const HostCache::Entry* entry1 = NULL; // Entry for key1 + const HostCache::Entry* entry2 = NULL; // Entry for key2 + + EXPECT_EQ(0U, cache.size()); + + // Add an entry for ("foobar.com", UNSPECIFIED) at t=0. + EXPECT_TRUE(cache.Lookup(key1, base::TimeTicks()) == NULL); + cache.Set(key1, OK, AddressList(), now); + entry1 = cache.Lookup(key1, base::TimeTicks()); + EXPECT_FALSE(entry1 == NULL); + EXPECT_EQ(1U, cache.size()); + + // Add an entry for ("foobar.com", IPV4_ONLY) at t=0. + EXPECT_TRUE(cache.Lookup(key2, base::TimeTicks()) == NULL); + cache.Set(key2, OK, AddressList(), now); + entry2 = cache.Lookup(key2, base::TimeTicks()); + EXPECT_FALSE(entry2 == NULL); + EXPECT_EQ(2U, cache.size()); + + // Even though the hostnames were the same, we should have two unique + // entries (because the address families differ). + EXPECT_NE(entry1, entry2); } TEST(HostCacheTest, NoCache) { @@ -208,9 +249,9 @@ TEST(HostCacheTest, NoCache) { base::TimeTicks now; // Lookup and Set should have no effect. - EXPECT_TRUE(cache.Lookup("foobar.com", base::TimeTicks()) == NULL); - cache.Set("foobar.com", OK, AddressList(), now); - EXPECT_TRUE(cache.Lookup("foobar.com", base::TimeTicks()) == NULL); + EXPECT_TRUE(cache.Lookup(Key("foobar.com"), base::TimeTicks()) == NULL); + cache.Set(Key("foobar.com"), OK, AddressList(), now); + EXPECT_TRUE(cache.Lookup(Key("foobar.com"), base::TimeTicks()) == NULL); EXPECT_EQ(0U, cache.size()); } diff --git a/net/base/host_resolver.h b/net/base/host_resolver.h index fe91b4d..4829c1c 100644 --- a/net/base/host_resolver.h +++ b/net/base/host_resolver.h @@ -9,6 +9,7 @@ #include "base/ref_counted.h" #include "googleurl/src/gurl.h" +#include "net/base/address_family.h" #include "net/base/completion_callback.h" class MessageLoop; @@ -36,6 +37,7 @@ class HostResolver : public base::RefCountedThreadSafe<HostResolver> { public: RequestInfo(const std::string& hostname, int port) : hostname_(hostname), + address_family_(ADDRESS_FAMILY_UNSPECIFIED), port_(port), allow_cached_response_(true), is_speculative_(false) {} @@ -43,6 +45,11 @@ class HostResolver : public base::RefCountedThreadSafe<HostResolver> { const int port() const { return port_; } const std::string& hostname() const { return hostname_; } + AddressFamily address_family() const { return address_family_; } + void set_address_family(AddressFamily address_family) { + address_family_ = address_family; + } + bool allow_cached_response() const { return allow_cached_response_; } void set_allow_cached_response(bool b) { allow_cached_response_ = b; } @@ -56,6 +63,9 @@ class HostResolver : public base::RefCountedThreadSafe<HostResolver> { // The hostname to resolve. std::string hostname_; + // The address family to restrict results to. + AddressFamily address_family_; + // The port number to set in the result's sockaddrs. int port_; @@ -138,6 +148,9 @@ class HostResolver : public base::RefCountedThreadSafe<HostResolver> { // TODO(eroman): temp hack for http://crbug.com/18373 virtual void Shutdown() = 0; + // Disables or enables support for IPv6 results. + virtual void DisableIPv6(bool disable_ipv6) {} + protected: HostResolver() { } diff --git a/net/base/host_resolver_impl.cc b/net/base/host_resolver_impl.cc index c93c8eb..e3cfcd3 100644 --- a/net/base/host_resolver_impl.cc +++ b/net/base/host_resolver_impl.cc @@ -41,13 +41,15 @@ HostResolver* CreateSystemHostResolver() { } static int ResolveAddrInfo(HostResolverProc* resolver_proc, - const std::string& host, AddressList* out) { + const std::string& host, + AddressFamily address_family, + AddressList* out) { if (resolver_proc) { // Use the custom procedure. - return resolver_proc->Resolve(host, out); + return resolver_proc->Resolve(host, address_family, out); } else { // Use the system procedure (getaddrinfo). - return SystemHostResolverProc(host, out); + return SystemHostResolverProc(host, address_family, out); } } @@ -142,8 +144,8 @@ class HostResolverImpl::Request { class HostResolverImpl::Job : public base::RefCountedThreadSafe<HostResolverImpl::Job> { public: - Job(HostResolverImpl* resolver, const std::string& host) - : host_(host), + Job(HostResolverImpl* resolver, const Key& key) + : key_(key), resolver_(resolver), origin_loop_(MessageLoop::current()), resolver_proc_(resolver->effective_resolver_proc()), @@ -206,8 +208,8 @@ class HostResolverImpl::Job } // Called from origin thread. - const std::string& host() const { - return host_; + const Key& key() const { + return key_; } // Called from origin thread. @@ -218,7 +220,10 @@ class HostResolverImpl::Job private: void DoLookup() { // Running on the worker thread - error_ = ResolveAddrInfo(resolver_proc_, host_, &results_); + error_ = ResolveAddrInfo(resolver_proc_, + key_.hostname, + key_.address_family, + &results_); Task* reply = NewRunnableMethod(this, &Job::OnLookupComplete); @@ -257,7 +262,7 @@ class HostResolverImpl::Job } // Set on the origin thread, read on the worker thread. - std::string host_; + Key key_; // Only used on the origin thread (where Resolve was called). HostResolverImpl* resolver_; @@ -285,8 +290,11 @@ class HostResolverImpl::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) { + : cache_(max_cache_entries, cache_duration_ms), + next_request_id_(0), + resolver_proc_(resolver_proc), + disable_ipv6_(false), + shutdown_(false) { #if defined(OS_WIN) EnsureWinsockInit(); #endif @@ -319,10 +327,16 @@ int HostResolverImpl::Resolve(const RequestInfo& info, // Update the load log and notify registered observers. OnStartRequest(load_log, request_id, info); + // Build a key that identifies the request in the cache and in the + // outstanding jobs map. + Key key(info.hostname(), info.address_family()); + if (disable_ipv6_) + key.address_family = ADDRESS_FAMILY_IPV4_ONLY; + // 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()); + key, base::TimeTicks::Now()); if (cache_entry) { addresses->SetFrom(cache_entry->addrlist, info.port()); int error = cache_entry->error; @@ -338,14 +352,14 @@ int HostResolverImpl::Resolve(const RequestInfo& info, if (!callback) { AddressList addrlist; int error = ResolveAddrInfo( - effective_resolver_proc(), info.hostname(), &addrlist); + effective_resolver_proc(), key.hostname, key.address_family, &addrlist); if (error == OK) { addrlist.SetPort(info.port()); *addresses = addrlist; } // Write to cache. - cache_.Set(info.hostname(), error, addrlist, base::TimeTicks::Now()); + cache_.Set(key, error, addrlist, base::TimeTicks::Now()); // Update the load log and notify registered observers. OnFinishRequest(load_log, request_id, info, error); @@ -363,14 +377,14 @@ int HostResolverImpl::Resolve(const RequestInfo& info, // calling "getaddrinfo(hostname)" on a worker thread. scoped_refptr<Job> job; - // If there is already an outstanding job to resolve |info.hostname()|, use + // If there is already an outstanding job to resolve |key|, use // it. This prevents starting concurrent resolves for the same hostname. - job = FindOutstandingJob(info.hostname()); + job = FindOutstandingJob(key); if (job) { job->AddRequest(req); } else { // Create a new job for this request. - job = new Job(this, info.hostname()); + job = new Job(this, key); job->AddRequest(req); AddOutstandingJob(job); // TODO(eroman): Bound the total number of concurrent jobs. @@ -431,21 +445,20 @@ void HostResolverImpl::Shutdown() { } void HostResolverImpl::AddOutstandingJob(Job* job) { - scoped_refptr<Job>& found_job = jobs_[job->host()]; + scoped_refptr<Job>& found_job = jobs_[job->key()]; DCHECK(!found_job); found_job = job; } -HostResolverImpl::Job* HostResolverImpl::FindOutstandingJob( - const std::string& hostname) { - JobMap::iterator it = jobs_.find(hostname); +HostResolverImpl::Job* HostResolverImpl::FindOutstandingJob(const Key& key) { + JobMap::iterator it = jobs_.find(key); if (it != jobs_.end()) return it->second; return NULL; } void HostResolverImpl::RemoveOutstandingJob(Job* job) { - JobMap::iterator it = jobs_.find(job->host()); + JobMap::iterator it = jobs_.find(job->key()); DCHECK(it != jobs_.end()); DCHECK_EQ(it->second.get(), job); jobs_.erase(it); @@ -457,7 +470,7 @@ void HostResolverImpl::OnJobComplete(Job* job, RemoveOutstandingJob(job); // Write result to the cache. - cache_.Set(job->host(), error, addrlist, base::TimeTicks::Now()); + cache_.Set(job->key(), error, addrlist, base::TimeTicks::Now()); // Make a note that we are executing within OnJobComplete() in case the // HostResolver is deleted by a callback invocation. diff --git a/net/base/host_resolver_impl.h b/net/base/host_resolver_impl.h index 74dea08..b641b0b 100644 --- a/net/base/host_resolver_impl.h +++ b/net/base/host_resolver_impl.h @@ -27,7 +27,7 @@ namespace net { // +----------- HostResolverImpl -------------+ // | | | // Job Job Job -// (for host1) (for host2) (for hostX) +// (for host1, fam1) (for host2, fam2) (for hostx, famx) // / | | / | | / | | // Request ... Request Request ... Request Request ... Request // (port1) (port2) (port3) (port4) (port5) (portX) @@ -70,11 +70,18 @@ class HostResolverImpl : public HostResolver { // TODO(eroman): temp hack for http://crbug.com/15513 virtual void Shutdown(); + // Prevents returning IPv6 addresses from Resolve(). + // The default is to allow IPv6 results. + virtual void DisableIPv6(bool disable_ipv6) { + disable_ipv6_ = disable_ipv6; + } + private: class Job; class Request; typedef std::vector<Request*> RequestsList; - typedef base::hash_map<std::string, scoped_refptr<Job> > JobMap; + typedef HostCache::Key Key; + typedef std::map<Key, scoped_refptr<Job> > JobMap; typedef std::vector<Observer*> ObserversList; // Returns the HostResolverProc to use for this instance. @@ -86,8 +93,8 @@ class HostResolverImpl : public HostResolver { // 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); + // Returns the outstanding job for |key|, or NULL if there is none. + Job* FindOutstandingJob(const Key& key); // Removes |job| from the outstanding jobs list. void RemoveOutstandingJob(Job* job); @@ -132,6 +139,9 @@ class HostResolverImpl : public HostResolver { // in the case of unit-tests which inject custom host resolving behaviors. scoped_refptr<HostResolverProc> resolver_proc_; + // Set to true if only IPv4 address are to be returned by Resolve(). + bool disable_ipv6_; + // TODO(eroman): temp hack for http://crbug.com/15513 bool shutdown_; diff --git a/net/base/host_resolver_impl_unittest.cc b/net/base/host_resolver_impl_unittest.cc index fd35d88..eee6fd1 100644 --- a/net/base/host_resolver_impl_unittest.cc +++ b/net/base/host_resolver_impl_unittest.cc @@ -46,13 +46,15 @@ class CapturingHostResolverProc : public HostResolverProc { event_.Signal(); } - virtual int Resolve(const std::string& host, AddressList* addrlist) { + virtual int Resolve(const std::string& host, + AddressFamily address_family, + AddressList* addrlist) { event_.Wait(); { AutoLock l(lock_); capture_list_.push_back(host); } - return ResolveUsingPrevious(host, addrlist); + return ResolveUsingPrevious(host, address_family, addrlist); } std::vector<std::string> GetCaptureList() const { diff --git a/net/base/host_resolver_proc.cc b/net/base/host_resolver_proc.cc index 8951646..af92087 100644 --- a/net/base/host_resolver_proc.cc +++ b/net/base/host_resolver_proc.cc @@ -52,12 +52,13 @@ HostResolverProc* HostResolverProc::GetDefault() { } int HostResolverProc::ResolveUsingPrevious(const std::string& host, + AddressFamily address_family, AddressList* addrlist) { if (previous_proc_) - return previous_proc_->Resolve(host, addrlist); + return previous_proc_->Resolve(host, address_family, addrlist); // Final fallback is the system resolver. - return SystemHostResolverProc(host, addrlist); + return SystemHostResolverProc(host, address_family, addrlist); } #if defined(OS_LINUX) @@ -124,7 +125,9 @@ ThreadLocalStorage::Slot DnsReloadTimer::tls_index_(base::LINKER_INITIALIZED); #endif // defined(OS_LINUX) -int SystemHostResolverProc(const std::string& host, AddressList* addrlist) { +int SystemHostResolverProc(const std::string& host, + AddressFamily address_family, + AddressList* addrlist) { // The result of |getaddrinfo| for empty hosts is inconsistent across systems. // On Windows it gives the default interface's address, whereas on Linux it // gives an error. We will make it fail on all platforms for consistency. @@ -133,7 +136,14 @@ int SystemHostResolverProc(const std::string& host, AddressList* addrlist) { struct addrinfo* ai = NULL; struct addrinfo hints = {0}; - hints.ai_family = AF_UNSPEC; + + switch (address_family) { + case ADDRESS_FAMILY_IPV4_ONLY: + hints.ai_family = AF_INET; + break; + default: + hints.ai_family = AF_UNSPEC; + } #if defined(OS_WIN) // DO NOT USE AI_ADDRCONFIG ON WINDOWS. diff --git a/net/base/host_resolver_proc.h b/net/base/host_resolver_proc.h index 66978f7..8507e03 100644 --- a/net/base/host_resolver_proc.h +++ b/net/base/host_resolver_proc.h @@ -8,6 +8,7 @@ #include <string> #include "base/ref_counted.h" +#include "net/base/address_family.h" namespace net { @@ -25,14 +26,18 @@ class HostResolverProc : public base::RefCountedThreadSafe<HostResolverProc> { 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; + // Resolves |host| to an address list, restricting the results to addresses + // in |address_family|. 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, + AddressFamily address_family, + AddressList* addrlist) = 0; protected: // Asks the fallback procedure (if set) to do the resolve. - int ResolveUsingPrevious(const std::string& host, AddressList* addrlist); + int ResolveUsingPrevious(const std::string& host, + AddressFamily address_family, + AddressList* addrlist); private: friend class HostResolverImpl; @@ -62,7 +67,9 @@ class HostResolverProc : public base::RefCountedThreadSafe<HostResolverProc> { // (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); +int SystemHostResolverProc(const std::string& host, + AddressFamily address_family, + AddressList* addrlist); } // namespace net diff --git a/net/base/mock_host_resolver.cc b/net/base/mock_host_resolver.cc index addccd0f..c6d31e9 100644 --- a/net/base/mock_host_resolver.cc +++ b/net/base/mock_host_resolver.cc @@ -165,6 +165,7 @@ void RuleBasedHostResolverProc::AddSimulatedFailure( } int RuleBasedHostResolverProc::Resolve(const std::string& host, + AddressFamily address_family, AddressList* addrlist) { RuleList::iterator r; for (r = rules_.begin(); r != rules_.end(); ++r) { @@ -181,7 +182,9 @@ int RuleBasedHostResolverProc::Resolve(const std::string& host, case Rule::kResolverTypeFail: return ERR_NAME_NOT_RESOLVED; case Rule::kResolverTypeSystem: - return SystemHostResolverProc(effective_host, addrlist); + return SystemHostResolverProc(effective_host, + address_family, + addrlist); case Rule::kResolverTypeIPV6Literal: return ResolveIPV6LiteralUsingGURL(effective_host, addrlist); default: @@ -190,7 +193,7 @@ int RuleBasedHostResolverProc::Resolve(const std::string& host, } } } - return ResolveUsingPrevious(host, addrlist); + return ResolveUsingPrevious(host, address_family, addrlist); } //----------------------------------------------------------------------------- diff --git a/net/base/mock_host_resolver.h b/net/base/mock_host_resolver.h index 01a2eab..de91576 100644 --- a/net/base/mock_host_resolver.h +++ b/net/base/mock_host_resolver.h @@ -121,7 +121,9 @@ class RuleBasedHostResolverProc : public HostResolverProc { void AddSimulatedFailure(const std::string& host); // HostResolverProc methods: - virtual int Resolve(const std::string& host, AddressList* addrlist); + virtual int Resolve(const std::string& host, + AddressFamily address_family, + AddressList* addrlist); private: struct Rule; @@ -141,9 +143,11 @@ class WaitingHostResolverProc : public HostResolverProc { } // HostResolverProc methods: - virtual int Resolve(const std::string& host, AddressList* addrlist) { + virtual int Resolve(const std::string& host, + AddressFamily address_family, + AddressList* addrlist) { event_.Wait(); - return ResolveUsingPrevious(host, addrlist); + return ResolveUsingPrevious(host, address_family, addrlist); } base::WaitableEvent event_; diff --git a/net/net.gyp b/net/net.gyp index f672009..298d0a7 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -24,6 +24,7 @@ ], 'msvs_guid': '326E9795-E760-410A-B69A-3F79DB3F5243', 'sources': [ + 'base/address_family.h', 'base/address_list.cc', 'base/address_list.h', 'base/auth.h', diff --git a/net/url_request/url_request_view_net_internals_job.cc b/net/url_request/url_request_view_net_internals_job.cc index 56aa8a8..5cadc075 100644 --- a/net/url_request/url_request_view_net_internals_job.cc +++ b/net/url_request/url_request_view_net_internals_job.cc @@ -225,6 +225,7 @@ class HostResolverCacheSubSection : public SubSection { out->append("<table border=1>" "<tr>" "<th>Host</th>" + "<th>Address family</th>" "<th>Address list</th>" "<th>Time to live (ms)</th>" "</tr>"); @@ -233,9 +234,12 @@ class HostResolverCacheSubSection : public SubSection { host_cache->entries().begin(); it != host_cache->entries().end(); ++it) { - const std::string& host = it->first; + const net::HostCache::Key& key = it->first; const net::HostCache::Entry* entry = it->second.get(); + std::string address_family_str = + AddressFamilyToString(key.address_family); + if (entry->error == net::OK) { // Note that ttl_ms may be negative, for the cases where entries have // expired but not been garbage collected yet. @@ -261,23 +265,35 @@ class HostResolverCacheSubSection : public SubSection { current_address = current_address->ai_next; } - out->append(StringPrintf("<td>%s</td><td>%s</td><td>%d</td></tr>", - EscapeForHTML(host).c_str(), + out->append(StringPrintf("<td>%s</td><td>%s</td><td>%s</td>" + "<td>%d</td></tr>", + EscapeForHTML(key.hostname).c_str(), + EscapeForHTML(address_family_str).c_str(), address_list_html.c_str(), ttl_ms)); } else { // This was an entry that failed to be resolved. // Color negative entries red. out->append(StringPrintf( - "<tr style='color:red'><td>%s</td>" + "<tr style='color:red'><td>%s</td><td>%s</td>" "<td colspan=2>%s</td></tr>", - EscapeForHTML(host).c_str(), + EscapeForHTML(key.hostname).c_str(), + EscapeForHTML(address_family_str).c_str(), EscapeForHTML(net::ErrorToString(entry->error)).c_str())); } } out->append("</table>"); } + + static std::string AddressFamilyToString(net::AddressFamily address_family) { + switch (address_family) { + case net::ADDRESS_FAMILY_IPV4_ONLY: + return "IPV4_ONLY"; + default: + return "UNSPECIFIED"; + } + } }; class HostResolverSubSection : public SubSection { |