Index: net/quic/chromium/quic_stream_factory_test.cc |
diff --git a/net/quic/chromium/quic_stream_factory_test.cc b/net/quic/chromium/quic_stream_factory_test.cc |
index 97413e245f6ea790a6c32c65dca242990f1ce466..60861660e692cfd9b7526bfa62bae2d73a9f1024 100644 |
--- a/net/quic/chromium/quic_stream_factory_test.cc |
+++ b/net/quic/chromium/quic_stream_factory_test.cc |
@@ -507,11 +507,17 @@ class QuicStreamFactoryTestBase { |
EXPECT_TRUE(socket_data2.AllWriteDataConsumed()); |
} |
+ void RunTestLoopUntilIdle() { |
+ while (runner_->GetPostedTasks().size() > 0) |
Ryan Hamilton
2016/09/12 03:42:01
nit: instead of size() > 0, !empty()
Ryan Hamilton
2016/09/13 00:33:18
ping
Jana
2016/09/13 01:12:18
Done.
|
+ runner_->RunNextTask(); |
+ } |
+ |
// Helper methods for tests of connection migration on write error. |
void MigrateSessionOnWriteErrorNonMigratableStream(IoMode mode); |
void MigrateSessionOnWriteErrorMigrationDisabled(IoMode mode); |
void MigrateSessionOnWriteError(IoMode mode); |
void MigrateSessionOnWriteErrorNoNewNetwork(IoMode mode); |
+ void MigrateSessionOnWriteErrorPauseBeforeConnected(IoMode mode); |
void MigrateSessionOnMultipleWriteErrors(IoMode mode1, IoMode mode2); |
void MigrateSessionOnWriteErrorWithNotificationQueued(bool disconnected); |
void MigrateSessionOnNotificationWithWriteErrorQueued(bool disconnected); |
@@ -1758,6 +1764,9 @@ TEST_P(QuicStreamFactoryTest, OnNetworkDisconnectedNoNewNetwork) { |
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails(); |
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); |
+ // Use the test task runner, to force the migration alarm timeout later. |
+ QuicStreamFactoryPeer::SetTaskRunner(factory_.get(), runner_.get()); |
+ |
MockQuicData socket_data; |
socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
socket_data.AddWrite(client_maker_.MakeRstPacket( |
@@ -1785,13 +1794,25 @@ TEST_P(QuicStreamFactoryTest, OnNetworkDisconnectedNoNewNetwork) { |
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. |
+ // to migrate to, this should cause the session to wait for a new network. |
scoped_mock_network_change_notifier_->mock_network_change_notifier() |
->NotifyNetworkDisconnected(kDefaultNetworkForTests); |
+ // The migration will not fail until the migration alarm timeout. |
+ EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
+ EXPECT_FALSE(HasActiveSession(host_port_pair_)); |
+ EXPECT_EQ(1u, session->GetNumActiveStreams()); |
+ EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(callback_.callback())); |
+ EXPECT_EQ(true, session->connection()->writer()->IsWriteBlocked()); |
+ |
+ // Force the migration alarm timeout to run. |
+ RunTestLoopUntilIdle(); |
+ |
+ // The connection should now be closed. A request for response |
+ // headers should fail. |
EXPECT_FALSE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
EXPECT_FALSE(HasActiveSession(host_port_pair_)); |
+ EXPECT_EQ(ERR_NETWORK_CHANGED, callback_.WaitForResult()); |
EXPECT_TRUE(socket_data.AllReadDataConsumed()); |
EXPECT_TRUE(socket_data.AllWriteDataConsumed()); |
@@ -2549,7 +2570,8 @@ TEST_P(QuicStreamFactoryTest, |
MigrateSessionOnWriteErrorNoNewNetwork(SYNCHRONOUS); |
} |
-TEST_P(QuicStreamFactoryTest, MigrateSessionOnWriteErrorNoNewNetworkAsync) { |
+TEST_P(QuicStreamFactoryTest, |
+ DISABLED_MigrateSessionOnWriteErrorNoNewNetworkAsync) { |
MigrateSessionOnWriteErrorNoNewNetwork(ASYNC); |
} |
@@ -2559,6 +2581,9 @@ void QuicStreamFactoryTestBase::MigrateSessionOnWriteErrorNoNewNetwork( |
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails(); |
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); |
+ // Use the test task runner, to force the migration alarm timeout later. |
+ QuicStreamFactoryPeer::SetTaskRunner(factory_.get(), runner_.get()); |
+ |
MockQuicData socket_data; |
socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
socket_data.AddWrite(mode, ERR_ADDRESS_UNREACHABLE); |
@@ -2586,20 +2611,169 @@ void QuicStreamFactoryTestBase::MigrateSessionOnWriteErrorNoNewNetwork( |
EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
EXPECT_TRUE(HasActiveSession(host_port_pair_)); |
+ // Send GET request on stream. This causes a write error, which triggers |
+ // a connection migration attempt. Since there are no networks |
+ // to migrate to, this causes the session to wait for a new network. |
+ HttpResponseInfo response; |
+ HttpRequestHeaders request_headers; |
+ EXPECT_EQ(OK, stream->SendRequest(request_headers, &response, |
+ callback_.callback())); |
+ |
+ // Complete any pending writes. |
+ if (mode == ASYNC) |
+ runner_->RunNextTask(); |
+ |
+ // Migration has not yet failed. The session should be alive and active. |
+ EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
+ EXPECT_TRUE(HasActiveSession(host_port_pair_)); |
+ EXPECT_EQ(1u, session->GetNumActiveStreams()); |
+ EXPECT_TRUE(session->connection()->writer()->IsWriteBlocked()); |
+ |
+ // The migration will not fail until the migration alarm timeout. |
+ EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
+ EXPECT_TRUE(HasActiveSession(host_port_pair_)); |
+ EXPECT_EQ(1u, session->GetNumActiveStreams()); |
+ EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(callback_.callback())); |
+ |
+ // Force migration alarm timeout to run. |
+ RunTestLoopUntilIdle(); |
+ |
+ // The connection should be closed. A request for response headers |
+ // should fail. |
+ EXPECT_FALSE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
+ EXPECT_FALSE(HasActiveSession(host_port_pair_)); |
+ EXPECT_EQ(ERR_NETWORK_CHANGED, callback_.WaitForResult()); |
+ EXPECT_EQ(ERR_NETWORK_CHANGED, |
+ stream->ReadResponseHeaders(callback_.callback())); |
+ |
+ EXPECT_TRUE(socket_data.AllReadDataConsumed()); |
+ EXPECT_TRUE(socket_data.AllWriteDataConsumed()); |
+} |
+ |
+TEST_P(QuicStreamFactoryTest, |
+ MigrateSessionOnWriteErrorPauseBeforeConnectedSync) { |
+ MigrateSessionOnWriteErrorPauseBeforeConnected(SYNCHRONOUS); |
+} |
+TEST_P(QuicStreamFactoryTest, |
+ MigrateSessionOnWriteErrorPauseBeforeConnectedAsync) { |
+ MigrateSessionOnWriteErrorPauseBeforeConnected(ASYNC); |
+} |
+ |
+void QuicStreamFactoryTestBase::MigrateSessionOnWriteErrorPauseBeforeConnected( |
+ IoMode mode) { |
+ InitializeConnectionMigrationTest({kDefaultNetworkForTests}); |
+ ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails(); |
+ crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); |
+ crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); |
+ |
+ MockQuicData socket_data; |
+ socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
+ socket_data.AddWrite(SYNCHRONOUS, ERR_FAILED); |
+ socket_data.AddSocketDataToFactory(&socket_factory_); |
+ |
+ // Create request and QuicHttpStream. |
+ QuicStreamRequest request(factory_.get()); |
+ EXPECT_EQ(ERR_IO_PENDING, |
+ request.Request(host_port_pair_, privacy_mode_, |
+ /*cert_verify_flags=*/0, url_, "GET", net_log_, |
+ callback_.callback())); |
+ EXPECT_EQ(OK, callback_.WaitForResult()); |
+ std::unique_ptr<QuicHttpStream> stream = request.CreateStream(); |
+ 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 = GetActiveSession(host_port_pair_); |
+ EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
+ EXPECT_TRUE(HasActiveSession(host_port_pair_)); |
+ |
// Send GET request on stream. This should cause a write error, which triggers |
// a connection migration attempt. |
HttpResponseInfo response; |
HttpRequestHeaders request_headers; |
EXPECT_EQ(OK, stream->SendRequest(request_headers, &response, |
callback_.callback())); |
- // Run message loop to execute migration attempt. |
+ |
+ // Run the message loop so that data queued in the new socket is read by the |
+ // packet reader. |
base::RunLoop().RunUntilIdle(); |
- // Migration fails, and session is closed and deleted. |
- EXPECT_FALSE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
+ |
+ // In this particular code path, the network will not yet be marked |
+ // as going away and the session will still be alive. |
+ EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); |
+ EXPECT_TRUE(HasActiveSession(host_port_pair_)); |
+ EXPECT_EQ(1u, session->GetNumActiveStreams()); |
+ EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(callback_.callback())); |
+ |
+ // On a DISCONNECTED notification, nothing happens. |
+ // @@@ uncomment after NCN migrations are handled. |
+ // scoped_mock_network_change_notifier_->mock_network_change_notifier() |
+ // ->NotifyNetworkDisconnected(kDefaultNetworkForTests); |
+ |
+ // Set up second socket data provider that is used after |
+ // migration. The request is rewritten to this new socket, and the |
+ // response to the request is read on this new socket. |
+ MockQuicData socket_data1; |
+ socket_data1.AddWrite( |
+ ConstructGetRequestPacket(1, kClientDataStreamId1, true, true)); |
+ socket_data1.AddRead( |
+ ConstructOkResponsePacket(1, kClientDataStreamId1, false, false)); |
+ socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
+ socket_data1.AddWrite(client_maker_.MakeAckAndRstPacket( |
+ 2, false, kClientDataStreamId1, QUIC_STREAM_CANCELLED, 1, 1, 1, true)); |
+ socket_data1.AddSocketDataToFactory(&socket_factory_); |
+ |
+ scoped_mock_network_change_notifier_->mock_network_change_notifier() |
+ ->SetConnectedNetworksList({kNewNetworkForTests}); |
+ scoped_mock_network_change_notifier_->mock_network_change_notifier() |
+ ->NotifyNetworkConnected(kNewNetworkForTests); |
+ |
+ // 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()); |
+ |
+ // This is the callback for the response headers that returned |
+ // pending previously, because no result was available. Check that |
+ // the result is now available due to the successful migration. |
+ EXPECT_THAT(callback_.WaitForResult(), IsOk()); |
+ EXPECT_EQ(200, response.headers->response_code()); |
+ |
+ // Create a new request for the same destination and verify that a |
+ // new session is created. |
+ MockQuicData socket_data2; |
+ socket_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING); |
+ socket_data2.AddSocketDataToFactory(&socket_factory_); |
+ |
+ QuicStreamRequest request2(factory_.get()); |
+ EXPECT_EQ(ERR_IO_PENDING, |
+ request2.Request(host_port_pair_, privacy_mode_, |
+ /*cert_verify_flags=*/0, url_, "GET", net_log_, |
+ callback_.callback())); |
+ EXPECT_THAT(callback_.WaitForResult(), IsOk()); |
+ std::unique_ptr<QuicHttpStream> stream2 = request2.CreateStream(); |
+ EXPECT_TRUE(stream2.get()); |
+ |
+ EXPECT_TRUE(HasActiveSession(host_port_pair_)); |
+ QuicChromiumClientSession* new_session = GetActiveSession(host_port_pair_); |
+ EXPECT_NE(session, new_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, |