Add multi-port support to a QUIC connection.
Client needs to turn on RVCM and MPQC to enable multi-port connection. The client will also need to introduce configs to set up probing frequency. The current default is every 3 seconds.
Note that this feature will interfere with port migration and early connection migration. I will need to add guards in Chrome once this CL is merged.
Protected by connection options MPQC.
PiperOrigin-RevId: 470073812
diff --git a/quiche/quic/core/crypto/crypto_protocol.h b/quiche/quic/core/crypto/crypto_protocol.h
index 6a78b6d..66db12d 100644
--- a/quiche/quic/core/crypto/crypto_protocol.h
+++ b/quiche/quic/core/crypto/crypto_protocol.h
@@ -439,6 +439,8 @@
const QuicTag kINVC = TAG('I', 'N', 'V', 'C'); // Send connection close for
// INVALID_VERSION
+const QuicTag kMPQC = TAG('M', 'P', 'Q', 'C'); // Multi-port QUIC connection
+
// Client Hints triggers.
const QuicTag kGWCH = TAG('G', 'W', 'C', 'H');
const QuicTag kYTCH = TAG('Y', 'T', 'C', 'H');
diff --git a/quiche/quic/core/http/end_to_end_test.cc b/quiche/quic/core/http/end_to_end_test.cc
index 713d7c5..f1db1c4 100644
--- a/quiche/quic/core/http/end_to_end_test.cc
+++ b/quiche/quic/core/http/end_to_end_test.cc
@@ -5402,6 +5402,49 @@
server_thread_->Resume();
}
+TEST_P(EndToEndTest, ClientMultiPortConnection) {
+ client_extra_copts_.push_back(kMPQC);
+ ASSERT_TRUE(Initialize());
+ if (!GetClientConnection()->connection_migration_use_new_cid()) {
+ return;
+ }
+ client_.reset(EndToEndTest::CreateQuicClient(nullptr));
+ QuicConnection* client_connection = GetClientConnection();
+ // Increase the probing frequency to speed up this test.
+ client_connection->SetMultiPortProbingInterval(
+ QuicTime::Delta::FromMilliseconds(100));
+ SendSynchronousFooRequestAndCheckResponse();
+ EXPECT_TRUE(client_->WaitUntil(1000, [&]() {
+ return 1u == client_connection->GetStats().num_path_response_received;
+ }));
+ // Verify that the alternative path keeps sending probes periodically.
+ EXPECT_TRUE(client_->WaitUntil(1000, [&]() {
+ return 2u == client_connection->GetStats().num_path_response_received;
+ }));
+ server_thread_->Pause();
+ QuicConnection* server_connection = GetServerConnection();
+ // Verify that no migration has happened.
+ if (server_connection != nullptr) {
+ EXPECT_EQ(0u, server_connection->GetStats()
+ .num_peer_migration_to_proactively_validated_address);
+ }
+ server_thread_->Resume();
+
+ // This will cause the next periodic probing to fail.
+ server_writer_->set_fake_packet_loss_percentage(100);
+ EXPECT_TRUE(client_->WaitUntil(
+ 1000, [&]() { return client_->client()->HasPendingPathValidation(); }));
+ // Now wait for path validation to timeout.
+ EXPECT_TRUE(client_->WaitUntil(
+ 2000, [&]() { return !client_->client()->HasPendingPathValidation(); }));
+ server_writer_->set_fake_packet_loss_percentage(0);
+ EXPECT_TRUE(client_->WaitUntil(1000, [&]() {
+ return 3u == client_connection->GetStats().num_path_response_received;
+ }));
+ // Verify that the previous path was retired.
+ EXPECT_EQ(1u, client_connection->GetStats().num_retire_connection_id_sent);
+}
+
TEST_P(EndToEndPacketReorderingTest, ReorderedPathChallenge) {
ASSERT_TRUE(Initialize());
if (!version_.HasIetfQuicFrames()) {
diff --git a/quiche/quic/core/quic_connection.cc b/quiche/quic/core/quic_connection.cc
index 27aef7f..73f75a0 100644
--- a/quiche/quic/core/quic_connection.cc
+++ b/quiche/quic/core/quic_connection.cc
@@ -183,6 +183,17 @@
}
};
+class MultiPortProbingAlarmDelegate : public QuicConnectionAlarmDelegate {
+ public:
+ using QuicConnectionAlarmDelegate::QuicConnectionAlarmDelegate;
+
+ void OnAlarm() override {
+ QUICHE_DCHECK(connection_->connected());
+ QUIC_DLOG(INFO) << "Alternative path probing alarm fired";
+ connection_->ProbeMultiPortPath();
+ }
+};
+
// When the clearer goes out of scope, the coalesced packet gets cleared.
class ScopedCoalescedPacketClearer {
public:
@@ -298,6 +309,8 @@
discard_zero_rtt_decryption_keys_alarm_(alarm_factory_->CreateAlarm(
arena_.New<DiscardZeroRttDecryptionKeysAlarmDelegate>(this),
&arena_)),
+ multi_port_probing_alarm_(alarm_factory_->CreateAlarm(
+ arena_.New<MultiPortProbingAlarmDelegate>(this), &arena_)),
visitor_(nullptr),
debug_visitor_(nullptr),
packet_creator_(server_connection_id, &framer_, random_generator_, this),
@@ -328,7 +341,8 @@
alarm_factory_, &context_),
path_validator_(alarm_factory_, &arena_, this, random_generator_, clock_,
&context_),
- ping_manager_(perspective, this, &arena_, alarm_factory_, &context_) {
+ ping_manager_(perspective, this, &arena_, alarm_factory_, &context_),
+ multi_port_probing_interval_(kDefaultMultiPortProbingInterval) {
QUICHE_DCHECK(perspective_ == Perspective::IS_CLIENT ||
default_path_.self_address.IsInitialized());
@@ -679,6 +693,10 @@
if (supports_release_time_) {
UpdateReleaseTimeIntoFuture();
}
+
+ multi_port_enabled_ =
+ connection_migration_use_new_cid_ &&
+ config.HasClientSentConnectionOption(kMPQC, perspective_);
}
void QuicConnection::EnableLegacyVersionEncapsulation(
@@ -1800,6 +1818,7 @@
<< "Processing PATH_RESPONSE frame when connection is closed. Received "
"packet info: "
<< last_received_packet_info_;
+ ++stats_.num_path_response_received;
if (!UpdatePacketContent(PATH_RESPONSE_FRAME)) {
return false;
}
@@ -2021,7 +2040,14 @@
if (debug_visitor_ != nullptr) {
debug_visitor_->OnNewConnectionIdFrame(frame);
}
- return OnNewConnectionIdFrameInner(frame);
+
+ if (!OnNewConnectionIdFrameInner(frame)) {
+ return false;
+ }
+ if (perspective_ == Perspective::IS_CLIENT && multi_port_enabled_) {
+ MaybeCreateMultiPortPath();
+ }
+ return true;
}
bool QuicConnection::OnRetireConnectionIdFrame(
@@ -4060,6 +4086,20 @@
kAlarmGranularity);
}
+void QuicConnection::MaybeCreateMultiPortPath() {
+ QUICHE_DCHECK_EQ(Perspective::IS_CLIENT, perspective_);
+ auto path_context = visitor_->CreateContextForMultiPortPath();
+ if (!path_context || path_validator_.HasPendingPathValidation()) {
+ return;
+ }
+ auto multi_port_validation_result_delegate =
+ std::make_unique<MultiPortPathValidationResultDelegate>(this);
+ multi_port_probing_alarm_->Cancel();
+ multi_port_path_context_ = nullptr;
+ ValidatePath(std::move(path_context),
+ std::move(multi_port_validation_result_delegate));
+}
+
void QuicConnection::SendOrQueuePacket(SerializedPacket packet) {
// The caller of this function is responsible for checking CanWrite().
WritePacket(&packet);
@@ -7101,6 +7141,47 @@
current_effective_peer_address.host());
}
+void QuicConnection::OnMultiPortPathProbingSuccess(
+ std::unique_ptr<QuicPathValidationContext> context) {
+ multi_port_path_context_ = std::move(context);
+ multi_port_probing_alarm_->Set(clock_->ApproximateNow() +
+ multi_port_probing_interval_);
+}
+
+void QuicConnection::ProbeMultiPortPath() {
+ if (!connected_ || path_validator_.HasPendingPathValidation() ||
+ !multi_port_path_context_ ||
+ alternative_path_.self_address !=
+ multi_port_path_context_->self_address() ||
+ alternative_path_.peer_address !=
+ multi_port_path_context_->peer_address()) {
+ return;
+ }
+ auto multi_port_validation_result_delegate =
+ std::make_unique<MultiPortPathValidationResultDelegate>(this);
+ path_validator_.StartPathValidation(
+ std::move(multi_port_path_context_),
+ std::move(multi_port_validation_result_delegate));
+}
+
+QuicConnection::MultiPortPathValidationResultDelegate::
+ MultiPortPathValidationResultDelegate(QuicConnection* connection)
+ : connection_(connection) {
+ QUICHE_DCHECK_EQ(Perspective::IS_CLIENT, connection->perspective());
+}
+
+void QuicConnection::MultiPortPathValidationResultDelegate::
+ OnPathValidationSuccess(std::unique_ptr<QuicPathValidationContext> context,
+ QuicTime /*start_time*/) {
+ connection_->OnMultiPortPathProbingSuccess(std::move(context));
+}
+
+void QuicConnection::MultiPortPathValidationResultDelegate::
+ OnPathValidationFailure(
+ std::unique_ptr<QuicPathValidationContext> /*context*/) {
+ connection_->OnPathValidationFailureAtClient();
+}
+
QuicConnection::ReversePathValidationResultDelegate::
ReversePathValidationResultDelegate(
QuicConnection* connection,
diff --git a/quiche/quic/core/quic_connection.h b/quiche/quic/core/quic_connection.h
index d7fa3e7..5c94c50 100644
--- a/quiche/quic/core/quic_connection.h
+++ b/quiche/quic/core/quic_connection.h
@@ -238,6 +238,10 @@
// When bandwidth update alarms.
virtual void OnBandwidthUpdateTimeout() = 0;
+
+ // Returns context needed for the connection to probe on the alternative path.
+ virtual std::unique_ptr<QuicPathValidationContext>
+ CreateContextForMultiPortPath() = 0;
};
// Interface which gets callbacks from the QuicConnection at interesting
@@ -737,6 +741,18 @@
// TODO(fayang): Add a guard that this only gets called once.
void OnHandshakeComplete();
+ // Creates and probes an multi-port path if none exists.
+ void MaybeCreateMultiPortPath();
+
+ // Called in multi-port QUIC when the alternative path validation succeeds.
+ // Stores the path validation context and prepares for the next validation.
+ void OnMultiPortPathProbingSuccess(
+ std::unique_ptr<QuicPathValidationContext> context);
+
+ // Probe the existing alternative path. Does not create a new alternative
+ // path. This method is the callback for |multi_port_probing_alarm_|.
+ void ProbeMultiPortPath();
+
// Accessors
void set_visitor(QuicConnectionVisitorInterface* visitor) {
visitor_ = visitor;
@@ -778,6 +794,7 @@
QuicByteCount max_packet_length() const;
void SetMaxPacketLength(QuicByteCount length);
+ bool multi_port_enabled() const { return multi_port_enabled_; }
size_t mtu_probe_count() const { return mtu_probe_count_; }
bool connected() const { return connected_; }
@@ -799,6 +816,10 @@
void SetNetworkTimeouts(QuicTime::Delta handshake_timeout,
QuicTime::Delta idle_timeout);
+ void SetMultiPortProbingInterval(QuicTime::Delta probing_interval) {
+ multi_port_probing_interval_ = probing_interval;
+ }
+
// Called when the ping alarm fires. Causes a ping frame to be sent only
// if the retransmission alarm is not running.
void OnPingTimeout();
@@ -1479,6 +1500,24 @@
AddressChangeType active_effective_peer_migration_type_;
};
+ // Keeps an ongoing alternative path. The connection will not migrate upon
+ // validation success.
+ class MultiPortPathValidationResultDelegate
+ : public QuicPathValidator::ResultDelegate {
+ public:
+ MultiPortPathValidationResultDelegate(QuicConnection* connection);
+
+ void OnPathValidationSuccess(
+ std::unique_ptr<QuicPathValidationContext> context,
+ QuicTime start_time) override;
+
+ void OnPathValidationFailure(
+ std::unique_ptr<QuicPathValidationContext> context) override;
+
+ private:
+ QuicConnection* connection_;
+ };
+
// A class which sets and clears in_on_retransmission_time_out_ when entering
// and exiting OnRetransmissionTimeout, respectively.
class QUIC_EXPORT_PRIVATE ScopedRetransmissionTimeoutIndicator {
@@ -2020,6 +2059,8 @@
// first 1-RTT packet has been decrypted. Only used on server connections with
// TLS handshaker.
QuicArenaScopedPtr<QuicAlarm> discard_zero_rtt_decryption_keys_alarm_;
+ // An alarm that fires to keep probing the multi-port path.
+ QuicArenaScopedPtr<QuicAlarm> multi_port_probing_alarm_;
// Neither visitor is owned by this class.
QuicConnectionVisitorInterface* visitor_;
QuicConnectionDebugVisitor* debug_visitor_;
@@ -2234,6 +2275,12 @@
// Records first serialized 1-RTT packet.
std::unique_ptr<BufferedPacket> first_serialized_one_rtt_packet_;
+ std::unique_ptr<QuicPathValidationContext> multi_port_path_context_;
+
+ bool multi_port_enabled_ = false;
+
+ QuicTime::Delta multi_port_probing_interval_;
+
RetransmittableOnWireBehavior retransmittable_on_wire_behavior_ = DEFAULT;
bool only_send_probing_frames_on_alternative_path_ =
diff --git a/quiche/quic/core/quic_connection_stats.cc b/quiche/quic/core/quic_connection_stats.cc
index bb53ca5..105d957 100644
--- a/quiche/quic/core/quic_connection_stats.cc
+++ b/quiche/quic/core/quic_connection_stats.cc
@@ -48,6 +48,7 @@
os << " blocked_frames_sent: " << s.blocked_frames_sent;
os << " num_connectivity_probing_received: "
<< s.num_connectivity_probing_received;
+ os << " num_path_response_received: " << s.num_path_response_received;
os << " retry_packet_processed: "
<< (s.retry_packet_processed ? "yes" : "no");
os << " num_coalesced_packets_received: " << s.num_coalesced_packets_received;
diff --git a/quiche/quic/core/quic_connection_stats.h b/quiche/quic/core/quic_connection_stats.h
index 4aaf780..2d25495 100644
--- a/quiche/quic/core/quic_connection_stats.h
+++ b/quiche/quic/core/quic_connection_stats.h
@@ -143,6 +143,9 @@
// Number of connectivity probing packets received by this connection.
uint64_t num_connectivity_probing_received = 0;
+ // Number of PATH_RESPONSE frame received by this connection.
+ uint64_t num_path_response_received = 0;
+
// Whether a RETRY packet was successfully processed.
bool retry_packet_processed = false;
diff --git a/quiche/quic/core/quic_connection_test.cc b/quiche/quic/core/quic_connection_test.cc
index 9f4e7c6..72530ae 100644
--- a/quiche/quic/core/quic_connection_test.cc
+++ b/quiche/quic/core/quic_connection_test.cc
@@ -468,6 +468,11 @@
QuicConnectionPeer::GetRetireSelfIssuedConnectionIdAlarm(this));
}
+ TestAlarmFactory::TestAlarm* GetMultiPortProbingAlarm() {
+ return reinterpret_cast<TestAlarmFactory::TestAlarm*>(
+ QuicConnectionPeer::GetMultiPortProbingAlarm(this));
+ }
+
void PathDegradingTimeout() {
QUICHE_DCHECK(PathDegradingDetectionInProgress());
GetBlackholeDetectorAlarm()->Fire();
@@ -13065,6 +13070,68 @@
&connection_, kNewSelfAddress, connection_.peer_address()));
}
+TEST_P(QuicConnectionTest, MultiPortCreation) {
+ set_perspective(Perspective::IS_CLIENT);
+ QuicConfig config;
+ config.SetConnectionOptionsToSend(QuicTagVector{kMPQC, kRVCM});
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ connection_.SetFromConfig(config);
+ if (!connection_.connection_migration_use_new_cid()) {
+ return;
+ }
+ connection_.CreateConnectionIdManager();
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+ connection_.OnHandshakeComplete();
+
+ auto self_address = connection_.self_address();
+ const QuicSocketAddress kNewSelfAddress(self_address.host(),
+ self_address.port() + 1);
+ EXPECT_NE(kNewSelfAddress, self_address);
+ TestPacketWriter new_writer(version(), &clock_, Perspective::IS_CLIENT);
+
+ QuicNewConnectionIdFrame frame;
+ frame.connection_id = TestConnectionId(1234);
+ ASSERT_NE(frame.connection_id, connection_.connection_id());
+ frame.stateless_reset_token =
+ QuicUtils::GenerateStatelessResetToken(frame.connection_id);
+ frame.retire_prior_to = 0u;
+ frame.sequence_number = 1u;
+ EXPECT_CALL(visitor_, CreateContextForMultiPortPath())
+ .WillRepeatedly(Return(
+ testing::ByMove(std::make_unique<TestQuicPathValidationContext>(
+ kNewSelfAddress, connection_.peer_address(), &new_writer))));
+ connection_.OnNewConnectionIdFrame(frame);
+ EXPECT_TRUE(connection_.HasPendingPathValidation());
+ EXPECT_TRUE(QuicConnectionPeer::IsAlternativePath(
+ &connection_, kNewSelfAddress, connection_.peer_address()));
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(QuicPathResponseFrame(
+ 99, new_writer.path_challenge_frames().front().data_buffer)));
+ ProcessFramesPacketWithAddresses(frames, kNewSelfAddress, kPeerAddress,
+ ENCRYPTION_FORWARD_SECURE);
+ // No migration should happen and the alternative path should still be alive.
+ EXPECT_FALSE(connection_.HasPendingPathValidation());
+ EXPECT_TRUE(QuicConnectionPeer::IsAlternativePath(
+ &connection_, kNewSelfAddress, connection_.peer_address()));
+
+ connection_.GetMultiPortProbingAlarm()->Fire();
+ EXPECT_TRUE(connection_.HasPendingPathValidation());
+ EXPECT_TRUE(QuicConnectionPeer::IsAlternativePath(
+ &connection_, kNewSelfAddress, connection_.peer_address()));
+ for (size_t i = 0; i < QuicPathValidator::kMaxRetryTimes + 1; ++i) {
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(3 * kInitialRttMs));
+ static_cast<TestAlarmFactory::TestAlarm*>(
+ QuicPathValidatorPeer::retry_timer(
+ QuicConnectionPeer::path_validator(&connection_)))
+ ->Fire();
+ }
+
+ EXPECT_FALSE(connection_.HasPendingPathValidation());
+ EXPECT_FALSE(QuicConnectionPeer::IsAlternativePath(
+ &connection_, kNewSelfAddress, connection_.peer_address()));
+}
+
TEST_P(QuicConnectionTest, SingleAckInPacket) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
EXPECT_CALL(visitor_, OnConnectionClosed(_, _));
diff --git a/quiche/quic/core/quic_constants.h b/quiche/quic/core/quic_constants.h
index 46af2e8..13fa288 100644
--- a/quiche/quic/core/quic_constants.h
+++ b/quiche/quic/core/quic_constants.h
@@ -321,6 +321,9 @@
inline constexpr uint64_t kHttpDatagramStreamIdDivisor = 4;
+inline constexpr QuicTime::Delta kDefaultMultiPortProbingInterval =
+ QuicTime::Delta::FromSeconds(3);
+
} // namespace quic
#endif // QUICHE_QUIC_CORE_QUIC_CONSTANTS_H_
diff --git a/quiche/quic/core/quic_session.h b/quiche/quic/core/quic_session.h
index e749abe..e51d77a 100644
--- a/quiche/quic/core/quic_session.h
+++ b/quiche/quic/core/quic_session.h
@@ -178,6 +178,10 @@
return false;
}
void OnBandwidthUpdateTimeout() override {}
+ std::unique_ptr<QuicPathValidationContext> CreateContextForMultiPortPath()
+ override {
+ return nullptr;
+ }
// QuicStreamFrameDataProducer
WriteStreamDataResult WriteStreamData(QuicStreamId id,
diff --git a/quiche/quic/test_tools/quic_connection_peer.cc b/quiche/quic/test_tools/quic_connection_peer.cc
index db827aa..eb28e2d 100644
--- a/quiche/quic/test_tools/quic_connection_peer.cc
+++ b/quiche/quic/test_tools/quic_connection_peer.cc
@@ -406,6 +406,12 @@
}
// static
+QuicAlarm* QuicConnectionPeer::GetMultiPortProbingAlarm(
+ QuicConnection* connection) {
+ return connection->multi_port_probing_alarm_.get();
+}
+
+// static
void QuicConnectionPeer::SetServerConnectionId(
QuicConnection* connection, const QuicConnectionId& server_connection_id) {
connection->default_path_.server_connection_id = server_connection_id;
diff --git a/quiche/quic/test_tools/quic_connection_peer.h b/quiche/quic/test_tools/quic_connection_peer.h
index 7ad551f..169a33c 100644
--- a/quiche/quic/test_tools/quic_connection_peer.h
+++ b/quiche/quic/test_tools/quic_connection_peer.h
@@ -224,6 +224,8 @@
static QuicCoalescedPacket& GetCoalescedPacket(QuicConnection* connection);
static void FlushCoalescedPacket(QuicConnection* connection);
+
+ static QuicAlarm* GetMultiPortProbingAlarm(QuicConnection* connection);
};
} // namespace test
diff --git a/quiche/quic/test_tools/quic_test_utils.h b/quiche/quic/test_tools/quic_test_utils.h
index af9cf2c..feb5719 100644
--- a/quiche/quic/test_tools/quic_test_utils.h
+++ b/quiche/quic/test_tools/quic_test_utils.h
@@ -501,6 +501,8 @@
MOCK_METHOD(void, BeforeConnectionCloseSent, (), (override));
MOCK_METHOD(bool, ValidateToken, (absl::string_view), (override));
MOCK_METHOD(bool, MaybeSendAddressToken, (), (override));
+ MOCK_METHOD(std::unique_ptr<QuicPathValidationContext>,
+ CreateContextForMultiPortPath, (), (override));
bool IsKnownServerAddress(
const QuicSocketAddress& /*address*/) const override {
diff --git a/quiche/quic/test_tools/simulator/quic_endpoint.h b/quiche/quic/test_tools/simulator/quic_endpoint.h
index 7654d89..99b3915 100644
--- a/quiche/quic/test_tools/simulator/quic_endpoint.h
+++ b/quiche/quic/test_tools/simulator/quic_endpoint.h
@@ -109,6 +109,10 @@
return false;
}
void OnBandwidthUpdateTimeout() override {}
+ std::unique_ptr<QuicPathValidationContext> CreateContextForMultiPortPath()
+ override {
+ return nullptr;
+ }
// End QuicConnectionVisitorInterface implementation.
diff --git a/quiche/quic/tools/quic_client.cc b/quiche/quic/tools/quic_client.cc
index c6de357..fca34d2 100644
--- a/quiche/quic/tools/quic_client.cc
+++ b/quiche/quic/tools/quic_client.cc
@@ -96,8 +96,9 @@
const ParsedQuicVersionVector& supported_versions,
QuicConnection* connection) {
return std::make_unique<QuicSimpleClientSession>(
- *config(), supported_versions, connection, server_id(), crypto_config(),
- push_promise_index(), drop_response_body(), enable_web_transport());
+ *config(), supported_versions, connection, network_helper(), server_id(),
+ crypto_config(), push_promise_index(), drop_response_body(),
+ enable_web_transport());
}
QuicClientEpollNetworkHelper* QuicClient::epoll_network_helper() {
diff --git a/quiche/quic/tools/quic_client_base.cc b/quiche/quic/tools/quic_client_base.cc
index 3a7dfd6..6cbfc13 100644
--- a/quiche/quic/tools/quic_client_base.cc
+++ b/quiche/quic/tools/quic_client_base.cc
@@ -18,24 +18,6 @@
namespace quic {
-// A path context which owns the writer.
-class QUIC_EXPORT_PRIVATE PathMigrationContext
- : public QuicPathValidationContext {
- public:
- PathMigrationContext(std::unique_ptr<QuicPacketWriter> writer,
- const QuicSocketAddress& self_address,
- const QuicSocketAddress& peer_address)
- : QuicPathValidationContext(self_address, peer_address),
- alternative_writer_(std::move(writer)) {}
-
- QuicPacketWriter* WriterToUse() override { return alternative_writer_.get(); }
-
- QuicPacketWriter* ReleaseWriter() { return alternative_writer_.release(); }
-
- private:
- std::unique_ptr<QuicPacketWriter> alternative_writer_;
-};
-
// Implements the basic feature of a result delegate for path validation for
// connection migration. If the validation succeeds, migrate to the alternative
// path. Otherwise, stay on the current path.
diff --git a/quiche/quic/tools/quic_client_base.h b/quiche/quic/tools/quic_client_base.h
index 91c597b..e9700a8 100644
--- a/quiche/quic/tools/quic_client_base.h
+++ b/quiche/quic/tools/quic_client_base.h
@@ -26,6 +26,24 @@
class QuicServerId;
class SessionCache;
+// A path context which owns the writer.
+class QUIC_EXPORT_PRIVATE PathMigrationContext
+ : public QuicPathValidationContext {
+ public:
+ PathMigrationContext(std::unique_ptr<QuicPacketWriter> writer,
+ const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address)
+ : QuicPathValidationContext(self_address, peer_address),
+ alternative_writer_(std::move(writer)) {}
+
+ QuicPacketWriter* WriterToUse() override { return alternative_writer_.get(); }
+
+ QuicPacketWriter* ReleaseWriter() { return alternative_writer_.release(); }
+
+ private:
+ std::unique_ptr<QuicPacketWriter> alternative_writer_;
+};
+
// QuicClientBase handles establishing a connection to the passed in
// server id, including ensuring that it supports the passed in versions
// and config.
diff --git a/quiche/quic/tools/quic_default_client.cc b/quiche/quic/tools/quic_default_client.cc
index de509c5..0f43aa5 100644
--- a/quiche/quic/tools/quic_default_client.cc
+++ b/quiche/quic/tools/quic_default_client.cc
@@ -86,8 +86,9 @@
const ParsedQuicVersionVector& supported_versions,
QuicConnection* connection) {
return std::make_unique<QuicSimpleClientSession>(
- *config(), supported_versions, connection, server_id(), crypto_config(),
- push_promise_index(), drop_response_body(), enable_web_transport());
+ *config(), supported_versions, connection, network_helper(), server_id(),
+ crypto_config(), push_promise_index(), drop_response_body(),
+ enable_web_transport());
}
QuicClientDefaultNetworkHelper* QuicDefaultClient::default_network_helper() {
diff --git a/quiche/quic/tools/quic_simple_client_session.cc b/quiche/quic/tools/quic_simple_client_session.cc
index 1a001fb..013167c 100644
--- a/quiche/quic/tools/quic_simple_client_session.cc
+++ b/quiche/quic/tools/quic_simple_client_session.cc
@@ -10,22 +10,13 @@
QuicSimpleClientSession::QuicSimpleClientSession(
const QuicConfig& config, const ParsedQuicVersionVector& supported_versions,
- QuicConnection* connection, const QuicServerId& server_id,
- QuicCryptoClientConfig* crypto_config,
- QuicClientPushPromiseIndex* push_promise_index, bool drop_response_body)
- : QuicSimpleClientSession(config, supported_versions, connection, server_id,
- crypto_config, push_promise_index,
- drop_response_body,
- /*enable_web_transport=*/false) {}
-
-QuicSimpleClientSession::QuicSimpleClientSession(
- const QuicConfig& config, const ParsedQuicVersionVector& supported_versions,
- QuicConnection* connection, const QuicServerId& server_id,
- QuicCryptoClientConfig* crypto_config,
+ QuicConnection* connection, QuicClientBase::NetworkHelper* network_helper,
+ const QuicServerId& server_id, QuicCryptoClientConfig* crypto_config,
QuicClientPushPromiseIndex* push_promise_index, bool drop_response_body,
bool enable_web_transport)
: QuicSpdyClientSession(config, supported_versions, connection, server_id,
crypto_config, push_promise_index),
+ network_helper_(network_helper),
drop_response_body_(drop_response_body),
enable_web_transport_(enable_web_transport) {}
@@ -45,4 +36,24 @@
: HttpDatagramSupport::kNone;
}
+std::unique_ptr<QuicPathValidationContext>
+QuicSimpleClientSession::CreateContextForMultiPortPath() {
+ if (!network_helper_ || !connection()->multi_port_enabled()) {
+ return nullptr;
+ }
+ auto self_address = connection()->self_address();
+ auto server_address = connection()->peer_address();
+ if (!network_helper_->CreateUDPSocketAndBind(
+ server_address, self_address.host(), self_address.port() + 1)) {
+ return nullptr;
+ }
+ QuicPacketWriter* writer = network_helper_->CreateQuicPacketWriter();
+ if (writer == nullptr) {
+ return nullptr;
+ }
+ return std::make_unique<PathMigrationContext>(
+ std::unique_ptr<QuicPacketWriter>(writer),
+ network_helper_->GetLatestClientAddress(), peer_address());
+}
+
} // namespace quic
diff --git a/quiche/quic/tools/quic_simple_client_session.h b/quiche/quic/tools/quic_simple_client_session.h
index 6b6f4a7..f3231ad 100644
--- a/quiche/quic/tools/quic_simple_client_session.h
+++ b/quiche/quic/tools/quic_simple_client_session.h
@@ -6,6 +6,7 @@
#define QUICHE_QUIC_TOOLS_QUIC_SIMPLE_CLIENT_SESSION_H_
#include "quiche/quic/core/http/quic_spdy_client_session.h"
+#include "quiche/quic/tools/quic_client_base.h"
#include "quiche/quic/tools/quic_simple_client_stream.h"
namespace quic {
@@ -15,13 +16,7 @@
QuicSimpleClientSession(const QuicConfig& config,
const ParsedQuicVersionVector& supported_versions,
QuicConnection* connection,
- const QuicServerId& server_id,
- QuicCryptoClientConfig* crypto_config,
- QuicClientPushPromiseIndex* push_promise_index,
- bool drop_response_body);
- QuicSimpleClientSession(const QuicConfig& config,
- const ParsedQuicVersionVector& supported_versions,
- QuicConnection* connection,
+ QuicClientBase::NetworkHelper* network_helper,
const QuicServerId& server_id,
QuicCryptoClientConfig* crypto_config,
QuicClientPushPromiseIndex* push_promise_index,
@@ -30,8 +25,11 @@
std::unique_ptr<QuicSpdyClientStream> CreateClientStream() override;
bool ShouldNegotiateWebTransport() override;
HttpDatagramSupport LocalHttpDatagramSupport() override;
+ std::unique_ptr<QuicPathValidationContext> CreateContextForMultiPortPath()
+ override;
private:
+ QuicClientBase::NetworkHelper* network_helper_;
const bool drop_response_body_;
const bool enable_web_transport_;
};