Chromium Code Reviews| Index: net/quic/quic_stream_factory_test.cc |
| diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc |
| index 30868c1458b4a86ddc78f29ef232468446908cb5..83794eb4a510c26f1c9f1ca7e47c4ca0ff6bafbb 100644 |
| --- a/net/quic/quic_stream_factory_test.cc |
| +++ b/net/quic/quic_stream_factory_test.cc |
| @@ -46,6 +46,10 @@ using std::string; |
| using std::vector; |
| namespace net { |
| + |
| +extern NetworkChangeNotifier::NetworkHandle kDefaultNetworkForTests; |
|
Ryan Hamilton
2015/12/29 05:29:36
Is this coming from socket_test_utils.cc? If so, I
Jana
2016/01/06 23:01:17
Done.
|
| +extern NetworkChangeNotifier::NetworkHandle kNewNetworkForTests; |
| + |
| namespace test { |
| namespace { |
| @@ -116,6 +120,56 @@ class MockQuicServerInfoFactory : public QuicServerInfoFactory { |
| } |
| }; |
| +class MockNetworkChangeNotifier : public NetworkChangeNotifier { |
| + public: |
| + MockNetworkChangeNotifier() : force_network_handles_supported_(false) {} |
| + |
| + ConnectionType GetCurrentConnectionType() const override { |
| + return CONNECTION_UNKNOWN; |
| + } |
| + |
| + void ForceNetworkHandlesSupported() { |
| + force_network_handles_supported_ = true; |
| + } |
| + |
| + bool AreNetworkHandlesCurrentlySupported() const override { |
| + return force_network_handles_supported_; |
| + } |
| + |
| + void SetConnectedNetworksList(NetworkList network_list) { |
|
Ryan Hamilton
2015/12/29 21:18:55
nit: const &
Jana
2016/01/06 23:01:17
Done.
|
| + connected_networks_ = network_list; |
| + } |
| + |
| + void GetCurrentConnectedNetworks(NetworkList* network_list) const override { |
| + network_list->clear(); |
| + *network_list = connected_networks_; |
| + } |
| + |
| + private: |
| + bool force_network_handles_supported_; |
| + NetworkChangeNotifier::NetworkList connected_networks_; |
| +}; |
| + |
| +// Class to replace existing NetworkChangeNotifier singleton with a |
| +// MockNetworkChangeNotifier for a test. To use, simply create a |
| +// ScopedMockNetworkChangeNotifier object in the test. |
| +class ScopedMockNetworkChangeNotifier { |
| + public: |
| + ScopedMockNetworkChangeNotifier() |
| + : disable_network_change_notifier_for_tests_( |
| + new NetworkChangeNotifier::DisableForTest()), |
| + mock_network_change_notifier_(new MockNetworkChangeNotifier()) {} |
| + |
| + MockNetworkChangeNotifier* mock_network_change_notifier() { |
| + return mock_network_change_notifier_.get(); |
| + } |
| + |
| + private: |
| + scoped_ptr<NetworkChangeNotifier::DisableForTest> |
| + disable_network_change_notifier_for_tests_; |
| + scoped_ptr<MockNetworkChangeNotifier> mock_network_change_notifier_; |
| +}; |
| + |
| class QuicStreamFactoryTest : public ::testing::TestWithParam<TestParams> { |
| protected: |
| QuicStreamFactoryTest() |
| @@ -128,6 +182,7 @@ class QuicStreamFactoryTest : public ::testing::TestWithParam<TestParams> { |
| new ChannelIDService(new DefaultChannelIDStore(nullptr), |
| base::ThreadTaskRunnerHandle::Get())), |
| cert_transparency_verifier_(new MultiLogCTVerifier()), |
| + scoped_mock_network_change_notifier_(nullptr), |
| factory_(nullptr), |
| host_port_pair_(kDefaultServerHostName, kDefaultServerPort), |
| privacy_mode_(PRIVACY_MODE_DISABLED), |
| @@ -148,7 +203,8 @@ class QuicStreamFactoryTest : public ::testing::TestWithParam<TestParams> { |
| delay_tcp_race_(false), |
| store_server_configs_in_properties_(false), |
| close_sessions_on_ip_change_(false), |
| - idle_connection_timeout_seconds_(kIdleConnectionTimeoutSeconds) { |
| + idle_connection_timeout_seconds_(kIdleConnectionTimeoutSeconds), |
| + migrate_sessions_on_network_change_(false) { |
| clock_->AdvanceTime(QuicTime::Delta::FromSeconds(1)); |
| } |
| @@ -169,11 +225,23 @@ class QuicStreamFactoryTest : public ::testing::TestWithParam<TestParams> { |
| threshold_public_resets_post_handshake_, receive_buffer_size_, |
| delay_tcp_race_, store_server_configs_in_properties_, |
| close_sessions_on_ip_change_, idle_connection_timeout_seconds_, |
| - QuicTagVector())); |
| + migrate_sessions_on_network_change_, QuicTagVector())); |
| factory_->set_require_confirmation(false); |
| factory_->set_quic_server_info_factory(new MockQuicServerInfoFactory()); |
| } |
| + void InitializeConnectionMigrationTest( |
| + NetworkChangeNotifier::NetworkList connected_networks) { |
| + scoped_mock_network_change_notifier_.reset( |
| + new ScopedMockNetworkChangeNotifier()); |
| + MockNetworkChangeNotifier* mock_ncn = |
| + scoped_mock_network_change_notifier_->mock_network_change_notifier(); |
| + mock_ncn->ForceNetworkHandlesSupported(); |
| + mock_ncn->SetConnectedNetworksList(connected_networks); |
| + migrate_sessions_on_network_change_ = true; |
| + Initialize(); |
| + } |
| + |
| bool HasActiveSession(const HostPortPair& host_port_pair) { |
| return QuicStreamFactoryPeer::HasActiveSession(factory_.get(), |
| host_port_pair); |
| @@ -261,8 +329,16 @@ class QuicStreamFactoryTest : public ::testing::TestWithParam<TestParams> { |
| void NotifyIPAddressChanged() { |
| NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests(); |
| - // For thread safety, the NCN queues tasks to do the actual notifications, |
| - // so we need to spin the message loop so the notification is delivered. |
| + // Spin the message loop so the notification is delivered. |
| + base::MessageLoop::current()->RunUntilIdle(); |
| + } |
| + |
| + void NotifySpecificNetworkChange( |
| + NetworkChangeNotifier::NetworkChangeType type, |
| + NetworkChangeNotifier::NetworkHandle network) { |
| + NetworkChangeNotifier::NotifyObserversOfSpecificNetworkChangeForTests( |
| + type, network); |
| + // Spin the message loop so the notification is delivered. |
| base::MessageLoop::current()->RunUntilIdle(); |
| } |
| @@ -278,6 +354,8 @@ class QuicStreamFactoryTest : public ::testing::TestWithParam<TestParams> { |
| scoped_ptr<ChannelIDService> channel_id_service_; |
| TransportSecurityState transport_security_state_; |
| scoped_ptr<CTVerifier> cert_transparency_verifier_; |
| + scoped_ptr<ScopedMockNetworkChangeNotifier> |
| + scoped_mock_network_change_notifier_; |
| scoped_ptr<QuicStreamFactory> factory_; |
| HostPortPair host_port_pair_; |
| PrivacyMode privacy_mode_; |
| @@ -303,6 +381,7 @@ class QuicStreamFactoryTest : public ::testing::TestWithParam<TestParams> { |
| bool store_server_configs_in_properties_; |
| bool close_sessions_on_ip_change_; |
| int idle_connection_timeout_seconds_; |
| + bool migrate_sessions_on_network_change_; |
| }; |
| INSTANTIATE_TEST_CASE_P(Version, |
| @@ -1133,7 +1212,7 @@ TEST_P(QuicStreamFactoryTest, CloseAllSessions) { |
| net_log_, CompletionCallback())); |
| // Close the session and verify that stream saw the error. |
| - factory_->CloseAllSessions(ERR_INTERNET_DISCONNECTED); |
| + factory_->CloseAllSessions(ERR_INTERNET_DISCONNECTED, QUIC_INTERNAL_ERROR); |
| EXPECT_EQ(ERR_INTERNET_DISCONNECTED, |
| stream->ReadResponseHeaders(callback_.callback())); |
| @@ -1210,6 +1289,445 @@ TEST_P(QuicStreamFactoryTest, OnIPAddressChanged) { |
| EXPECT_TRUE(socket_data2.AllWriteDataConsumed()); |
| } |
| +TEST_P(QuicStreamFactoryTest, OnNetworkChangeSoonToDisconnect) { |
| + InitializeConnectionMigrationTest( |
| + {kDefaultNetworkForTests, kNewNetworkForTests}); |
| + |
| + MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)}; |
| + SequencedSocketData socket_data(reads, arraysize(reads), nullptr, 0); |
| + socket_factory_.AddSocketDataProvider(&socket_data); |
| + |
| + // Create request and QuicHttpStream. |
| + QuicStreamRequest request(factory_.get()); |
| + EXPECT_EQ(ERR_IO_PENDING, |
| + request.Request(host_port_pair_, privacy_mode_, |
| + /*cert_verify_flags=*/0, host_port_pair_.host(), |
| + "GET", net_log_, callback_.callback())); |
| + EXPECT_EQ(OK, callback_.WaitForResult()); |
| + scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); |
| + EXPECT_TRUE(stream.get()); |
| + |
| + // Cause QUIC stream to be created. |
| + HttpRequestInfo request_info; |
| + EXPECT_EQ(OK, stream->InitializeStream(&request_info, DEFAULT_PRIORITY, |
| + net_log_, CompletionCallback())); |
| + |
| + // Ensure that session is alive and active. |
| + QuicChromiumClientSession* session = |
| + QuicStreamFactoryPeer::GetActiveSession(factory_.get(), host_port_pair_); |
| + EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
| + EXPECT_TRUE(HasActiveSession(host_port_pair_)); |
| + |
| + // Set up second socket data provider that is used after migration. |
| + MockRead reads1[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)}; |
| + scoped_ptr<QuicEncryptedPacket> ping( |
| + maker_.MakePingPacket(1, /*include_version=*/true)); |
| + MockWrite writes1[] = { |
| + MockWrite(SYNCHRONOUS, ping->data(), ping->length(), 1)}; |
| + SequencedSocketData socket_data1(reads1, arraysize(reads1), writes1, |
| + arraysize(writes1)); |
| + socket_factory_.AddSocketDataProvider(&socket_data1); |
| + |
| + // Cause connection migration to happen. This should cause a PING frame |
| + // to be emitted. |
| + NotifySpecificNetworkChange(NetworkChangeNotifier::SOON_TO_DISCONNECT, |
| + kDefaultNetworkForTests); |
| + |
| + // The session should now be marked as going away. Ensure that |
| + // while it is still alive, it is no longer active. |
| + EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
| + EXPECT_FALSE(HasActiveSession(host_port_pair_)); |
| + EXPECT_EQ(1u, session->GetNumActiveStreams()); |
| + |
| + // Create a new request for the same destination and verify that a |
| + // new session is created. |
| + MockRead reads2[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)}; |
| + SequencedSocketData socket_data2(reads2, arraysize(reads2), nullptr, 0); |
| + socket_factory_.AddSocketDataProvider(&socket_data2); |
| + |
| + QuicStreamRequest request2(factory_.get()); |
| + EXPECT_EQ(ERR_IO_PENDING, |
| + request2.Request(host_port_pair_, privacy_mode_, |
| + /*cert_verify_flags=*/0, host_port_pair_.host(), |
| + "GET", net_log_, callback_.callback())); |
| + EXPECT_EQ(OK, callback_.WaitForResult()); |
| + scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream(); |
| + EXPECT_TRUE(stream2.get()); |
| + |
| + EXPECT_TRUE( |
| + QuicStreamFactoryPeer::HasActiveSession(factory_.get(), host_port_pair_)); |
| + EXPECT_NE(session, QuicStreamFactoryPeer::GetActiveSession(factory_.get(), |
| + host_port_pair_)); |
| + |
| + // On a DISCONNECTED notification, nothing happens to the migrated session. |
| + NotifySpecificNetworkChange(NetworkChangeNotifier::DISCONNECTED, |
| + kDefaultNetworkForTests); |
| + EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
| + EXPECT_EQ(1u, session->GetNumActiveStreams()); |
| + |
| + EXPECT_TRUE(socket_data.AllReadDataConsumed()); |
| + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); |
| + EXPECT_TRUE(socket_data1.AllReadDataConsumed()); |
| + EXPECT_TRUE(socket_data1.AllWriteDataConsumed()); |
| + EXPECT_TRUE(socket_data2.AllReadDataConsumed()); |
| + EXPECT_TRUE(socket_data2.AllWriteDataConsumed()); |
| +} |
| + |
| +TEST_P(QuicStreamFactoryTest, OnNetworkChangeDisconnected) { |
| + InitializeConnectionMigrationTest( |
| + {kDefaultNetworkForTests, kNewNetworkForTests}); |
| + |
| + MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)}; |
| + SequencedSocketData socket_data(reads, arraysize(reads), nullptr, 0); |
| + socket_factory_.AddSocketDataProvider(&socket_data); |
| + |
| + // Create request and QuicHttpStream. |
| + QuicStreamRequest request(factory_.get()); |
| + EXPECT_EQ(ERR_IO_PENDING, |
| + request.Request(host_port_pair_, privacy_mode_, |
| + /*cert_verify_flags=*/0, host_port_pair_.host(), |
| + "GET", net_log_, callback_.callback())); |
| + EXPECT_EQ(OK, callback_.WaitForResult()); |
| + scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); |
| + EXPECT_TRUE(stream.get()); |
| + |
| + // Cause QUIC stream to be created. |
| + HttpRequestInfo request_info; |
| + EXPECT_EQ(OK, stream->InitializeStream(&request_info, DEFAULT_PRIORITY, |
| + net_log_, CompletionCallback())); |
| + |
| + // Ensure that session is alive and active. |
| + QuicChromiumClientSession* session = |
| + QuicStreamFactoryPeer::GetActiveSession(factory_.get(), host_port_pair_); |
| + EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
| + EXPECT_TRUE(HasActiveSession(host_port_pair_)); |
| + |
| + // Set up second socket data provider that is used after migration. |
| + MockRead reads1[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)}; |
| + scoped_ptr<QuicEncryptedPacket> ping( |
| + maker_.MakePingPacket(1, /*include_version=*/true)); |
| + scoped_ptr<QuicEncryptedPacket> client_rst(maker_.MakeRstPacket( |
| + 2, true, kClientDataStreamId1, QUIC_STREAM_CANCELLED)); |
| + MockWrite writes1[] = { |
| + MockWrite(SYNCHRONOUS, ping->data(), ping->length(), 1), |
| + MockWrite(SYNCHRONOUS, client_rst->data(), client_rst->length(), 2)}; |
| + SequencedSocketData socket_data1(reads1, arraysize(reads1), writes1, |
| + arraysize(writes1)); |
| + socket_factory_.AddSocketDataProvider(&socket_data1); |
| + |
| + // Cause connection migration to happen. This should cause a PING frame |
| + // to be emitted. |
| + NotifySpecificNetworkChange(NetworkChangeNotifier::DISCONNECTED, |
| + kDefaultNetworkForTests); |
| + |
| + // The session should now be marked as going away. Ensure that |
| + // while it is still alive, it is no longer active. |
| + EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
| + EXPECT_FALSE(HasActiveSession(host_port_pair_)); |
| + EXPECT_EQ(1u, session->GetNumActiveStreams()); |
| + |
| + // Create a new request for the same destination and verify that a |
| + // new session is created. |
| + MockRead reads2[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)}; |
| + SequencedSocketData socket_data2(reads2, arraysize(reads2), nullptr, 0); |
| + socket_factory_.AddSocketDataProvider(&socket_data2); |
| + |
| + QuicStreamRequest request2(factory_.get()); |
| + EXPECT_EQ(ERR_IO_PENDING, |
| + request2.Request(host_port_pair_, privacy_mode_, |
| + /*cert_verify_flags=*/0, host_port_pair_.host(), |
| + "GET", net_log_, callback_.callback())); |
| + EXPECT_EQ(OK, callback_.WaitForResult()); |
| + scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream(); |
| + EXPECT_TRUE(stream2.get()); |
| + |
| + EXPECT_TRUE( |
| + QuicStreamFactoryPeer::HasActiveSession(factory_.get(), host_port_pair_)); |
| + EXPECT_NE(session, QuicStreamFactoryPeer::GetActiveSession(factory_.get(), |
| + host_port_pair_)); |
| + EXPECT_EQ(true, |
| + QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
| + |
| + stream.reset(); |
| + stream2.reset(); |
| + |
| + EXPECT_TRUE(socket_data.AllReadDataConsumed()); |
| + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); |
| + EXPECT_TRUE(socket_data1.AllReadDataConsumed()); |
| + EXPECT_TRUE(socket_data1.AllWriteDataConsumed()); |
| + EXPECT_TRUE(socket_data2.AllReadDataConsumed()); |
| + EXPECT_TRUE(socket_data2.AllWriteDataConsumed()); |
| +} |
| + |
| +TEST_P(QuicStreamFactoryTest, OnNetworkChangeSoonToDisconnectNoNetworks) { |
| + NetworkChangeNotifier::NetworkList no_networks(0); |
| + InitializeConnectionMigrationTest(no_networks); |
| + |
| + MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)}; |
| + scoped_ptr<QuicEncryptedPacket> client_rst(maker_.MakeRstPacket( |
| + 1, true, kClientDataStreamId1, QUIC_STREAM_CANCELLED)); |
| + MockWrite writes[] = { |
| + MockWrite(SYNCHRONOUS, client_rst->data(), client_rst->length(), 1), |
| + }; |
| + SequencedSocketData socket_data(reads, arraysize(reads), writes, |
| + arraysize(writes)); |
| + socket_factory_.AddSocketDataProvider(&socket_data); |
| + |
| + // Create request and QuicHttpStream. |
| + QuicStreamRequest request(factory_.get()); |
| + EXPECT_EQ(ERR_IO_PENDING, |
| + request.Request(host_port_pair_, privacy_mode_, |
| + /*cert_verify_flags=*/0, host_port_pair_.host(), |
| + "GET", net_log_, callback_.callback())); |
| + EXPECT_EQ(OK, callback_.WaitForResult()); |
| + scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); |
| + EXPECT_TRUE(stream.get()); |
| + |
| + // Cause QUIC stream to be created. |
| + HttpRequestInfo request_info; |
| + EXPECT_EQ(OK, stream->InitializeStream(&request_info, DEFAULT_PRIORITY, |
| + net_log_, CompletionCallback())); |
| + |
| + // Ensure that session is alive and active. |
| + QuicChromiumClientSession* session = |
| + QuicStreamFactoryPeer::GetActiveSession(factory_.get(), host_port_pair_); |
| + EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
| + EXPECT_TRUE(HasActiveSession(host_port_pair_)); |
| + EXPECT_EQ(1u, session->GetNumActiveStreams()); |
| + |
| + // Cause connection migration to happen. Since there are no networks |
| + // to migrate to, this should cause the session to continue on the same |
| + // socket, but be marked as going away. |
| + NotifySpecificNetworkChange(NetworkChangeNotifier::SOON_TO_DISCONNECT, |
| + kDefaultNetworkForTests); |
| + |
| + EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
| + EXPECT_FALSE(HasActiveSession(host_port_pair_)); |
| + EXPECT_EQ(1u, session->GetNumActiveStreams()); |
| + |
| + stream.reset(); |
| + |
| + EXPECT_TRUE(socket_data.AllReadDataConsumed()); |
| + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); |
| +} |
| + |
| +TEST_P(QuicStreamFactoryTest, OnNetworkChangeDisconnectedNoNetworks) { |
| + NetworkChangeNotifier::NetworkList no_networks(0); |
| + InitializeConnectionMigrationTest(no_networks); |
| + |
| + MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)}; |
| + scoped_ptr<QuicEncryptedPacket> client_rst(maker_.MakeRstPacket( |
| + 1, true, kClientDataStreamId1, QUIC_RST_ACKNOWLEDGEMENT)); |
| + MockWrite writes[] = { |
| + MockWrite(ASYNC, client_rst->data(), client_rst->length(), 1), |
| + }; |
| + SequencedSocketData socket_data(reads, arraysize(reads), writes, |
| + arraysize(writes)); |
| + socket_factory_.AddSocketDataProvider(&socket_data); |
| + |
| + // Create request and QuicHttpStream. |
| + QuicStreamRequest request(factory_.get()); |
| + EXPECT_EQ(ERR_IO_PENDING, |
| + request.Request(host_port_pair_, privacy_mode_, |
| + /*cert_verify_flags=*/0, host_port_pair_.host(), |
| + "GET", net_log_, callback_.callback())); |
| + EXPECT_EQ(OK, callback_.WaitForResult()); |
| + scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); |
| + EXPECT_TRUE(stream.get()); |
| + |
| + // Cause QUIC stream to be created. |
| + HttpRequestInfo request_info; |
| + EXPECT_EQ(OK, stream->InitializeStream(&request_info, DEFAULT_PRIORITY, |
| + net_log_, CompletionCallback())); |
| + |
| + // Ensure that session is alive and active. |
| + QuicChromiumClientSession* session = |
| + QuicStreamFactoryPeer::GetActiveSession(factory_.get(), host_port_pair_); |
| + EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
| + EXPECT_TRUE(HasActiveSession(host_port_pair_)); |
| + |
| + // Cause connection migration to happen. Since there are no networks |
| + // to migrate to, this should cause a RST_STREAM frame to be emitted |
| + // and the session to be closed. |
| + NotifySpecificNetworkChange(NetworkChangeNotifier::DISCONNECTED, |
| + kDefaultNetworkForTests); |
| + |
| + EXPECT_FALSE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
| + EXPECT_FALSE(HasActiveSession(host_port_pair_)); |
| + |
| + EXPECT_TRUE(socket_data.AllReadDataConsumed()); |
| + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); |
| +} |
| + |
| +TEST_P(QuicStreamFactoryTest, OnNetworkChangeSoonToDisconnectNoNewNetwork) { |
| + InitializeConnectionMigrationTest({kDefaultNetworkForTests}); |
| + |
| + MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)}; |
| + scoped_ptr<QuicEncryptedPacket> client_rst(maker_.MakeRstPacket( |
| + 1, true, kClientDataStreamId1, QUIC_STREAM_CANCELLED)); |
| + MockWrite writes[] = { |
| + MockWrite(SYNCHRONOUS, client_rst->data(), client_rst->length(), 1), |
| + }; |
| + SequencedSocketData socket_data(reads, arraysize(reads), writes, |
| + arraysize(writes)); |
| + socket_factory_.AddSocketDataProvider(&socket_data); |
| + |
| + // Create request and QuicHttpStream. |
| + QuicStreamRequest request(factory_.get()); |
| + EXPECT_EQ(ERR_IO_PENDING, |
| + request.Request(host_port_pair_, privacy_mode_, |
| + /*cert_verify_flags=*/0, host_port_pair_.host(), |
| + "GET", net_log_, callback_.callback())); |
| + EXPECT_EQ(OK, callback_.WaitForResult()); |
| + scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); |
| + EXPECT_TRUE(stream.get()); |
| + |
| + // Cause QUIC stream to be created. |
| + HttpRequestInfo request_info; |
| + EXPECT_EQ(OK, stream->InitializeStream(&request_info, DEFAULT_PRIORITY, |
| + net_log_, CompletionCallback())); |
| + |
| + // Ensure that session is alive and active. |
| + QuicChromiumClientSession* session = |
| + QuicStreamFactoryPeer::GetActiveSession(factory_.get(), host_port_pair_); |
| + EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
| + EXPECT_TRUE(HasActiveSession(host_port_pair_)); |
| + |
| + // Cause connection migration to happen. Since there are no networks |
| + // to migrate to, this should cause session to be continue but be marked as |
| + // going away. |
| + NotifySpecificNetworkChange(NetworkChangeNotifier::SOON_TO_DISCONNECT, |
| + kDefaultNetworkForTests); |
| + |
| + EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
| + EXPECT_FALSE(HasActiveSession(host_port_pair_)); |
| + EXPECT_EQ(1u, session->GetNumActiveStreams()); |
| + |
| + stream.reset(); |
| + |
| + EXPECT_TRUE(socket_data.AllReadDataConsumed()); |
| + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); |
| +} |
| + |
| +TEST_P(QuicStreamFactoryTest, OnNetworkChangeDisconnectedNoNewNetwork) { |
| + InitializeConnectionMigrationTest({kDefaultNetworkForTests}); |
| + |
| + MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)}; |
| + scoped_ptr<QuicEncryptedPacket> client_rst(maker_.MakeRstPacket( |
| + 1, true, kClientDataStreamId1, QUIC_RST_ACKNOWLEDGEMENT)); |
| + MockWrite writes[] = { |
| + MockWrite(ASYNC, client_rst->data(), client_rst->length(), 1), |
| + }; |
| + SequencedSocketData socket_data(reads, arraysize(reads), writes, |
| + arraysize(writes)); |
| + socket_factory_.AddSocketDataProvider(&socket_data); |
| + |
| + // Create request and QuicHttpStream. |
| + QuicStreamRequest request(factory_.get()); |
| + EXPECT_EQ(ERR_IO_PENDING, |
| + request.Request(host_port_pair_, privacy_mode_, |
| + /*cert_verify_flags=*/0, host_port_pair_.host(), |
| + "GET", net_log_, callback_.callback())); |
| + EXPECT_EQ(OK, callback_.WaitForResult()); |
| + scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); |
| + EXPECT_TRUE(stream.get()); |
| + |
| + // Cause QUIC stream to be created. |
| + HttpRequestInfo request_info; |
| + EXPECT_EQ(OK, stream->InitializeStream(&request_info, DEFAULT_PRIORITY, |
| + net_log_, CompletionCallback())); |
| + |
| + // Ensure that session is alive and active. |
| + QuicChromiumClientSession* session = |
| + QuicStreamFactoryPeer::GetActiveSession(factory_.get(), host_port_pair_); |
| + EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
| + EXPECT_TRUE(HasActiveSession(host_port_pair_)); |
| + |
| + // Cause connection migration to happen. Since there are no networks |
| + // to migrate to, this should cause a RST_STREAM frame to be emitted |
| + // with QUIC_RST_ACKNOWLEDGEMENT error code, and the session will be closed. |
| + NotifySpecificNetworkChange(NetworkChangeNotifier::DISCONNECTED, |
| + kDefaultNetworkForTests); |
| + |
| + EXPECT_FALSE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
| + EXPECT_FALSE(HasActiveSession(host_port_pair_)); |
| + |
| + EXPECT_TRUE(socket_data.AllReadDataConsumed()); |
| + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); |
| +} |
| + |
| +TEST_P(QuicStreamFactoryTest, OnNetworkChangeSoonToDisconnectNoOpenStreams) { |
| + InitializeConnectionMigrationTest( |
| + {kDefaultNetworkForTests, kNewNetworkForTests}); |
| + |
| + MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)}; |
| + SequencedSocketData socket_data(reads, arraysize(reads), nullptr, 0u); |
| + socket_factory_.AddSocketDataProvider(&socket_data); |
| + |
| + // Create request and QuicHttpStream. |
| + QuicStreamRequest request(factory_.get()); |
| + EXPECT_EQ(ERR_IO_PENDING, |
| + request.Request(host_port_pair_, privacy_mode_, |
| + /*cert_verify_flags=*/0, host_port_pair_.host(), |
| + "GET", net_log_, callback_.callback())); |
| + EXPECT_EQ(OK, callback_.WaitForResult()); |
| + scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); |
| + EXPECT_TRUE(stream.get()); |
| + |
| + // Ensure that session is alive and active. |
| + QuicChromiumClientSession* session = |
| + QuicStreamFactoryPeer::GetActiveSession(factory_.get(), host_port_pair_); |
| + EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
| + EXPECT_TRUE(HasActiveSession(host_port_pair_)); |
| + |
| + // Cause connection migration to happen. Since there are no active streams, |
| + // the session will be closed. |
| + NotifySpecificNetworkChange(NetworkChangeNotifier::DISCONNECTED, |
| + kDefaultNetworkForTests); |
| + |
| + EXPECT_FALSE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
| + EXPECT_FALSE(HasActiveSession(host_port_pair_)); |
| + |
| + EXPECT_TRUE(socket_data.AllReadDataConsumed()); |
| + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); |
| +} |
| + |
| +TEST_P(QuicStreamFactoryTest, OnNetworkChangeDisconnectedNoOpenStreams) { |
| + InitializeConnectionMigrationTest( |
| + {kDefaultNetworkForTests, kNewNetworkForTests}); |
| + |
| + MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)}; |
| + SequencedSocketData socket_data(reads, arraysize(reads), nullptr, 0u); |
| + socket_factory_.AddSocketDataProvider(&socket_data); |
| + |
| + // Create request and QuicHttpStream. |
| + QuicStreamRequest request(factory_.get()); |
| + EXPECT_EQ(ERR_IO_PENDING, |
| + request.Request(host_port_pair_, privacy_mode_, |
| + /*cert_verify_flags=*/0, host_port_pair_.host(), |
| + "GET", net_log_, callback_.callback())); |
| + EXPECT_EQ(OK, callback_.WaitForResult()); |
| + scoped_ptr<QuicHttpStream> stream = request.ReleaseStream(); |
| + EXPECT_TRUE(stream.get()); |
| + |
| + // Ensure that session is alive and active. |
| + QuicChromiumClientSession* session = |
| + QuicStreamFactoryPeer::GetActiveSession(factory_.get(), host_port_pair_); |
| + EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
| + EXPECT_TRUE(HasActiveSession(host_port_pair_)); |
| + |
| + // Cause connection migration to happen. Since there are no active streams, |
| + // the session will be closed. |
| + NotifySpecificNetworkChange(NetworkChangeNotifier::DISCONNECTED, |
| + kDefaultNetworkForTests); |
| + |
| + EXPECT_FALSE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
| + EXPECT_FALSE(HasActiveSession(host_port_pair_)); |
| + |
| + EXPECT_TRUE(socket_data.AllReadDataConsumed()); |
| + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); |
| +} |
| + |
| TEST_P(QuicStreamFactoryTest, OnSSLConfigChanged) { |
| Initialize(); |
| MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)}; |