Commit aba4a831 authored by Zhongyi Shi's avatar Zhongyi Shi Committed by Commit Bot

Migrate for path degrading when a new network is connected.

Attempt connection migration by calling OnPathDegrading if the path is
still degrading when a new network is connected.

Bug: 835444
Change-Id: I3e37d664019af21e7a61d9b49a0fb92d936e6141
Reviewed-on: https://chromium-review.googlesource.com/1026691
Commit-Queue: Zhongyi Shi <zhongyi@chromium.org>
Reviewed-by: default avatarRyan Hamilton <rch@chromium.org>
Cr-Commit-Position: refs/heads/master@{#554859}
parent 2b04fae0
......@@ -1816,27 +1816,31 @@ void QuicChromiumClientSession::OnNetworkConnected(
net_log_.AddEvent(
NetLogEventType::QUIC_CONNECTION_MIGRATION_ON_NETWORK_CONNECTED,
NetLog::Int64Callback("connected_network", network));
// If migration_pending_ is false, there was no migration pending or
// an earlier task completed migration.
if (!migration_pending_)
// If there was no migration pending and the path is not degrading, ignore
// this signal.
if (!migration_pending_ && !connection()->IsPathDegrading())
return;
current_connection_migration_cause_ = ON_NETWORK_CONNECTED;
// |migration_pending_| is true, there was no working network previously.
// |network| is now the only possible candidate, migrate immediately.
if (migrate_session_on_network_change_v2_) {
MigrateImmediately(network);
if (migration_pending_) {
// |migration_pending_| is true, there was no working network previously.
// |network| is now the only possible candidate, migrate immediately.
MigrateImmediately(network);
} else {
// The connection is path degrading.
DCHECK(connection()->IsPathDegrading());
OnPathDegrading();
}
return;
}
if (!migrate_session_on_network_change_v2_) {
// TODO(jri): Ensure that OnSessionGoingAway is called consistently,
// and that it's always called at the same time in the whole
// migration process. Allows tests to be more uniform.
stream_factory_->OnSessionGoingAway(this);
Migrate(network, connection()->peer_address().impl().socket_address(),
/*close_session_on_error=*/true, net_log);
}
// TODO(jri): Ensure that OnSessionGoingAway is called consistently,
// and that it's always called at the same time in the whole
// migration process. Allows tests to be more uniform.
stream_factory_->OnSessionGoingAway(this);
Migrate(network, connection()->peer_address().impl().socket_address(),
/*close_session_on_error=*/true, net_log);
}
void QuicChromiumClientSession::OnNetworkDisconnected(
......
......@@ -3140,9 +3140,170 @@ TEST_P(QuicStreamFactoryTest, MigrateEarlyOnPathDegrading) {
EXPECT_EQ(OK, stream->SendRequest(request_headers, &response,
callback_.callback()));
// Notify the session that path is degrading. Session will start to probe the
// alternate network.
session->OnPathDegrading();
// Cause the connection to report path degrading to the session.
// Session will start to probe the alternate network.
session->connection()->OnPathDegradingTimeout();
// Next connectivity probe is scheduled to be sent in 2 *
// kDefaultRTTMilliSecs.
EXPECT_EQ(1u, task_runner->GetPendingTaskCount());
base::TimeDelta next_task_delay = task_runner->NextPendingTaskDelay();
EXPECT_EQ(base::TimeDelta::FromMilliseconds(2 * kDefaultRTTMilliSecs),
next_task_delay);
// The connection should still be alive, and not marked as going away.
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()));
// Resume quic data and a connectivity probe response will be read on the new
// socket.
quic_data2.Resume();
EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
EXPECT_TRUE(HasActiveSession(host_port_pair_));
EXPECT_EQ(1u, session->GetNumActiveStreams());
// There should be three pending tasks, the nearest one will complete
// migration to the new network.
EXPECT_EQ(3u, task_runner->GetPendingTaskCount());
next_task_delay = task_runner->NextPendingTaskDelay();
EXPECT_EQ(base::TimeDelta(), next_task_delay);
task_runner->FastForwardBy(next_task_delay);
// Response headers are received over the new network.
EXPECT_THAT(callback_.WaitForResult(), IsOk());
EXPECT_EQ(200, response.headers->response_code());
// Now there are two pending tasks, the nearest one was to send connectivity
// probe and has been cancelled due to successful migration.
EXPECT_EQ(2u, task_runner->GetPendingTaskCount());
next_task_delay = task_runner->NextPendingTaskDelay();
EXPECT_EQ(base::TimeDelta::FromMilliseconds(2 * kDefaultRTTMilliSecs),
next_task_delay);
task_runner->FastForwardBy(next_task_delay);
// There's one more task to mgirate back to the default network in 0.4s.
EXPECT_EQ(1u, task_runner->GetPendingTaskCount());
next_task_delay = task_runner->NextPendingTaskDelay();
base::TimeDelta expected_delay =
base::TimeDelta::FromSeconds(kMinRetryTimeForDefaultNetworkSecs) -
base::TimeDelta::FromMilliseconds(2 * kDefaultRTTMilliSecs);
EXPECT_EQ(expected_delay, next_task_delay);
// Deliver a signal that the alternate network now becomes default to session,
// this will cancel mgirate back to default network timer.
scoped_mock_network_change_notifier_->mock_network_change_notifier()
->NotifyNetworkMadeDefault(kNewNetworkForTests);
task_runner->FastForwardBy(next_task_delay);
EXPECT_EQ(0u, task_runner->GetPendingTaskCount());
// Verify that the session is still alive.
EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
EXPECT_TRUE(HasActiveSession(host_port_pair_));
stream.reset();
EXPECT_TRUE(quic_data1.AllReadDataConsumed());
EXPECT_TRUE(quic_data1.AllWriteDataConsumed());
EXPECT_TRUE(quic_data2.AllReadDataConsumed());
EXPECT_TRUE(quic_data2.AllWriteDataConsumed());
}
// Regression test for http://crbug.com/835444.
// This test verifies that the connection migrates to the alternate network
// when the alternate network is connected after path has been degrading.
TEST_P(QuicStreamFactoryTest, MigrateOnNewNetworkConnectAfterPathDegrading) {
InitializeConnectionMigrationV2Test({kDefaultNetworkForTests});
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
// Using a testing task runner so that we can control time.
auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
QuicStreamFactoryPeer::SetTaskRunner(factory_.get(), task_runner.get());
scoped_mock_network_change_notifier_->mock_network_change_notifier()
->QueueNetworkMadeDefault(kDefaultNetworkForTests);
MockQuicData quic_data1;
QuicStreamOffset header_stream_offset = 0;
quic_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // Hanging Read.
quic_data1.AddWrite(SYNCHRONOUS,
ConstructInitialSettingsPacket(1, &header_stream_offset));
quic_data1.AddWrite(SYNCHRONOUS, ConstructGetRequestPacket(
2, GetNthClientInitiatedStreamId(0),
true, true, &header_stream_offset));
quic_data1.AddSocketDataToFactory(socket_factory_.get());
// Set up the second socket data provider that is used after migration.
// The response to the earlier request is read on the new socket.
MockQuicData quic_data2;
// Connectivity probe to be sent on the new path.
quic_data2.AddWrite(
SYNCHRONOUS, client_maker_.MakeConnectivityProbingPacket(3, true, 1338));
quic_data2.AddRead(ASYNC, ERR_IO_PENDING); // Pause
// Connectivity probe to receive from the server.
quic_data2.AddRead(
ASYNC, server_maker_.MakeConnectivityProbingPacket(1, false, 1338));
// Ping packet to send after migration is completed.
quic_data2.AddWrite(ASYNC,
client_maker_.MakeAckAndPingPacket(4, false, 1, 1, 1));
quic_data2.AddRead(
ASYNC, ConstructOkResponsePacket(2, GetNthClientInitiatedStreamId(0),
false, false));
quic_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
quic_data2.AddWrite(SYNCHRONOUS,
client_maker_.MakeAckAndRstPacket(
5, false, GetNthClientInitiatedStreamId(0),
QUIC_STREAM_CANCELLED, 2, 2, 1, true));
quic_data2.AddSocketDataToFactory(socket_factory_.get());
// Create request and QuicHttpStream.
QuicStreamRequest request(factory_.get());
EXPECT_EQ(ERR_IO_PENDING,
request.Request(host_port_pair_, version_, privacy_mode_,
DEFAULT_PRIORITY, SocketTag(),
/*cert_verify_flags=*/0, url_, net_log_,
&net_error_details_, callback_.callback()));
EXPECT_THAT(callback_.WaitForResult(), IsOk());
std::unique_ptr<HttpStream> stream = CreateStream(&request);
EXPECT_TRUE(stream.get());
// Cause QUIC stream to be created.
HttpRequestInfo request_info;
request_info.method = "GET";
request_info.url = url_;
request_info.traffic_annotation =
MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
EXPECT_EQ(OK, stream->InitializeStream(&request_info, true, DEFAULT_PRIORITY,
net_log_, CompletionOnceCallback()));
// 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.
HttpResponseInfo response;
HttpRequestHeaders request_headers;
EXPECT_EQ(OK, stream->SendRequest(request_headers, &response,
callback_.callback()));
// Cause the connection to report path degrading to the session.
// Due to lack of alternate network, session will not mgirate connection.
EXPECT_EQ(0u, task_runner->GetPendingTaskCount());
session->connection()->OnPathDegradingTimeout();
EXPECT_EQ(0u, task_runner->GetPendingTaskCount());
// Deliver a signal that a alternate network is connected now, this should
// cause the connection to start early migration on path degrading.
scoped_mock_network_change_notifier_->mock_network_change_notifier()
->SetConnectedNetworksList(
{kDefaultNetworkForTests, kNewNetworkForTests});
scoped_mock_network_change_notifier_->mock_network_change_notifier()
->NotifyNetworkConnected(kNewNetworkForTests);
// Next connectivity probe is scheduled to be sent in 2 *
// kDefaultRTTMilliSecs.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment