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 477bb5252f3cc652edf91e619115159930b15ebc..1e1e1d6102f975dff66f9815b0499dd827d75b55 100644 |
--- a/net/quic/quic_stream_factory_test.cc |
+++ b/net/quic/quic_stream_factory_test.cc |
@@ -24,6 +24,7 @@ |
#include "net/quic/crypto/quic_encrypter.h" |
#include "net/quic/crypto/quic_server_info.h" |
#include "net/quic/quic_http_stream.h" |
+#include "net/quic/quic_http_utils.h" |
#include "net/quic/quic_server_id.h" |
#include "net/quic/test_tools/mock_clock.h" |
#include "net/quic/test_tools/mock_crypto_client_stream_factory.h" |
@@ -46,6 +47,7 @@ using std::string; |
using std::vector; |
namespace net { |
+ |
namespace test { |
namespace { |
@@ -119,6 +121,71 @@ 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(const NetworkList& network_list) { |
+ connected_networks_ = network_list; |
+ } |
+ |
+ void GetCurrentConnectedNetworks(NetworkList* network_list) const override { |
+ network_list->clear(); |
+ *network_list = connected_networks_; |
+ } |
+ |
+ void NotifyNetworkSoonToDisconnect( |
+ NetworkChangeNotifier::NetworkHandle network) { |
+ NetworkChangeNotifier::NotifyObserversOfSpecificNetworkChange( |
+ NetworkChangeNotifier::SOON_TO_DISCONNECT, network); |
+ // Spin the message loop so the notification is delivered. |
+ base::MessageLoop::current()->RunUntilIdle(); |
+ } |
+ |
+ void NotifyNetworkDisconnected(NetworkChangeNotifier::NetworkHandle network) { |
+ NetworkChangeNotifier::NotifyObserversOfSpecificNetworkChange( |
+ NetworkChangeNotifier::DISCONNECTED, network); |
+ // Spin the message loop so the notification is delivered. |
+ base::MessageLoop::current()->RunUntilIdle(); |
+ } |
+ |
+ 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() |
@@ -131,6 +198,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), |
@@ -151,7 +219,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)); |
} |
@@ -172,11 +241,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); |
@@ -263,14 +344,40 @@ 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(); |
} |
+ scoped_ptr<QuicEncryptedPacket> ConstructGetRequestPacket( |
+ QuicPacketNumber packet_number, |
+ QuicStreamId stream_id, |
+ bool should_include_version, |
+ bool fin) { |
+ SpdyHeaderBlock headers = maker_.GetRequestHeaders("GET", "https", "/"); |
+ SpdyPriority priority = |
+ ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY); |
+ size_t spdy_headers_frame_len; |
+ return maker_.MakeRequestHeadersPacket( |
+ packet_number, stream_id, should_include_version, fin, priority, |
+ headers, &spdy_headers_frame_len); |
+ } |
+ |
+ scoped_ptr<QuicEncryptedPacket> ConstructOkResponsePacket( |
+ QuicPacketNumber packet_number, |
+ QuicStreamId stream_id, |
+ bool should_include_version, |
+ bool fin) { |
+ SpdyHeaderBlock headers = maker_.GetResponseHeaders("200 OK"); |
+ size_t spdy_headers_frame_len; |
+ return maker_.MakeResponseHeadersPacket(packet_number, stream_id, |
+ should_include_version, fin, |
+ headers, &spdy_headers_frame_len); |
+ } |
+ |
MockHostResolver host_resolver_; |
MockClientSocketFactory socket_factory_; |
MockCryptoClientStreamFactory crypto_client_stream_factory_; |
+ ProofVerifyDetailsChromium verify_details_; |
MockRandom random_generator_; |
MockClock* clock_; // Owned by factory_. |
scoped_refptr<TestTaskRunner> runner_; |
@@ -280,6 +387,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_; |
@@ -305,6 +414,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, |
@@ -1172,7 +1282,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())); |
@@ -1252,6 +1362,499 @@ TEST_P(QuicStreamFactoryTest, OnIPAddressChanged) { |
EXPECT_TRUE(socket_data2.AllWriteDataConsumed()); |
} |
+TEST_P(QuicStreamFactoryTest, OnNetworkChangeSoonToDisconnect) { |
+ InitializeConnectionMigrationTest( |
+ {kDefaultNetworkForTests, kNewNetworkForTests}); |
+ ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails(); |
+ crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); |
+ crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); |
+ |
+ MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)}; |
+ scoped_ptr<QuicEncryptedPacket> request_packet( |
+ ConstructGetRequestPacket(1, kClientDataStreamId1, true, true)); |
+ MockWrite writes[] = {MockWrite(SYNCHRONOUS, request_packet->data(), |
+ request_packet->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; |
+ request_info.method = "GET"; |
+ request_info.url = GURL("https://www.example.org/"); |
+ 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_)); |
+ |
+ // Send GET request on stream. |
+ HttpResponseInfo response; |
+ HttpRequestHeaders request_headers; |
+ EXPECT_EQ(OK, stream->SendRequest(request_headers, &response, |
+ callback_.callback())); |
+ |
+ // Set up second socket data provider that is used after migration. |
+ // The response to the earlier request is read on this new socket. |
+ scoped_ptr<QuicEncryptedPacket> ping( |
+ maker_.MakePingPacket(2, /*include_version=*/true)); |
+ MockWrite writes1[] = { |
+ MockWrite(SYNCHRONOUS, ping->data(), ping->length(), 0)}; |
+ scoped_ptr<QuicEncryptedPacket> response_headers_packet( |
+ ConstructOkResponsePacket(1, kClientDataStreamId1, false, false)); |
+ MockRead reads1[] = {MockRead(ASYNC, response_headers_packet->data(), |
+ response_headers_packet->length(), 1), |
+ MockRead(SYNCHRONOUS, ERR_IO_PENDING, 2)}; |
+ SequencedSocketData socket_data1(reads1, arraysize(reads1), writes1, |
+ arraysize(writes1)); |
+ socket_factory_.AddSocketDataProvider(&socket_data1); |
+ |
+ // Trigger connection migration. This should cause a PING frame |
+ // to be emitted. |
+ scoped_mock_network_change_notifier_->mock_network_change_notifier() |
+ ->NotifyNetworkSoonToDisconnect(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()); |
+ |
+ // Verify that response headers on the migrated socket were delivered to the |
+ // stream. |
+ EXPECT_EQ(OK, stream->ReadResponseHeaders(callback_.callback())); |
+ EXPECT_EQ(200, response.headers->response_code()); |
+ |
+ // 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. |
+ scoped_mock_network_change_notifier_->mock_network_change_notifier() |
+ ->NotifyNetworkDisconnected(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}); |
+ ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails(); |
+ crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); |
+ crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); |
+ |
+ MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)}; |
+ scoped_ptr<QuicEncryptedPacket> request_packet( |
+ ConstructGetRequestPacket(1, kClientDataStreamId1, true, true)); |
+ MockWrite writes[] = {MockWrite(SYNCHRONOUS, request_packet->data(), |
+ request_packet->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; |
+ request_info.method = "GET"; |
+ request_info.url = GURL("https://www.example.org/"); |
+ 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_)); |
+ |
+ // Send GET request on stream. |
+ HttpResponseInfo response_info; |
+ HttpRequestHeaders request_headers; |
+ EXPECT_EQ(OK, stream->SendRequest(request_headers, &response_info, |
+ callback_.callback())); |
+ |
+ // Set up second socket data provider that is used after migration. |
+ scoped_ptr<QuicEncryptedPacket> ping( |
+ maker_.MakePingPacket(2, /*include_version=*/true)); |
+ scoped_ptr<QuicEncryptedPacket> client_rst(maker_.MakeRstPacket( |
+ 3, true, kClientDataStreamId1, QUIC_STREAM_CANCELLED)); |
+ MockWrite writes1[] = { |
+ MockWrite(SYNCHRONOUS, ping->data(), ping->length(), 0)}; |
+ scoped_ptr<QuicEncryptedPacket> response_packet( |
+ ConstructOkResponsePacket(1, kClientDataStreamId1, false, false)); |
+ MockRead reads1[] = { |
+ MockRead(ASYNC, response_packet->data(), response_packet->length(), 1), |
+ MockRead(SYNCHRONOUS, ERR_IO_PENDING, 2)}; |
+ SequencedSocketData socket_data1(reads1, arraysize(reads1), writes1, |
+ arraysize(writes1)); |
+ socket_factory_.AddSocketDataProvider(&socket_data1); |
+ |
+ // Trigger connection migration. This should cause a PING frame |
+ // to be emitted. |
+ scoped_mock_network_change_notifier_->mock_network_change_notifier() |
+ ->NotifyNetworkDisconnected(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)); |
+ |
+ 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); |
+ ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails(); |
+ crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); |
+ |
+ 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()); |
+ |
+ // Trigger connection migration. 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. |
+ scoped_mock_network_change_notifier_->mock_network_change_notifier() |
+ ->NotifyNetworkSoonToDisconnect(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); |
+ ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails(); |
+ crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); |
+ |
+ 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_)); |
+ |
+ // Trigger connection migration. Since there are no networks |
+ // to migrate to, this should cause a RST_STREAM frame to be emitted |
+ // and the session to be closed. |
+ scoped_mock_network_change_notifier_->mock_network_change_notifier() |
+ ->NotifyNetworkDisconnected(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}); |
+ ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails(); |
+ crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); |
+ |
+ 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_)); |
+ |
+ // Trigger connection migration. Since there are no networks |
+ // to migrate to, this should cause session to be continue but be marked as |
+ // going away. |
+ scoped_mock_network_change_notifier_->mock_network_change_notifier() |
+ ->NotifyNetworkSoonToDisconnect(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}); |
+ ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails(); |
+ crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); |
+ |
+ 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_)); |
+ |
+ // Trigger connection migration. 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. |
+ scoped_mock_network_change_notifier_->mock_network_change_notifier() |
+ ->NotifyNetworkDisconnected(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}); |
+ ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails(); |
+ crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); |
+ |
+ 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_)); |
+ |
+ // Trigger connection migration. Since there are no active streams, |
+ // the session will be closed. |
+ scoped_mock_network_change_notifier_->mock_network_change_notifier() |
+ ->NotifyNetworkDisconnected(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}); |
+ ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails(); |
+ crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); |
+ |
+ 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_)); |
+ |
+ // Trigger connection migration. Since there are no active streams, |
+ // the session will be closed. |
+ scoped_mock_network_change_notifier_->mock_network_change_notifier() |
+ ->NotifyNetworkDisconnected(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(); |
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails(); |