Index: net/spdy/spdy_session_spdy2_unittest.cc |
diff --git a/net/spdy/spdy_session_spdy2_unittest.cc b/net/spdy/spdy_session_spdy2_unittest.cc |
index 08d2afcd952098cb9c73e5ad92285f7cc6ed2b13..73f9197d766cb9e6dd0f0b98db4fb247edb7143b 100644 |
--- a/net/spdy/spdy_session_spdy2_unittest.cc |
+++ b/net/spdy/spdy_session_spdy2_unittest.cc |
@@ -1067,4 +1067,144 @@ TEST_F(SpdySessionSpdy2Test, CloseSessionOnError) { |
EXPECT_EQ(ERR_CONNECTION_CLOSED, request_params->status()); |
} |
+TEST_F(SpdySessionSpdy2Test, CloseOneIdleConnection) { |
+ MockHostResolver host_resolver; |
+ CapturingBoundNetLog log(CapturingNetLog::kUnbounded); |
+ ClientSocketPoolHistograms tcp_histograms(""); |
+ MockClientSocketFactory socket_factory; |
+ 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); |
+ socket_factory.AddSocketDataProvider(&data); |
+ socket_factory.AddSocketDataProvider(&data); |
+ socket_factory.AddSocketDataProvider(&data); |
+ socket_factory.AddSocketDataProvider(&data); |
+ socket_factory.AddSocketDataProvider(&data); |
+ socket_factory.AddSocketDataProvider(&data); |
+ TransportClientSocketPool pool( |
+ 3, 2, |
+ &tcp_histograms, |
+ &host_resolver, |
+ &socket_factory, NULL); |
+ // Now if I check out 1 socket from 3 different groups, the next request |
+ // will leave us stalled. |
+ |
+ TestCompletionCallback callback1; |
+ HostPortPair host_port1("1.com", 80); |
+ scoped_refptr<TransportSocketParams> params1( |
+ new TransportSocketParams(host_port1, MEDIUM, false, false)); |
+ scoped_ptr<ClientSocketHandle> connection1(new ClientSocketHandle); |
+ EXPECT_EQ(ERR_IO_PENDING, |
+ connection1->Init(host_port1.ToString(), params1, MEDIUM, |
+ callback1.callback(), &pool, log.bound())); |
+ EXPECT_EQ(OK, callback1.WaitForResult()); |
+ EXPECT_FALSE(pool.IsStalled()); |
+ EXPECT_TRUE(connection1->is_initialized()); |
+ EXPECT_TRUE(connection1->socket()); |
+ |
+ TestCompletionCallback callback2; |
+ HostPortPair host_port2("2.com", 80); |
+ scoped_refptr<TransportSocketParams> params2( |
+ new TransportSocketParams(host_port2, MEDIUM, false, false)); |
+ scoped_ptr<ClientSocketHandle> connection2(new ClientSocketHandle); |
+ EXPECT_EQ(ERR_IO_PENDING, |
+ connection2->Init(host_port2.ToString(), params2, MEDIUM, |
+ callback2.callback(), &pool, log.bound())); |
+ EXPECT_EQ(OK, callback2.WaitForResult()); |
+ EXPECT_FALSE(pool.IsStalled()); |
+ |
+ TestCompletionCallback callback3; |
+ HostPortPair host_port3("3.com", 80); |
+ scoped_refptr<TransportSocketParams> params3( |
+ new TransportSocketParams(host_port3, MEDIUM, false, false)); |
+ scoped_ptr<ClientSocketHandle> connection3(new ClientSocketHandle); |
+ EXPECT_EQ(ERR_IO_PENDING, |
+ connection3->Init(host_port3.ToString(), params3, MEDIUM, |
+ callback3.callback(), &pool, log.bound())); |
+ EXPECT_EQ(OK, callback3.WaitForResult()); |
+ EXPECT_FALSE(pool.IsStalled()); |
+ |
+ TestCompletionCallback callback4; |
+ HostPortPair host_port4("4.com", 80); |
+ scoped_refptr<TransportSocketParams> params4( |
+ new TransportSocketParams(host_port4, MEDIUM, false, false)); |
+ scoped_ptr<ClientSocketHandle> connection4(new ClientSocketHandle); |
+ EXPECT_EQ(ERR_IO_PENDING, |
+ connection4->Init(host_port4.ToString(), params4, MEDIUM, |
+ callback4.callback(), &pool, log.bound())); |
+ EXPECT_TRUE(pool.IsStalled()); |
+ |
+ // Return 1 socket to the pool so that we are no longer stalled |
+ connection3->socket()->Disconnect(); |
+ connection3->Reset(); |
+ EXPECT_EQ(OK, callback4.WaitForResult()); |
+ EXPECT_FALSE(pool.IsStalled()); |
+ |
+ // Now, wrap one of the sockets in a SpdySession |
+ HttpServerPropertiesImpl props; |
+ SpdySessionPool spdy_session_pool(&host_resolver, NULL, &props); |
+ HostPortProxyPair pair1(host_port1, ProxyServer::Direct()); |
+ EXPECT_FALSE(spdy_session_pool.HasSession(pair1)); |
+ scoped_refptr<SpdySession> session1 = |
+ spdy_session_pool.Get(pair1, log.bound()); |
+ EXPECT_TRUE(spdy_session_pool.HasSession(pair1)); |
+ EXPECT_EQ(OK, |
+ session1->InitializeWithSocket(connection1.release(), false, OK)); |
+ session1 = NULL; |
+ EXPECT_TRUE(spdy_session_pool.HasSession(pair1)); |
+ |
+ // The SpdySession is now idle. When we request the next socket from the |
+ // transport pool, the session will be closed via CloseOneIdleConnection(). |
+ TestCompletionCallback callback5; |
+ HostPortPair host_port5("5.com", 80); |
+ scoped_refptr<TransportSocketParams> params5( |
+ new TransportSocketParams(host_port5, MEDIUM, false, false)); |
+ scoped_ptr<ClientSocketHandle> connection5(new ClientSocketHandle); |
+ EXPECT_EQ(ERR_IO_PENDING, |
+ connection5->Init(host_port5.ToString(), params5, MEDIUM, |
+ callback5.callback(), &pool, log.bound())); |
+ EXPECT_FALSE(pool.IsStalled()); |
+ EXPECT_EQ(OK, callback5.WaitForResult()); |
+ EXPECT_FALSE(spdy_session_pool.HasSession(pair1)); |
+ EXPECT_FALSE(pool.IsStalled()); |
+ |
+ // Now, wrap one of the sockets in a SpdySession |
+ HostPortProxyPair pair2(host_port2, ProxyServer::Direct()); |
+ EXPECT_FALSE(spdy_session_pool.HasSession(pair2)); |
+ scoped_refptr<SpdySession> session2 = |
+ spdy_session_pool.Get(pair2, log.bound()); |
+ EXPECT_TRUE(spdy_session_pool.HasSession(pair2)); |
+ EXPECT_EQ(OK, |
+ session2->InitializeWithSocket(connection2.release(), false, OK)); |
+ |
+ // Manually remove the socket from the pool. This does *not* return the |
+ // transport socket. It will be returned only when the SpdySession is |
+ // destructed. |
+ session2->RemoveFromPool(); |
+ EXPECT_FALSE(spdy_session_pool.HasSession(pair2)); |
+ |
+ // Although there are no active streams on the session, the pool does not |
+ // hold a reference. This means that CloseOneIdleConnection should not |
+ // return true, and this request should stall. |
+ TestCompletionCallback callback6; |
+ HostPortPair host_port6("6.com", 80); |
+ scoped_refptr<TransportSocketParams> params6( |
+ new TransportSocketParams(host_port5, MEDIUM, false, false)); |
+ scoped_ptr<ClientSocketHandle> connection6(new ClientSocketHandle); |
+ EXPECT_EQ(ERR_IO_PENDING, |
+ connection6->Init(host_port6.ToString(), params6, MEDIUM, |
+ callback6.callback(), &pool, log.bound())); |
+ EXPECT_TRUE(pool.IsStalled()); |
+ |
+ // But now if we drop our reference to the session, it will be destructed |
+ // and the transport socket will return to the pool, unblocking this |
+ // request. |
+ session2 = NULL; |
+ EXPECT_EQ(OK, callback6.WaitForResult()); |
+ EXPECT_FALSE(pool.IsStalled()); |
+} |
+ |
} // namespace net |