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