Home | History | Annotate | Download | only in spdy
      1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "net/spdy/spdy_session_pool.h"
      6 
      7 #include <cstddef>
      8 #include <string>
      9 
     10 #include "base/memory/ref_counted.h"
     11 #include "base/memory/scoped_ptr.h"
     12 #include "net/dns/host_cache.h"
     13 #include "net/http/http_network_session.h"
     14 #include "net/socket/client_socket_handle.h"
     15 #include "net/socket/transport_client_socket_pool.h"
     16 #include "net/spdy/spdy_session.h"
     17 #include "net/spdy/spdy_test_util_common.h"
     18 #include "testing/gtest/include/gtest/gtest.h"
     19 
     20 namespace net {
     21 
     22 namespace {
     23 
     24 class SpdySessionPoolTest : public ::testing::Test,
     25                             public ::testing::WithParamInterface<NextProto> {
     26  protected:
     27   // Used by RunIPPoolingTest().
     28   enum SpdyPoolCloseSessionsType {
     29     SPDY_POOL_CLOSE_SESSIONS_MANUALLY,
     30     SPDY_POOL_CLOSE_CURRENT_SESSIONS,
     31     SPDY_POOL_CLOSE_IDLE_SESSIONS,
     32   };
     33 
     34   SpdySessionPoolTest()
     35       : session_deps_(GetParam()),
     36         spdy_session_pool_(NULL) {}
     37 
     38   void CreateNetworkSession() {
     39     http_session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps_);
     40     spdy_session_pool_ = http_session_->spdy_session_pool();
     41   }
     42 
     43   void RunIPPoolingTest(SpdyPoolCloseSessionsType close_sessions_type);
     44 
     45   SpdySessionDependencies session_deps_;
     46   scoped_refptr<HttpNetworkSession> http_session_;
     47   SpdySessionPool* spdy_session_pool_;
     48 };
     49 
     50 INSTANTIATE_TEST_CASE_P(
     51     NextProto,
     52     SpdySessionPoolTest,
     53     testing::Values(kProtoDeprecatedSPDY2,
     54                     kProtoSPDY3, kProtoSPDY31, kProtoSPDY4a2,
     55                     kProtoHTTP2Draft04));
     56 
     57 // A delegate that opens a new session when it is closed.
     58 class SessionOpeningDelegate : public SpdyStream::Delegate {
     59  public:
     60   SessionOpeningDelegate(SpdySessionPool* spdy_session_pool,
     61                          const SpdySessionKey& key)
     62       : spdy_session_pool_(spdy_session_pool),
     63         key_(key) {}
     64 
     65   virtual ~SessionOpeningDelegate() {}
     66 
     67   virtual void OnRequestHeadersSent() OVERRIDE {}
     68 
     69   virtual SpdyResponseHeadersStatus OnResponseHeadersUpdated(
     70       const SpdyHeaderBlock& response_headers) OVERRIDE {
     71     return RESPONSE_HEADERS_ARE_COMPLETE;
     72   }
     73 
     74   virtual void OnDataReceived(scoped_ptr<SpdyBuffer> buffer) OVERRIDE {}
     75 
     76   virtual void OnDataSent() OVERRIDE {}
     77 
     78   virtual void OnClose(int status) OVERRIDE {
     79     ignore_result(CreateFakeSpdySession(spdy_session_pool_, key_));
     80   }
     81 
     82  private:
     83   SpdySessionPool* const spdy_session_pool_;
     84   const SpdySessionKey key_;
     85 };
     86 
     87 // Set up a SpdyStream to create a new session when it is closed.
     88 // CloseCurrentSessions should not close the newly-created session.
     89 TEST_P(SpdySessionPoolTest, CloseCurrentSessions) {
     90   const char kTestHost[] = "www.foo.com";
     91   const int kTestPort = 80;
     92 
     93   session_deps_.host_resolver->set_synchronous_mode(true);
     94 
     95   HostPortPair test_host_port_pair(kTestHost, kTestPort);
     96   SpdySessionKey test_key =
     97       SpdySessionKey(
     98           test_host_port_pair, ProxyServer::Direct(),
     99           kPrivacyModeDisabled);
    100 
    101   MockConnect connect_data(SYNCHRONOUS, OK);
    102   MockRead reads[] = {
    103     MockRead(SYNCHRONOUS, ERR_IO_PENDING)  // Stall forever.
    104   };
    105 
    106   StaticSocketDataProvider data(reads, arraysize(reads), NULL, 0);
    107   data.set_connect_data(connect_data);
    108   session_deps_.socket_factory->AddSocketDataProvider(&data);
    109 
    110   SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
    111   session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
    112 
    113   CreateNetworkSession();
    114 
    115   // Setup the first session to the first host.
    116   base::WeakPtr<SpdySession> session =
    117       CreateInsecureSpdySession(http_session_, test_key, BoundNetLog());
    118 
    119   // Flush the SpdySession::OnReadComplete() task.
    120   base::MessageLoop::current()->RunUntilIdle();
    121 
    122   // Verify that we have sessions for everything.
    123   EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_key));
    124 
    125   // Set the stream to create a new session when it is closed.
    126   base::WeakPtr<SpdyStream> spdy_stream =
    127       CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM,
    128                                 session, GURL("http://www.foo.com"),
    129                                 MEDIUM, BoundNetLog());
    130   SessionOpeningDelegate delegate(spdy_session_pool_, test_key);
    131   spdy_stream->SetDelegate(&delegate);
    132 
    133   // Close the current session.
    134   spdy_session_pool_->CloseCurrentSessions(net::ERR_ABORTED);
    135 
    136   EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_key));
    137 }
    138 
    139 TEST_P(SpdySessionPoolTest, CloseCurrentIdleSessions) {
    140   MockConnect connect_data(SYNCHRONOUS, OK);
    141   MockRead reads[] = {
    142     MockRead(ASYNC, 0, 0)  // EOF
    143   };
    144 
    145   session_deps_.host_resolver->set_synchronous_mode(true);
    146 
    147   StaticSocketDataProvider data(reads, arraysize(reads), NULL, 0);
    148   data.set_connect_data(connect_data);
    149   session_deps_.socket_factory->AddSocketDataProvider(&data);
    150 
    151   SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
    152   session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
    153 
    154   CreateNetworkSession();
    155 
    156   // Set up session 1
    157   const std::string kTestHost1("http://www.a.com");
    158   HostPortPair test_host_port_pair1(kTestHost1, 80);
    159   SpdySessionKey key1(test_host_port_pair1, ProxyServer::Direct(),
    160                       kPrivacyModeDisabled);
    161   base::WeakPtr<SpdySession> session1 =
    162       CreateInsecureSpdySession(http_session_, key1, BoundNetLog());
    163   GURL url1(kTestHost1);
    164   base::WeakPtr<SpdyStream> spdy_stream1 =
    165       CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM,
    166                                 session1, url1, MEDIUM, BoundNetLog());
    167   ASSERT_TRUE(spdy_stream1.get() != NULL);
    168 
    169   // Set up session 2
    170   session_deps_.socket_factory->AddSocketDataProvider(&data);
    171   const std::string kTestHost2("http://www.b.com");
    172   HostPortPair test_host_port_pair2(kTestHost2, 80);
    173   SpdySessionKey key2(test_host_port_pair2, ProxyServer::Direct(),
    174                       kPrivacyModeDisabled);
    175   base::WeakPtr<SpdySession> session2 =
    176       CreateInsecureSpdySession(http_session_, key2, BoundNetLog());
    177   GURL url2(kTestHost2);
    178   base::WeakPtr<SpdyStream> spdy_stream2 =
    179       CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM,
    180                                 session2, url2, MEDIUM, BoundNetLog());
    181   ASSERT_TRUE(spdy_stream2.get() != NULL);
    182 
    183   // Set up session 3
    184   session_deps_.socket_factory->AddSocketDataProvider(&data);
    185   const std::string kTestHost3("http://www.c.com");
    186   HostPortPair test_host_port_pair3(kTestHost3, 80);
    187   SpdySessionKey key3(test_host_port_pair3, ProxyServer::Direct(),
    188                       kPrivacyModeDisabled);
    189   base::WeakPtr<SpdySession> session3 =
    190       CreateInsecureSpdySession(http_session_, key3, BoundNetLog());
    191   GURL url3(kTestHost3);
    192   base::WeakPtr<SpdyStream> spdy_stream3 =
    193       CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM,
    194                                 session3, url3, MEDIUM, BoundNetLog());
    195   ASSERT_TRUE(spdy_stream3.get() != NULL);
    196 
    197   // All sessions are active and not closed
    198   EXPECT_TRUE(session1->is_active());
    199   EXPECT_FALSE(session1->IsClosed());
    200   EXPECT_TRUE(session2->is_active());
    201   EXPECT_FALSE(session2->IsClosed());
    202   EXPECT_TRUE(session3->is_active());
    203   EXPECT_FALSE(session3->IsClosed());
    204 
    205   // Should not do anything, all are active
    206   spdy_session_pool_->CloseCurrentIdleSessions();
    207   EXPECT_TRUE(session1->is_active());
    208   EXPECT_FALSE(session1->IsClosed());
    209   EXPECT_TRUE(session2->is_active());
    210   EXPECT_FALSE(session2->IsClosed());
    211   EXPECT_TRUE(session3->is_active());
    212   EXPECT_FALSE(session3->IsClosed());
    213 
    214   // Make sessions 1 and 3 inactive, but keep them open.
    215   // Session 2 still open and active
    216   session1->CloseCreatedStream(spdy_stream1, OK);
    217   EXPECT_EQ(NULL, spdy_stream1.get());
    218   session3->CloseCreatedStream(spdy_stream3, OK);
    219   EXPECT_EQ(NULL, spdy_stream3.get());
    220   EXPECT_FALSE(session1->is_active());
    221   EXPECT_FALSE(session1->IsClosed());
    222   EXPECT_TRUE(session2->is_active());
    223   EXPECT_FALSE(session2->IsClosed());
    224   EXPECT_FALSE(session3->is_active());
    225   EXPECT_FALSE(session3->IsClosed());
    226 
    227   // Should close session 1 and 3, 2 should be left open
    228   spdy_session_pool_->CloseCurrentIdleSessions();
    229   EXPECT_TRUE(session1 == NULL);
    230   EXPECT_TRUE(session2->is_active());
    231   EXPECT_FALSE(session2->IsClosed());
    232   EXPECT_TRUE(session3 == NULL);
    233 
    234   // Should not do anything
    235   spdy_session_pool_->CloseCurrentIdleSessions();
    236   EXPECT_TRUE(session2->is_active());
    237   EXPECT_FALSE(session2->IsClosed());
    238 
    239   // Make 2 not active
    240   session2->CloseCreatedStream(spdy_stream2, OK);
    241   EXPECT_EQ(NULL, spdy_stream2.get());
    242   EXPECT_FALSE(session2->is_active());
    243   EXPECT_FALSE(session2->IsClosed());
    244 
    245   // This should close session 2
    246   spdy_session_pool_->CloseCurrentIdleSessions();
    247   EXPECT_TRUE(session2 == NULL);
    248 }
    249 
    250 // Set up a SpdyStream to create a new session when it is closed.
    251 // CloseAllSessions should close the newly-created session.
    252 TEST_P(SpdySessionPoolTest, CloseAllSessions) {
    253   const char kTestHost[] = "www.foo.com";
    254   const int kTestPort = 80;
    255 
    256   session_deps_.host_resolver->set_synchronous_mode(true);
    257 
    258   HostPortPair test_host_port_pair(kTestHost, kTestPort);
    259   SpdySessionKey test_key =
    260       SpdySessionKey(
    261           test_host_port_pair, ProxyServer::Direct(),
    262           kPrivacyModeDisabled);
    263 
    264   MockConnect connect_data(SYNCHRONOUS, OK);
    265   MockRead reads[] = {
    266     MockRead(SYNCHRONOUS, ERR_IO_PENDING)  // Stall forever.
    267   };
    268 
    269   StaticSocketDataProvider data(reads, arraysize(reads), NULL, 0);
    270   data.set_connect_data(connect_data);
    271   session_deps_.socket_factory->AddSocketDataProvider(&data);
    272 
    273   SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
    274   session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
    275 
    276   CreateNetworkSession();
    277 
    278   // Setup the first session to the first host.
    279   base::WeakPtr<SpdySession> session =
    280       CreateInsecureSpdySession(http_session_, test_key, BoundNetLog());
    281 
    282   // Flush the SpdySession::OnReadComplete() task.
    283   base::MessageLoop::current()->RunUntilIdle();
    284 
    285   // Verify that we have sessions for everything.
    286   EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_key));
    287 
    288   // Set the stream to create a new session when it is closed.
    289   base::WeakPtr<SpdyStream> spdy_stream =
    290       CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM,
    291                                 session, GURL("http://www.foo.com"),
    292                                 MEDIUM, BoundNetLog());
    293   SessionOpeningDelegate delegate(spdy_session_pool_, test_key);
    294   spdy_stream->SetDelegate(&delegate);
    295 
    296   // Close the current session.
    297   spdy_session_pool_->CloseAllSessions();
    298 
    299   EXPECT_FALSE(HasSpdySession(spdy_session_pool_, test_key));
    300 }
    301 
    302 // This test has three variants, one for each style of closing the connection.
    303 // If |clean_via_close_current_sessions| is SPDY_POOL_CLOSE_SESSIONS_MANUALLY,
    304 // the sessions are closed manually, calling SpdySessionPool::Remove() directly.
    305 // If |clean_via_close_current_sessions| is SPDY_POOL_CLOSE_CURRENT_SESSIONS,
    306 // sessions are closed with SpdySessionPool::CloseCurrentSessions().
    307 // If |clean_via_close_current_sessions| is SPDY_POOL_CLOSE_IDLE_SESSIONS,
    308 // sessions are closed with SpdySessionPool::CloseIdleSessions().
    309 void SpdySessionPoolTest::RunIPPoolingTest(
    310     SpdyPoolCloseSessionsType close_sessions_type) {
    311   const int kTestPort = 80;
    312   struct TestHosts {
    313     std::string url;
    314     std::string name;
    315     std::string iplist;
    316     SpdySessionKey key;
    317     AddressList addresses;
    318   } test_hosts[] = {
    319     { "http:://www.foo.com",
    320       "www.foo.com",
    321       "192.0.2.33,192.168.0.1,192.168.0.5"
    322     },
    323     { "http://js.foo.com",
    324       "js.foo.com",
    325       "192.168.0.2,192.168.0.3,192.168.0.5,192.0.2.33"
    326     },
    327     { "http://images.foo.com",
    328       "images.foo.com",
    329       "192.168.0.4,192.168.0.3"
    330     },
    331   };
    332 
    333   session_deps_.host_resolver->set_synchronous_mode(true);
    334   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_hosts); i++) {
    335     session_deps_.host_resolver->rules()->AddIPLiteralRule(
    336         test_hosts[i].name, test_hosts[i].iplist, std::string());
    337 
    338     // This test requires that the HostResolver cache be populated.  Normal
    339     // code would have done this already, but we do it manually.
    340     HostResolver::RequestInfo info(HostPortPair(test_hosts[i].name, kTestPort));
    341     session_deps_.host_resolver->Resolve(info,
    342                                          DEFAULT_PRIORITY,
    343                                          &test_hosts[i].addresses,
    344                                          CompletionCallback(),
    345                                          NULL,
    346                                          BoundNetLog());
    347 
    348     // Setup a SpdySessionKey
    349     test_hosts[i].key = SpdySessionKey(
    350         HostPortPair(test_hosts[i].name, kTestPort), ProxyServer::Direct(),
    351         kPrivacyModeDisabled);
    352   }
    353 
    354   MockConnect connect_data(SYNCHRONOUS, OK);
    355   MockRead reads[] = {
    356     MockRead(SYNCHRONOUS, ERR_IO_PENDING)  // Stall forever.
    357   };
    358 
    359   StaticSocketDataProvider data(reads, arraysize(reads), NULL, 0);
    360   data.set_connect_data(connect_data);
    361   session_deps_.socket_factory->AddSocketDataProvider(&data);
    362 
    363   SSLSocketDataProvider ssl(SYNCHRONOUS, OK);
    364   session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
    365 
    366   CreateNetworkSession();
    367 
    368   // Setup the first session to the first host.
    369   base::WeakPtr<SpdySession> session =
    370       CreateInsecureSpdySession(
    371           http_session_, test_hosts[0].key, BoundNetLog());
    372 
    373   // Flush the SpdySession::OnReadComplete() task.
    374   base::MessageLoop::current()->RunUntilIdle();
    375 
    376   // The third host has no overlap with the first, so it can't pool IPs.
    377   EXPECT_FALSE(HasSpdySession(spdy_session_pool_, test_hosts[2].key));
    378 
    379   // The second host overlaps with the first, and should IP pool.
    380   EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_hosts[1].key));
    381 
    382   // Verify that the second host, through a proxy, won't share the IP.
    383   SpdySessionKey proxy_key(test_hosts[1].key.host_port_pair(),
    384       ProxyServer::FromPacString("HTTP http://proxy.foo.com/"),
    385       kPrivacyModeDisabled);
    386   EXPECT_FALSE(HasSpdySession(spdy_session_pool_, proxy_key));
    387 
    388   // Overlap between 2 and 3 does is not transitive to 1.
    389   EXPECT_FALSE(HasSpdySession(spdy_session_pool_, test_hosts[2].key));
    390 
    391   // Create a new session to host 2.
    392   session_deps_.socket_factory->AddSocketDataProvider(&data);
    393   base::WeakPtr<SpdySession> session2 =
    394       CreateInsecureSpdySession(
    395           http_session_, test_hosts[2].key, BoundNetLog());
    396 
    397   // Verify that we have sessions for everything.
    398   EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_hosts[0].key));
    399   EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_hosts[1].key));
    400   EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_hosts[2].key));
    401 
    402   // Grab the session to host 1 and verify that it is the same session
    403   // we got with host 0, and that is a different from host 2's session.
    404   base::WeakPtr<SpdySession> session1 =
    405       spdy_session_pool_->FindAvailableSession(
    406           test_hosts[1].key, BoundNetLog());
    407   EXPECT_EQ(session.get(), session1.get());
    408   EXPECT_NE(session2.get(), session1.get());
    409 
    410   // Remove the aliases and observe that we still have a session for host1.
    411   SpdySessionPoolPeer pool_peer(spdy_session_pool_);
    412   pool_peer.RemoveAliases(test_hosts[0].key);
    413   pool_peer.RemoveAliases(test_hosts[1].key);
    414   EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_hosts[1].key));
    415 
    416   // Expire the host cache
    417   session_deps_.host_resolver->GetHostCache()->clear();
    418   EXPECT_TRUE(HasSpdySession(spdy_session_pool_, test_hosts[1].key));
    419 
    420   // Cleanup the sessions.
    421   switch (close_sessions_type) {
    422     case SPDY_POOL_CLOSE_SESSIONS_MANUALLY:
    423       session->CloseSessionOnError(ERR_ABORTED, std::string());
    424       EXPECT_TRUE(session == NULL);
    425       session2->CloseSessionOnError(ERR_ABORTED, std::string());
    426       EXPECT_TRUE(session2 == NULL);
    427       break;
    428     case SPDY_POOL_CLOSE_CURRENT_SESSIONS:
    429       spdy_session_pool_->CloseCurrentSessions(ERR_ABORTED);
    430       break;
    431     case SPDY_POOL_CLOSE_IDLE_SESSIONS:
    432       GURL url(test_hosts[0].url);
    433       base::WeakPtr<SpdyStream> spdy_stream =
    434           CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM,
    435                                     session, url, MEDIUM, BoundNetLog());
    436       GURL url1(test_hosts[1].url);
    437       base::WeakPtr<SpdyStream> spdy_stream1 =
    438           CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM,
    439                                     session1, url1, MEDIUM, BoundNetLog());
    440       GURL url2(test_hosts[2].url);
    441       base::WeakPtr<SpdyStream> spdy_stream2 =
    442           CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM,
    443                                     session2, url2, MEDIUM, BoundNetLog());
    444 
    445       // Close streams to make spdy_session and spdy_session1 inactive.
    446       session->CloseCreatedStream(spdy_stream, OK);
    447       EXPECT_EQ(NULL, spdy_stream.get());
    448       session1->CloseCreatedStream(spdy_stream1, OK);
    449       EXPECT_EQ(NULL, spdy_stream1.get());
    450 
    451       // Check spdy_session and spdy_session1 are not closed.
    452       EXPECT_FALSE(session->is_active());
    453       EXPECT_FALSE(session->IsClosed());
    454       EXPECT_FALSE(session1->is_active());
    455       EXPECT_FALSE(session1->IsClosed());
    456       EXPECT_TRUE(session2->is_active());
    457       EXPECT_FALSE(session2->IsClosed());
    458 
    459       // Test that calling CloseIdleSessions, does not cause a crash.
    460       // http://crbug.com/181400
    461       spdy_session_pool_->CloseCurrentIdleSessions();
    462 
    463       // Verify spdy_session and spdy_session1 are closed.
    464       EXPECT_TRUE(session == NULL);
    465       EXPECT_TRUE(session1 == NULL);
    466       EXPECT_TRUE(session2->is_active());
    467       EXPECT_FALSE(session2->IsClosed());
    468 
    469       spdy_stream2->Cancel();
    470       EXPECT_EQ(NULL, spdy_stream.get());
    471       EXPECT_EQ(NULL, spdy_stream1.get());
    472       EXPECT_EQ(NULL, spdy_stream2.get());
    473       session2->CloseSessionOnError(ERR_ABORTED, std::string());
    474       EXPECT_TRUE(session2 == NULL);
    475       break;
    476   }
    477 
    478   // Verify that the map is all cleaned up.
    479   EXPECT_FALSE(HasSpdySession(spdy_session_pool_, test_hosts[0].key));
    480   EXPECT_FALSE(HasSpdySession(spdy_session_pool_, test_hosts[1].key));
    481   EXPECT_FALSE(HasSpdySession(spdy_session_pool_, test_hosts[2].key));
    482 }
    483 
    484 TEST_P(SpdySessionPoolTest, IPPooling) {
    485   RunIPPoolingTest(SPDY_POOL_CLOSE_SESSIONS_MANUALLY);
    486 }
    487 
    488 TEST_P(SpdySessionPoolTest, IPPoolingCloseCurrentSessions) {
    489   RunIPPoolingTest(SPDY_POOL_CLOSE_CURRENT_SESSIONS);
    490 }
    491 
    492 TEST_P(SpdySessionPoolTest, IPPoolingCloseIdleSessions) {
    493   RunIPPoolingTest(SPDY_POOL_CLOSE_IDLE_SESSIONS);
    494 }
    495 
    496 }  // namespace
    497 
    498 }  // namespace net
    499