Finch protect QUIC connection migration for idle sessions with "migrate_idle_sessions".
Bug: 929351
Change-Id: I452cb2c5e393568e65ce0cca80da26a60d5dd889
Reviewed-on: https://chromium-review.googlesource.com/c/1490410
Reviewed-by: Ryan Hamilton <[email protected]>
Reviewed-by: Misha Efimov <[email protected]>
Commit-Queue: Zhongyi Shi <[email protected]>
Cr-Commit-Position: refs/heads/master@{#636221}
diff --git a/net/quic/bidirectional_stream_quic_impl_unittest.cc b/net/quic/bidirectional_stream_quic_impl_unittest.cc
index b5737a3..b90b26b 100644
--- a/net/quic/bidirectional_stream_quic_impl_unittest.cc
+++ b/net/quic/bidirectional_stream_quic_impl_unittest.cc
@@ -516,6 +516,7 @@
/*default_network=*/NetworkChangeNotifier::kInvalidNetworkHandle,
quic::QuicTime::Delta::FromMilliseconds(
kDefaultRetransmittableOnWireTimeoutMillisecs),
+ /*migrate_idle_session=*/false,
base::TimeDelta::FromSeconds(kDefaultIdleSessionMigrationPeriodSeconds),
base::TimeDelta::FromSeconds(kMaxTimeOnNonDefaultNetworkSecs),
kMaxMigrationsToNonDefaultNetworkOnWriteError,
diff --git a/net/quic/quic_chromium_client_session.cc b/net/quic/quic_chromium_client_session.cc
index 8f8c0ffa..592054bc8 100644
--- a/net/quic/quic_chromium_client_session.cc
+++ b/net/quic/quic_chromium_client_session.cc
@@ -679,6 +679,7 @@
bool migrate_sessions_on_network_change_v2,
NetworkChangeNotifier::NetworkHandle default_network,
quic::QuicTime::Delta retransmittable_on_wire_timeout,
+ bool migrate_idle_session,
base::TimeDelta idle_migration_period,
base::TimeDelta max_time_on_non_default_network,
int max_migrations_to_non_default_network_on_write_error,
@@ -708,6 +709,7 @@
migrate_session_early_v2_(migrate_session_early_v2),
migrate_session_on_network_change_v2_(
migrate_sessions_on_network_change_v2),
+ migrate_idle_session_(migrate_idle_session),
idle_migration_period_(idle_migration_period),
max_time_on_non_default_network_(max_time_on_non_default_network),
max_migrations_to_non_default_network_on_write_error_(
@@ -1804,9 +1806,18 @@
current_connection_migration_cause_ = ON_WRITE_ERROR;
- if (CheckIdleTimeExceedsIdleMigrationPeriod())
+ if (migrate_idle_session_ && CheckIdleTimeExceedsIdleMigrationPeriod())
return;
+ if (!migrate_idle_session_ && GetNumActiveStreams() == 0 &&
+ GetNumDrainingStreams() == 0) {
+ // connection close packet to be sent since socket may be borked.
+ connection()->CloseConnection(quic::QUIC_PACKET_WRITE_ERROR,
+ "Write error for non-migratable session",
+ quic::ConnectionCloseBehavior::SILENT_CLOSE);
+ return;
+ }
+
// Do not migrate if connection migration is disabled.
if (config()->DisableConnectionMigration()) {
HistogramAndLogMigrationFailure(
@@ -1958,7 +1969,17 @@
// Close streams that are not migratable to the probed |network|.
ResetNonMigratableStreams();
- if (CheckIdleTimeExceedsIdleMigrationPeriod())
+ if (!migrate_idle_session_ && GetNumActiveStreams() == 0 &&
+ GetNumDrainingStreams() == 0) {
+ // If idle sessions won't be migrated, close the connection.
+ CloseSessionOnErrorLater(
+ ERR_NETWORK_CHANGED,
+ quic::QUIC_CONNECTION_MIGRATION_NO_MIGRATABLE_STREAMS,
+ quic::ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return;
+ }
+
+ if (migrate_idle_session_ && CheckIdleTimeExceedsIdleMigrationPeriod())
return;
// Migrate to the probed socket immediately: socket, writer and reader will
@@ -2144,7 +2165,19 @@
// - otherwise, it's brought to default network, cancel the running timer to
// migrate back.
- if (CheckIdleTimeExceedsIdleMigrationPeriod())
+ if (!migrate_idle_session_ && GetNumActiveStreams() == 0 &&
+ GetNumDrainingStreams() == 0) {
+ HistogramAndLogMigrationFailure(net_log_,
+ MIGRATION_STATUS_NO_MIGRATABLE_STREAMS,
+ connection_id(), "No active streams");
+ CloseSessionOnErrorLater(
+ ERR_NETWORK_CHANGED,
+ quic::QUIC_CONNECTION_MIGRATION_NO_MIGRATABLE_STREAMS,
+ quic::ConnectionCloseBehavior::SILENT_CLOSE);
+ return;
+ }
+
+ if (migrate_idle_session_ && CheckIdleTimeExceedsIdleMigrationPeriod())
return;
// Do not migrate if connection migration is disabled.
@@ -2428,7 +2461,19 @@
CHECK_NE(NetworkChangeNotifier::kInvalidNetworkHandle, network);
- if (CheckIdleTimeExceedsIdleMigrationPeriod())
+ if (!migrate_idle_session_ && GetNumActiveStreams() == 0 &&
+ GetNumDrainingStreams() == 0) {
+ HistogramAndLogMigrationFailure(migration_net_log,
+ MIGRATION_STATUS_NO_MIGRATABLE_STREAMS,
+ connection_id(), "No active streams");
+ CloseSessionOnErrorLater(
+ ERR_NETWORK_CHANGED,
+ quic::QUIC_CONNECTION_MIGRATION_NO_MIGRATABLE_STREAMS,
+ quic::ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return ProbingResult::DISABLED_WITH_IDLE_SESSION;
+ }
+
+ if (migrate_idle_session_ && CheckIdleTimeExceedsIdleMigrationPeriod())
return ProbingResult::DISABLED_WITH_IDLE_SESSION;
// Abort probing if connection migration is disabled by config.
@@ -2554,6 +2599,9 @@
}
bool QuicChromiumClientSession::CheckIdleTimeExceedsIdleMigrationPeriod() {
+ if (!migrate_idle_session_)
+ return false;
+
if (GetNumActiveStreams() != 0 || GetNumDrainingStreams() != 0) {
return false;
}
@@ -2824,6 +2872,17 @@
if (network != NetworkChangeNotifier::kInvalidNetworkHandle) {
// This is a migration attempt from connection migration.
ResetNonMigratableStreams();
+ if (!migrate_idle_session_ && GetNumActiveStreams() == 0 &&
+ GetNumDrainingStreams() == 0) {
+ // If idle sessions can not be migrated, close the session if needed.
+ if (close_session_on_error) {
+ CloseSessionOnErrorLater(
+ ERR_NETWORK_CHANGED,
+ quic::QUIC_CONNECTION_MIGRATION_NO_MIGRATABLE_STREAMS,
+ quic::ConnectionCloseBehavior::SILENT_CLOSE);
+ }
+ return MigrationResult::FAILURE;
+ }
}
// Create and configure socket on |network|.
diff --git a/net/quic/quic_chromium_client_session.h b/net/quic/quic_chromium_client_session.h
index bfcc5f9..658e96f 100644
--- a/net/quic/quic_chromium_client_session.h
+++ b/net/quic/quic_chromium_client_session.h
@@ -377,6 +377,7 @@
bool migrate_session_on_network_change_v2,
NetworkChangeNotifier::NetworkHandle default_network,
quic::QuicTime::Delta retransmittable_on_wire_timeout,
+ bool migrate_idle_session,
base::TimeDelta idle_migration_period,
base::TimeDelta max_time_on_non_default_network,
int max_migrations_to_non_default_network_on_write_error,
@@ -714,8 +715,10 @@
void TryMigrateBackToDefaultNetwork(base::TimeDelta timeout);
void MaybeRetryMigrateBackToDefaultNetwork();
- // Returns true and post a task to close the connection if session's
- // idle time exceeds the |idle_migration_period_|.
+ // If migrate idle session is enabled, returns true and post a task to close
+ // the connection if session's idle time exceeds the |idle_migration_period_|.
+ // If migrate idle session is not enabled, returns true and posts a task to
+ // close the connection if session doesn't have outstanding streams.
bool CheckIdleTimeExceedsIdleMigrationPeriod();
// Close non-migratable streams in both directions by sending reset stream to
@@ -750,6 +753,7 @@
bool require_confirmation_;
bool migrate_session_early_v2_;
bool migrate_session_on_network_change_v2_;
+ bool migrate_idle_session_;
// Session can be migrated if its idle time is within this period.
base::TimeDelta idle_migration_period_;
base::TimeDelta max_time_on_non_default_network_;
diff --git a/net/quic/quic_chromium_client_session_test.cc b/net/quic/quic_chromium_client_session_test.cc
index d3e28d6c..2031c63 100644
--- a/net/quic/quic_chromium_client_session_test.cc
+++ b/net/quic/quic_chromium_client_session_test.cc
@@ -165,6 +165,7 @@
/*defaulet_network=*/NetworkChangeNotifier::kInvalidNetworkHandle,
quic::QuicTime::Delta::FromMilliseconds(
kDefaultRetransmittableOnWireTimeoutMillisecs),
+ /*migrate_idle_session=*/false,
base::TimeDelta::FromSeconds(kDefaultIdleSessionMigrationPeriodSeconds),
base::TimeDelta::FromSeconds(kMaxTimeOnNonDefaultNetworkSecs),
kMaxMigrationsToNonDefaultNetworkOnWriteError,
diff --git a/net/quic/quic_http_stream_test.cc b/net/quic/quic_http_stream_test.cc
index 1d14ef04..a48f5a8 100644
--- a/net/quic/quic_http_stream_test.cc
+++ b/net/quic/quic_http_stream_test.cc
@@ -326,6 +326,7 @@
/*default_network=*/NetworkChangeNotifier::kInvalidNetworkHandle,
quic::QuicTime::Delta::FromMilliseconds(
kDefaultRetransmittableOnWireTimeoutMillisecs),
+ /*migrate_idle_session=*/false,
base::TimeDelta::FromSeconds(kDefaultIdleSessionMigrationPeriodSeconds),
base::TimeDelta::FromSeconds(kMaxTimeOnNonDefaultNetworkSecs),
kMaxMigrationsToNonDefaultNetworkOnWriteError,
diff --git a/net/quic/quic_proxy_client_socket_unittest.cc b/net/quic/quic_proxy_client_socket_unittest.cc
index 0cb728b..ad30d277 100644
--- a/net/quic/quic_proxy_client_socket_unittest.cc
+++ b/net/quic/quic_proxy_client_socket_unittest.cc
@@ -216,6 +216,7 @@
/*default_network=*/NetworkChangeNotifier::kInvalidNetworkHandle,
quic::QuicTime::Delta::FromMilliseconds(
kDefaultRetransmittableOnWireTimeoutMillisecs),
+ /*migrate_idle_session=*/true,
base::TimeDelta::FromSeconds(kDefaultIdleSessionMigrationPeriodSeconds),
base::TimeDelta::FromSeconds(kMaxTimeOnNonDefaultNetworkSecs),
kMaxMigrationsToNonDefaultNetworkOnWriteError,
diff --git a/net/quic/quic_stream_factory.cc b/net/quic/quic_stream_factory.cc
index 5abe80f..ba11def3 100644
--- a/net/quic/quic_stream_factory.cc
+++ b/net/quic/quic_stream_factory.cc
@@ -1025,6 +1025,7 @@
bool migrate_sessions_on_network_change_v2,
bool migrate_sessions_early_v2,
bool retry_on_alternate_network_before_handshake,
+ bool migrate_idle_sessions,
base::TimeDelta idle_session_migration_period,
base::TimeDelta max_time_on_non_default_network,
int max_migrations_to_non_default_network_on_write_error,
@@ -1086,6 +1087,8 @@
retry_on_alternate_network_before_handshake &&
migrate_sessions_on_network_change_v2_),
default_network_(NetworkChangeNotifier::kInvalidNetworkHandle),
+ migrate_idle_sessions_(migrate_idle_sessions &&
+ migrate_sessions_on_network_change_v2_),
idle_session_migration_period_(idle_session_migration_period),
max_time_on_non_default_network_(max_time_on_non_default_network),
max_migrations_to_non_default_network_on_write_error_(
@@ -1842,7 +1845,8 @@
std::move(server_info), key.session_key(), require_confirmation,
migrate_sessions_early_v2_, migrate_sessions_on_network_change_v2_,
default_network_, retransmittable_on_wire_timeout_,
- idle_session_migration_period_, max_time_on_non_default_network_,
+ migrate_idle_sessions_, idle_session_migration_period_,
+ max_time_on_non_default_network_,
max_migrations_to_non_default_network_on_write_error_,
max_migrations_to_non_default_network_on_path_degrading_,
yield_after_packets_, yield_after_duration_, go_away_on_path_degrading_,
diff --git a/net/quic/quic_stream_factory.h b/net/quic/quic_stream_factory.h
index bd271be..0a64637 100644
--- a/net/quic/quic_stream_factory.h
+++ b/net/quic/quic_stream_factory.h
@@ -262,6 +262,7 @@
bool migrate_sessions_on_network_change_v2,
bool migrate_sessions_early_v2,
bool retry_on_alternate_network_before_handshake,
+ bool migrate_idle_sessions,
base::TimeDelta idle_session_migration_period,
base::TimeDelta max_time_on_non_default_network,
int max_migrations_to_non_default_network_on_write_error,
@@ -566,6 +567,10 @@
// Otherwise, always set to NetworkChangeNotifier::kInvalidNetwork.
NetworkChangeNotifier::NetworkHandle default_network_;
+ // Set if idle sessions can be migrated within
+ // |idle_session_migration_period_| when connection migration is triggered.
+ const bool migrate_idle_sessions_;
+
// Sessions can migrate if they have been idle for less than this period.
const base::TimeDelta idle_session_migration_period_;
diff --git a/net/quic/quic_stream_factory_fuzzer.cc b/net/quic/quic_stream_factory_fuzzer.cc
index 5c741443..8835927 100644
--- a/net/quic/quic_stream_factory_fuzzer.cc
+++ b/net/quic/quic_stream_factory_fuzzer.cc
@@ -106,6 +106,7 @@
bool migrate_sessions_early_v2 = false;
bool migrate_sessions_on_network_change_v2 = false;
bool retry_on_alternate_network_before_handshake = false;
+ bool migrate_idle_sessions = false;
bool go_away_on_path_degrading = false;
if (!close_sessions_on_ip_change) {
@@ -116,6 +117,7 @@
migrate_sessions_early_v2 = data_provider.ConsumeBool();
retry_on_alternate_network_before_handshake =
data_provider.ConsumeBool();
+ migrate_idle_sessions = data_provider.ConsumeBool();
}
}
}
@@ -138,7 +140,7 @@
kDefaultRetransmittableOnWireTimeoutMillisecs,
quic::kMaxTimeForCryptoHandshakeSecs, quic::kInitialIdleTimeoutSecs,
migrate_sessions_on_network_change_v2, migrate_sessions_early_v2,
- retry_on_alternate_network_before_handshake,
+ retry_on_alternate_network_before_handshake, migrate_idle_sessions,
base::TimeDelta::FromSeconds(
kDefaultIdleSessionMigrationPeriodSeconds),
base::TimeDelta::FromSeconds(kMaxTimeOnNonDefaultNetworkSecs),
diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc
index 87cf7f85..39ad1dd 100644
--- a/net/quic/quic_stream_factory_test.cc
+++ b/net/quic/quic_stream_factory_test.cc
@@ -282,6 +282,7 @@
test_params_.quic_migrate_sessions_on_network_change_v2,
test_params_.quic_migrate_sessions_early_v2,
test_params_.quic_retry_on_alternate_network_before_handshake,
+ test_params_.quic_migrate_idle_sessions,
test_params_.quic_idle_session_migration_period,
test_params_.quic_max_time_on_non_default_network,
test_params_.quic_max_migrations_to_non_default_network_on_write_error,
@@ -804,7 +805,8 @@
void OnFailedOnDefaultNetwork(int rv) { failed_on_default_network_ = true; }
// Helper methods for tests of connection migration on write error.
- void TestMigrationOnWriteErrorNonMigratableStream(IoMode write_error_mode);
+ void TestMigrationOnWriteErrorNonMigratableStream(IoMode write_error_mode,
+ bool migrate_idle_sessions);
// Migratable stream triggers write error.
void TestMigrationOnWriteErrorMixedStreams(IoMode write_error_mode);
// Non-migratable stream triggers write error.
@@ -831,6 +833,11 @@
void TestNoAlternateNetworkBeforeHandshake(quic::QuicErrorCode error);
void TestNewConnectionOnAlternateNetworkBeforeHandshake(
quic::QuicErrorCode error);
+ void TestOnNetworkMadeDefaultNonMigratableStream(bool migrate_idle_sessions);
+ void TestMigrateSessionEarlyNonMigratableStream(bool migrate_idle_sessions);
+ void TestOnNetworkDisconnectedNoOpenStreams(bool migrate_idle_sessions);
+ void TestOnNetworkMadeDefaultNoOpenStreams(bool migrate_idle_sessions);
+ void TestOnNetworkDisconnectedNonMigratableStream(bool migrate_idle_sessions);
QuicFlagSaver flags_; // Save/restore all QUIC flag values.
std::unique_ptr<MockHostResolverBase> host_resolver_;
@@ -2683,7 +2690,23 @@
// This test verifies that connectivity probes will be sent even if there is
// a non-migratable stream. However, when connection migrates to the
// successfully probed path, any non-migratable streams will be reset.
-TEST_P(QuicStreamFactoryTest, OnNetworkMadeDefaultNonMigratableStream) {
+TEST_P(QuicStreamFactoryTest,
+ OnNetworkMadeDefaultNonMigratableStream_MigrateIdleSessions) {
+ TestOnNetworkMadeDefaultNonMigratableStream(true);
+}
+
+// This test verifies that connectivity probes will be sent even if there is
+// a non-migratable stream. However, when connection migrates to the
+// successfully probed path, any non-migratable stream will be reset. And if
+// the connection becomes idle then, close the connection.
+TEST_P(QuicStreamFactoryTest,
+ OnNetworkMadeDefaultNonMigratableStream_DoNotMigrateIdleSessions) {
+ TestOnNetworkMadeDefaultNonMigratableStream(false);
+}
+
+void QuicStreamFactoryTestBase::TestOnNetworkMadeDefaultNonMigratableStream(
+ bool migrate_idle_sessions) {
+ test_params_.quic_migrate_idle_sessions = migrate_idle_sessions;
InitializeConnectionMigrationV2Test(
{kDefaultNetworkForTests, kNewNetworkForTests});
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
@@ -2692,6 +2715,16 @@
MockQuicData socket_data;
socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
+ if (!migrate_idle_sessions) {
+ socket_data.AddWrite(
+ SYNCHRONOUS,
+ client_maker_.MakeRstAckAndConnectionClosePacket(
+ 3, false, GetNthClientInitiatedBidirectionalStreamId(0),
+ quic::QUIC_STREAM_CANCELLED,
+ quic::QuicTime::Delta::FromMilliseconds(0), 1, 1, 1,
+ quic::QUIC_CONNECTION_MIGRATION_NO_MIGRATABLE_STREAMS,
+ "net error"));
+ }
socket_data.AddSocketDataToFactory(socket_factory_.get());
@@ -2704,15 +2737,18 @@
// Connectivity probe to receive from the server.
quic_data1.AddRead(ASYNC,
server_maker_.MakeConnectivityProbingPacket(1, false));
- quic_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // Hanging read.
- // A RESET will be sent to the peer to cancel the non-migratable stream.
- quic_data1.AddWrite(
- SYNCHRONOUS, client_maker_.MakeRstPacket(
- 3, false, GetNthClientInitiatedBidirectionalStreamId(0),
- quic::QUIC_STREAM_CANCELLED));
- // Ping packet to send after migration is completed.
- quic_data1.AddWrite(SYNCHRONOUS,
- client_maker_.MakeAckAndPingPacket(4, false, 1, 1, 1));
+ if (migrate_idle_sessions) {
+ quic_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // Hanging read.
+ // A RESET will be sent to the peer to cancel the non-migratable stream.
+ quic_data1.AddWrite(
+ SYNCHRONOUS,
+ client_maker_.MakeRstPacket(
+ 3, false, GetNthClientInitiatedBidirectionalStreamId(0),
+ quic::QUIC_STREAM_CANCELLED));
+ // Ping packet to send after migration is completed.
+ quic_data1.AddWrite(SYNCHRONOUS,
+ client_maker_.MakeAckAndPingPacket(4, false, 1, 1, 1));
+ }
quic_data1.AddSocketDataToFactory(socket_factory_.get());
// Create request and QuicHttpStream.
@@ -2754,7 +2790,7 @@
// non-migtable streams to be closed.
quic_data1.Resume();
EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
- EXPECT_TRUE(HasActiveSession(host_port_pair_));
+ EXPECT_EQ(migrate_idle_sessions, HasActiveSession(host_port_pair_));
EXPECT_EQ(0u, session->GetNumActiveStreams());
base::RunLoop().RunUntilIdle();
@@ -2824,31 +2860,53 @@
EXPECT_TRUE(socket_data.AllWriteDataConsumed());
}
-TEST_P(QuicStreamFactoryTest, OnNetworkDisconnectedNonMigratableStream) {
+TEST_P(QuicStreamFactoryTest,
+ OnNetworkDisconnectedNonMigratableStream_DoNotMigrateIdleSessions) {
+ TestOnNetworkDisconnectedNonMigratableStream(false);
+}
+
+TEST_P(QuicStreamFactoryTest,
+ OnNetworkDisconnectedNonMigratableStream_MigrateIdleSessions) {
+ TestOnNetworkDisconnectedNonMigratableStream(true);
+}
+
+void QuicStreamFactoryTestBase::TestOnNetworkDisconnectedNonMigratableStream(
+ bool migrate_idle_sessions) {
+ test_params_.quic_migrate_idle_sessions = migrate_idle_sessions;
InitializeConnectionMigrationV2Test(
{kDefaultNetworkForTests, kNewNetworkForTests});
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
- quic::QuicStreamOffset header_stream_offset = 0;
MockQuicData failed_socket_data;
- failed_socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
- failed_socket_data.AddWrite(
- SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
- // A RESET will be sent to the peer to cancel the non-migratable stream.
- failed_socket_data.AddWrite(
- SYNCHRONOUS, client_maker_.MakeRstPacket(
- 2, true, GetNthClientInitiatedBidirectionalStreamId(0),
- quic::QUIC_STREAM_CANCELLED));
- failed_socket_data.AddSocketDataToFactory(socket_factory_.get());
-
- // Set up second socket data provider that is used after migration.
MockQuicData socket_data;
- socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // Hanging read.
- // Ping packet to send after migration.
- socket_data.AddWrite(
- SYNCHRONOUS, client_maker_.MakePingPacket(3, /*include_version=*/true));
- socket_data.AddSocketDataToFactory(socket_factory_.get());
+ if (migrate_idle_sessions) {
+ quic::QuicStreamOffset header_stream_offset = 0;
+ failed_socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+ failed_socket_data.AddWrite(
+ SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
+ // A RESET will be sent to the peer to cancel the non-migratable stream.
+ failed_socket_data.AddWrite(
+ SYNCHRONOUS, client_maker_.MakeRstPacket(
+ 2, true, GetNthClientInitiatedBidirectionalStreamId(0),
+ quic::QUIC_STREAM_CANCELLED));
+ failed_socket_data.AddSocketDataToFactory(socket_factory_.get());
+
+ // Set up second socket data provider that is used after migration.
+ socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // Hanging read.
+ // Ping packet to send after migration.
+ socket_data.AddWrite(
+ SYNCHRONOUS, client_maker_.MakePingPacket(3, /*include_version=*/true));
+ socket_data.AddSocketDataToFactory(socket_factory_.get());
+ } else {
+ socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+ socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
+ socket_data.AddWrite(
+ SYNCHRONOUS, client_maker_.MakeRstPacket(
+ 2, true, GetNthClientInitiatedBidirectionalStreamId(0),
+ quic::QUIC_STREAM_CANCELLED));
+ socket_data.AddSocketDataToFactory(socket_factory_.get());
+ }
// Create request and QuicHttpStream.
QuicStreamRequest request(factory_.get());
@@ -2878,18 +2936,22 @@
// Trigger connection migration. Since there is a non-migratable stream,
// this should cause a RST_STREAM frame to be emitted with
// quic::QUIC_STREAM_CANCELLED error code.
- // The connection will then be migrated to the alternate network.
+ // If migate idle session, the connection will then be migrated to the
+ // alternate network. Otherwise, the connection will be closed.
scoped_mock_network_change_notifier_->mock_network_change_notifier()
->NotifyNetworkDisconnected(kDefaultNetworkForTests);
- EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
- EXPECT_TRUE(HasActiveSession(host_port_pair_));
- EXPECT_EQ(0u, session->GetNumActiveStreams());
+ EXPECT_EQ(migrate_idle_sessions,
+ QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
+ EXPECT_EQ(migrate_idle_sessions, HasActiveSession(host_port_pair_));
- base::RunLoop().RunUntilIdle();
+ if (migrate_idle_sessions) {
+ EXPECT_EQ(0u, session->GetNumActiveStreams());
+ base::RunLoop().RunUntilIdle();
- EXPECT_TRUE(failed_socket_data.AllReadDataConsumed());
- EXPECT_TRUE(failed_socket_data.AllWriteDataConsumed());
+ EXPECT_TRUE(failed_socket_data.AllReadDataConsumed());
+ EXPECT_TRUE(failed_socket_data.AllWriteDataConsumed());
+ }
EXPECT_TRUE(socket_data.AllReadDataConsumed());
EXPECT_TRUE(socket_data.AllWriteDataConsumed());
}
@@ -2952,7 +3014,19 @@
EXPECT_TRUE(socket_data.AllWriteDataConsumed());
}
-TEST_P(QuicStreamFactoryTest, OnNetworkMadeDefaultNoOpenStreams) {
+TEST_P(QuicStreamFactoryTest,
+ OnNetworkMadeDefaultNoOpenStreams_DoNotMigrateIdleSessions) {
+ TestOnNetworkMadeDefaultNoOpenStreams(false);
+}
+
+TEST_P(QuicStreamFactoryTest,
+ OnNetworkMadeDefaultNoOpenStreams_MigrateIdleSessions) {
+ TestOnNetworkMadeDefaultNoOpenStreams(true);
+}
+
+void QuicStreamFactoryTestBase::TestOnNetworkMadeDefaultNoOpenStreams(
+ bool migrate_idle_sessions) {
+ test_params_.quic_migrate_idle_sessions = migrate_idle_sessions;
InitializeConnectionMigrationV2Test(
{kDefaultNetworkForTests, kNewNetworkForTests});
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
@@ -2961,22 +3035,31 @@
MockQuicData socket_data;
socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
+ if (!migrate_idle_sessions) {
+ socket_data.AddWrite(
+ SYNCHRONOUS,
+ client_maker_.MakeConnectionClosePacket(
+ 2, true, quic::QUIC_CONNECTION_MIGRATION_NO_MIGRATABLE_STREAMS,
+ "net error"));
+ }
socket_data.AddSocketDataToFactory(socket_factory_.get());
- // Set up the second socket data provider that is used for probing.
MockQuicData quic_data1;
- // Connectivity probe to be sent on the new path.
- quic_data1.AddWrite(SYNCHRONOUS,
- client_maker_.MakeConnectivityProbingPacket(2, true));
- quic_data1.AddRead(ASYNC, ERR_IO_PENDING); // Pause
- // Connectivity probe to receive from the server.
- quic_data1.AddRead(ASYNC,
- server_maker_.MakeConnectivityProbingPacket(1, false));
- quic_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // Hanging read.
- // Ping packet to send after migration is completed.
- quic_data1.AddWrite(SYNCHRONOUS,
- client_maker_.MakeAckAndPingPacket(3, false, 1, 1, 1));
- quic_data1.AddSocketDataToFactory(socket_factory_.get());
+ if (migrate_idle_sessions) {
+ // Set up the second socket data provider that is used for probing.
+ // Connectivity probe to be sent on the new path.
+ quic_data1.AddWrite(SYNCHRONOUS,
+ client_maker_.MakeConnectivityProbingPacket(2, true));
+ quic_data1.AddRead(ASYNC, ERR_IO_PENDING); // Pause
+ // Connectivity probe to receive from the server.
+ quic_data1.AddRead(ASYNC,
+ server_maker_.MakeConnectivityProbingPacket(1, false));
+ quic_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // Hanging read.
+ // Ping packet to send after migration is completed.
+ quic_data1.AddWrite(SYNCHRONOUS,
+ client_maker_.MakeAckAndPingPacket(3, false, 1, 1, 1));
+ quic_data1.AddSocketDataToFactory(socket_factory_.get());
+ }
// Create request and QuicHttpStream.
QuicStreamRequest request(factory_.get());
@@ -3000,19 +3083,31 @@
// Trigger connection migration.
scoped_mock_network_change_notifier_->mock_network_change_notifier()
->NotifyNetworkMadeDefault(kNewNetworkForTests);
- // Verify that the session is still active.
- EXPECT_TRUE(HasActiveSession(host_port_pair_));
+ EXPECT_EQ(migrate_idle_sessions, HasActiveSession(host_port_pair_));
- quic_data1.Resume();
- base::RunLoop().RunUntilIdle();
-
+ if (migrate_idle_sessions) {
+ quic_data1.Resume();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(quic_data1.AllReadDataConsumed());
+ EXPECT_TRUE(quic_data1.AllWriteDataConsumed());
+ }
EXPECT_TRUE(socket_data.AllReadDataConsumed());
EXPECT_TRUE(socket_data.AllWriteDataConsumed());
- EXPECT_TRUE(quic_data1.AllReadDataConsumed());
- EXPECT_TRUE(quic_data1.AllWriteDataConsumed());
}
-TEST_P(QuicStreamFactoryTest, OnNetworkDisconnectedNoOpenStreams) {
+TEST_P(QuicStreamFactoryTest,
+ OnNetworkDisconnectedNoOpenStreams_DoNotMigateIdleSessions) {
+ TestOnNetworkDisconnectedNoOpenStreams(false);
+}
+
+TEST_P(QuicStreamFactoryTest,
+ OnNetworkDisconnectedNoOpenStreams_MigateIdleSessions) {
+ TestOnNetworkDisconnectedNoOpenStreams(true);
+}
+
+void QuicStreamFactoryTestBase::TestOnNetworkDisconnectedNoOpenStreams(
+ bool migrate_idle_sessions) {
+ test_params_.quic_migrate_idle_sessions = migrate_idle_sessions;
InitializeConnectionMigrationV2Test(
{kDefaultNetworkForTests, kNewNetworkForTests});
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
@@ -3023,13 +3118,16 @@
default_socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
default_socket_data.AddSocketDataToFactory(socket_factory_.get());
- // Set up second socket data provider that is used after migration.
MockQuicData alternate_socket_data;
- alternate_socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // Hanging read.
- // Ping packet to send after migration.
- alternate_socket_data.AddWrite(
- SYNCHRONOUS, client_maker_.MakePingPacket(2, /*include_version=*/true));
- alternate_socket_data.AddSocketDataToFactory(socket_factory_.get());
+ if (migrate_idle_sessions) {
+ // Set up second socket data provider that is used after migration.
+ alternate_socket_data.AddRead(SYNCHRONOUS,
+ ERR_IO_PENDING); // Hanging read.
+ // Ping packet to send after migration.
+ alternate_socket_data.AddWrite(
+ SYNCHRONOUS, client_maker_.MakePingPacket(2, /*include_version=*/true));
+ alternate_socket_data.AddSocketDataToFactory(socket_factory_.get());
+ }
// Create request and QuicHttpStream.
QuicStreamRequest request(factory_.get());
@@ -3051,12 +3149,14 @@
scoped_mock_network_change_notifier_->mock_network_change_notifier()
->NotifyNetworkDisconnected(kDefaultNetworkForTests);
- EXPECT_TRUE(HasActiveSession(host_port_pair_));
+ EXPECT_EQ(migrate_idle_sessions, HasActiveSession(host_port_pair_));
EXPECT_TRUE(default_socket_data.AllReadDataConsumed());
EXPECT_TRUE(default_socket_data.AllWriteDataConsumed());
- EXPECT_TRUE(alternate_socket_data.AllReadDataConsumed());
- EXPECT_TRUE(alternate_socket_data.AllWriteDataConsumed());
+ if (migrate_idle_sessions) {
+ EXPECT_TRUE(alternate_socket_data.AllReadDataConsumed());
+ EXPECT_TRUE(alternate_socket_data.AllWriteDataConsumed());
+ }
}
// This test verifies session migrates to the alternate network immediately when
@@ -4430,7 +4530,19 @@
// This test verifies that session with non-migratable stream will probe the
// alternate network on path degrading, and close the non-migratable streams
// when probe is successful.
-TEST_P(QuicStreamFactoryTest, MigrateSessionEarlyNonMigratableStream) {
+TEST_P(QuicStreamFactoryTest,
+ MigrateSessionEarlyNonMigratableStream_DoNotMigrateIdleSessions) {
+ TestMigrateSessionEarlyNonMigratableStream(false);
+}
+
+TEST_P(QuicStreamFactoryTest,
+ MigrateSessionEarlyNonMigratableStream_MigrateIdleSessions) {
+ TestMigrateSessionEarlyNonMigratableStream(true);
+}
+
+void QuicStreamFactoryTestBase::TestMigrateSessionEarlyNonMigratableStream(
+ bool migrate_idle_sessions) {
+ test_params_.quic_migrate_idle_sessions = migrate_idle_sessions;
InitializeConnectionMigrationV2Test(
{kDefaultNetworkForTests, kNewNetworkForTests});
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
@@ -4439,6 +4551,16 @@
MockQuicData socket_data;
socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
+ if (!migrate_idle_sessions) {
+ socket_data.AddWrite(
+ SYNCHRONOUS,
+ client_maker_.MakeRstAckAndConnectionClosePacket(
+ 3, false, GetNthClientInitiatedBidirectionalStreamId(0),
+ quic::QUIC_STREAM_CANCELLED,
+ quic::QuicTime::Delta::FromMilliseconds(0), 1, 1, 1,
+ quic::QUIC_CONNECTION_MIGRATION_NO_MIGRATABLE_STREAMS,
+ "net error"));
+ }
socket_data.AddSocketDataToFactory(socket_factory_.get());
// Set up the second socket data provider that is used for probing.
@@ -4450,15 +4572,18 @@
// Connectivity probe to receive from the server.
quic_data1.AddRead(ASYNC,
server_maker_.MakeConnectivityProbingPacket(1, false));
- quic_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // Hanging read.
- // A RESET will be sent to the peer to cancel the non-migratable stream.
- quic_data1.AddWrite(
- SYNCHRONOUS, client_maker_.MakeRstPacket(
- 3, false, GetNthClientInitiatedBidirectionalStreamId(0),
- quic::QUIC_STREAM_CANCELLED));
- // Ping packet to send after migration is completed.
- quic_data1.AddWrite(SYNCHRONOUS,
- client_maker_.MakeAckAndPingPacket(4, false, 1, 1, 1));
+ if (migrate_idle_sessions) {
+ quic_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // Hanging read.
+ // A RESET will be sent to the peer to cancel the non-migratable stream.
+ quic_data1.AddWrite(
+ SYNCHRONOUS,
+ client_maker_.MakeRstPacket(
+ 3, false, GetNthClientInitiatedBidirectionalStreamId(0),
+ quic::QUIC_STREAM_CANCELLED));
+ // Ping packet to send after migration is completed.
+ quic_data1.AddWrite(SYNCHRONOUS,
+ client_maker_.MakeAckAndPingPacket(4, false, 1, 1, 1));
+ }
quic_data1.AddSocketDataToFactory(socket_factory_.get());
// Create request and QuicHttpStream.
@@ -4500,9 +4625,10 @@
// Resume the data to read the connectivity probing response to declare probe
// as successful. Non-migratable streams will be closed.
quic_data1.Resume();
- base::RunLoop().RunUntilIdle();
+ if (migrate_idle_sessions)
+ base::RunLoop().RunUntilIdle();
- EXPECT_TRUE(HasActiveSession(host_port_pair_));
+ EXPECT_EQ(migrate_idle_sessions, HasActiveSession(host_port_pair_));
EXPECT_EQ(0u, session->GetNumActiveStreams());
EXPECT_TRUE(quic_data1.AllReadDataConsumed());
@@ -5958,39 +6084,49 @@
// This test verifies that when a connection encounters a packet write error, it
// will cancel non-migratable streams, and migrate to the alternate network.
void QuicStreamFactoryTestBase::TestMigrationOnWriteErrorNonMigratableStream(
- IoMode write_error_mode) {
+ IoMode write_error_mode,
+ bool migrate_idle_sessions) {
DVLOG(1) << "Write error mode: "
<< ((write_error_mode == SYNCHRONOUS) ? "SYNCHRONOUS" : "ASYNC");
+ DVLOG(1) << "Migrate idle sessions: " << migrate_idle_sessions;
+ test_params_.quic_migrate_idle_sessions = migrate_idle_sessions;
InitializeConnectionMigrationV2Test(
{kDefaultNetworkForTests, kNewNetworkForTests});
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
- quic::QuicStreamOffset header_stream_offset = 0;
- // The socket data provider for the original socket before migration.
MockQuicData failed_socket_data;
- failed_socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
- failed_socket_data.AddWrite(
- SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
- failed_socket_data.AddWrite(write_error_mode, ERR_ADDRESS_UNREACHABLE);
- failed_socket_data.AddSocketDataToFactory(socket_factory_.get());
-
- // Set up second socket data provider that is used after migration.
MockQuicData socket_data;
- socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // Hanging read.
- // Although the write error occurs when writing a packet for the
- // non-migratable stream and the stream will be cancelled during migration,
- // the packet will still be retransimitted at the connection level.
- socket_data.AddWrite(
- SYNCHRONOUS, ConstructGetRequestPacket(
- 2, GetNthClientInitiatedBidirectionalStreamId(0), true,
- true, &header_stream_offset));
- // A RESET will be sent to the peer to cancel the non-migratable stream.
- socket_data.AddWrite(
- SYNCHRONOUS, client_maker_.MakeRstPacket(
- 3, true, GetNthClientInitiatedBidirectionalStreamId(0),
- quic::QUIC_STREAM_CANCELLED));
- socket_data.AddSocketDataToFactory(socket_factory_.get());
+ if (migrate_idle_sessions) {
+ quic::QuicStreamOffset header_stream_offset = 0;
+ // The socket data provider for the original socket before migration.
+ failed_socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+ failed_socket_data.AddWrite(
+ SYNCHRONOUS, ConstructInitialSettingsPacket(1, &header_stream_offset));
+ failed_socket_data.AddWrite(write_error_mode, ERR_ADDRESS_UNREACHABLE);
+ failed_socket_data.AddSocketDataToFactory(socket_factory_.get());
+
+ // Set up second socket data provider that is used after migration.
+ socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // Hanging read.
+ // Although the write error occurs when writing a packet for the
+ // non-migratable stream and the stream will be cancelled during migration,
+ // the packet will still be retransimitted at the connection level.
+ socket_data.AddWrite(
+ SYNCHRONOUS, ConstructGetRequestPacket(
+ 2, GetNthClientInitiatedBidirectionalStreamId(0), true,
+ true, &header_stream_offset));
+ // A RESET will be sent to the peer to cancel the non-migratable stream.
+ socket_data.AddWrite(
+ SYNCHRONOUS, client_maker_.MakeRstPacket(
+ 3, true, GetNthClientInitiatedBidirectionalStreamId(0),
+ quic::QUIC_STREAM_CANCELLED));
+ socket_data.AddSocketDataToFactory(socket_factory_.get());
+ } else {
+ socket_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+ socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
+ socket_data.AddWrite(write_error_mode, ERR_ADDRESS_UNREACHABLE);
+ socket_data.AddSocketDataToFactory(socket_factory_.get());
+ }
// Create request and QuicHttpStream.
QuicStreamRequest request(factory_.get());
@@ -6029,25 +6165,41 @@
// Run message loop to execute migration attempt.
base::RunLoop().RunUntilIdle();
- // Migration closes the non-migratable stream and then migrates to the
- // alternate network successfully..
- EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
- EXPECT_TRUE(HasActiveSession(host_port_pair_));
+ // Migration closes the non-migratable stream and:
+ // if migrate idle session is enabled, it migrates to the alternate network
+ // successfully; otherwise the connection is closed.
+ EXPECT_EQ(migrate_idle_sessions,
+ QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session));
+ EXPECT_EQ(migrate_idle_sessions, HasActiveSession(host_port_pair_));
- EXPECT_TRUE(failed_socket_data.AllReadDataConsumed());
- EXPECT_TRUE(failed_socket_data.AllWriteDataConsumed());
+ if (migrate_idle_sessions) {
+ EXPECT_TRUE(failed_socket_data.AllReadDataConsumed());
+ EXPECT_TRUE(failed_socket_data.AllWriteDataConsumed());
+ }
EXPECT_TRUE(socket_data.AllReadDataConsumed());
EXPECT_TRUE(socket_data.AllWriteDataConsumed());
}
-TEST_P(QuicStreamFactoryTest,
- MigrateSessionOnWriteErrorNonMigratableStreamSynchronous) {
- TestMigrationOnWriteErrorNonMigratableStream(SYNCHRONOUS);
+TEST_P(
+ QuicStreamFactoryTest,
+ MigrateSessionOnWriteErrorNonMigratableStreamSync_DoNotMigrateIdleSessions) {
+ TestMigrationOnWriteErrorNonMigratableStream(SYNCHRONOUS, false);
+}
+
+TEST_P(
+ QuicStreamFactoryTest,
+ MigrateSessionOnWriteErrorNonMigratableStreamAsync_DoNotMigrateIdleSessions) {
+ TestMigrationOnWriteErrorNonMigratableStream(ASYNC, false);
}
TEST_P(QuicStreamFactoryTest,
- MigrateSessionOnWriteErrorNonMigratableStreamAsync) {
- TestMigrationOnWriteErrorNonMigratableStream(ASYNC);
+ MigrateSessionOnWriteErrorNonMigratableStreamSync_MigrateIdleSessions) {
+ TestMigrationOnWriteErrorNonMigratableStream(SYNCHRONOUS, true);
+}
+
+TEST_P(QuicStreamFactoryTest,
+ MigrateSessionOnWriteErrorNonMigratableStreamAsync_MigrateIdleSessions) {
+ TestMigrationOnWriteErrorNonMigratableStream(ASYNC, true);
}
void QuicStreamFactoryTestBase::TestMigrationOnWriteErrorMigrationDisabled(
@@ -7329,6 +7481,7 @@
// default network or the idle migration period threshold is exceeded.
// The default threshold is 30s.
TEST_P(QuicStreamFactoryTest, DefaultIdleMigrationPeriod) {
+ test_params_.quic_migrate_idle_sessions = true;
InitializeConnectionMigrationV2Test(
{kDefaultNetworkForTests, kNewNetworkForTests});
ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
@@ -7445,6 +7598,7 @@
TEST_P(QuicStreamFactoryTest, CustomIdleMigrationPeriod) {
// The customized threshold is 15s.
+ test_params_.quic_migrate_idle_sessions = true;
test_params_.quic_idle_session_migration_period =
base::TimeDelta::FromSeconds(15);
InitializeConnectionMigrationV2Test(