summaryrefslogtreecommitdiffstats
path: root/net/spdy/spdy_session_pool_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'net/spdy/spdy_session_pool_unittest.cc')
-rw-r--r--net/spdy/spdy_session_pool_unittest.cc497
1 files changed, 497 insertions, 0 deletions
diff --git a/net/spdy/spdy_session_pool_unittest.cc b/net/spdy/spdy_session_pool_unittest.cc
new file mode 100644
index 0000000..7d99ea3
--- /dev/null
+++ b/net/spdy/spdy_session_pool_unittest.cc
@@ -0,0 +1,497 @@
+// Copyright (c) 2013 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/spdy/spdy_session_pool.h"
+
+#include <cstddef>
+#include <string>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/dns/host_cache.h"
+#include "net/http/http_network_session.h"
+#include "net/socket/client_socket_handle.h"
+#include "net/socket/transport_client_socket_pool.h"
+#include "net/spdy/spdy_session.h"
+#include "net/spdy/spdy_test_util_common.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+class SpdySessionPoolTest : public ::testing::Test,
+ public ::testing::WithParamInterface<NextProto> {
+ protected:
+ // Used by RunIPPoolingTest().
+ enum SpdyPoolCloseSessionsType {
+ SPDY_POOL_CLOSE_SESSIONS_MANUALLY,
+ SPDY_POOL_CLOSE_CURRENT_SESSIONS,
+ SPDY_POOL_CLOSE_IDLE_SESSIONS,
+ };
+
+ SpdySessionPoolTest()
+ : session_deps_(GetParam()),
+ spdy_session_pool_(NULL) {}
+
+ void CreateNetworkSession() {
+ http_session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps_);
+ spdy_session_pool_ = http_session_->spdy_session_pool();
+ }
+
+ void RunIPPoolingTest(SpdyPoolCloseSessionsType close_sessions_type);
+
+ SpdySessionDependencies session_deps_;
+ scoped_refptr<HttpNetworkSession> http_session_;
+ SpdySessionPool* spdy_session_pool_;
+};
+
+INSTANTIATE_TEST_CASE_P(
+ NextProto,
+ SpdySessionPoolTest,
+ testing::Values(kProtoSPDY2, kProtoSPDY3, kProtoSPDY31, kProtoSPDY4a2));
+
+// A delegate that opens a new session when it is closed.
+class SessionOpeningDelegate : public SpdyStream::Delegate {
+ public:
+ SessionOpeningDelegate(SpdySessionPool* spdy_session_pool,
+ const SpdySessionKey& key)
+ : spdy_session_pool_(spdy_session_pool),
+ key_(key) {}
+
+ virtual ~SessionOpeningDelegate() {}
+
+ virtual void OnRequestHeadersSent() OVERRIDE {}
+
+ virtual SpdyResponseHeadersStatus OnResponseHeadersUpdated(
+ const SpdyHeaderBlock& response_headers) OVERRIDE {
+ return RESPONSE_HEADERS_ARE_COMPLETE;
+ }
+
+ virtual void OnDataReceived(scoped_ptr<SpdyBuffer> buffer) OVERRIDE {}
+
+ virtual void OnDataSent() OVERRIDE {}
+
+ virtual void OnClose(int status) OVERRIDE {
+ ignore_result(CreateFakeSpdySession(spdy_session_pool_, key_));
+ }
+
+ private:
+ SpdySessionPool* const spdy_session_pool_;
+ const SpdySessionKey key_;
+};
+
+// Set up a SpdyStream to create a new session when it is closed.
+// CloseCurrentSessions should not close the newly-created session.
+TEST_P(SpdySessionPoolTest, CloseCurrentSessions) {
+ const char kTestHost[] = "www.foo.com";
+ const int kTestPort = 80;
+
+ session_deps_.host_resolver->set_synchronous_mode(true);
+
+ HostPortPair test_host_port_pair(kTestHost, kTestPort);
+ SpdySessionKey test_key =
+ SpdySessionKey(
+ test_host_port_pair, ProxyServer::Direct(),
+ kPrivacyModeDisabled);
+
+ MockConnect connect_data(SYNCHRONOUS, OK);
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, 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(SYNCHRONOUS, OK);
+ session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
+
+ CreateNetworkSession();
+
+ // Setup the first session to the first host.
+ scoped_refptr<SpdySession> session =
+ CreateInsecureSpdySession(http_session_, test_key, BoundNetLog());
+
+ // Flush the SpdySession::OnReadComplete() task.
+ base::MessageLoop::current()->RunUntilIdle();
+
+ // Verify that we have sessions for everything.
+ EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_key));
+
+ // Set the stream to create a new session when it is closed.
+ base::WeakPtr<SpdyStream> spdy_stream =
+ CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM,
+ session, GURL("http://www.foo.com"),
+ MEDIUM, BoundNetLog());
+ SessionOpeningDelegate delegate(spdy_session_pool_, test_key);
+ spdy_stream->SetDelegate(&delegate);
+
+ // Close the current session.
+ spdy_session_pool_->CloseCurrentSessions(net::ERR_ABORTED);
+
+ EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_key));
+}
+
+TEST_P(SpdySessionPoolTest, CloseCurrentIdleSessions) {
+ MockConnect connect_data(SYNCHRONOUS, OK);
+ MockRead reads[] = {
+ MockRead(ASYNC, 0, 0) // EOF
+ };
+
+ session_deps_.host_resolver->set_synchronous_mode(true);
+
+ StaticSocketDataProvider data(reads, arraysize(reads), NULL, 0);
+ data.set_connect_data(connect_data);
+ session_deps_.socket_factory->AddSocketDataProvider(&data);
+
+ SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
+ session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
+
+ CreateNetworkSession();
+
+ // Set up session 1
+ const std::string kTestHost1("http://www.a.com");
+ HostPortPair test_host_port_pair1(kTestHost1, 80);
+ SpdySessionKey key1(test_host_port_pair1, ProxyServer::Direct(),
+ kPrivacyModeDisabled);
+ scoped_refptr<SpdySession> session1 =
+ CreateInsecureSpdySession(http_session_, key1, BoundNetLog());
+ GURL url1(kTestHost1);
+ base::WeakPtr<SpdyStream> spdy_stream1 =
+ CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM,
+ session1, url1, MEDIUM, BoundNetLog());
+ ASSERT_TRUE(spdy_stream1.get() != NULL);
+
+ // Set up session 2
+ session_deps_.socket_factory->AddSocketDataProvider(&data);
+ const std::string kTestHost2("http://www.b.com");
+ HostPortPair test_host_port_pair2(kTestHost2, 80);
+ SpdySessionKey key2(test_host_port_pair2, ProxyServer::Direct(),
+ kPrivacyModeDisabled);
+ scoped_refptr<SpdySession> session2 =
+ CreateInsecureSpdySession(http_session_, key2, BoundNetLog());
+ GURL url2(kTestHost2);
+ base::WeakPtr<SpdyStream> spdy_stream2 =
+ CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM,
+ session2, url2, MEDIUM, BoundNetLog());
+ ASSERT_TRUE(spdy_stream2.get() != NULL);
+
+ // Set up session 3
+ session_deps_.socket_factory->AddSocketDataProvider(&data);
+ const std::string kTestHost3("http://www.c.com");
+ HostPortPair test_host_port_pair3(kTestHost3, 80);
+ SpdySessionKey key3(test_host_port_pair3, ProxyServer::Direct(),
+ kPrivacyModeDisabled);
+ scoped_refptr<SpdySession> session3 =
+ CreateInsecureSpdySession(http_session_, key3, BoundNetLog());
+ GURL url3(kTestHost3);
+ base::WeakPtr<SpdyStream> spdy_stream3 =
+ CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM,
+ session3, url3, MEDIUM, BoundNetLog());
+ ASSERT_TRUE(spdy_stream3.get() != NULL);
+
+ // All sessions are active and not closed
+ EXPECT_TRUE(session1->is_active());
+ EXPECT_FALSE(session1->IsClosed());
+ EXPECT_TRUE(session2->is_active());
+ EXPECT_FALSE(session2->IsClosed());
+ EXPECT_TRUE(session3->is_active());
+ EXPECT_FALSE(session3->IsClosed());
+
+ // Should not do anything, all are active
+ spdy_session_pool_->CloseCurrentIdleSessions();
+ EXPECT_TRUE(session1->is_active());
+ EXPECT_FALSE(session1->IsClosed());
+ EXPECT_TRUE(session2->is_active());
+ EXPECT_FALSE(session2->IsClosed());
+ EXPECT_TRUE(session3->is_active());
+ EXPECT_FALSE(session3->IsClosed());
+
+ // Make sessions 1 and 3 inactive, but keep them open.
+ // Session 2 still open and active
+ session1->CloseCreatedStream(spdy_stream1, OK);
+ EXPECT_EQ(NULL, spdy_stream1.get());
+ session3->CloseCreatedStream(spdy_stream3, OK);
+ EXPECT_EQ(NULL, spdy_stream3.get());
+ EXPECT_FALSE(session1->is_active());
+ EXPECT_FALSE(session1->IsClosed());
+ EXPECT_TRUE(session2->is_active());
+ EXPECT_FALSE(session2->IsClosed());
+ EXPECT_FALSE(session3->is_active());
+ EXPECT_FALSE(session3->IsClosed());
+
+ // Should close session 1 and 3, 2 should be left open
+ spdy_session_pool_->CloseCurrentIdleSessions();
+ EXPECT_FALSE(session1->is_active());
+ EXPECT_TRUE(session1->IsClosed());
+ EXPECT_TRUE(session2->is_active());
+ EXPECT_FALSE(session2->IsClosed());
+ EXPECT_FALSE(session3->is_active());
+ EXPECT_TRUE(session3->IsClosed());
+
+ // Should not do anything
+ spdy_session_pool_->CloseCurrentIdleSessions();
+ EXPECT_TRUE(session2->is_active());
+ EXPECT_FALSE(session2->IsClosed());
+
+ // Make 2 not active
+ session2->CloseCreatedStream(spdy_stream2, OK);
+ EXPECT_EQ(NULL, spdy_stream2.get());
+ EXPECT_FALSE(session2->is_active());
+ EXPECT_FALSE(session2->IsClosed());
+
+ // This should close session 2
+ spdy_session_pool_->CloseCurrentIdleSessions();
+ EXPECT_FALSE(session2->is_active());
+ EXPECT_TRUE(session2->IsClosed());
+}
+
+// Set up a SpdyStream to create a new session when it is closed.
+// CloseAllSessions should close the newly-created session.
+TEST_P(SpdySessionPoolTest, CloseAllSessions) {
+ const char kTestHost[] = "www.foo.com";
+ const int kTestPort = 80;
+
+ session_deps_.host_resolver->set_synchronous_mode(true);
+
+ HostPortPair test_host_port_pair(kTestHost, kTestPort);
+ SpdySessionKey test_key =
+ SpdySessionKey(
+ test_host_port_pair, ProxyServer::Direct(),
+ kPrivacyModeDisabled);
+
+ MockConnect connect_data(SYNCHRONOUS, OK);
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, 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(SYNCHRONOUS, OK);
+ session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
+
+ CreateNetworkSession();
+
+ // Setup the first session to the first host.
+ scoped_refptr<SpdySession> session =
+ CreateInsecureSpdySession(http_session_, test_key, BoundNetLog());
+
+ // Flush the SpdySession::OnReadComplete() task.
+ base::MessageLoop::current()->RunUntilIdle();
+
+ // Verify that we have sessions for everything.
+ EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_key));
+
+ // Set the stream to create a new session when it is closed.
+ base::WeakPtr<SpdyStream> spdy_stream =
+ CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM,
+ session, GURL("http://www.foo.com"),
+ MEDIUM, BoundNetLog());
+ SessionOpeningDelegate delegate(spdy_session_pool_, test_key);
+ spdy_stream->SetDelegate(&delegate);
+
+ // Close the current session.
+ spdy_session_pool_->CloseAllSessions();
+
+ EXPECT_FALSE(HasSpdySession(spdy_session_pool_, test_key));
+}
+
+// This test has three variants, one for each style of closing the connection.
+// If |clean_via_close_current_sessions| is SPDY_POOL_CLOSE_SESSIONS_MANUALLY,
+// the sessions are closed manually, calling SpdySessionPool::Remove() directly.
+// If |clean_via_close_current_sessions| is SPDY_POOL_CLOSE_CURRENT_SESSIONS,
+// sessions are closed with SpdySessionPool::CloseCurrentSessions().
+// If |clean_via_close_current_sessions| is SPDY_POOL_CLOSE_IDLE_SESSIONS,
+// sessions are closed with SpdySessionPool::CloseIdleSessions().
+void SpdySessionPoolTest::RunIPPoolingTest(
+ SpdyPoolCloseSessionsType close_sessions_type) {
+ const int kTestPort = 80;
+ struct TestHosts {
+ std::string url;
+ std::string name;
+ std::string iplist;
+ SpdySessionKey key;
+ AddressList addresses;
+ } test_hosts[] = {
+ { "http:://www.foo.com",
+ "www.foo.com",
+ "192.0.2.33,192.168.0.1,192.168.0.5"
+ },
+ { "http://js.foo.com",
+ "js.foo.com",
+ "192.168.0.2,192.168.0.3,192.168.0.5,192.0.2.33"
+ },
+ { "http://images.foo.com",
+ "images.foo.com",
+ "192.168.0.4,192.168.0.3"
+ },
+ };
+
+ 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, std::string());
+
+ // 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));
+ session_deps_.host_resolver->Resolve(
+ info, &test_hosts[i].addresses, CompletionCallback(), NULL,
+ BoundNetLog());
+
+ // Setup a SpdySessionKey
+ test_hosts[i].key = SpdySessionKey(
+ HostPortPair(test_hosts[i].name, kTestPort), ProxyServer::Direct(),
+ kPrivacyModeDisabled);
+ }
+
+ MockConnect connect_data(SYNCHRONOUS, OK);
+ MockRead reads[] = {
+ MockRead(SYNCHRONOUS, 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(SYNCHRONOUS, OK);
+ session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
+
+ CreateNetworkSession();
+
+ // Setup the first session to the first host.
+ scoped_refptr<SpdySession> session =
+ CreateInsecureSpdySession(
+ http_session_, test_hosts[0].key, BoundNetLog());
+
+ // Flush the SpdySession::OnReadComplete() task.
+ base::MessageLoop::current()->RunUntilIdle();
+
+ // The third host has no overlap with the first, so it can't pool IPs.
+ EXPECT_FALSE(HasSpdySession(spdy_session_pool_, test_hosts[2].key));
+
+ // The second host overlaps with the first, and should IP pool.
+ EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_hosts[1].key));
+
+ // Verify that the second host, through a proxy, won't share the IP.
+ SpdySessionKey proxy_key(test_hosts[1].key.host_port_pair(),
+ ProxyServer::FromPacString("HTTP http://proxy.foo.com/"),
+ kPrivacyModeDisabled);
+ EXPECT_FALSE(HasSpdySession(spdy_session_pool_, proxy_key));
+
+ // Overlap between 2 and 3 does is not transitive to 1.
+ EXPECT_FALSE(HasSpdySession(spdy_session_pool_, test_hosts[2].key));
+
+ // Create a new session to host 2.
+ session_deps_.socket_factory->AddSocketDataProvider(&data);
+ scoped_refptr<SpdySession> session2 =
+ CreateInsecureSpdySession(
+ http_session_, test_hosts[2].key, BoundNetLog());
+
+ // Verify that we have sessions for everything.
+ EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_hosts[0].key));
+ EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_hosts[1].key));
+ EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_hosts[2].key));
+
+ // Grab the session to host 1 and verify that it is the same session
+ // we got with host 0, and that is a different from host 2's session.
+ scoped_refptr<SpdySession> session1 =
+ spdy_session_pool_->GetIfExists(test_hosts[1].key, BoundNetLog());
+ EXPECT_EQ(session.get(), session1.get());
+ EXPECT_NE(session2.get(), session1.get());
+
+ // Remove the aliases and observe that we still have a session for host1.
+ SpdySessionPoolPeer pool_peer(spdy_session_pool_);
+ pool_peer.RemoveAliases(test_hosts[0].key);
+ pool_peer.RemoveAliases(test_hosts[1].key);
+ EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_hosts[1].key));
+
+ // Expire the host cache
+ session_deps_.host_resolver->GetHostCache()->clear();
+ EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_hosts[1].key));
+
+ // Cleanup the sessions.
+ switch (close_sessions_type) {
+ case SPDY_POOL_CLOSE_SESSIONS_MANUALLY:
+ spdy_session_pool_->Remove(session);
+ session = NULL;
+ spdy_session_pool_->Remove(session2);
+ session2 = NULL;
+ break;
+ case SPDY_POOL_CLOSE_CURRENT_SESSIONS:
+ spdy_session_pool_->CloseCurrentSessions(net::ERR_ABORTED);
+ break;
+ case SPDY_POOL_CLOSE_IDLE_SESSIONS:
+ GURL url(test_hosts[0].url);
+ base::WeakPtr<SpdyStream> spdy_stream =
+ CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM,
+ session, url, MEDIUM, BoundNetLog());
+ GURL url1(test_hosts[1].url);
+ base::WeakPtr<SpdyStream> spdy_stream1 =
+ CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM,
+ session1, url1, MEDIUM, BoundNetLog());
+ GURL url2(test_hosts[2].url);
+ base::WeakPtr<SpdyStream> spdy_stream2 =
+ CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM,
+ session2, url2, MEDIUM, BoundNetLog());
+
+ // Close streams to make spdy_session and spdy_session1 inactive.
+ session->CloseCreatedStream(spdy_stream, OK);
+ EXPECT_EQ(NULL, spdy_stream.get());
+ session1->CloseCreatedStream(spdy_stream1, OK);
+ EXPECT_EQ(NULL, spdy_stream1.get());
+
+ // Check spdy_session and spdy_session1 are not closed.
+ EXPECT_FALSE(session->is_active());
+ EXPECT_FALSE(session->IsClosed());
+ EXPECT_FALSE(session1->is_active());
+ EXPECT_FALSE(session1->IsClosed());
+ EXPECT_TRUE(session2->is_active());
+ EXPECT_FALSE(session2->IsClosed());
+
+ // Test that calling CloseIdleSessions, does not cause a crash.
+ // http://crbug.com/181400
+ spdy_session_pool_->CloseCurrentIdleSessions();
+
+ // Verify spdy_session and spdy_session1 are closed.
+ EXPECT_FALSE(session->is_active());
+ EXPECT_TRUE(session->IsClosed());
+ EXPECT_FALSE(session1->is_active());
+ EXPECT_TRUE(session1->IsClosed());
+ EXPECT_TRUE(session2->is_active());
+ EXPECT_FALSE(session2->IsClosed());
+
+ spdy_stream2->Cancel();
+ EXPECT_EQ(NULL, spdy_stream.get());
+ EXPECT_EQ(NULL, spdy_stream1.get());
+ EXPECT_EQ(NULL, spdy_stream2.get());
+ spdy_session_pool_->Remove(session2);
+ session2 = NULL;
+ break;
+ }
+
+ // Verify that the map is all cleaned up.
+ EXPECT_FALSE(HasSpdySession(spdy_session_pool_, test_hosts[0].key));
+ EXPECT_FALSE(HasSpdySession(spdy_session_pool_, test_hosts[1].key));
+ EXPECT_FALSE(HasSpdySession(spdy_session_pool_, test_hosts[2].key));
+}
+
+TEST_P(SpdySessionPoolTest, IPPooling) {
+ RunIPPoolingTest(SPDY_POOL_CLOSE_SESSIONS_MANUALLY);
+}
+
+TEST_P(SpdySessionPoolTest, IPPoolingCloseCurrentSessions) {
+ RunIPPoolingTest(SPDY_POOL_CLOSE_CURRENT_SESSIONS);
+}
+
+TEST_P(SpdySessionPoolTest, IPPoolingCloseIdleSessions) {
+ RunIPPoolingTest(SPDY_POOL_CLOSE_IDLE_SESSIONS);
+}
+
+} // namespace
+
+} // namespace net