summaryrefslogtreecommitdiffstats
path: root/net/spdy
diff options
context:
space:
mode:
authormbelshe@chromium.org <mbelshe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-03-25 05:33:02 +0000
committermbelshe@chromium.org <mbelshe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-03-25 05:33:02 +0000
commit8b114dd781075604530544cf60e665fa4306903c (patch)
treecaa2d8ef403d08ffab8dac31a126d84947990b1c /net/spdy
parentbd0875ed222d58f51816ebb08e6203ef94053276 (diff)
downloadchromium_src-8b114dd781075604530544cf60e665fa4306903c.zip
chromium_src-8b114dd781075604530544cf60e665fa4306903c.tar.gz
chromium_src-8b114dd781075604530544cf60e665fa4306903c.tar.bz2
Enable IP pooling for SPDY.
Added a command-line switch: --enable-ip-pooling BUG=42669 TEST=SpdySessionTest.IPPool* Review URL: http://codereview.chromium.org/6594116 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@79372 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/spdy')
-rw-r--r--net/spdy/spdy_session.cc12
-rw-r--r--net/spdy/spdy_session.h7
-rw-r--r--net/spdy/spdy_session_pool.cc214
-rw-r--r--net/spdy/spdy_session_pool.h37
-rw-r--r--net/spdy/spdy_session_unittest.cc103
-rw-r--r--net/spdy/spdy_test_util.cc2
6 files changed, 349 insertions, 26 deletions
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc
index 9ab0155..048c837 100644
--- a/net/spdy/spdy_session.cc
+++ b/net/spdy/spdy_session.cc
@@ -302,6 +302,18 @@ net::Error SpdySession::InitializeWithSocket(
return error;
}
+bool SpdySession::VerifyDomainAuthentication(const std::string& domain) {
+ if (state_ != CONNECTED)
+ return false;
+
+ SSLInfo ssl_info;
+ bool was_npn_negotiated;
+ if (!GetSSLInfo(&ssl_info, &was_npn_negotiated))
+ return true; // This is not a secure session, so all domains are okay.
+
+ return ssl_info.cert->VerifyNameMatch(domain);
+}
+
int SpdySession::GetPushStream(
const GURL& url,
scoped_refptr<SpdyStream>* stream,
diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h
index 3afa7c0..b1abb13 100644
--- a/net/spdy/spdy_session.h
+++ b/net/spdy/spdy_session.h
@@ -97,6 +97,13 @@ class SpdySession : public base::RefCounted<SpdySession>,
bool is_secure,
int certificate_error_code);
+ // Check to see if this SPDY session can support an additional domain.
+ // If the session is un-authenticated, then this call always returns true.
+ // For SSL-based sessions, verifies that the certificate in use by this
+ // session provides authentication for the domain.
+ // NOTE: This function can have false negatives on some platforms.
+ bool VerifyDomainAuthentication(const std::string& domain);
+
// Send the SYN frame for |stream_id|.
int WriteSynStream(
spdy::SpdyStreamId stream_id,
diff --git a/net/spdy/spdy_session_pool.cc b/net/spdy/spdy_session_pool.cc
index 0220d11..de83271 100644
--- a/net/spdy/spdy_session_pool.cc
+++ b/net/spdy/spdy_session_pool.cc
@@ -5,20 +5,43 @@
#include "net/spdy/spdy_session_pool.h"
#include "base/logging.h"
+#include "base/metrics/histogram.h"
#include "base/values.h"
+#include "net/base/sys_addrinfo.h"
#include "net/http/http_network_session.h"
#include "net/spdy/spdy_session.h"
+
namespace net {
+namespace {
+
+enum SpdySessionGetTypes {
+ CREATED_NEW = 0,
+ FOUND_EXISTING = 1,
+ FOUND_EXISTING_FROM_IP_POOL = 2,
+ IMPORTED_FROM_SOCKET = 3,
+ SPDY_SESSION_GET_MAX = 4
+};
+
+bool HostPortProxyPairsAreEqual(const HostPortProxyPair& a,
+ const HostPortProxyPair& b) {
+ return a.first.Equals(b.first) && a.second == b.second;
+}
+
+}
+
// The maximum number of sessions to open to a single domain.
static const size_t kMaxSessionsPerDomain = 1;
-int SpdySessionPool::g_max_sessions_per_domain = kMaxSessionsPerDomain;
+size_t SpdySessionPool::g_max_sessions_per_domain = kMaxSessionsPerDomain;
bool SpdySessionPool::g_force_single_domain = false;
+bool SpdySessionPool::g_enable_ip_pooling = true;
-SpdySessionPool::SpdySessionPool(SSLConfigService* ssl_config_service)
- : ssl_config_service_(ssl_config_service) {
+SpdySessionPool::SpdySessionPool(HostResolver* resolver,
+ SSLConfigService* ssl_config_service)
+ : ssl_config_service_(ssl_config_service),
+ resolver_(resolver) {
NetworkChangeNotifier::AddIPAddressObserver(this);
if (ssl_config_service_)
ssl_config_service_->AddObserver(this);
@@ -39,32 +62,46 @@ scoped_refptr<SpdySession> SpdySessionPool::Get(
const BoundNetLog& net_log) {
scoped_refptr<SpdySession> spdy_session;
SpdySessionList* list = GetSessionList(host_port_proxy_pair);
- if (list) {
- if (list->size() >= static_cast<unsigned int>(g_max_sessions_per_domain)) {
- spdy_session = list->front();
- list->pop_front();
+ if (!list) {
+ // Check if we have a Session through a domain alias.
+ spdy_session = GetFromAlias(host_port_proxy_pair, net_log, true);
+ if (spdy_session) {
+ UMA_HISTOGRAM_ENUMERATION("Net.SpdySessionGet",
+ FOUND_EXISTING_FROM_IP_POOL,
+ SPDY_SESSION_GET_MAX);
net_log.AddEvent(
- NetLog::TYPE_SPDY_SESSION_POOL_FOUND_EXISTING_SESSION,
+ NetLog::TYPE_SPDY_SESSION_POOL_FOUND_EXISTING_SESSION_FROM_IP_POOL,
make_scoped_refptr(new NetLogSourceParameter(
- "session", spdy_session->net_log().source())));
+ "session", spdy_session->net_log().source())));
+ return spdy_session;
}
- } else {
list = AddSessionList(host_port_proxy_pair);
}
DCHECK(list);
- if (!spdy_session) {
- spdy_session = new SpdySession(host_port_proxy_pair, this, &spdy_settings_,
- net_log.net_log());
+ if (list->size() && list->size() == g_max_sessions_per_domain) {
+ UMA_HISTOGRAM_ENUMERATION("Net.SpdySessionGet",
+ FOUND_EXISTING,
+ SPDY_SESSION_GET_MAX);
+ spdy_session = GetExistingSession(list, net_log);
net_log.AddEvent(
- NetLog::TYPE_SPDY_SESSION_POOL_CREATED_NEW_SESSION,
- make_scoped_refptr(new NetLogSourceParameter(
- "session", spdy_session->net_log().source())));
+ NetLog::TYPE_SPDY_SESSION_POOL_FOUND_EXISTING_SESSION,
+ make_scoped_refptr(new NetLogSourceParameter(
+ "session", spdy_session->net_log().source())));
+ return spdy_session;
}
- DCHECK(spdy_session);
+ spdy_session = new SpdySession(host_port_proxy_pair, this, &spdy_settings_,
+ net_log.net_log());
+ UMA_HISTOGRAM_ENUMERATION("Net.SpdySessionGet",
+ CREATED_NEW,
+ SPDY_SESSION_GET_MAX);
list->push_back(spdy_session);
- DCHECK_LE(list->size(), static_cast<unsigned int>(g_max_sessions_per_domain));
+ net_log.AddEvent(
+ NetLog::TYPE_SPDY_SESSION_POOL_CREATED_NEW_SESSION,
+ make_scoped_refptr(new NetLogSourceParameter(
+ "session", spdy_session->net_log().source())));
+ DCHECK_LE(list->size(), g_max_sessions_per_domain);
return spdy_session;
}
@@ -75,6 +112,9 @@ net::Error SpdySessionPool::GetSpdySessionFromSocket(
int certificate_error_code,
scoped_refptr<SpdySession>* spdy_session,
bool is_secure) {
+ UMA_HISTOGRAM_ENUMERATION("Net.SpdySessionGet",
+ IMPORTED_FROM_SOCKET,
+ SPDY_SESSION_GET_MAX);
// Create the SPDY session and add it to the pool.
*spdy_session = new SpdySession(host_port_proxy_pair, this, &spdy_settings_,
net_log.net_log());
@@ -98,7 +138,11 @@ bool SpdySessionPool::HasSession(
const HostPortProxyPair& host_port_proxy_pair) const {
if (GetSessionList(host_port_proxy_pair))
return true;
- return false;
+
+ // Check if we have a session via an alias.
+ scoped_refptr<SpdySession> spdy_session =
+ GetFromAlias(host_port_proxy_pair, BoundNetLog(), false);
+ return spdy_session.get() != NULL;
}
void SpdySessionPool::Remove(const scoped_refptr<SpdySession>& session) {
@@ -138,6 +182,72 @@ void SpdySessionPool::OnSSLConfigChanged() {
CloseCurrentSessions();
}
+scoped_refptr<SpdySession> SpdySessionPool::GetExistingSession(
+ SpdySessionList* list,
+ const BoundNetLog& net_log) const {
+ DCHECK(list);
+ DCHECK_LT(0u, list->size());
+ scoped_refptr<SpdySession> spdy_session = list->front();
+ if (list->size() > 1) {
+ list->pop_front(); // Rotate the list.
+ list->push_back(spdy_session);
+ }
+
+ return spdy_session;
+}
+
+scoped_refptr<SpdySession> SpdySessionPool::GetFromAlias(
+ const HostPortProxyPair& host_port_proxy_pair,
+ const BoundNetLog& net_log,
+ bool record_histograms) const {
+ // We should only be checking aliases when there is no direct session.
+ DCHECK(!GetSessionList(host_port_proxy_pair));
+
+ if (!g_enable_ip_pooling)
+ return NULL;
+
+ AddressList addresses;
+ if (!LookupAddresses(host_port_proxy_pair, &addresses))
+ return NULL;
+ const addrinfo* address = addresses.head();
+ while (address) {
+ IPEndPoint endpoint;
+ endpoint.FromSockAddr(address->ai_addr, address->ai_addrlen);
+ address = address->ai_next;
+
+ SpdyAliasMap::const_iterator it = aliases_.find(endpoint);
+ if (it == aliases_.end())
+ continue;
+
+ // We found an alias.
+ const HostPortProxyPair& alias_pair = it->second;
+
+ // If the proxy settings match, we can reuse this session.
+ if (!(alias_pair.second == host_port_proxy_pair.second))
+ continue;
+
+ SpdySessionList* list = GetSessionList(alias_pair);
+ if (!list) {
+ NOTREACHED(); // It shouldn't be in the aliases table if we can't get it!
+ continue;
+ }
+
+ scoped_refptr<SpdySession> spdy_session = GetExistingSession(list, net_log);
+ // If the SPDY session is a secure one, we need to verify that the server
+ // is authenticated to serve traffic for |host_port_proxy_pair| too.
+ if (!spdy_session->VerifyDomainAuthentication(
+ host_port_proxy_pair.first.host())) {
+ if (record_histograms)
+ UMA_HISTOGRAM_ENUMERATION("Net.SpdyIPPoolDomainMatch", 0, 2);
+ continue;
+ }
+ if (record_histograms)
+ UMA_HISTOGRAM_ENUMERATION("Net.SpdyIPPoolDomainMatch", 1, 2);
+ return spdy_session;
+ }
+ return NULL;
+}
+
void SpdySessionPool::OnUserCertAdded(X509Certificate* cert) {
CloseCurrentSessions();
}
@@ -163,6 +273,16 @@ SpdySessionPool::SpdySessionList*
DCHECK(sessions_.find(pair) == sessions_.end());
SpdySessionPool::SpdySessionList* list = new SpdySessionList();
sessions_[pair] = list;
+
+ // We have a new session. Lookup the IP addresses for this session so that
+ // we can match future Sessions (potentially to different domains) which can
+ // potentially be pooled with this one.
+ if (g_enable_ip_pooling) {
+ AddressList addresses;
+ if (LookupAddresses(host_port_proxy_pair, &addresses))
+ AddAliases(addresses, host_port_proxy_pair);
+ }
+
return list;
}
@@ -171,9 +291,9 @@ SpdySessionPool::SpdySessionList*
const HostPortProxyPair& host_port_proxy_pair) const {
const HostPortProxyPair& pair = NormalizeListPair(host_port_proxy_pair);
SpdySessionsMap::const_iterator it = sessions_.find(pair);
- if (it == sessions_.end())
- return NULL;
- return it->second;
+ if (it != sessions_.end())
+ return it->second;
+ return NULL;
}
void SpdySessionPool::RemoveSessionList(
@@ -186,6 +306,56 @@ void SpdySessionPool::RemoveSessionList(
} else {
DCHECK(false) << "removing orphaned session list";
}
+ RemoveAliases(host_port_proxy_pair);
+}
+
+bool SpdySessionPool::LookupAddresses(const HostPortProxyPair& pair,
+ AddressList* addresses) const {
+ net::HostResolver::RequestInfo resolve_info(pair.first);
+ resolve_info.set_only_use_cached_response(true);
+ int rv = resolver_->Resolve(resolve_info,
+ addresses,
+ NULL,
+ NULL,
+ net::BoundNetLog());
+ DCHECK_NE(ERR_IO_PENDING, rv);
+ return rv == OK;
+}
+
+void SpdySessionPool::AddAliases(const AddressList& addresses,
+ const HostPortProxyPair& pair) {
+ // Note: it is possible to think of strange overlapping sets of ip addresses
+ // for hosts such that a new session can override the alias for an IP
+ // address that was previously aliased to a different host. This is probably
+ // undesirable, but seemingly unlikely and complicated to fix.
+ // Example:
+ // host1 = 1.1.1.1, 1.1.1.4
+ // host2 = 1.1.1.4, 1.1.1.5
+ // host3 = 1.1.1.3, 1.1.1.5
+ // Creating session1 (to host1), creates an alias for host2 to host1.
+ // Creating session2 (to host3), overrides the alias for host2 to host3.
+
+ const addrinfo* address = addresses.head();
+ while (address) {
+ IPEndPoint endpoint;
+ endpoint.FromSockAddr(address->ai_addr, address->ai_addrlen);
+ aliases_[endpoint] = pair;
+ address = address->ai_next;
+ }
+}
+
+void SpdySessionPool::RemoveAliases(const HostPortProxyPair& pair) {
+ // Walk the aliases map, find references to this pair.
+ // TODO(mbelshe): Figure out if this is too expensive.
+ SpdyAliasMap::iterator alias_it = aliases_.begin();
+ while (alias_it != aliases_.end()) {
+ if (HostPortProxyPairsAreEqual(alias_it->second, pair)) {
+ aliases_.erase(alias_it);
+ alias_it = aliases_.begin(); // Iterator was invalidated.
+ continue;
+ }
+ ++alias_it;
+ }
}
void SpdySessionPool::CloseAllSessions() {
diff --git a/net/spdy/spdy_session_pool.h b/net/spdy/spdy_session_pool.h
index caaaa67..eb127b5 100644
--- a/net/spdy/spdy_session_pool.h
+++ b/net/spdy/spdy_session_pool.h
@@ -16,6 +16,7 @@
#include "base/scoped_ptr.h"
#include "net/base/cert_database.h"
#include "net/base/host_port_pair.h"
+#include "net/base/ip_endpoint.h"
#include "net/base/net_errors.h"
#include "net/base/network_change_notifier.h"
#include "net/base/ssl_config_service.h"
@@ -25,19 +26,21 @@
namespace net {
+class AddressList;
class BoundNetLog;
class ClientSocketHandle;
+class HostResolver;
class HttpNetworkSession;
class SpdySession;
// This is a very simple pool for open SpdySessions.
-// TODO(mbelshe): Make this production ready.
class SpdySessionPool
: public NetworkChangeNotifier::IPAddressObserver,
public SSLConfigService::Observer,
public CertDatabase::Observer {
public:
- explicit SpdySessionPool(SSLConfigService* ssl_config_service);
+ explicit SpdySessionPool(HostResolver* host_resolver,
+ SSLConfigService* ssl_config_service);
virtual ~SpdySessionPool();
// Either returns an existing SpdySession or creates a new SpdySession for
@@ -107,6 +110,10 @@ class SpdySessionPool
// A debugging mode where we compress all accesses through a single domain.
static void ForceSingleDomain() { g_force_single_domain = true; }
+ // Controls whether the pool allows use of a common session for domains
+ // which share IP address resolutions.
+ static void enable_ip_pooling(bool value) { g_enable_ip_pooling = value; }
+
// CertDatabase::Observer methods:
virtual void OnUserCertAdded(X509Certificate* cert);
@@ -117,6 +124,15 @@ class SpdySessionPool
typedef std::list<scoped_refptr<SpdySession> > SpdySessionList;
typedef std::map<HostPortProxyPair, SpdySessionList*> SpdySessionsMap;
+ typedef std::map<IPEndPoint, HostPortProxyPair> SpdyAliasMap;
+
+ scoped_refptr<SpdySession> GetExistingSession(
+ SpdySessionList* list,
+ const BoundNetLog& net_log) const;
+ scoped_refptr<SpdySession> GetFromAlias(
+ const HostPortProxyPair& host_port_proxy_pair,
+ const BoundNetLog& net_log,
+ bool record_histograms) const;
// Helper functions for manipulating the lists.
const HostPortProxyPair& NormalizeListPair(
@@ -127,15 +143,30 @@ class SpdySessionPool
const HostPortProxyPair& host_port_proxy_pair) const;
void RemoveSessionList(const HostPortProxyPair& host_port_proxy_pair);
+ // Does a DNS cache lookup for |pair|, and returns the |addresses| found.
+ // Returns true if addresses found, false otherwise.
+ bool LookupAddresses(const HostPortProxyPair& pair,
+ AddressList* addresses) const;
+
+ // Add a set of |addresses| as IP-equivalent addresses for |pair|.
+ void AddAliases(const AddressList& addresses, const HostPortProxyPair& pair);
+
+ // Remove all aliases for |pair| from the aliases table.
+ void RemoveAliases(const HostPortProxyPair& pair);
+
SpdySettingsStorage spdy_settings_;
// This is our weak session pool - one session per domain.
SpdySessionsMap sessions_;
+ // A map of IPEndPoint aliases for sessions.
+ SpdyAliasMap aliases_;
- static int g_max_sessions_per_domain;
+ static size_t g_max_sessions_per_domain;
static bool g_force_single_domain;
+ static bool g_enable_ip_pooling;
const scoped_refptr<SSLConfigService> ssl_config_service_;
+ HostResolver* resolver_;
DISALLOW_COPY_AND_ASSIGN(SpdySessionPool);
};
diff --git a/net/spdy/spdy_session_unittest.cc b/net/spdy/spdy_session_unittest.cc
index ee59f8b..473ec60 100644
--- a/net/spdy/spdy_session_unittest.cc
+++ b/net/spdy/spdy_session_unittest.cc
@@ -386,6 +386,109 @@ TEST_F(SpdySessionTest, SendSettingsOnNewSession) {
EXPECT_TRUE(data.at_write_eof());
}
+TEST_F(SpdySessionTest, IPPooling) {
+ const int kTestPort = 80;
+ struct TestHosts {
+ std::string name;
+ std::string iplist;
+ HostPortProxyPair pair;
+ } test_hosts[] = {
+ { "www.foo.com", "192.168.0.1,192.168.0.5" },
+ { "images.foo.com", "192.168.0.2,192.168.0.3,192.168.0.5" },
+ { "js.foo.com", "192.168.0.4,192.168.0.3" },
+ };
+
+ SpdySessionDependencies session_deps;
+ session_deps.host_resolver->set_synchronous_mode(true);
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_hosts); i++) {
+ session_deps.host_resolver->rules()->AddIPLiteralRule(test_hosts[i].name,
+ test_hosts[i].iplist, "");
+
+ // This test requires that the HostResolver cache be populated. Normal
+ // code would have done this already, but we do it manually.
+ HostResolver::RequestInfo info(HostPortPair(test_hosts[i].name, kTestPort));
+ AddressList result;
+ session_deps.host_resolver->Resolve(
+ info, &result, NULL, NULL, BoundNetLog());
+
+ // Setup a HostPortProxyPair
+ test_hosts[i].pair = HostPortProxyPair(
+ HostPortPair(test_hosts[i].name, kTestPort), ProxyServer::Direct());
+ }
+
+ MockConnect connect_data(false, OK);
+ MockRead reads[] = {
+ MockRead(false, ERR_IO_PENDING) // Stall forever.
+ };
+
+ StaticSocketDataProvider data(reads, arraysize(reads), NULL, 0);
+ data.set_connect_data(connect_data);
+ session_deps.socket_factory->AddSocketDataProvider(&data);
+
+ SSLSocketDataProvider ssl(false, OK);
+ session_deps.socket_factory->AddSSLSocketDataProvider(&ssl);
+
+ scoped_refptr<HttpNetworkSession> http_session(
+ SpdySessionDependencies::SpdyCreateSession(&session_deps));
+
+ // Setup the first session to the first host.
+ SpdySessionPool* spdy_session_pool(http_session->spdy_session_pool());
+ EXPECT_FALSE(spdy_session_pool->HasSession(test_hosts[0].pair));
+ scoped_refptr<SpdySession> session =
+ spdy_session_pool->Get(test_hosts[0].pair, BoundNetLog());
+ EXPECT_TRUE(spdy_session_pool->HasSession(test_hosts[0].pair));
+
+ HostPortPair test_host_port_pair(test_hosts[0].name, kTestPort);
+ scoped_refptr<TCPSocketParams> tcp_params(
+ new TCPSocketParams(test_host_port_pair,
+ MEDIUM,
+ GURL(),
+ false));
+ scoped_ptr<ClientSocketHandle> connection(new ClientSocketHandle);
+ EXPECT_EQ(OK,
+ connection->Init(test_host_port_pair.ToString(), tcp_params, MEDIUM,
+ NULL, http_session->tcp_socket_pool(),
+ BoundNetLog()));
+ EXPECT_EQ(OK, session->InitializeWithSocket(connection.release(), false, OK));
+
+ // Flush the SpdySession::OnReadComplete() task.
+ MessageLoop::current()->RunAllPending();
+
+ // The third host has no overlap with the first, so it can't pool IPs.
+ EXPECT_FALSE(spdy_session_pool->HasSession(test_hosts[2].pair));
+
+ // The second host overlaps with the first, and should IP pool.
+ EXPECT_TRUE(spdy_session_pool->HasSession(test_hosts[1].pair));
+
+ // Verify that the second host, through a proxy, won't share the IP.
+ HostPortProxyPair proxy_pair(test_hosts[1].pair.first,
+ ProxyServer::FromPacString("HTTP http://proxy.foo.com/"));
+ EXPECT_FALSE(spdy_session_pool->HasSession(proxy_pair));
+
+ // Overlap between 2 and 3 does is not transitive to 1.
+ EXPECT_FALSE(spdy_session_pool->HasSession(test_hosts[2].pair));
+
+ // Create a new session to host 2.
+ scoped_refptr<SpdySession> session2 =
+ spdy_session_pool->Get(test_hosts[2].pair, BoundNetLog());
+
+ // Verify that we have sessions for everything.
+ EXPECT_TRUE(spdy_session_pool->HasSession(test_hosts[0].pair));
+ EXPECT_TRUE(spdy_session_pool->HasSession(test_hosts[1].pair));
+ EXPECT_TRUE(spdy_session_pool->HasSession(test_hosts[2].pair));
+
+ // Cleanup the sessions.
+ spdy_session_pool->Remove(session);
+ session = NULL;
+ spdy_session_pool->Remove(session2);
+ session2 = NULL;
+
+ // Verify that the map is all cleaned up.
+ EXPECT_FALSE(spdy_session_pool->HasSession(test_hosts[0].pair));
+ EXPECT_FALSE(spdy_session_pool->HasSession(test_hosts[1].pair));
+ EXPECT_FALSE(spdy_session_pool->HasSession(test_hosts[2].pair));
+}
+
} // namespace
} // namespace net
diff --git a/net/spdy/spdy_test_util.cc b/net/spdy/spdy_test_util.cc
index 7e1711a..1adcb24 100644
--- a/net/spdy/spdy_test_util.cc
+++ b/net/spdy/spdy_test_util.cc
@@ -884,7 +884,7 @@ int CombineFrames(const spdy::SpdyFrame** frames, int num_frames,
}
SpdySessionDependencies::SpdySessionDependencies()
- : host_resolver(new MockHostResolver),
+ : host_resolver(new MockCachingHostResolver),
cert_verifier(new CertVerifier),
proxy_service(ProxyService::CreateDirect()),
ssl_config_service(new SSLConfigServiceDefaults),