Chromium Code Reviews| Index: net/spdy/spdy_session_unittest.cc |
| diff --git a/net/spdy/spdy_session_unittest.cc b/net/spdy/spdy_session_unittest.cc |
| index fa4e12be8c4a0327d6e0b21147ee0c1518f1a36f..cce77ef1b197466cfb39aab31b16186299bdb2a5 100644 |
| --- a/net/spdy/spdy_session_unittest.cc |
| +++ b/net/spdy/spdy_session_unittest.cc |
| @@ -219,13 +219,7 @@ INSTANTIATE_TEST_CASE_P( |
| SpdySessionTest, |
| testing::Values(kProtoSPDY2, kProtoSPDY3, kProtoSPDY31, kProtoSPDY4a2)); |
| -// TODO(akalin): Don't early-exit in the tests below for values > |
| -// kProtoSPDY3. |
| - |
| TEST_P(SpdySessionTest, GoAway) { |
| - if (GetParam() > kProtoSPDY3) |
| - return; |
| - |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| @@ -308,9 +302,6 @@ TEST_P(SpdySessionTest, GoAway) { |
| } |
| TEST_P(SpdySessionTest, ClientPing) { |
| - if (GetParam() > kProtoSPDY3) |
| - return; |
| - |
| session_deps_.enable_ping = true; |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| @@ -367,9 +358,6 @@ TEST_P(SpdySessionTest, ClientPing) { |
| } |
| TEST_P(SpdySessionTest, ServerPing) { |
| - if (GetParam() > kProtoSPDY3) |
| - return; |
| - |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| @@ -412,9 +400,6 @@ TEST_P(SpdySessionTest, ServerPing) { |
| } |
| TEST_P(SpdySessionTest, DeleteExpiredPushStreams) { |
| - if (GetParam() > kProtoSPDY3) |
| - return; |
| - |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| SSLSocketDataProvider ssl(SYNCHRONOUS, OK); |
| @@ -471,9 +456,6 @@ TEST_P(SpdySessionTest, DeleteExpiredPushStreams) { |
| } |
| TEST_P(SpdySessionTest, FailedPing) { |
| - if (GetParam() > kProtoSPDY3) |
| - return; |
| - |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| @@ -536,9 +518,6 @@ TEST_P(SpdySessionTest, FailedPing) { |
| } |
| TEST_P(SpdySessionTest, CloseIdleSessions) { |
| - if (GetParam() > kProtoSPDY3) |
| - return; |
| - |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| MockRead reads[] = { |
| MockRead(ASYNC, 0, 0) // EOF |
| @@ -662,9 +641,6 @@ TEST_P(SpdySessionTest, CloseIdleSessions) { |
| // Make sure nothing blows up. |
| // http://crbug.com/57331 |
| TEST_P(SpdySessionTest, OnSettings) { |
| - if (GetParam() > kProtoSPDY3) |
| - return; |
| - |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| SettingsMap new_settings; |
| @@ -723,9 +699,6 @@ TEST_P(SpdySessionTest, OnSettings) { |
| // settings frame setting max concurrent streams to 2 and which also clears the |
| // persisted data. Verify that persisted data is correct. |
| TEST_P(SpdySessionTest, ClearSettings) { |
| - if (GetParam() > kProtoSPDY3) |
| - return; |
| - |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| SettingsMap new_settings; |
| @@ -740,7 +713,7 @@ TEST_P(SpdySessionTest, ClearSettings) { |
| scoped_ptr<SpdyFrame> settings_frame( |
| spdy_util_.ConstructSpdySettings(new_settings)); |
| uint8 flags = SETTINGS_FLAG_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS; |
| - test::SetFrameFlags(settings_frame.get(), flags, SPDY3); |
| + test::SetFrameFlags(settings_frame.get(), flags, spdy_util_.spdy_version()); |
| MockRead reads[] = { |
| CreateMockRead(*settings_frame), |
| MockRead(SYNCHRONOUS, 0, 0) // EOF |
| @@ -799,9 +772,6 @@ TEST_P(SpdySessionTest, ClearSettings) { |
| // second stream creation. Then cancel that one immediately. Don't crash. |
| // http://crbug.com/63532 |
| TEST_P(SpdySessionTest, CancelPendingCreateStream) { |
| - if (GetParam() > kProtoSPDY3) |
| - return; |
| - |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| MockRead reads[] = { |
| @@ -857,9 +827,6 @@ TEST_P(SpdySessionTest, CancelPendingCreateStream) { |
| } |
| TEST_P(SpdySessionTest, SendInitialSettingsOnNewSession) { |
| - if (GetParam() > kProtoSPDY3) |
| - return; |
| - |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| MockRead reads[] = { |
| @@ -879,13 +846,22 @@ TEST_P(SpdySessionTest, SendInitialSettingsOnNewSession) { |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| scoped_ptr<SpdyFrame> settings_frame( |
| spdy_util_.ConstructSpdySettings(settings)); |
| + scoped_ptr<SpdyFrame> initial_window_update( |
| + spdy_util_.ConstructSpdyWindowUpdate( |
| + kSessionFlowControlStreamId, |
| + kDefaultInitialRecvWindowSize - kSpdySessionInitialWindowSize)); |
| MockWrite writes[] = { |
| CreateMockWrite(*settings_frame), |
| + CreateMockWrite(*initial_window_update), |
| }; |
| session_deps_.stream_initial_recv_window_size = kInitialRecvWindowSize; |
| + int num_writes = arraysize(writes); |
| + // We don't have session windows for SPDY versions less than 3.1. |
| + if (spdy_util_.protocol() < kProtoSPDY31) { |
| + --num_writes; |
| + } |
| - StaticSocketDataProvider data( |
| - reads, arraysize(reads), writes, arraysize(writes)); |
| + StaticSocketDataProvider data(reads, arraysize(reads), writes, num_writes); |
| data.set_connect_data(connect_data); |
| session_deps_.socket_factory->AddSocketDataProvider(&data); |
| @@ -904,9 +880,6 @@ TEST_P(SpdySessionTest, SendInitialSettingsOnNewSession) { |
| } |
| TEST_P(SpdySessionTest, SendSettingsOnNewSession) { |
| - if (GetParam() > kProtoSPDY3) |
| - return; |
| - |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| MockRead reads[] = { |
| @@ -1204,9 +1177,6 @@ TEST_P(SpdySessionTest, IPPoolingCloseIdleSessions) { |
| } |
| TEST_P(SpdySessionTest, ClearSettingsStorageOnIPAddressChanged) { |
| - if (GetParam() > kProtoSPDY3) |
| - return; |
| - |
| CreateNetworkSession(); |
| HttpServerProperties* test_http_server_properties = |
| @@ -1225,9 +1195,6 @@ TEST_P(SpdySessionTest, ClearSettingsStorageOnIPAddressChanged) { |
| } |
| TEST_P(SpdySessionTest, Initialize) { |
| - if (GetParam() > kProtoSPDY3) |
| - return; |
| - |
| CapturingBoundNetLog log; |
| session_deps_.net_log = log.bound().net_log(); |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| @@ -1277,9 +1244,6 @@ TEST_P(SpdySessionTest, Initialize) { |
| } |
| TEST_P(SpdySessionTest, CloseSessionOnError) { |
| - if (GetParam() > kProtoSPDY3) |
| - return; |
| - |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| @@ -1333,9 +1297,6 @@ TEST_P(SpdySessionTest, CloseSessionOnError) { |
| // one. The high priority one should still send first and receive |
| // first. |
| TEST_P(SpdySessionTest, OutOfOrderSynStreams) { |
| - if (GetParam() > kProtoSPDY3) |
| - return; |
| - |
| // Construct the request. |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| scoped_ptr<SpdyFrame> req_highest( |
| @@ -1418,9 +1379,6 @@ TEST_P(SpdySessionTest, OutOfOrderSynStreams) { |
| } |
| TEST_P(SpdySessionTest, CancelStream) { |
| - if (GetParam() > kProtoSPDY3) |
| - return; |
| - |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| // Request 1, at HIGHEST priority, will be cancelled before it writes data. |
| // Request 2, at LOWEST priority, will be a full request and will be id 1. |
| @@ -1500,9 +1458,6 @@ TEST_P(SpdySessionTest, CancelStream) { |
| // and then close the session. Nothing should blow up. Also a |
| // regression test for http://crbug.com/139518 . |
| TEST_P(SpdySessionTest, CloseSessionWithTwoCreatedSelfClosingStreams) { |
| - if (GetParam() > kProtoSPDY3) |
| - return; |
| - |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| @@ -1576,9 +1531,6 @@ TEST_P(SpdySessionTest, CloseSessionWithTwoCreatedSelfClosingStreams) { |
| // Create two streams that are set to close each other on close, and |
| // then close the session. Nothing should blow up. |
| TEST_P(SpdySessionTest, CloseSessionWithTwoCreatedMutuallyClosingStreams) { |
| - if (GetParam() > kProtoSPDY3) |
| - return; |
| - |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| @@ -1654,9 +1606,6 @@ TEST_P(SpdySessionTest, CloseSessionWithTwoCreatedMutuallyClosingStreams) { |
| // Create two streams that are set to re-close themselves on close, |
| // activate them, and then close the session. Nothing should blow up. |
| TEST_P(SpdySessionTest, CloseSessionWithTwoActivatedSelfClosingStreams) { |
| - if (GetParam() > kProtoSPDY3) |
| - return; |
| - |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| @@ -1740,9 +1689,6 @@ TEST_P(SpdySessionTest, CloseSessionWithTwoActivatedSelfClosingStreams) { |
| // Create two streams that are set to close each other on close, |
| // activate them, and then close the session. Nothing should blow up. |
| TEST_P(SpdySessionTest, CloseSessionWithTwoActivatedMutuallyClosingStreams) { |
| - if (GetParam() > kProtoSPDY3) |
| - return; |
| - |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| @@ -1826,9 +1772,6 @@ TEST_P(SpdySessionTest, CloseSessionWithTwoActivatedMutuallyClosingStreams) { |
| } |
| TEST_P(SpdySessionTest, VerifyDomainAuthentication) { |
| - if (GetParam() > kProtoSPDY3) |
| - return; |
| - |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| @@ -1897,9 +1840,6 @@ TEST_P(SpdySessionTest, VerifyDomainAuthentication) { |
| } |
| TEST_P(SpdySessionTest, ConnectionPooledWithTlsChannelId) { |
| - if (GetParam() > kProtoSPDY3) |
| - return; |
| - |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| @@ -1969,9 +1909,6 @@ TEST_P(SpdySessionTest, ConnectionPooledWithTlsChannelId) { |
| } |
| TEST_P(SpdySessionTest, CloseTwoStalledCreateStream) { |
| - if (GetParam() > kProtoSPDY3) |
| - return; |
| - |
| // TODO(rtenneti): Define a helper class/methods and move the common code in |
| // this file. |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| @@ -2110,9 +2047,6 @@ TEST_P(SpdySessionTest, CloseTwoStalledCreateStream) { |
| } |
| TEST_P(SpdySessionTest, CancelTwoStalledCreateStream) { |
| - if (GetParam() > kProtoSPDY3) |
| - return; |
| - |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| MockRead reads[] = { |
| @@ -2190,9 +2124,6 @@ TEST_P(SpdySessionTest, CancelTwoStalledCreateStream) { |
| } |
| TEST_P(SpdySessionTest, NeedsCredentials) { |
| - if (GetParam() > kProtoSPDY3) |
| - return; |
| - |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| MockRead reads[] = { |
| MockRead(SYNCHRONOUS, ERR_IO_PENDING) // Stall forever. |
| @@ -2251,141 +2182,10 @@ TEST_P(SpdySessionTest, NeedsCredentials) { |
| spdy_session_pool_->Remove(session); |
| } |
| -TEST_P(SpdySessionTest, SendCredentials) { |
|
Ryan Hamilton
2013/06/27 17:23:50
More huge diffs in this file. Any chance they can
akalin
2013/06/27 20:00:06
moved back.
|
| - if (GetParam() > kProtoSPDY3) |
| - return; |
| - |
| - MockConnect connect_data(SYNCHRONOUS, OK); |
| - MockRead reads[] = { |
| - MockRead(SYNCHRONOUS, ERR_IO_PENDING) // Stall forever. |
| - }; |
| - SettingsMap settings; |
| - scoped_ptr<SpdyFrame> settings_frame( |
| - spdy_util_.ConstructSpdySettings(settings)); |
| - MockWrite writes[] = { |
| - CreateMockWrite(*settings_frame), |
| - }; |
| - StaticSocketDataProvider data(reads, arraysize(reads), |
| - writes, arraysize(writes)); |
| - data.set_connect_data(connect_data); |
| - session_deps_.socket_factory->AddSocketDataProvider(&data); |
| - |
| - SSLSocketDataProvider ssl(SYNCHRONOUS, OK); |
| - ssl.channel_id_sent = true; |
| - ssl.protocol_negotiated = kProtoSPDY3; |
| - session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl); |
| - |
| - CreateNetworkSession(); |
| - |
| - const GURL kTestUrl("https://www.foo.com"); |
| - HostPortPair test_host_port_pair(kTestUrl.host(), 443); |
| - SpdySessionKey key(test_host_port_pair, ProxyServer::Direct(), |
| - kPrivacyModeDisabled); |
| - |
| - scoped_refptr<SpdySession> session = GetSession(key); |
| - |
| - SSLConfig ssl_config; |
| - scoped_refptr<TransportSocketParams> transport_params( |
| - new TransportSocketParams(test_host_port_pair, |
| - MEDIUM, |
| - false, |
| - false, |
| - OnHostResolutionCallback())); |
| - scoped_refptr<SOCKSSocketParams> socks_params; |
| - scoped_refptr<HttpProxySocketParams> http_proxy_params; |
| - scoped_refptr<SSLSocketParams> ssl_params( |
| - new SSLSocketParams(transport_params, |
| - socks_params, |
| - http_proxy_params, |
| - ProxyServer::SCHEME_DIRECT, |
| - test_host_port_pair, |
| - ssl_config, |
| - 0, |
| - false, |
| - false)); |
| - scoped_ptr<ClientSocketHandle> connection(new ClientSocketHandle); |
| - EXPECT_EQ(OK, connection->Init(test_host_port_pair.ToString(), |
| - ssl_params, MEDIUM, CompletionCallback(), |
| - http_session_->GetSSLSocketPool( |
| - HttpNetworkSession::NORMAL_SOCKET_POOL), |
| - BoundNetLog())); |
| - |
| - EXPECT_EQ(OK, session->InitializeWithSocket(connection.release(), true, OK)); |
| - EXPECT_TRUE(session->NeedsCredentials()); |
| - |
| - // Flush the SpdySession::OnReadComplete() task. |
| - base::MessageLoop::current()->RunUntilIdle(); |
| - |
| - spdy_session_pool_->Remove(session); |
| - EXPECT_FALSE(spdy_session_pool_->HasSession(key)); |
| -} |
| - |
| -TEST_P(SpdySessionTest, UpdateStreamsSendWindowSize) { |
| - if (GetParam() != kProtoSPDY3) |
| - return; |
| - |
| - // Set SETTINGS_INITIAL_WINDOW_SIZE to a small number so that WINDOW_UPDATE |
| - // gets sent. |
| - SettingsMap new_settings; |
| - int32 window_size = 1; |
| - new_settings[SETTINGS_INITIAL_WINDOW_SIZE] = |
| - SettingsFlagsAndValue(SETTINGS_FLAG_NONE, window_size); |
| - |
| - // Set up the socket so we read a SETTINGS frame that sets |
| - // INITIAL_WINDOW_SIZE. |
| - MockConnect connect_data(SYNCHRONOUS, OK); |
| - scoped_ptr<SpdyFrame> settings_frame( |
| - spdy_util_.ConstructSpdySettings(new_settings)); |
| - MockRead reads[] = { |
| - CreateMockRead(*settings_frame, 0), |
| - MockRead(ASYNC, 0, 1) // EOF |
| - }; |
| - |
| - session_deps_.host_resolver->set_synchronous_mode(true); |
| - |
| - scoped_ptr<DeterministicSocketData> data( |
| - new DeterministicSocketData(reads, arraysize(reads), NULL, 0)); |
| - data->set_connect_data(connect_data); |
| - session_deps_.deterministic_socket_factory->AddSocketDataProvider(data.get()); |
| - |
| - SSLSocketDataProvider ssl(SYNCHRONOUS, OK); |
| - session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl); |
| - |
| - CreateDeterministicNetworkSession(); |
| - |
| - scoped_refptr<SpdySession> session = CreateInitializedSession(); |
| - base::WeakPtr<SpdyStream> spdy_stream1 = |
| - CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM, |
| - session, test_url_, MEDIUM, BoundNetLog()); |
| - ASSERT_TRUE(spdy_stream1.get() != NULL); |
| - TestCompletionCallback callback1; |
| - EXPECT_NE(spdy_stream1->send_window_size(), window_size); |
| - |
| - data->RunFor(1); // Process the SETTINGS frame, but not the EOF |
| - base::MessageLoop::current()->RunUntilIdle(); |
| - EXPECT_EQ(session->stream_initial_send_window_size(), window_size); |
| - EXPECT_EQ(spdy_stream1->send_window_size(), window_size); |
| - |
| - // Release the first one, this will allow the second to be created. |
| - spdy_stream1->Cancel(); |
| - EXPECT_EQ(NULL, spdy_stream1.get()); |
| - |
| - base::WeakPtr<SpdyStream> spdy_stream2 = |
| - CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM, |
| - session, test_url_, MEDIUM, BoundNetLog()); |
| - ASSERT_TRUE(spdy_stream2.get() != NULL); |
| - EXPECT_EQ(spdy_stream2->send_window_size(), window_size); |
| - spdy_stream2->Cancel(); |
| - EXPECT_EQ(NULL, spdy_stream2.get()); |
| -} |
| - |
| // Test that SpdySession::DoRead reads data from the socket without yielding. |
| // This test makes 32k - 1 bytes of data available on the socket for reading. It |
| // then verifies that it has read all the available data without yielding. |
| TEST_P(SpdySessionTest, ReadDataWithoutYielding) { |
| - if (GetParam() > kProtoSPDY3) |
| - return; |
| - |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| BufferedSpdyFramer framer(spdy_util_.spdy_version(), false); |
| @@ -2475,9 +2275,6 @@ TEST_P(SpdySessionTest, ReadDataWithoutYielding) { |
| // that DoRead has yielded even though there is data available for it to read |
| // (i.e, socket()->Read didn't return ERR_IO_PENDING during socket reads). |
| TEST_P(SpdySessionTest, TestYieldingDuringReadData) { |
| - if (GetParam() > kProtoSPDY3) |
| - return; |
| - |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| BufferedSpdyFramer framer(spdy_util_.spdy_version(), false); |
| @@ -2575,9 +2372,6 @@ TEST_P(SpdySessionTest, TestYieldingDuringReadData) { |
| // will read the results from the async read, and rest of the data |
| // synchronously. |
| TEST_P(SpdySessionTest, TestYieldingDuringAsyncReadData) { |
| - if (GetParam() > kProtoSPDY3) |
| - return; |
| - |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| BufferedSpdyFramer framer(spdy_util_.spdy_version(), false); |
| @@ -2683,9 +2477,6 @@ TEST_P(SpdySessionTest, TestYieldingDuringAsyncReadData) { |
| // GoAway could delete the SpdySession from the SpdySessionPool and the last |
| // reference to SpdySession. |
| TEST_P(SpdySessionTest, GoAwayWhileInDoLoop) { |
| - if (GetParam() > kProtoSPDY3) |
| - return; |
| - |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| BufferedSpdyFramer framer(spdy_util_.spdy_version(), false); |
| @@ -2793,75 +2584,531 @@ TEST_P(SpdySessionTest, ProtocolNegotiation) { |
| session->flow_control_state()); |
| EXPECT_EQ(kSpdySessionInitialWindowSize, |
| session->session_send_window_size_); |
| - EXPECT_EQ(kDefaultInitialRecvWindowSize, |
| + EXPECT_EQ(kSpdySessionInitialWindowSize, |
| session->session_recv_window_size_); |
| } |
| EXPECT_EQ(0, session->session_unacked_recv_window_bytes_); |
| } |
| -// SpdySession::{Increase,Decrease}RecvWindowSize should properly |
| -// adjust the session receive window size when the "enable_spdy_31" |
| -// flag is set. In addition, SpdySession::IncreaseRecvWindowSize |
| -// should trigger sending a WINDOW_UPDATE frame for a large enough |
| -// delta. |
| -TEST_P(SpdySessionTest, AdjustRecvWindowSize) { |
| - if (GetParam() < kProtoSPDY31) |
| - return; |
| - |
| - session_deps_.host_resolver->set_synchronous_mode(true); |
| - |
| - const int32 delta_window_size = 100; |
| +// Tests the case of a non-SPDY request closing an idle SPDY session when no |
| +// pointers to the idle session are currently held. |
| +TEST_P(SpdySessionTest, CloseOneIdleConnection) { |
| + ClientSocketPoolManager::set_max_sockets_per_group( |
| + HttpNetworkSession::NORMAL_SOCKET_POOL, 1); |
| + ClientSocketPoolManager::set_max_sockets_per_pool( |
| + HttpNetworkSession::NORMAL_SOCKET_POOL, 1); |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| MockRead reads[] = { |
| - MockRead(ASYNC, 0, 2) // EOF |
| - }; |
| - scoped_ptr<SpdyFrame> initial_window_update( |
| - spdy_util_.ConstructSpdyWindowUpdate( |
| - kSessionFlowControlStreamId, |
| - kDefaultInitialRecvWindowSize - kSpdySessionInitialWindowSize)); |
| - scoped_ptr<SpdyFrame> window_update( |
| - spdy_util_.ConstructSpdyWindowUpdate( |
| - kSessionFlowControlStreamId, |
| - kSpdySessionInitialWindowSize + delta_window_size)); |
| - MockWrite writes[] = { |
| - CreateMockWrite(*initial_window_update, 0), |
| - CreateMockWrite(*window_update, 1), |
| + MockRead(SYNCHRONOUS, ERR_IO_PENDING) // Stall forever. |
| }; |
| - DeterministicSocketData data(reads, arraysize(reads), |
| - writes, arraysize(writes)); |
| + StaticSocketDataProvider data(reads, arraysize(reads), NULL, 0); |
| data.set_connect_data(connect_data); |
| - session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data); |
| - |
| - SSLSocketDataProvider ssl(SYNCHRONOUS, OK); |
| - session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl); |
| + session_deps_.socket_factory->AddSocketDataProvider(&data); |
| + session_deps_.socket_factory->AddSocketDataProvider(&data); |
| - CreateDeterministicNetworkSession(); |
| - scoped_refptr<SpdySession> session = GetSession(key_); |
| - InitializeSession( |
| - http_session_.get(), session.get(), test_host_port_pair_); |
| - EXPECT_EQ(SpdySession::FLOW_CONTROL_STREAM_AND_SESSION, |
| - session->flow_control_state()); |
| + CreateNetworkSession(); |
| - EXPECT_EQ(kDefaultInitialRecvWindowSize, session->session_recv_window_size_); |
| - EXPECT_EQ(0, session->session_unacked_recv_window_bytes_); |
| + TransportClientSocketPool* pool = |
| + http_session_->GetTransportSocketPool( |
| + HttpNetworkSession::NORMAL_SOCKET_POOL); |
| - session->IncreaseRecvWindowSize(delta_window_size); |
| - EXPECT_EQ(kDefaultInitialRecvWindowSize + delta_window_size, |
| - session->session_recv_window_size_); |
| - EXPECT_EQ(delta_window_size, session->session_unacked_recv_window_bytes_); |
| + // Create an idle SPDY session. |
| + SpdySessionKey key1(HostPortPair("1.com", 80), ProxyServer::Direct(), |
| + kPrivacyModeDisabled); |
| + scoped_refptr<SpdySession> session1 = GetSession(key1); |
| + EXPECT_EQ( |
| + OK, |
| + InitializeSession(http_session_.get(), session1.get(), |
| + key1.host_port_pair())); |
| + EXPECT_FALSE(pool->IsStalled()); |
| + // Release the pointer to the session so it can be closed. |
| + session1 = NULL; |
| - // Should trigger sending a WINDOW_UPDATE frame. |
| - session->IncreaseRecvWindowSize(kSpdySessionInitialWindowSize); |
| - EXPECT_EQ(kDefaultInitialRecvWindowSize + delta_window_size + |
| - kSpdySessionInitialWindowSize, |
| + // Trying to create a new connection should cause the pool to be stalled, and |
| + // post a task asynchronously to try and close the session. |
| + TestCompletionCallback callback2; |
| + HostPortPair host_port2("2.com", 80); |
| + scoped_refptr<TransportSocketParams> params2( |
| + new TransportSocketParams(host_port2, DEFAULT_PRIORITY, false, false, |
| + OnHostResolutionCallback())); |
| + scoped_ptr<ClientSocketHandle> connection2(new ClientSocketHandle); |
| + EXPECT_EQ(ERR_IO_PENDING, |
| + connection2->Init(host_port2.ToString(), params2, DEFAULT_PRIORITY, |
| + callback2.callback(), pool, BoundNetLog())); |
| + EXPECT_TRUE(pool->IsStalled()); |
| + |
| + // The socket pool should close the connection asynchronously and establish a |
| + // new connection. |
| + EXPECT_EQ(OK, callback2.WaitForResult()); |
| + EXPECT_FALSE(pool->IsStalled()); |
| +} |
| + |
| +// Tests the case of a non-SPDY request closing an idle SPDY session when no |
| +// pointers to the idle session are currently held, in the case the SPDY session |
| +// has an alias. |
| +TEST_P(SpdySessionTest, CloseOneIdleConnectionWithAlias) { |
| + ClientSocketPoolManager::set_max_sockets_per_group( |
| + HttpNetworkSession::NORMAL_SOCKET_POOL, 1); |
| + ClientSocketPoolManager::set_max_sockets_per_pool( |
| + HttpNetworkSession::NORMAL_SOCKET_POOL, 1); |
| + |
| + 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); |
| + session_deps_.socket_factory->AddSocketDataProvider(&data); |
| + |
| + session_deps_.host_resolver->set_synchronous_mode(true); |
| + session_deps_.host_resolver->rules()->AddIPLiteralRule( |
| + "1.com", "192.168.0.2", std::string()); |
| + session_deps_.host_resolver->rules()->AddIPLiteralRule( |
| + "2.com", "192.168.0.2", std::string()); |
| + // Not strictly needed. |
| + session_deps_.host_resolver->rules()->AddIPLiteralRule( |
| + "3.com", "192.168.0.3", std::string()); |
| + |
| + CreateNetworkSession(); |
| + |
| + TransportClientSocketPool* pool = |
| + http_session_->GetTransportSocketPool( |
| + HttpNetworkSession::NORMAL_SOCKET_POOL); |
| + |
| + // Create an idle SPDY session. |
| + SpdySessionKey key1(HostPortPair("1.com", 80), ProxyServer::Direct(), |
| + kPrivacyModeDisabled); |
| + scoped_refptr<SpdySession> session1 = GetSession(key1); |
| + EXPECT_EQ( |
| + OK, |
| + InitializeSession(http_session_.get(), session1.get(), |
| + key1.host_port_pair())); |
| + EXPECT_FALSE(pool->IsStalled()); |
| + |
| + // Set up an alias for the idle SPDY session, increasing its ref count to 2. |
| + SpdySessionKey key2(HostPortPair("2.com", 80), ProxyServer::Direct(), |
| + kPrivacyModeDisabled); |
| + SpdySessionPoolPeer pool_peer(spdy_session_pool_); |
| + HostResolver::RequestInfo info(key2.host_port_pair()); |
| + AddressList addresses; |
| + // Pre-populate the DNS cache, since a synchronous resolution is required in |
| + // order to create the alias. |
| + session_deps_.host_resolver->Resolve( |
| + info, &addresses, CompletionCallback(), NULL, BoundNetLog()); |
| + // Add the alias for the first session's key. Has to be done manually since |
| + // the usual process is bypassed. |
| + pool_peer.AddAlias(addresses.front(), key1); |
| + // Get a session for |key2|, which should return the session created earlier. |
| + scoped_refptr<SpdySession> session2 = |
| + spdy_session_pool_->Get(key2, BoundNetLog()); |
| + ASSERT_EQ(session1.get(), session2.get()); |
| + EXPECT_FALSE(pool->IsStalled()); |
| + |
| + // Release both the pointers to the session so it can be closed. |
| + session1 = NULL; |
| + session2 = NULL; |
| + |
| + // Trying to create a new connection should cause the pool to be stalled, and |
| + // post a task asynchronously to try and close the session. |
| + TestCompletionCallback callback3; |
| + HostPortPair host_port3("3.com", 80); |
| + scoped_refptr<TransportSocketParams> params3( |
| + new TransportSocketParams(host_port3, DEFAULT_PRIORITY, false, false, |
| + OnHostResolutionCallback())); |
| + scoped_ptr<ClientSocketHandle> connection3(new ClientSocketHandle); |
| + EXPECT_EQ(ERR_IO_PENDING, |
| + connection3->Init(host_port3.ToString(), params3, DEFAULT_PRIORITY, |
| + callback3.callback(), pool, BoundNetLog())); |
| + EXPECT_TRUE(pool->IsStalled()); |
| + |
| + // The socket pool should close the connection asynchronously and establish a |
| + // new connection. |
| + EXPECT_EQ(OK, callback3.WaitForResult()); |
| + EXPECT_FALSE(pool->IsStalled()); |
| +} |
| + |
| +// Tests the case of a non-SPDY request closing an idle SPDY session when a |
| +// pointer to the idle session is still held. |
| +TEST_P(SpdySessionTest, CloseOneIdleConnectionSessionStillHeld) { |
| + ClientSocketPoolManager::set_max_sockets_per_group( |
| + HttpNetworkSession::NORMAL_SOCKET_POOL, 1); |
| + ClientSocketPoolManager::set_max_sockets_per_pool( |
| + HttpNetworkSession::NORMAL_SOCKET_POOL, 1); |
| + |
| + 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); |
| + session_deps_.socket_factory->AddSocketDataProvider(&data); |
| + |
| + CreateNetworkSession(); |
| + |
| + TransportClientSocketPool* pool = |
| + http_session_->GetTransportSocketPool( |
| + HttpNetworkSession::NORMAL_SOCKET_POOL); |
| + |
| + // Create an idle SPDY session. |
| + SpdySessionKey key1(HostPortPair("1.com", 80), ProxyServer::Direct(), |
| + kPrivacyModeDisabled); |
| + scoped_refptr<SpdySession> session1 = GetSession(key1); |
| + EXPECT_EQ( |
| + OK, |
| + InitializeSession(http_session_.get(), session1.get(), |
| + key1.host_port_pair())); |
| + EXPECT_FALSE(pool->IsStalled()); |
| + |
| + // Trying to create a new connection should cause the pool to be stalled, and |
| + // post a task asynchronously to try and close the session. |
| + TestCompletionCallback callback2; |
| + HostPortPair host_port2("2.com", 80); |
| + scoped_refptr<TransportSocketParams> params2( |
| + new TransportSocketParams(host_port2, DEFAULT_PRIORITY, false, false, |
| + OnHostResolutionCallback())); |
| + scoped_ptr<ClientSocketHandle> connection2(new ClientSocketHandle); |
| + EXPECT_EQ(ERR_IO_PENDING, |
| + connection2->Init(host_port2.ToString(), params2, DEFAULT_PRIORITY, |
| + callback2.callback(), pool, BoundNetLog())); |
| + EXPECT_TRUE(pool->IsStalled()); |
| + |
| + // Running the message loop should cause the session to prepare to be closed, |
| + // but since there's still an outstanding reference, it should not be closed |
| + // yet. |
| + base::RunLoop().RunUntilIdle(); |
| + EXPECT_TRUE(pool->IsStalled()); |
| + EXPECT_FALSE(callback2.have_result()); |
| + |
| + // Release the pointer to the session so it can be closed. |
| + session1 = NULL; |
| + EXPECT_EQ(OK, callback2.WaitForResult()); |
| + EXPECT_FALSE(pool->IsStalled()); |
| +} |
| + |
| +// Tests that a non-SPDY request can't close a SPDY session that's currently in |
| +// use. |
| +TEST_P(SpdySessionTest, CloseOneIdleConnectionFailsWhenSessionInUse) { |
| + ClientSocketPoolManager::set_max_sockets_per_group( |
| + HttpNetworkSession::NORMAL_SOCKET_POOL, 1); |
| + ClientSocketPoolManager::set_max_sockets_per_pool( |
| + HttpNetworkSession::NORMAL_SOCKET_POOL, 1); |
| + |
| + MockConnect connect_data(SYNCHRONOUS, OK); |
| + MockRead reads[] = { |
| + MockRead(SYNCHRONOUS, ERR_IO_PENDING) // Stall forever. |
| + }; |
| + scoped_ptr<SpdyFrame> req1( |
| + spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, true)); |
| + scoped_ptr<SpdyFrame> cancel1( |
| + spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_CANCEL)); |
| + MockWrite writes[] = { |
| + CreateMockWrite(*req1, 1), |
| + CreateMockWrite(*cancel1, 1), |
| + }; |
| + StaticSocketDataProvider data(reads, arraysize(reads), |
| + writes, arraysize(writes)); |
| + data.set_connect_data(connect_data); |
| + session_deps_.socket_factory->AddSocketDataProvider(&data); |
| + |
| + CreateNetworkSession(); |
| + |
| + TransportClientSocketPool* pool = |
| + http_session_->GetTransportSocketPool( |
| + HttpNetworkSession::NORMAL_SOCKET_POOL); |
| + |
| + // Create a SPDY session. |
| + GURL url1("http://www.google.com"); |
| + SpdySessionKey key1(HostPortPair(url1.host(), 80), |
| + ProxyServer::Direct(), kPrivacyModeDisabled); |
| + scoped_refptr<SpdySession> session1 = GetSession(key1); |
| + EXPECT_EQ( |
| + OK, |
| + InitializeSession(http_session_.get(), session1.get(), |
| + key1.host_port_pair())); |
| + EXPECT_FALSE(pool->IsStalled()); |
| + |
| + // Create a stream using the session, and send a request. |
| + |
| + TestCompletionCallback callback1; |
| + base::WeakPtr<SpdyStream> spdy_stream1 = |
| + CreateStreamSynchronously(SPDY_REQUEST_RESPONSE_STREAM, |
| + session1, url1, DEFAULT_PRIORITY, |
| + BoundNetLog()); |
| + ASSERT_TRUE(spdy_stream1.get()); |
| + test::StreamDelegateDoNothing delegate1(spdy_stream1); |
| + spdy_stream1->SetDelegate(&delegate1); |
| + |
| + scoped_ptr<SpdyHeaderBlock> headers1( |
| + spdy_util_.ConstructGetHeaderBlock(url1.spec())); |
| + EXPECT_EQ(ERR_IO_PENDING, |
| + spdy_stream1->SendRequestHeaders( |
| + headers1.Pass(), NO_MORE_DATA_TO_SEND)); |
| + EXPECT_TRUE(spdy_stream1->HasUrl()); |
| + |
| + base::MessageLoop::current()->RunUntilIdle(); |
| + |
| + // Release the session, so holding onto a pointer here does not affect |
| + // anything. |
| + session1 = NULL; |
| + |
| + // Trying to create a new connection should cause the pool to be stalled, and |
| + // post a task asynchronously to try and close the session. |
| + TestCompletionCallback callback2; |
| + HostPortPair host_port2("2.com", 80); |
| + scoped_refptr<TransportSocketParams> params2( |
| + new TransportSocketParams(host_port2, DEFAULT_PRIORITY, false, false, |
| + OnHostResolutionCallback())); |
| + scoped_ptr<ClientSocketHandle> connection2(new ClientSocketHandle); |
| + EXPECT_EQ(ERR_IO_PENDING, |
| + connection2->Init(host_port2.ToString(), params2, DEFAULT_PRIORITY, |
| + callback2.callback(), pool, BoundNetLog())); |
| + EXPECT_TRUE(pool->IsStalled()); |
| + |
| + // Running the message loop should cause the socket pool to ask the SPDY |
| + // session to close an idle socket, but since the socket is in use, nothing |
| + // happens. |
| + base::RunLoop().RunUntilIdle(); |
| + EXPECT_TRUE(pool->IsStalled()); |
| + EXPECT_FALSE(callback2.have_result()); |
| + |
| + // Cancelling the request should still not release the session's socket, |
| + // since the session is still kept alive by the SpdySessionPool. |
| + ASSERT_TRUE(spdy_stream1.get()); |
| + spdy_stream1->Cancel(); |
| + base::RunLoop().RunUntilIdle(); |
| + EXPECT_TRUE(pool->IsStalled()); |
| + EXPECT_FALSE(callback2.have_result()); |
| +} |
| + |
| +// Verify that SpdySessionKey and therefore SpdySession is different when |
| +// privacy mode is enabled or disabled. |
| +TEST_P(SpdySessionTest, SpdySessionKeyPrivacyMode) { |
| + CreateDeterministicNetworkSession(); |
| + |
| + HostPortPair host_port_pair("www.google.com", 443); |
| + SpdySessionKey key_privacy_enabled(host_port_pair, ProxyServer::Direct(), |
| + kPrivacyModeEnabled); |
| + SpdySessionKey key_privacy_disabled(host_port_pair, ProxyServer::Direct(), |
| + kPrivacyModeDisabled); |
| + |
| + EXPECT_FALSE(spdy_session_pool_->HasSession(key_privacy_enabled)); |
| + EXPECT_FALSE(spdy_session_pool_->HasSession(key_privacy_disabled)); |
| + |
| + // Add SpdySession with PrivacyMode Enabled to the pool. |
| + scoped_refptr<SpdySession> session_privacy_enabled = |
| + spdy_session_pool_->Get(key_privacy_enabled, BoundNetLog()); |
| + |
| + EXPECT_TRUE(spdy_session_pool_->HasSession(key_privacy_enabled)); |
| + EXPECT_FALSE(spdy_session_pool_->HasSession(key_privacy_disabled)); |
| + |
| + // Add SpdySession with PrivacyMode Disabled to the pool. |
| + scoped_refptr<SpdySession> session_privacy_disabled = |
| + spdy_session_pool_->Get(key_privacy_disabled, BoundNetLog()); |
| + |
| + EXPECT_TRUE(spdy_session_pool_->HasSession(key_privacy_enabled)); |
| + EXPECT_TRUE(spdy_session_pool_->HasSession(key_privacy_disabled)); |
| + |
| + spdy_session_pool_->Remove(session_privacy_enabled); |
| + EXPECT_FALSE(spdy_session_pool_->HasSession(key_privacy_enabled)); |
| + EXPECT_TRUE(spdy_session_pool_->HasSession(key_privacy_disabled)); |
| + |
| + spdy_session_pool_->Remove(session_privacy_disabled); |
| + EXPECT_FALSE(spdy_session_pool_->HasSession(key_privacy_enabled)); |
| + EXPECT_FALSE(spdy_session_pool_->HasSession(key_privacy_disabled)); |
| +} |
| + |
| +// The tests below are only for SPDY/3 and above. |
| + |
| +TEST_P(SpdySessionTest, SendCredentials) { |
| + if (GetParam() < kProtoSPDY3) |
| + return; |
| + |
| + MockConnect connect_data(SYNCHRONOUS, OK); |
| + MockRead reads[] = { |
| + MockRead(SYNCHRONOUS, ERR_IO_PENDING) // Stall forever. |
| + }; |
| + SettingsMap settings; |
| + scoped_ptr<SpdyFrame> settings_frame( |
| + spdy_util_.ConstructSpdySettings(settings)); |
| + MockWrite writes[] = { |
| + CreateMockWrite(*settings_frame), |
| + }; |
| + StaticSocketDataProvider data(reads, arraysize(reads), |
| + writes, arraysize(writes)); |
| + data.set_connect_data(connect_data); |
| + session_deps_.socket_factory->AddSocketDataProvider(&data); |
| + |
| + SSLSocketDataProvider ssl(SYNCHRONOUS, OK); |
| + ssl.channel_id_sent = true; |
| + ssl.protocol_negotiated = GetParam(); |
| + session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl); |
| + |
| + CreateNetworkSession(); |
| + |
| + const GURL kTestUrl("https://www.foo.com"); |
| + HostPortPair test_host_port_pair(kTestUrl.host(), 443); |
| + SpdySessionKey key(test_host_port_pair, ProxyServer::Direct(), |
| + kPrivacyModeDisabled); |
| + |
| + scoped_refptr<SpdySession> session = GetSession(key); |
| + |
| + SSLConfig ssl_config; |
| + scoped_refptr<TransportSocketParams> transport_params( |
| + new TransportSocketParams(test_host_port_pair, |
| + MEDIUM, |
| + false, |
| + false, |
| + OnHostResolutionCallback())); |
| + scoped_refptr<SOCKSSocketParams> socks_params; |
| + scoped_refptr<HttpProxySocketParams> http_proxy_params; |
| + scoped_refptr<SSLSocketParams> ssl_params( |
| + new SSLSocketParams(transport_params, |
| + socks_params, |
| + http_proxy_params, |
| + ProxyServer::SCHEME_DIRECT, |
| + test_host_port_pair, |
| + ssl_config, |
| + 0, |
| + false, |
| + false)); |
| + scoped_ptr<ClientSocketHandle> connection(new ClientSocketHandle); |
| + EXPECT_EQ(OK, connection->Init(test_host_port_pair.ToString(), |
| + ssl_params, MEDIUM, CompletionCallback(), |
| + http_session_->GetSSLSocketPool( |
| + HttpNetworkSession::NORMAL_SOCKET_POOL), |
| + BoundNetLog())); |
| + |
| + EXPECT_EQ(OK, session->InitializeWithSocket(connection.release(), true, OK)); |
| + EXPECT_TRUE(session->NeedsCredentials()); |
| + |
| + // Flush the SpdySession::OnReadComplete() task. |
| + base::MessageLoop::current()->RunUntilIdle(); |
| + |
| + spdy_session_pool_->Remove(session); |
| + EXPECT_FALSE(spdy_session_pool_->HasSession(key)); |
| +} |
| + |
| +TEST_P(SpdySessionTest, UpdateStreamsSendWindowSize) { |
| + if (GetParam() < kProtoSPDY3) |
| + return; |
| + |
| + // Set SETTINGS_INITIAL_WINDOW_SIZE to a small number so that WINDOW_UPDATE |
| + // gets sent. |
| + SettingsMap new_settings; |
| + int32 window_size = 1; |
| + new_settings[SETTINGS_INITIAL_WINDOW_SIZE] = |
| + SettingsFlagsAndValue(SETTINGS_FLAG_NONE, window_size); |
| + |
| + // Set up the socket so we read a SETTINGS frame that sets |
| + // INITIAL_WINDOW_SIZE. |
| + MockConnect connect_data(SYNCHRONOUS, OK); |
| + scoped_ptr<SpdyFrame> settings_frame( |
| + spdy_util_.ConstructSpdySettings(new_settings)); |
| + MockRead reads[] = { |
| + CreateMockRead(*settings_frame, 0), |
| + MockRead(ASYNC, 0, 1) // EOF |
| + }; |
| + |
| + session_deps_.host_resolver->set_synchronous_mode(true); |
| + |
| + scoped_ptr<DeterministicSocketData> data( |
| + new DeterministicSocketData(reads, arraysize(reads), NULL, 0)); |
| + data->set_connect_data(connect_data); |
| + session_deps_.deterministic_socket_factory->AddSocketDataProvider(data.get()); |
| + |
| + SSLSocketDataProvider ssl(SYNCHRONOUS, OK); |
| + session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl); |
| + |
| + CreateDeterministicNetworkSession(); |
| + |
| + scoped_refptr<SpdySession> session = CreateInitializedSession(); |
| + base::WeakPtr<SpdyStream> spdy_stream1 = |
| + CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM, |
| + session, test_url_, MEDIUM, BoundNetLog()); |
| + ASSERT_TRUE(spdy_stream1.get() != NULL); |
| + TestCompletionCallback callback1; |
| + EXPECT_NE(spdy_stream1->send_window_size(), window_size); |
| + |
| + data->RunFor(1); // Process the SETTINGS frame, but not the EOF |
| + base::MessageLoop::current()->RunUntilIdle(); |
| + EXPECT_EQ(session->stream_initial_send_window_size(), window_size); |
| + EXPECT_EQ(spdy_stream1->send_window_size(), window_size); |
| + |
| + // Release the first one, this will allow the second to be created. |
| + spdy_stream1->Cancel(); |
| + EXPECT_EQ(NULL, spdy_stream1.get()); |
| + |
| + base::WeakPtr<SpdyStream> spdy_stream2 = |
| + CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM, |
| + session, test_url_, MEDIUM, BoundNetLog()); |
| + ASSERT_TRUE(spdy_stream2.get() != NULL); |
| + EXPECT_EQ(spdy_stream2->send_window_size(), window_size); |
| + spdy_stream2->Cancel(); |
| + EXPECT_EQ(NULL, spdy_stream2.get()); |
| +} |
| + |
| +// The tests below are only for SPDY/3.1 and above. |
| + |
| +// SpdySession::{Increase,Decrease}RecvWindowSize should properly |
| +// adjust the session receive window size for SPDY 3.1 and higher. In |
| +// addition, SpdySession::IncreaseRecvWindowSize should trigger |
| +// sending a WINDOW_UPDATE frame for a large enough delta. |
| +TEST_P(SpdySessionTest, AdjustRecvWindowSize) { |
| + if (GetParam() < kProtoSPDY31) |
| + return; |
| + |
| + session_deps_.host_resolver->set_synchronous_mode(true); |
| + |
| + const int32 delta_window_size = 100; |
| + |
| + MockConnect connect_data(SYNCHRONOUS, OK); |
| + MockRead reads[] = { |
| + MockRead(ASYNC, 0, 1) // EOF |
| + }; |
| + scoped_ptr<SpdyFrame> window_update( |
| + spdy_util_.ConstructSpdyWindowUpdate( |
| + kSessionFlowControlStreamId, |
| + kSpdySessionInitialWindowSize + delta_window_size)); |
| + MockWrite writes[] = { |
| + CreateMockWrite(*window_update, 0), |
| + }; |
| + DeterministicSocketData data(reads, arraysize(reads), |
| + writes, arraysize(writes)); |
| + data.set_connect_data(connect_data); |
| + session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data); |
| + |
| + SSLSocketDataProvider ssl(SYNCHRONOUS, OK); |
| + session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl); |
| + |
| + CreateDeterministicNetworkSession(); |
| + scoped_refptr<SpdySession> session = GetSession(key_); |
| + InitializeSession( |
| + http_session_.get(), session.get(), test_host_port_pair_); |
| + EXPECT_EQ(SpdySession::FLOW_CONTROL_STREAM_AND_SESSION, |
| + session->flow_control_state()); |
| + |
| + EXPECT_EQ(kSpdySessionInitialWindowSize, session->session_recv_window_size_); |
| + EXPECT_EQ(0, session->session_unacked_recv_window_bytes_); |
| + |
| + session->IncreaseRecvWindowSize(delta_window_size); |
| + EXPECT_EQ(kSpdySessionInitialWindowSize + delta_window_size, |
| + session->session_recv_window_size_); |
| + EXPECT_EQ(delta_window_size, session->session_unacked_recv_window_bytes_); |
| + |
| + // Should trigger sending a WINDOW_UPDATE frame. |
| + session->IncreaseRecvWindowSize(kSpdySessionInitialWindowSize); |
| + EXPECT_EQ(kSpdySessionInitialWindowSize + delta_window_size + |
| + kSpdySessionInitialWindowSize, |
| session->session_recv_window_size_); |
| EXPECT_EQ(0, session->session_unacked_recv_window_bytes_); |
| - data.RunFor(2); |
| + data.RunFor(1); |
| session->DecreaseRecvWindowSize( |
| - kDefaultInitialRecvWindowSize + delta_window_size + |
| + kSpdySessionInitialWindowSize + delta_window_size + |
| kSpdySessionInitialWindowSize); |
| EXPECT_EQ(0, session->session_recv_window_size_); |
| EXPECT_EQ(0, session->session_unacked_recv_window_bytes_); |
| @@ -2914,18 +3161,10 @@ TEST_P(SpdySessionTest, SessionFlowControlInactiveStream) { |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyBodyFrame(1, false)); |
| MockRead reads[] = { |
| - CreateMockRead(*resp, 1), |
| - MockRead(ASYNC, 0, 2) // EOF |
| - }; |
| - scoped_ptr<SpdyFrame> initial_window_update( |
| - spdy_util_.ConstructSpdyWindowUpdate( |
| - kSessionFlowControlStreamId, |
| - kDefaultInitialRecvWindowSize - kSpdySessionInitialWindowSize)); |
| - MockWrite writes[] = { |
| - CreateMockWrite(*initial_window_update, 0), |
| + CreateMockRead(*resp, 0), |
| + MockRead(ASYNC, 0, 1) // EOF |
| }; |
| - DeterministicSocketData data(reads, arraysize(reads), |
| - writes, arraysize(writes)); |
| + DeterministicSocketData data(reads, arraysize(reads), NULL, 0); |
| data.set_connect_data(connect_data); |
| session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data); |
| @@ -2939,12 +3178,12 @@ TEST_P(SpdySessionTest, SessionFlowControlInactiveStream) { |
| EXPECT_EQ(SpdySession::FLOW_CONTROL_STREAM_AND_SESSION, |
| session->flow_control_state()); |
| - EXPECT_EQ(kDefaultInitialRecvWindowSize, session->session_recv_window_size_); |
| + EXPECT_EQ(kSpdySessionInitialWindowSize, session->session_recv_window_size_); |
| EXPECT_EQ(0, session->session_unacked_recv_window_bytes_); |
| - data.RunFor(3); |
| + data.RunFor(2); |
| - EXPECT_EQ(kDefaultInitialRecvWindowSize, session->session_recv_window_size_); |
| + EXPECT_EQ(kSpdySessionInitialWindowSize, session->session_recv_window_size_); |
| EXPECT_EQ(0, session->session_unacked_recv_window_bytes_); |
| } |
| @@ -2975,10 +3214,6 @@ TEST_P(SpdySessionTest, SessionFlowControlNoReceiveLeaks) { |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| - scoped_ptr<SpdyFrame> initial_window_update( |
| - spdy_util_.ConstructSpdyWindowUpdate( |
| - kSessionFlowControlStreamId, |
| - kDefaultInitialRecvWindowSize - kSpdySessionInitialWindowSize)); |
| scoped_ptr<SpdyFrame> req( |
| spdy_util_.ConstructSpdyPost( |
| kStreamUrl, 1, msg_data_size, MEDIUM, NULL, 0)); |
| @@ -2986,9 +3221,8 @@ TEST_P(SpdySessionTest, SessionFlowControlNoReceiveLeaks) { |
| spdy_util_.ConstructSpdyBodyFrame( |
| 1, msg_data.data(), msg_data_size, false)); |
| MockWrite writes[] = { |
| - CreateMockWrite(*initial_window_update, 0), |
| - CreateMockWrite(*req, 1), |
| - CreateMockWrite(*msg, 3), |
| + CreateMockWrite(*req, 0), |
| + CreateMockWrite(*msg, 2), |
| }; |
| scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1)); |
| @@ -2999,9 +3233,9 @@ TEST_P(SpdySessionTest, SessionFlowControlNoReceiveLeaks) { |
| spdy_util_.ConstructSpdyWindowUpdate( |
| kSessionFlowControlStreamId, msg_data_size)); |
| MockRead reads[] = { |
| - CreateMockRead(*resp, 2), |
| - CreateMockRead(*echo, 4), |
| - MockRead(ASYNC, 0, 5) // EOF |
| + CreateMockRead(*resp, 1), |
| + CreateMockRead(*echo, 3), |
| + MockRead(ASYNC, 0, 4) // EOF |
| }; |
| // Create SpdySession and SpdyStream and send the request. |
| @@ -3034,15 +3268,15 @@ TEST_P(SpdySessionTest, SessionFlowControlNoReceiveLeaks) { |
| stream->SendRequestHeaders(headers.Pass(), MORE_DATA_TO_SEND)); |
| EXPECT_TRUE(stream->HasUrl()); |
| - EXPECT_EQ(kDefaultInitialRecvWindowSize, session->session_recv_window_size_); |
| + EXPECT_EQ(kSpdySessionInitialWindowSize, session->session_recv_window_size_); |
| EXPECT_EQ(0, session->session_unacked_recv_window_bytes_); |
| - data.RunFor(5); |
| + data.RunFor(4); |
| EXPECT_TRUE(data.at_write_eof()); |
| EXPECT_TRUE(data.at_read_eof()); |
| - EXPECT_EQ(kDefaultInitialRecvWindowSize, session->session_recv_window_size_); |
| + EXPECT_EQ(kSpdySessionInitialWindowSize, session->session_recv_window_size_); |
| EXPECT_EQ(msg_data_size, session->session_unacked_recv_window_bytes_); |
| stream->Close(); |
| @@ -3050,7 +3284,7 @@ TEST_P(SpdySessionTest, SessionFlowControlNoReceiveLeaks) { |
| EXPECT_EQ(OK, delegate.WaitForClose()); |
| - EXPECT_EQ(kDefaultInitialRecvWindowSize, session->session_recv_window_size_); |
| + EXPECT_EQ(kSpdySessionInitialWindowSize, session->session_recv_window_size_); |
| EXPECT_EQ(msg_data_size, session->session_unacked_recv_window_bytes_); |
| } |
| @@ -3068,22 +3302,17 @@ TEST_P(SpdySessionTest, SessionFlowControlNoSendLeaks) { |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| - scoped_ptr<SpdyFrame> initial_window_update( |
| - spdy_util_.ConstructSpdyWindowUpdate( |
| - kSessionFlowControlStreamId, |
| - kDefaultInitialRecvWindowSize - kSpdySessionInitialWindowSize)); |
| scoped_ptr<SpdyFrame> req( |
| spdy_util_.ConstructSpdyPost( |
| kStreamUrl, 1, msg_data_size, MEDIUM, NULL, 0)); |
| MockWrite writes[] = { |
| - CreateMockWrite(*initial_window_update, 0), |
| - CreateMockWrite(*req, 1), |
| + CreateMockWrite(*req, 0), |
| }; |
| scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1)); |
| MockRead reads[] = { |
| - CreateMockRead(*resp, 2), |
| - MockRead(ASYNC, 0, 3) // EOF |
| + CreateMockRead(*resp, 1), |
| + MockRead(ASYNC, 0, 2) // EOF |
| }; |
| // Create SpdySession and SpdyStream and send the request. |
| @@ -3118,7 +3347,7 @@ TEST_P(SpdySessionTest, SessionFlowControlNoSendLeaks) { |
| EXPECT_EQ(kSpdySessionInitialWindowSize, session->session_send_window_size_); |
| - data.RunFor(2); |
| + data.RunFor(1); |
| EXPECT_EQ(kSpdySessionInitialWindowSize, session->session_send_window_size_); |
| @@ -3152,10 +3381,6 @@ TEST_P(SpdySessionTest, SessionFlowControlEndToEnd) { |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| - scoped_ptr<SpdyFrame> initial_window_update( |
| - spdy_util_.ConstructSpdyWindowUpdate( |
| - kSessionFlowControlStreamId, |
| - kDefaultInitialRecvWindowSize - kSpdySessionInitialWindowSize)); |
| scoped_ptr<SpdyFrame> req( |
| spdy_util_.ConstructSpdyPost( |
| kStreamUrl, 1, msg_data_size, MEDIUM, NULL, 0)); |
| @@ -3163,9 +3388,8 @@ TEST_P(SpdySessionTest, SessionFlowControlEndToEnd) { |
| spdy_util_.ConstructSpdyBodyFrame( |
| 1, msg_data.data(), msg_data_size, false)); |
| MockWrite writes[] = { |
| - CreateMockWrite(*initial_window_update, 0), |
| - CreateMockWrite(*req, 1), |
| - CreateMockWrite(*msg, 3), |
| + CreateMockWrite(*req, 0), |
| + CreateMockWrite(*msg, 2), |
| }; |
| scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1)); |
| @@ -3176,10 +3400,10 @@ TEST_P(SpdySessionTest, SessionFlowControlEndToEnd) { |
| spdy_util_.ConstructSpdyWindowUpdate( |
| kSessionFlowControlStreamId, msg_data_size)); |
| MockRead reads[] = { |
| - CreateMockRead(*resp, 2), |
| - CreateMockRead(*echo, 4), |
| - CreateMockRead(*window_update, 5), |
| - MockRead(ASYNC, 0, 6) // EOF |
| + CreateMockRead(*resp, 1), |
| + CreateMockRead(*echo, 3), |
| + CreateMockRead(*window_update, 4), |
| + MockRead(ASYNC, 0, 5) // EOF |
| }; |
| // Create SpdySession and SpdyStream and send the request. |
| @@ -3213,41 +3437,41 @@ TEST_P(SpdySessionTest, SessionFlowControlEndToEnd) { |
| EXPECT_TRUE(stream->HasUrl()); |
| EXPECT_EQ(kSpdySessionInitialWindowSize, session->session_send_window_size_); |
| - EXPECT_EQ(kDefaultInitialRecvWindowSize, session->session_recv_window_size_); |
| + EXPECT_EQ(kSpdySessionInitialWindowSize, session->session_recv_window_size_); |
| EXPECT_EQ(0, session->session_unacked_recv_window_bytes_); |
| - data.RunFor(2); |
| + data.RunFor(1); |
| EXPECT_EQ(kSpdySessionInitialWindowSize, session->session_send_window_size_); |
| - EXPECT_EQ(kDefaultInitialRecvWindowSize, session->session_recv_window_size_); |
| + EXPECT_EQ(kSpdySessionInitialWindowSize, session->session_recv_window_size_); |
| EXPECT_EQ(0, session->session_unacked_recv_window_bytes_); |
| data.RunFor(1); |
| EXPECT_EQ(kSpdySessionInitialWindowSize - msg_data_size, |
| session->session_send_window_size_); |
| - EXPECT_EQ(kDefaultInitialRecvWindowSize, session->session_recv_window_size_); |
| + EXPECT_EQ(kSpdySessionInitialWindowSize, session->session_recv_window_size_); |
| EXPECT_EQ(0, session->session_unacked_recv_window_bytes_); |
| data.RunFor(1); |
| EXPECT_EQ(kSpdySessionInitialWindowSize - msg_data_size, |
| session->session_send_window_size_); |
| - EXPECT_EQ(kDefaultInitialRecvWindowSize, session->session_recv_window_size_); |
| + EXPECT_EQ(kSpdySessionInitialWindowSize, session->session_recv_window_size_); |
| EXPECT_EQ(0, session->session_unacked_recv_window_bytes_); |
| data.RunFor(1); |
| EXPECT_EQ(kSpdySessionInitialWindowSize - msg_data_size, |
| session->session_send_window_size_); |
| - EXPECT_EQ(kDefaultInitialRecvWindowSize - msg_data_size, |
| + EXPECT_EQ(kSpdySessionInitialWindowSize - msg_data_size, |
| session->session_recv_window_size_); |
| EXPECT_EQ(0, session->session_unacked_recv_window_bytes_); |
| data.RunFor(1); |
| EXPECT_EQ(kSpdySessionInitialWindowSize, session->session_send_window_size_); |
| - EXPECT_EQ(kDefaultInitialRecvWindowSize - msg_data_size, |
| + EXPECT_EQ(kSpdySessionInitialWindowSize - msg_data_size, |
| session->session_recv_window_size_); |
| EXPECT_EQ(0, session->session_unacked_recv_window_bytes_); |
| @@ -3259,7 +3483,7 @@ TEST_P(SpdySessionTest, SessionFlowControlEndToEnd) { |
| // Draining the delegate's read queue should increase the session's |
| // receive window. |
| EXPECT_EQ(kSpdySessionInitialWindowSize, session->session_send_window_size_); |
| - EXPECT_EQ(kDefaultInitialRecvWindowSize, session->session_recv_window_size_); |
| + EXPECT_EQ(kSpdySessionInitialWindowSize, session->session_recv_window_size_); |
| EXPECT_EQ(msg_data_size, session->session_unacked_recv_window_bytes_); |
| stream->Close(); |
| @@ -3268,7 +3492,7 @@ TEST_P(SpdySessionTest, SessionFlowControlEndToEnd) { |
| EXPECT_EQ(OK, delegate.WaitForClose()); |
| EXPECT_EQ(kSpdySessionInitialWindowSize, session->session_send_window_size_); |
| - EXPECT_EQ(kDefaultInitialRecvWindowSize, session->session_recv_window_size_); |
| + EXPECT_EQ(kSpdySessionInitialWindowSize, session->session_recv_window_size_); |
| EXPECT_EQ(msg_data_size, session->session_unacked_recv_window_bytes_); |
| } |
| @@ -3283,19 +3507,14 @@ void SpdySessionTest::RunResumeAfterUnstallTest( |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| - scoped_ptr<SpdyFrame> initial_window_update( |
| - spdy_util_.ConstructSpdyWindowUpdate( |
| - kSessionFlowControlStreamId, |
| - kDefaultInitialRecvWindowSize - kSpdySessionInitialWindowSize)); |
| scoped_ptr<SpdyFrame> req( |
| spdy_util_.ConstructSpdyPost( |
| kStreamUrl, 1, kBodyDataSize, LOWEST, NULL, 0)); |
| scoped_ptr<SpdyFrame> body( |
| spdy_util_.ConstructSpdyBodyFrame(1, kBodyData, kBodyDataSize, true)); |
| MockWrite writes[] = { |
| - CreateMockWrite(*initial_window_update, 0), |
| - CreateMockWrite(*req, 1), |
| - CreateMockWrite(*body, 2), |
| + CreateMockWrite(*req, 0), |
| + CreateMockWrite(*body, 1), |
| }; |
| scoped_ptr<SpdyFrame> resp( |
| @@ -3303,8 +3522,8 @@ void SpdySessionTest::RunResumeAfterUnstallTest( |
| scoped_ptr<SpdyFrame> echo( |
| spdy_util_.ConstructSpdyBodyFrame(1, kBodyData, kBodyDataSize, false)); |
| MockRead reads[] = { |
| - CreateMockRead(*resp, 3), |
| - MockRead(ASYNC, 0, 0, 4), // EOF |
| + CreateMockRead(*resp, 2), |
| + MockRead(ASYNC, 0, 0, 3), // EOF |
| }; |
| DeterministicSocketData data(reads, arraysize(reads), |
| @@ -3341,7 +3560,7 @@ void SpdySessionTest::RunResumeAfterUnstallTest( |
| stall_fn.Run(session.get(), stream.get()); |
| - data.RunFor(2); |
| + data.RunFor(1); |
| EXPECT_TRUE(stream->send_stalled_by_flow_control()); |
| @@ -3443,10 +3662,6 @@ TEST_P(SpdySessionTest, ResumeByPriorityAfterSendWindowSizeIncrease) { |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| - scoped_ptr<SpdyFrame> initial_window_update( |
| - spdy_util_.ConstructSpdyWindowUpdate( |
| - kSessionFlowControlStreamId, |
| - kDefaultInitialRecvWindowSize - kSpdySessionInitialWindowSize)); |
| scoped_ptr<SpdyFrame> req1( |
| spdy_util_.ConstructSpdyPost( |
| kStreamUrl, 1, kBodyDataSize, LOWEST, NULL, 0)); |
| @@ -3458,19 +3673,18 @@ TEST_P(SpdySessionTest, ResumeByPriorityAfterSendWindowSizeIncrease) { |
| scoped_ptr<SpdyFrame> body2( |
| spdy_util_.ConstructSpdyBodyFrame(3, kBodyData, kBodyDataSize, true)); |
| MockWrite writes[] = { |
| - CreateMockWrite(*initial_window_update, 0), |
| - CreateMockWrite(*req1, 1), |
| - CreateMockWrite(*req2, 2), |
| - CreateMockWrite(*body2, 3), |
| - CreateMockWrite(*body1, 4), |
| + CreateMockWrite(*req1, 0), |
| + CreateMockWrite(*req2, 1), |
| + CreateMockWrite(*body2, 2), |
| + CreateMockWrite(*body1, 3), |
| }; |
| scoped_ptr<SpdyFrame> resp1(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1)); |
| scoped_ptr<SpdyFrame> resp2(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3)); |
| MockRead reads[] = { |
| - CreateMockRead(*resp1, 5), |
| - CreateMockRead(*resp2, 6), |
| - MockRead(ASYNC, 0, 0, 7), // EOF |
| + CreateMockRead(*resp1, 4), |
| + CreateMockRead(*resp2, 5), |
| + MockRead(ASYNC, 0, 0, 6), // EOF |
| }; |
| DeterministicSocketData data(reads, arraysize(reads), |
| @@ -3519,7 +3733,7 @@ TEST_P(SpdySessionTest, ResumeByPriorityAfterSendWindowSizeIncrease) { |
| EXPECT_TRUE(stream1->HasUrl()); |
| EXPECT_EQ(kStreamUrl, stream1->GetUrl().spec()); |
| - data.RunFor(2); |
| + data.RunFor(1); |
| EXPECT_EQ(1u, stream1->stream_id()); |
| EXPECT_TRUE(stream1->send_stalled_by_flow_control()); |
| @@ -3548,207 +3762,17 @@ TEST_P(SpdySessionTest, ResumeByPriorityAfterSendWindowSizeIncrease) { |
| // This should then unstall stream1. |
| UnstallSessionSend(session.get(), kBodyDataSize); |
| - EXPECT_FALSE(stream1->send_stalled_by_flow_control()); |
| - EXPECT_FALSE(stream2->send_stalled_by_flow_control()); |
| - |
| - data.RunFor(4); |
| - |
| - EXPECT_EQ(ERR_CONNECTION_CLOSED, delegate1.WaitForClose()); |
| - EXPECT_EQ(ERR_CONNECTION_CLOSED, delegate2.WaitForClose()); |
| - |
| - EXPECT_TRUE(delegate1.send_headers_completed()); |
| - EXPECT_EQ("200", delegate1.GetResponseHeaderValue(":status")); |
| - EXPECT_EQ("HTTP/1.1", delegate1.GetResponseHeaderValue(":version")); |
| - EXPECT_EQ(std::string(), delegate1.TakeReceivedData()); |
| - |
| - EXPECT_TRUE(delegate2.send_headers_completed()); |
| - EXPECT_EQ("200", delegate2.GetResponseHeaderValue(":status")); |
| - EXPECT_EQ("HTTP/1.1", delegate2.GetResponseHeaderValue(":version")); |
| - EXPECT_EQ(std::string(), delegate2.TakeReceivedData()); |
| - |
| - EXPECT_TRUE(data.at_write_eof()); |
| -} |
| - |
| -// Delegate that closes a given stream after sending its body. |
| -class StreamClosingDelegate : public test::StreamDelegateWithBody { |
| - public: |
| - StreamClosingDelegate(const base::WeakPtr<SpdyStream>& stream, |
| - base::StringPiece data) |
| - : StreamDelegateWithBody(stream, data) {} |
| - |
| - virtual ~StreamClosingDelegate() {} |
| - |
| - void set_stream_to_close(const base::WeakPtr<SpdyStream>& stream_to_close) { |
| - stream_to_close_ = stream_to_close; |
| - } |
| - |
| - virtual void OnDataSent() OVERRIDE { |
| - test::StreamDelegateWithBody::OnDataSent(); |
| - if (stream_to_close_.get()) { |
| - stream_to_close_->Close(); |
| - EXPECT_EQ(NULL, stream_to_close_.get()); |
| - } |
| - } |
| - |
| - private: |
| - base::WeakPtr<SpdyStream> stream_to_close_; |
| -}; |
| - |
| -// Cause a stall by reducing the flow control send window to |
| -// 0. Unstalling the session should properly handle deleted streams. |
| -TEST_P(SpdySessionTest, SendWindowSizeIncreaseWithDeletedStreams) { |
| - if (GetParam() < kProtoSPDY31) |
| - return; |
| - |
| - const char kStreamUrl[] = "http://www.google.com/"; |
| - GURL url(kStreamUrl); |
| - |
| - session_deps_.host_resolver->set_synchronous_mode(true); |
| - |
| - scoped_ptr<SpdyFrame> initial_window_update( |
| - spdy_util_.ConstructSpdyWindowUpdate( |
| - kSessionFlowControlStreamId, |
| - kDefaultInitialRecvWindowSize - kSpdySessionInitialWindowSize)); |
| - scoped_ptr<SpdyFrame> req1( |
| - spdy_util_.ConstructSpdyPost( |
| - kStreamUrl, 1, kBodyDataSize, LOWEST, NULL, 0)); |
| - scoped_ptr<SpdyFrame> req2( |
| - spdy_util_.ConstructSpdyPost( |
| - kStreamUrl, 3, kBodyDataSize, LOWEST, NULL, 0)); |
| - scoped_ptr<SpdyFrame> req3( |
| - spdy_util_.ConstructSpdyPost( |
| - kStreamUrl, 5, kBodyDataSize, LOWEST, NULL, 0)); |
| - scoped_ptr<SpdyFrame> body2( |
| - spdy_util_.ConstructSpdyBodyFrame(3, kBodyData, kBodyDataSize, true)); |
| - MockWrite writes[] = { |
| - CreateMockWrite(*initial_window_update, 0), |
| - CreateMockWrite(*req1, 1), |
| - CreateMockWrite(*req2, 2), |
| - CreateMockWrite(*req3, 3), |
| - CreateMockWrite(*body2, 4), |
| - }; |
| - |
| - scoped_ptr<SpdyFrame> resp2(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3)); |
| - MockRead reads[] = { |
| - CreateMockRead(*resp2, 5), |
| - MockRead(ASYNC, 0, 0, 6), // EOF |
| - }; |
| - |
| - DeterministicSocketData data(reads, arraysize(reads), |
| - writes, arraysize(writes)); |
| - MockConnect connect_data(SYNCHRONOUS, OK); |
| - data.set_connect_data(connect_data); |
| - |
| - session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data); |
| - |
| - CreateDeterministicNetworkSession(); |
| - scoped_refptr<SpdySession> session = GetSession(key_); |
| - InitializeSession( |
| - http_session_.get(), session.get(), test_host_port_pair_); |
| - EXPECT_EQ(SpdySession::FLOW_CONTROL_STREAM_AND_SESSION, |
| - session->flow_control_state()); |
| - |
| - base::WeakPtr<SpdyStream> stream1 = |
| - CreateStreamSynchronously(SPDY_REQUEST_RESPONSE_STREAM, |
| - session, url, LOWEST, BoundNetLog()); |
| - ASSERT_TRUE(stream1.get() != NULL); |
| - |
| - test::StreamDelegateWithBody delegate1(stream1, kBodyDataStringPiece); |
| - stream1->SetDelegate(&delegate1); |
| - |
| - EXPECT_FALSE(stream1->HasUrl()); |
| - |
| - base::WeakPtr<SpdyStream> stream2 = |
| - CreateStreamSynchronously(SPDY_REQUEST_RESPONSE_STREAM, |
| - session, url, LOWEST, BoundNetLog()); |
| - ASSERT_TRUE(stream2.get() != NULL); |
| - |
| - StreamClosingDelegate delegate2(stream2, kBodyDataStringPiece); |
| - stream2->SetDelegate(&delegate2); |
| - |
| - EXPECT_FALSE(stream2->HasUrl()); |
| - |
| - base::WeakPtr<SpdyStream> stream3 = |
| - CreateStreamSynchronously(SPDY_REQUEST_RESPONSE_STREAM, |
| - session, url, LOWEST, BoundNetLog()); |
| - ASSERT_TRUE(stream3.get() != NULL); |
| - |
| - test::StreamDelegateWithBody delegate3(stream3, kBodyDataStringPiece); |
| - stream3->SetDelegate(&delegate3); |
| - |
| - EXPECT_FALSE(stream3->HasUrl()); |
| - |
| - EXPECT_FALSE(stream1->send_stalled_by_flow_control()); |
| - EXPECT_FALSE(stream2->send_stalled_by_flow_control()); |
| - EXPECT_FALSE(stream3->send_stalled_by_flow_control()); |
| - |
| - StallSessionSend(session.get()); |
| - |
| - scoped_ptr<SpdyHeaderBlock> headers1( |
| - spdy_util_.ConstructPostHeaderBlock(kStreamUrl, kBodyDataSize)); |
| - EXPECT_EQ(ERR_IO_PENDING, |
| - stream1->SendRequestHeaders(headers1.Pass(), MORE_DATA_TO_SEND)); |
| - EXPECT_TRUE(stream1->HasUrl()); |
| - EXPECT_EQ(kStreamUrl, stream1->GetUrl().spec()); |
| - |
| - data.RunFor(2); |
| - EXPECT_EQ(1u, stream1->stream_id()); |
| - EXPECT_TRUE(stream1->send_stalled_by_flow_control()); |
| - |
| - scoped_ptr<SpdyHeaderBlock> headers2( |
| - spdy_util_.ConstructPostHeaderBlock(kStreamUrl, kBodyDataSize)); |
| - EXPECT_EQ(ERR_IO_PENDING, |
| - stream2->SendRequestHeaders(headers2.Pass(), MORE_DATA_TO_SEND)); |
| - EXPECT_TRUE(stream2->HasUrl()); |
| - EXPECT_EQ(kStreamUrl, stream2->GetUrl().spec()); |
| - |
| - data.RunFor(1); |
| - EXPECT_EQ(3u, stream2->stream_id()); |
| - EXPECT_TRUE(stream2->send_stalled_by_flow_control()); |
| - |
| - scoped_ptr<SpdyHeaderBlock> headers3( |
| - spdy_util_.ConstructPostHeaderBlock(kStreamUrl, kBodyDataSize)); |
| - EXPECT_EQ(ERR_IO_PENDING, |
| - stream3->SendRequestHeaders(headers3.Pass(), MORE_DATA_TO_SEND)); |
| - EXPECT_TRUE(stream3->HasUrl()); |
| - EXPECT_EQ(kStreamUrl, stream3->GetUrl().spec()); |
| - |
| - data.RunFor(1); |
| - EXPECT_EQ(5u, stream3->stream_id()); |
| - EXPECT_TRUE(stream3->send_stalled_by_flow_control()); |
| - |
| - SpdyStreamId stream_id1 = stream1->stream_id(); |
| - SpdyStreamId stream_id2 = stream2->stream_id(); |
| - SpdyStreamId stream_id3 = stream3->stream_id(); |
| - |
| - // Close stream1 preemptively. |
| - session->CloseActiveStream(stream_id1, ERR_CONNECTION_CLOSED); |
| - EXPECT_EQ(NULL, stream1.get()); |
| - |
| - EXPECT_FALSE(session->IsStreamActive(stream_id1)); |
| - EXPECT_TRUE(session->IsStreamActive(stream_id2)); |
| - EXPECT_TRUE(session->IsStreamActive(stream_id3)); |
| - |
| - // Unstall stream2, which should then close stream3. |
| - delegate2.set_stream_to_close(stream3); |
| - UnstallSessionSend(session.get(), kBodyDataSize); |
| - |
| - data.RunFor(1); |
| - EXPECT_EQ(NULL, stream3.get()); |
| - |
| - EXPECT_FALSE(stream2->send_stalled_by_flow_control()); |
| - EXPECT_FALSE(session->IsStreamActive(stream_id1)); |
| - EXPECT_TRUE(session->IsStreamActive(stream_id2)); |
| - EXPECT_FALSE(session->IsStreamActive(stream_id3)); |
| + EXPECT_FALSE(stream1->send_stalled_by_flow_control()); |
| + EXPECT_FALSE(stream2->send_stalled_by_flow_control()); |
| - data.RunFor(2); |
| - EXPECT_EQ(NULL, stream2.get()); |
| + data.RunFor(4); |
| EXPECT_EQ(ERR_CONNECTION_CLOSED, delegate1.WaitForClose()); |
| EXPECT_EQ(ERR_CONNECTION_CLOSED, delegate2.WaitForClose()); |
| - EXPECT_EQ(OK, delegate3.WaitForClose()); |
| EXPECT_TRUE(delegate1.send_headers_completed()); |
| + EXPECT_EQ("200", delegate1.GetResponseHeaderValue(":status")); |
| + EXPECT_EQ("HTTP/1.1", delegate1.GetResponseHeaderValue(":version")); |
| EXPECT_EQ(std::string(), delegate1.TakeReceivedData()); |
| EXPECT_TRUE(delegate2.send_headers_completed()); |
| @@ -3756,16 +3780,37 @@ TEST_P(SpdySessionTest, SendWindowSizeIncreaseWithDeletedStreams) { |
| EXPECT_EQ("HTTP/1.1", delegate2.GetResponseHeaderValue(":version")); |
| EXPECT_EQ(std::string(), delegate2.TakeReceivedData()); |
| - EXPECT_TRUE(delegate3.send_headers_completed()); |
| - EXPECT_EQ(std::string(), delegate3.TakeReceivedData()); |
| - |
| EXPECT_TRUE(data.at_write_eof()); |
| } |
| +// Delegate that closes a given stream after sending its body. |
| +class StreamClosingDelegate : public test::StreamDelegateWithBody { |
| + public: |
| + StreamClosingDelegate(const base::WeakPtr<SpdyStream>& stream, |
| + base::StringPiece data) |
| + : StreamDelegateWithBody(stream, data) {} |
| + |
| + virtual ~StreamClosingDelegate() {} |
| + |
| + void set_stream_to_close(const base::WeakPtr<SpdyStream>& stream_to_close) { |
| + stream_to_close_ = stream_to_close; |
| + } |
| + |
| + virtual void OnDataSent() OVERRIDE { |
| + test::StreamDelegateWithBody::OnDataSent(); |
| + if (stream_to_close_.get()) { |
| + stream_to_close_->Close(); |
| + EXPECT_EQ(NULL, stream_to_close_.get()); |
| + } |
| + } |
| + |
| + private: |
| + base::WeakPtr<SpdyStream> stream_to_close_; |
| +}; |
| + |
| // Cause a stall by reducing the flow control send window to |
| -// 0. Unstalling the session should properly handle the session itself |
| -// being closed. |
| -TEST_P(SpdySessionTest, SendWindowSizeIncreaseWithDeletedSession) { |
| +// 0. Unstalling the session should properly handle deleted streams. |
| +TEST_P(SpdySessionTest, SendWindowSizeIncreaseWithDeletedStreams) { |
| if (GetParam() < kProtoSPDY31) |
| return; |
| @@ -3774,26 +3819,28 @@ TEST_P(SpdySessionTest, SendWindowSizeIncreaseWithDeletedSession) { |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| - scoped_ptr<SpdyFrame> initial_window_update( |
| - spdy_util_.ConstructSpdyWindowUpdate( |
| - kSessionFlowControlStreamId, |
| - kDefaultInitialRecvWindowSize - kSpdySessionInitialWindowSize)); |
| scoped_ptr<SpdyFrame> req1( |
| spdy_util_.ConstructSpdyPost( |
| kStreamUrl, 1, kBodyDataSize, LOWEST, NULL, 0)); |
| scoped_ptr<SpdyFrame> req2( |
| spdy_util_.ConstructSpdyPost( |
| kStreamUrl, 3, kBodyDataSize, LOWEST, NULL, 0)); |
| - scoped_ptr<SpdyFrame> body1( |
| - spdy_util_.ConstructSpdyBodyFrame(1, kBodyData, kBodyDataSize, false)); |
| + scoped_ptr<SpdyFrame> req3( |
| + spdy_util_.ConstructSpdyPost( |
| + kStreamUrl, 5, kBodyDataSize, LOWEST, NULL, 0)); |
| + scoped_ptr<SpdyFrame> body2( |
| + spdy_util_.ConstructSpdyBodyFrame(3, kBodyData, kBodyDataSize, true)); |
| MockWrite writes[] = { |
| - CreateMockWrite(*initial_window_update, 0), |
| - CreateMockWrite(*req1, 1), |
| - CreateMockWrite(*req2, 2), |
| + CreateMockWrite(*req1, 0), |
| + CreateMockWrite(*req2, 1), |
| + CreateMockWrite(*req3, 2), |
| + CreateMockWrite(*body2, 3), |
| }; |
| + scoped_ptr<SpdyFrame> resp2(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3)); |
| MockRead reads[] = { |
| - MockRead(ASYNC, 0, 0, 3), // EOF |
| + CreateMockRead(*resp2, 4), |
| + MockRead(ASYNC, 0, 0, 5), // EOF |
| }; |
| DeterministicSocketData data(reads, arraysize(reads), |
| @@ -3825,13 +3872,24 @@ TEST_P(SpdySessionTest, SendWindowSizeIncreaseWithDeletedSession) { |
| session, url, LOWEST, BoundNetLog()); |
| ASSERT_TRUE(stream2.get() != NULL); |
| - test::StreamDelegateWithBody delegate2(stream2, kBodyDataStringPiece); |
| + StreamClosingDelegate delegate2(stream2, kBodyDataStringPiece); |
| stream2->SetDelegate(&delegate2); |
| EXPECT_FALSE(stream2->HasUrl()); |
| + base::WeakPtr<SpdyStream> stream3 = |
| + CreateStreamSynchronously(SPDY_REQUEST_RESPONSE_STREAM, |
| + session, url, LOWEST, BoundNetLog()); |
| + ASSERT_TRUE(stream3.get() != NULL); |
| + |
| + test::StreamDelegateWithBody delegate3(stream3, kBodyDataStringPiece); |
| + stream3->SetDelegate(&delegate3); |
| + |
| + EXPECT_FALSE(stream3->HasUrl()); |
| + |
| EXPECT_FALSE(stream1->send_stalled_by_flow_control()); |
| EXPECT_FALSE(stream2->send_stalled_by_flow_control()); |
| + EXPECT_FALSE(stream3->send_stalled_by_flow_control()); |
| StallSessionSend(session.get()); |
| @@ -3842,7 +3900,7 @@ TEST_P(SpdySessionTest, SendWindowSizeIncreaseWithDeletedSession) { |
| EXPECT_TRUE(stream1->HasUrl()); |
| EXPECT_EQ(kStreamUrl, stream1->GetUrl().spec()); |
| - data.RunFor(2); |
| + data.RunFor(1); |
| EXPECT_EQ(1u, stream1->stream_id()); |
| EXPECT_TRUE(stream1->send_stalled_by_flow_control()); |
| @@ -3857,370 +3915,174 @@ TEST_P(SpdySessionTest, SendWindowSizeIncreaseWithDeletedSession) { |
| EXPECT_EQ(3u, stream2->stream_id()); |
| EXPECT_TRUE(stream2->send_stalled_by_flow_control()); |
| - EXPECT_TRUE(spdy_session_pool_->HasSession(key_)); |
| - |
| - // Unstall stream1. |
| - UnstallSessionSend(session.get(), kBodyDataSize); |
| - |
| - // Close the session (since we can't do it from within the delegate |
| - // method, since it's in the stream's loop). |
| - session->CloseSessionOnError(ERR_CONNECTION_CLOSED, true, "Closing session"); |
| - session = NULL; |
| - |
| - EXPECT_FALSE(spdy_session_pool_->HasSession(key_)); |
| - |
| - EXPECT_EQ(ERR_CONNECTION_CLOSED, delegate1.WaitForClose()); |
| - EXPECT_EQ(ERR_CONNECTION_CLOSED, delegate2.WaitForClose()); |
| - |
| - EXPECT_TRUE(delegate1.send_headers_completed()); |
| - EXPECT_EQ(std::string(), delegate1.TakeReceivedData()); |
| - |
| - EXPECT_TRUE(delegate2.send_headers_completed()); |
| - EXPECT_EQ(std::string(), delegate2.TakeReceivedData()); |
| - |
| - EXPECT_TRUE(data.at_write_eof()); |
| -} |
| - |
| -// Tests the case of a non-SPDY request closing an idle SPDY session when no |
| -// pointers to the idle session are currently held. |
| -TEST_P(SpdySessionTest, CloseOneIdleConnection) { |
| - if (GetParam() > kProtoSPDY3) |
| - return; |
| - |
| - ClientSocketPoolManager::set_max_sockets_per_group( |
| - HttpNetworkSession::NORMAL_SOCKET_POOL, 1); |
| - ClientSocketPoolManager::set_max_sockets_per_pool( |
| - HttpNetworkSession::NORMAL_SOCKET_POOL, 1); |
| - |
| - 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); |
| - session_deps_.socket_factory->AddSocketDataProvider(&data); |
| - |
| - CreateNetworkSession(); |
| - |
| - TransportClientSocketPool* pool = |
| - http_session_->GetTransportSocketPool( |
| - HttpNetworkSession::NORMAL_SOCKET_POOL); |
| - |
| - // Create an idle SPDY session. |
| - SpdySessionKey key1(HostPortPair("1.com", 80), ProxyServer::Direct(), |
| - kPrivacyModeDisabled); |
| - scoped_refptr<SpdySession> session1 = GetSession(key1); |
| - EXPECT_EQ( |
| - OK, |
| - InitializeSession(http_session_.get(), session1.get(), |
| - key1.host_port_pair())); |
| - EXPECT_FALSE(pool->IsStalled()); |
| - // Release the pointer to the session so it can be closed. |
| - session1 = NULL; |
| - |
| - // Trying to create a new connection should cause the pool to be stalled, and |
| - // post a task asynchronously to try and close the session. |
| - TestCompletionCallback callback2; |
| - HostPortPair host_port2("2.com", 80); |
| - scoped_refptr<TransportSocketParams> params2( |
| - new TransportSocketParams(host_port2, DEFAULT_PRIORITY, false, false, |
| - OnHostResolutionCallback())); |
| - scoped_ptr<ClientSocketHandle> connection2(new ClientSocketHandle); |
| + scoped_ptr<SpdyHeaderBlock> headers3( |
| + spdy_util_.ConstructPostHeaderBlock(kStreamUrl, kBodyDataSize)); |
| EXPECT_EQ(ERR_IO_PENDING, |
| - connection2->Init(host_port2.ToString(), params2, DEFAULT_PRIORITY, |
| - callback2.callback(), pool, BoundNetLog())); |
| - EXPECT_TRUE(pool->IsStalled()); |
| - |
| - // The socket pool should close the connection asynchronously and establish a |
| - // new connection. |
| - EXPECT_EQ(OK, callback2.WaitForResult()); |
| - EXPECT_FALSE(pool->IsStalled()); |
| -} |
| - |
| -// Tests the case of a non-SPDY request closing an idle SPDY session when no |
| -// pointers to the idle session are currently held, in the case the SPDY session |
| -// has an alias. |
| -TEST_P(SpdySessionTest, CloseOneIdleConnectionWithAlias) { |
| - if (GetParam() > kProtoSPDY3) |
| - return; |
| - |
| - ClientSocketPoolManager::set_max_sockets_per_group( |
| - HttpNetworkSession::NORMAL_SOCKET_POOL, 1); |
| - ClientSocketPoolManager::set_max_sockets_per_pool( |
| - HttpNetworkSession::NORMAL_SOCKET_POOL, 1); |
| - |
| - 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); |
| - session_deps_.socket_factory->AddSocketDataProvider(&data); |
| - |
| - session_deps_.host_resolver->set_synchronous_mode(true); |
| - session_deps_.host_resolver->rules()->AddIPLiteralRule( |
| - "1.com", "192.168.0.2", std::string()); |
| - session_deps_.host_resolver->rules()->AddIPLiteralRule( |
| - "2.com", "192.168.0.2", std::string()); |
| - // Not strictly needed. |
| - session_deps_.host_resolver->rules()->AddIPLiteralRule( |
| - "3.com", "192.168.0.3", std::string()); |
| - |
| - CreateNetworkSession(); |
| - |
| - TransportClientSocketPool* pool = |
| - http_session_->GetTransportSocketPool( |
| - HttpNetworkSession::NORMAL_SOCKET_POOL); |
| - |
| - // Create an idle SPDY session. |
| - SpdySessionKey key1(HostPortPair("1.com", 80), ProxyServer::Direct(), |
| - kPrivacyModeDisabled); |
| - scoped_refptr<SpdySession> session1 = GetSession(key1); |
| - EXPECT_EQ( |
| - OK, |
| - InitializeSession(http_session_.get(), session1.get(), |
| - key1.host_port_pair())); |
| - EXPECT_FALSE(pool->IsStalled()); |
| - |
| - // Set up an alias for the idle SPDY session, increasing its ref count to 2. |
| - SpdySessionKey key2(HostPortPair("2.com", 80), ProxyServer::Direct(), |
| - kPrivacyModeDisabled); |
| - SpdySessionPoolPeer pool_peer(spdy_session_pool_); |
| - HostResolver::RequestInfo info(key2.host_port_pair()); |
| - AddressList addresses; |
| - // Pre-populate the DNS cache, since a synchronous resolution is required in |
| - // order to create the alias. |
| - session_deps_.host_resolver->Resolve( |
| - info, &addresses, CompletionCallback(), NULL, BoundNetLog()); |
| - // Add the alias for the first session's key. Has to be done manually since |
| - // the usual process is bypassed. |
| - pool_peer.AddAlias(addresses.front(), key1); |
| - // Get a session for |key2|, which should return the session created earlier. |
| - scoped_refptr<SpdySession> session2 = |
| - spdy_session_pool_->Get(key2, BoundNetLog()); |
| - ASSERT_EQ(session1.get(), session2.get()); |
| - EXPECT_FALSE(pool->IsStalled()); |
| + stream3->SendRequestHeaders(headers3.Pass(), MORE_DATA_TO_SEND)); |
| + EXPECT_TRUE(stream3->HasUrl()); |
| + EXPECT_EQ(kStreamUrl, stream3->GetUrl().spec()); |
| - // Release both the pointers to the session so it can be closed. |
| - session1 = NULL; |
| - session2 = NULL; |
| + data.RunFor(1); |
| + EXPECT_EQ(5u, stream3->stream_id()); |
| + EXPECT_TRUE(stream3->send_stalled_by_flow_control()); |
| - // Trying to create a new connection should cause the pool to be stalled, and |
| - // post a task asynchronously to try and close the session. |
| - TestCompletionCallback callback3; |
| - HostPortPair host_port3("3.com", 80); |
| - scoped_refptr<TransportSocketParams> params3( |
| - new TransportSocketParams(host_port3, DEFAULT_PRIORITY, false, false, |
| - OnHostResolutionCallback())); |
| - scoped_ptr<ClientSocketHandle> connection3(new ClientSocketHandle); |
| - EXPECT_EQ(ERR_IO_PENDING, |
| - connection3->Init(host_port3.ToString(), params3, DEFAULT_PRIORITY, |
| - callback3.callback(), pool, BoundNetLog())); |
| - EXPECT_TRUE(pool->IsStalled()); |
| + SpdyStreamId stream_id1 = stream1->stream_id(); |
| + SpdyStreamId stream_id2 = stream2->stream_id(); |
| + SpdyStreamId stream_id3 = stream3->stream_id(); |
| - // The socket pool should close the connection asynchronously and establish a |
| - // new connection. |
| - EXPECT_EQ(OK, callback3.WaitForResult()); |
| - EXPECT_FALSE(pool->IsStalled()); |
| -} |
| + // Close stream1 preemptively. |
| + session->CloseActiveStream(stream_id1, ERR_CONNECTION_CLOSED); |
| + EXPECT_EQ(NULL, stream1.get()); |
| -// Tests the case of a non-SPDY request closing an idle SPDY session when a |
| -// pointer to the idle session is still held. |
| -TEST_P(SpdySessionTest, CloseOneIdleConnectionSessionStillHeld) { |
| - if (GetParam() > kProtoSPDY3) |
| - return; |
| + EXPECT_FALSE(session->IsStreamActive(stream_id1)); |
| + EXPECT_TRUE(session->IsStreamActive(stream_id2)); |
| + EXPECT_TRUE(session->IsStreamActive(stream_id3)); |
| - ClientSocketPoolManager::set_max_sockets_per_group( |
| - HttpNetworkSession::NORMAL_SOCKET_POOL, 1); |
| - ClientSocketPoolManager::set_max_sockets_per_pool( |
| - HttpNetworkSession::NORMAL_SOCKET_POOL, 1); |
| + // Unstall stream2, which should then close stream3. |
| + delegate2.set_stream_to_close(stream3); |
| + UnstallSessionSend(session.get(), kBodyDataSize); |
| - 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); |
| - session_deps_.socket_factory->AddSocketDataProvider(&data); |
| + data.RunFor(1); |
| + EXPECT_EQ(NULL, stream3.get()); |
| - CreateNetworkSession(); |
| + EXPECT_FALSE(stream2->send_stalled_by_flow_control()); |
| + EXPECT_FALSE(session->IsStreamActive(stream_id1)); |
| + EXPECT_TRUE(session->IsStreamActive(stream_id2)); |
| + EXPECT_FALSE(session->IsStreamActive(stream_id3)); |
| - TransportClientSocketPool* pool = |
| - http_session_->GetTransportSocketPool( |
| - HttpNetworkSession::NORMAL_SOCKET_POOL); |
| + data.RunFor(2); |
| + EXPECT_EQ(NULL, stream2.get()); |
| - // Create an idle SPDY session. |
| - SpdySessionKey key1(HostPortPair("1.com", 80), ProxyServer::Direct(), |
| - kPrivacyModeDisabled); |
| - scoped_refptr<SpdySession> session1 = GetSession(key1); |
| - EXPECT_EQ( |
| - OK, |
| - InitializeSession(http_session_.get(), session1.get(), |
| - key1.host_port_pair())); |
| - EXPECT_FALSE(pool->IsStalled()); |
| + EXPECT_EQ(ERR_CONNECTION_CLOSED, delegate1.WaitForClose()); |
| + EXPECT_EQ(ERR_CONNECTION_CLOSED, delegate2.WaitForClose()); |
| + EXPECT_EQ(OK, delegate3.WaitForClose()); |
| - // Trying to create a new connection should cause the pool to be stalled, and |
| - // post a task asynchronously to try and close the session. |
| - TestCompletionCallback callback2; |
| - HostPortPair host_port2("2.com", 80); |
| - scoped_refptr<TransportSocketParams> params2( |
| - new TransportSocketParams(host_port2, DEFAULT_PRIORITY, false, false, |
| - OnHostResolutionCallback())); |
| - scoped_ptr<ClientSocketHandle> connection2(new ClientSocketHandle); |
| - EXPECT_EQ(ERR_IO_PENDING, |
| - connection2->Init(host_port2.ToString(), params2, DEFAULT_PRIORITY, |
| - callback2.callback(), pool, BoundNetLog())); |
| - EXPECT_TRUE(pool->IsStalled()); |
| + EXPECT_TRUE(delegate1.send_headers_completed()); |
| + EXPECT_EQ(std::string(), delegate1.TakeReceivedData()); |
| - // Running the message loop should cause the session to prepare to be closed, |
| - // but since there's still an outstanding reference, it should not be closed |
| - // yet. |
| - base::RunLoop().RunUntilIdle(); |
| - EXPECT_TRUE(pool->IsStalled()); |
| - EXPECT_FALSE(callback2.have_result()); |
| + EXPECT_TRUE(delegate2.send_headers_completed()); |
| + EXPECT_EQ("200", delegate2.GetResponseHeaderValue(":status")); |
| + EXPECT_EQ("HTTP/1.1", delegate2.GetResponseHeaderValue(":version")); |
| + EXPECT_EQ(std::string(), delegate2.TakeReceivedData()); |
| - // Release the pointer to the session so it can be closed. |
| - session1 = NULL; |
| - EXPECT_EQ(OK, callback2.WaitForResult()); |
| - EXPECT_FALSE(pool->IsStalled()); |
| + EXPECT_TRUE(delegate3.send_headers_completed()); |
| + EXPECT_EQ(std::string(), delegate3.TakeReceivedData()); |
| + |
| + EXPECT_TRUE(data.at_write_eof()); |
| } |
| -// Tests that a non-SPDY request can't close a SPDY session that's currently in |
| -// use. |
| -TEST_P(SpdySessionTest, CloseOneIdleConnectionFailsWhenSessionInUse) { |
| - if (GetParam() > kProtoSPDY3) |
| +// Cause a stall by reducing the flow control send window to |
| +// 0. Unstalling the session should properly handle the session itself |
| +// being closed. |
| +TEST_P(SpdySessionTest, SendWindowSizeIncreaseWithDeletedSession) { |
| + if (GetParam() < kProtoSPDY31) |
| return; |
| - ClientSocketPoolManager::set_max_sockets_per_group( |
| - HttpNetworkSession::NORMAL_SOCKET_POOL, 1); |
| - ClientSocketPoolManager::set_max_sockets_per_pool( |
| - HttpNetworkSession::NORMAL_SOCKET_POOL, 1); |
| + const char kStreamUrl[] = "http://www.google.com/"; |
| + GURL url(kStreamUrl); |
| + |
| + session_deps_.host_resolver->set_synchronous_mode(true); |
| - MockConnect connect_data(SYNCHRONOUS, OK); |
| - MockRead reads[] = { |
| - MockRead(SYNCHRONOUS, ERR_IO_PENDING) // Stall forever. |
| - }; |
| scoped_ptr<SpdyFrame> req1( |
| - spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, true)); |
| - scoped_ptr<SpdyFrame> cancel1( |
| - spdy_util_.ConstructSpdyRstStream(1, RST_STREAM_CANCEL)); |
| + spdy_util_.ConstructSpdyPost( |
| + kStreamUrl, 1, kBodyDataSize, LOWEST, NULL, 0)); |
| + scoped_ptr<SpdyFrame> req2( |
| + spdy_util_.ConstructSpdyPost( |
| + kStreamUrl, 3, kBodyDataSize, LOWEST, NULL, 0)); |
| + scoped_ptr<SpdyFrame> body1( |
| + spdy_util_.ConstructSpdyBodyFrame(1, kBodyData, kBodyDataSize, false)); |
| MockWrite writes[] = { |
| - CreateMockWrite(*req1, 1), |
| - CreateMockWrite(*cancel1, 1), |
| + CreateMockWrite(*req1, 0), |
| + CreateMockWrite(*req2, 1), |
| }; |
| - StaticSocketDataProvider data(reads, arraysize(reads), |
| - writes, arraysize(writes)); |
| + |
| + MockRead reads[] = { |
| + MockRead(ASYNC, 0, 0, 2), // EOF |
| + }; |
| + |
| + DeterministicSocketData data(reads, arraysize(reads), |
| + writes, arraysize(writes)); |
| + MockConnect connect_data(SYNCHRONOUS, OK); |
| data.set_connect_data(connect_data); |
| - session_deps_.socket_factory->AddSocketDataProvider(&data); |
| - CreateNetworkSession(); |
| + session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data); |
| - TransportClientSocketPool* pool = |
| - http_session_->GetTransportSocketPool( |
| - HttpNetworkSession::NORMAL_SOCKET_POOL); |
| + CreateDeterministicNetworkSession(); |
| + scoped_refptr<SpdySession> session = GetSession(key_); |
| + InitializeSession( |
| + http_session_.get(), session.get(), test_host_port_pair_); |
| + EXPECT_EQ(SpdySession::FLOW_CONTROL_STREAM_AND_SESSION, |
| + session->flow_control_state()); |
| - // Create a SPDY session. |
| - GURL url1("http://www.google.com"); |
| - SpdySessionKey key1(HostPortPair(url1.host(), 80), |
| - ProxyServer::Direct(), kPrivacyModeDisabled); |
| - scoped_refptr<SpdySession> session1 = GetSession(key1); |
| - EXPECT_EQ( |
| - OK, |
| - InitializeSession(http_session_.get(), session1.get(), |
| - key1.host_port_pair())); |
| - EXPECT_FALSE(pool->IsStalled()); |
| + base::WeakPtr<SpdyStream> stream1 = |
| + CreateStreamSynchronously(SPDY_REQUEST_RESPONSE_STREAM, |
| + session, url, LOWEST, BoundNetLog()); |
| + ASSERT_TRUE(stream1.get() != NULL); |
| - // Create a stream using the session, and send a request. |
| + test::StreamDelegateWithBody delegate1(stream1, kBodyDataStringPiece); |
| + stream1->SetDelegate(&delegate1); |
| - TestCompletionCallback callback1; |
| - base::WeakPtr<SpdyStream> spdy_stream1 = |
| + EXPECT_FALSE(stream1->HasUrl()); |
| + |
| + base::WeakPtr<SpdyStream> stream2 = |
| CreateStreamSynchronously(SPDY_REQUEST_RESPONSE_STREAM, |
| - session1, url1, DEFAULT_PRIORITY, |
| - BoundNetLog()); |
| - ASSERT_TRUE(spdy_stream1.get()); |
| - test::StreamDelegateDoNothing delegate1(spdy_stream1); |
| - spdy_stream1->SetDelegate(&delegate1); |
| + session, url, LOWEST, BoundNetLog()); |
| + ASSERT_TRUE(stream2.get() != NULL); |
| - scoped_ptr<SpdyHeaderBlock> headers1( |
| - spdy_util_.ConstructGetHeaderBlock(url1.spec())); |
| - EXPECT_EQ(ERR_IO_PENDING, |
| - spdy_stream1->SendRequestHeaders( |
| - headers1.Pass(), NO_MORE_DATA_TO_SEND)); |
| - EXPECT_TRUE(spdy_stream1->HasUrl()); |
| + test::StreamDelegateWithBody delegate2(stream2, kBodyDataStringPiece); |
| + stream2->SetDelegate(&delegate2); |
| - base::MessageLoop::current()->RunUntilIdle(); |
| + EXPECT_FALSE(stream2->HasUrl()); |
| - // Release the session, so holding onto a pointer here does not affect |
| - // anything. |
| - session1 = NULL; |
| + EXPECT_FALSE(stream1->send_stalled_by_flow_control()); |
| + EXPECT_FALSE(stream2->send_stalled_by_flow_control()); |
| - // Trying to create a new connection should cause the pool to be stalled, and |
| - // post a task asynchronously to try and close the session. |
| - TestCompletionCallback callback2; |
| - HostPortPair host_port2("2.com", 80); |
| - scoped_refptr<TransportSocketParams> params2( |
| - new TransportSocketParams(host_port2, DEFAULT_PRIORITY, false, false, |
| - OnHostResolutionCallback())); |
| - scoped_ptr<ClientSocketHandle> connection2(new ClientSocketHandle); |
| + StallSessionSend(session.get()); |
| + |
| + scoped_ptr<SpdyHeaderBlock> headers1( |
| + spdy_util_.ConstructPostHeaderBlock(kStreamUrl, kBodyDataSize)); |
| EXPECT_EQ(ERR_IO_PENDING, |
| - connection2->Init(host_port2.ToString(), params2, DEFAULT_PRIORITY, |
| - callback2.callback(), pool, BoundNetLog())); |
| - EXPECT_TRUE(pool->IsStalled()); |
| + stream1->SendRequestHeaders(headers1.Pass(), MORE_DATA_TO_SEND)); |
| + EXPECT_TRUE(stream1->HasUrl()); |
| + EXPECT_EQ(kStreamUrl, stream1->GetUrl().spec()); |
| - // Running the message loop should cause the socket pool to ask the SPDY |
| - // session to close an idle socket, but since the socket is in use, nothing |
| - // happens. |
| - base::RunLoop().RunUntilIdle(); |
| - EXPECT_TRUE(pool->IsStalled()); |
| - EXPECT_FALSE(callback2.have_result()); |
| + data.RunFor(1); |
| + EXPECT_EQ(1u, stream1->stream_id()); |
| + EXPECT_TRUE(stream1->send_stalled_by_flow_control()); |
| - // Cancelling the request should still not release the session's socket, |
| - // since the session is still kept alive by the SpdySessionPool. |
| - ASSERT_TRUE(spdy_stream1.get()); |
| - spdy_stream1->Cancel(); |
| - base::RunLoop().RunUntilIdle(); |
| - EXPECT_TRUE(pool->IsStalled()); |
| - EXPECT_FALSE(callback2.have_result()); |
| -} |
| + scoped_ptr<SpdyHeaderBlock> headers2( |
| + spdy_util_.ConstructPostHeaderBlock(kStreamUrl, kBodyDataSize)); |
| + EXPECT_EQ(ERR_IO_PENDING, |
| + stream2->SendRequestHeaders(headers2.Pass(), MORE_DATA_TO_SEND)); |
| + EXPECT_TRUE(stream2->HasUrl()); |
| + EXPECT_EQ(kStreamUrl, stream2->GetUrl().spec()); |
| -// Verify that SpdySessionKey and therefore SpdySession is different when |
| -// privacy mode is enabled or disabled. |
| -TEST_P(SpdySessionTest, SpdySessionKeyPrivacyMode) { |
| - CreateDeterministicNetworkSession(); |
| + data.RunFor(1); |
| + EXPECT_EQ(3u, stream2->stream_id()); |
| + EXPECT_TRUE(stream2->send_stalled_by_flow_control()); |
| - HostPortPair host_port_pair("www.google.com", 443); |
| - SpdySessionKey key_privacy_enabled(host_port_pair, ProxyServer::Direct(), |
| - kPrivacyModeEnabled); |
| - SpdySessionKey key_privacy_disabled(host_port_pair, ProxyServer::Direct(), |
| - kPrivacyModeDisabled); |
| + EXPECT_TRUE(spdy_session_pool_->HasSession(key_)); |
| - EXPECT_FALSE(spdy_session_pool_->HasSession(key_privacy_enabled)); |
| - EXPECT_FALSE(spdy_session_pool_->HasSession(key_privacy_disabled)); |
| + // Unstall stream1. |
| + UnstallSessionSend(session.get(), kBodyDataSize); |
| - // Add SpdySession with PrivacyMode Enabled to the pool. |
| - scoped_refptr<SpdySession> session_privacy_enabled = |
| - spdy_session_pool_->Get(key_privacy_enabled, BoundNetLog()); |
| + // Close the session (since we can't do it from within the delegate |
| + // method, since it's in the stream's loop). |
| + session->CloseSessionOnError(ERR_CONNECTION_CLOSED, true, "Closing session"); |
| + session = NULL; |
| - EXPECT_TRUE(spdy_session_pool_->HasSession(key_privacy_enabled)); |
| - EXPECT_FALSE(spdy_session_pool_->HasSession(key_privacy_disabled)); |
| + EXPECT_FALSE(spdy_session_pool_->HasSession(key_)); |
| - // Add SpdySession with PrivacyMode Disabled to the pool. |
| - scoped_refptr<SpdySession> session_privacy_disabled = |
| - spdy_session_pool_->Get(key_privacy_disabled, BoundNetLog()); |
| + EXPECT_EQ(ERR_CONNECTION_CLOSED, delegate1.WaitForClose()); |
| + EXPECT_EQ(ERR_CONNECTION_CLOSED, delegate2.WaitForClose()); |
| - EXPECT_TRUE(spdy_session_pool_->HasSession(key_privacy_enabled)); |
| - EXPECT_TRUE(spdy_session_pool_->HasSession(key_privacy_disabled)); |
| + EXPECT_TRUE(delegate1.send_headers_completed()); |
| + EXPECT_EQ(std::string(), delegate1.TakeReceivedData()); |
| - spdy_session_pool_->Remove(session_privacy_enabled); |
| - EXPECT_FALSE(spdy_session_pool_->HasSession(key_privacy_enabled)); |
| - EXPECT_TRUE(spdy_session_pool_->HasSession(key_privacy_disabled)); |
| + EXPECT_TRUE(delegate2.send_headers_completed()); |
| + EXPECT_EQ(std::string(), delegate2.TakeReceivedData()); |
| - spdy_session_pool_->Remove(session_privacy_disabled); |
| - EXPECT_FALSE(spdy_session_pool_->HasSession(key_privacy_enabled)); |
| - EXPECT_FALSE(spdy_session_pool_->HasSession(key_privacy_disabled)); |
| + EXPECT_TRUE(data.at_write_eof()); |
| } |
| } // namespace net |