Implement MOQT draft-16 version negotiation. Breaks interop with all hosts, as no other version-16 features are yet supported. Integration tests will fail until WebTransport APIs are in place. PiperOrigin-RevId: 853922338
diff --git a/quiche/quic/moqt/moqt_framer.cc b/quiche/quic/moqt/moqt_framer.cc index 5b54788..918b0e0 100644 --- a/quiche/quic/moqt/moqt_framer.cc +++ b/quiche/quic/moqt/moqt_framer.cc
@@ -401,8 +401,6 @@ } return SerializeControlMessage( MoqtMessageType::kClientSetup, - WireVarInt62(message.supported_versions.size()), - WireSpan<WireVarInt62, MoqtVersion>(message.supported_versions), WireKeyValuePairList(parameters)); } @@ -418,7 +416,6 @@ return quiche::QuicheBuffer(); } return SerializeControlMessage(MoqtMessageType::kServerSetup, - WireVarInt62(message.selected_version), WireKeyValuePairList(parameters)); }
diff --git a/quiche/quic/moqt/moqt_integration_test.cc b/quiche/quic/moqt/moqt_integration_test.cc index a887b43..e3c05f4 100644 --- a/quiche/quic/moqt/moqt_integration_test.cc +++ b/quiche/quic/moqt/moqt_integration_test.cc
@@ -142,9 +142,9 @@ } TEST_F(MoqtIntegrationTest, VersionMismatch) { - client_ = std::make_unique<MoqtClientEndpoint>( - &test_harness_.simulator(), "Client", "Server", - MoqtVersion::kUnrecognizedVersionForTests); + client_ = std::make_unique<MoqtClientEndpoint>(&test_harness_.simulator(), + "Client", "Server", + kUnrecognizedVersionForTests); server_ = std::make_unique<MoqtServerEndpoint>( &test_harness_.simulator(), "Server", "Client", kDefaultMoqtVersion); SetupCallbacks();
diff --git a/quiche/quic/moqt/moqt_messages.h b/quiche/quic/moqt/moqt_messages.h index c55b86e..1ed2b9b 100644 --- a/quiche/quic/moqt/moqt_messages.h +++ b/quiche/quic/moqt/moqt_messages.h
@@ -38,14 +38,12 @@ return quic::ParsedQuicVersionVector{quic::ParsedQuicVersion::RFCv1()}; } -enum class MoqtVersion : uint64_t { - kDraft14 = 0xff00000e, - kUnrecognizedVersionForTests = 0xfe0000ff, -}; +inline constexpr absl::string_view kDraft16 = "moqt-16"; +inline constexpr absl::string_view kDefaultMoqtVersion = kDraft16; +inline constexpr absl::string_view kImplementationName = + "Google QUICHE MOQT draft 16"; +inline constexpr absl::string_view kUnrecognizedVersionForTests = "moqt-15"; -inline constexpr MoqtVersion kDefaultMoqtVersion = MoqtVersion::kDraft14; -inline constexpr absl::string_view kVersionString = - "Google QUICHE MOQT draft 14"; inline constexpr uint64_t kDefaultInitialMaxRequestId = 100; // TODO(martinduke): Implement an auth token cache. inline constexpr uint64_t kDefaultMaxAuthTokenCacheSize = 0; @@ -97,7 +95,7 @@ : perspective(perspective), max_request_id(max_request_id) {} bool operator==(const MoqtSessionParameters& other) const = default; - MoqtVersion version = kDefaultMoqtVersion; + std::string version = std::string(kDefaultMoqtVersion); bool deliver_partial_objects = false; quic::Perspective perspective = quic::Perspective::IS_SERVER; bool using_webtrans = true; @@ -535,12 +533,10 @@ // TODO(martinduke): Collapse both Setup messages into MoqtSessionParameters. struct QUICHE_EXPORT MoqtClientSetup { - std::vector<MoqtVersion> supported_versions; MoqtSessionParameters parameters; }; struct QUICHE_EXPORT MoqtServerSetup { - MoqtVersion selected_version; MoqtSessionParameters parameters; };
diff --git a/quiche/quic/moqt/moqt_parser.cc b/quiche/quic/moqt/moqt_parser.cc index 3017ff9..c1ac1ac 100644 --- a/quiche/quic/moqt/moqt_parser.cc +++ b/quiche/quic/moqt/moqt_parser.cc
@@ -325,17 +325,6 @@ MoqtClientSetup setup; setup.parameters.using_webtrans = uses_web_transport_; setup.parameters.perspective = quic::Perspective::IS_CLIENT; - uint64_t number_of_supported_versions; - if (!reader.ReadVarInt62(&number_of_supported_versions)) { - return 0; - } - uint64_t version; - for (uint64_t i = 0; i < number_of_supported_versions; ++i) { - if (!reader.ReadVarInt62(&version)) { - return 0; - } - setup.supported_versions.push_back(static_cast<MoqtVersion>(version)); - } KeyValuePairList parameters; if (!ParseKeyValuePairList(reader, parameters)) { return 0; @@ -358,11 +347,6 @@ MoqtServerSetup setup; setup.parameters.using_webtrans = uses_web_transport_; setup.parameters.perspective = quic::Perspective::IS_SERVER; - uint64_t version; - if (!reader.ReadVarInt62(&version)) { - return 0; - } - setup.selected_version = static_cast<MoqtVersion>(version); KeyValuePairList parameters; if (!ParseKeyValuePairList(reader, parameters)) { return 0;
diff --git a/quiche/quic/moqt/moqt_parser_test.cc b/quiche/quic/moqt/moqt_parser_test.cc index 3af3cf0..057c3bf 100644 --- a/quiche/quic/moqt/moqt_parser_test.cc +++ b/quiche/quic/moqt/moqt_parser_test.cc
@@ -491,11 +491,11 @@ webtransport::test::InMemoryStream stream(/*stream_id=*/0); MoqtControlParser parser(kRawQuic, &stream, visitor_); char setup[] = { - 0x20, 0x00, 0x0d, 0x02, 0x01, 0x02, // versions - 0x03, // 3 params - 0x01, 0x03, 0x66, 0x6f, 0x6f, // path = "foo" - 0x01, 0x32, // max_request_id = 50 - 0x00, 0x32, // max_request_id = 50 + 0x20, 0x00, 0x0a, + 0x03, // 3 params + 0x01, 0x03, 0x66, 0x6f, 0x6f, // path = "foo" + 0x01, 0x32, // max_request_id = 50 + 0x00, 0x32, // max_request_id = 50 }; stream.Receive(absl::string_view(setup, sizeof(setup)), false); parser.ReadAndDispatchMessages(); @@ -509,7 +509,7 @@ webtransport::test::InMemoryStream stream(/*stream_id=*/0); MoqtControlParser parser(kRawQuic, &stream, visitor_); char setup[] = { - 0x20, 0x00, 0x13, 0x02, 0x01, 0x02, // versions + 0x20, 0x00, 0x10, 0x03, // 3 params 0x01, 0x03, 0x66, 0x6f, 0x6f, // path = "foo" 0x01, 0x32, // max_request_id = 50 @@ -525,8 +525,7 @@ webtransport::test::InMemoryStream stream(/*stream_id=*/0); MoqtControlParser parser(kRawQuic, &stream, visitor_); char setup[] = { - 0x21, 0x00, 0x07, - 0x01, // version = 1 + 0x21, 0x00, 0x06, 0x01, // 1 param 0x01, 0x03, 0x66, 0x6f, 0x6f, // path = "foo" }; @@ -542,8 +541,7 @@ webtransport::test::InMemoryStream stream(/*stream_id=*/0); MoqtControlParser parser(kRawQuic, &stream, visitor_); char setup[] = { - 0x21, 0x00, 0x07, - 0x01, // version = 1 + 0x21, 0x00, 0x06, 0x01, // 1 param 0x05, 0x03, 0x66, 0x6f, 0x6f, // authority = "foo" }; @@ -559,10 +557,10 @@ webtransport::test::InMemoryStream stream(/*stream_id=*/0); MoqtControlParser parser(kRawQuic, &stream, visitor_); char setup[] = { - 0x20, 0x00, 0x0e, 0x02, 0x01, 0x02, // versions = 1, 2 - 0x02, // 2 params - 0x01, 0x03, 0x66, 0x6f, 0x6f, // path = "foo" - 0x00, 0x03, 0x66, 0x6f, 0x6f, // path = "foo" + 0x20, 0x00, 0x0b, + 0x02, // 2 params + 0x01, 0x03, 0x66, 0x6f, 0x6f, // path = "foo" + 0x00, 0x03, 0x66, 0x6f, 0x6f, // path = "foo" }; stream.Receive(absl::string_view(setup, sizeof(setup)), false); parser.ReadAndDispatchMessages(); @@ -576,9 +574,9 @@ webtransport::test::InMemoryStream stream(/*stream_id=*/0); MoqtControlParser parser(kWebTrans, &stream, visitor_); char setup[] = { - 0x20, 0x00, 0x09, 0x02, 0x01, 0x02, // versions = 1, 2 - 0x01, // 1 param - 0x01, 0x03, 0x66, 0x6f, 0x6f, // path = "foo" + 0x20, 0x00, 0x06, + 0x01, // 1 param + 0x01, 0x03, 0x66, 0x6f, 0x6f, // path = "foo" }; stream.Receive(absl::string_view(setup, sizeof(setup)), false); parser.ReadAndDispatchMessages(); @@ -592,9 +590,9 @@ webtransport::test::InMemoryStream stream(/*stream_id=*/0); MoqtControlParser parser(kWebTrans, &stream, visitor_); char setup[] = { - 0x20, 0x00, 0x09, 0x02, 0x01, 0x02, // versions = 1, 2 - 0x01, // 1 param - 0x05, 0x03, 0x66, 0x6f, 0x6f, // authority = "foo" + 0x20, 0x00, 0x06, + 0x01, // 1 param + 0x05, 0x03, 0x66, 0x6f, 0x6f, // authority = "foo" }; stream.Receive(absl::string_view(setup, sizeof(setup)), false); parser.ReadAndDispatchMessages(); @@ -608,8 +606,10 @@ webtransport::test::InMemoryStream stream(/*stream_id=*/0); MoqtControlParser parser(kRawQuic, &stream, visitor_); char setup[] = { - 0x20, 0x00, 0x04, 0x02, 0x01, 0x02, // versions = 1, 2 - 0x00, // no param + 0x20, + 0x00, + 0x01, + 0x00, // no param }; stream.Receive(absl::string_view(setup, sizeof(setup)), false); parser.ReadAndDispatchMessages(); @@ -623,11 +623,11 @@ webtransport::test::InMemoryStream stream(/*stream_id=*/0); MoqtControlParser parser(kRawQuic, &stream, visitor_); char setup[] = { - 0x20, 0x00, 0x0d, 0x02, 0x01, 0x02, // versions = 1, 2 - 0x03, // 4 params - 0x01, 0x03, 0x66, 0x6f, 0x6f, // path = "foo" - 0x01, 0x32, // max_request_id = 50 - 0x00, 0x32, // max_request_id = 50 + 0x20, 0x00, 0x0a, + 0x03, // 3 params + 0x01, 0x03, 0x66, 0x6f, 0x6f, // path = "foo" + 0x01, 0x32, // max_request_id = 50 + 0x00, 0x32, // max_request_id = 50 }; stream.Receive(absl::string_view(setup, sizeof(setup)), false); parser.ReadAndDispatchMessages(); @@ -641,9 +641,9 @@ webtransport::test::InMemoryStream stream(/*stream_id=*/0); MoqtControlParser parser(kRawQuic, &stream, visitor_); char setup[] = { - 0x20, 0x00, 0x09, 0x02, 0x01, 0x02, // versions = 1, 2 - 0x01, // 1 param - 0x01, 0x03, 0x66, 0x5c, 0x6f, // path = "f\o" + 0x20, 0x00, 0x06, + 0x01, // 1 param + 0x01, 0x03, 0x66, 0x5c, 0x6f, // path = "f\o" }; stream.Receive(absl::string_view(setup, sizeof(setup)), false); parser.ReadAndDispatchMessages(); @@ -656,10 +656,10 @@ webtransport::test::InMemoryStream stream(/*stream_id=*/0); MoqtControlParser parser(kRawQuic, &stream, visitor_); char setup[] = { - 0x20, 0x00, 0x0e, 0x02, 0x01, 0x02, // versions = 1, 2 - 0x02, // 2 params - 0x01, 0x03, 0x66, 0x6f, 0x6f, // path = "foo" - 0x04, 0x03, 0x66, 0x5c, 0x6f, // authority = "f\o" + 0x20, 0x00, 0x0b, + 0x02, // 2 params + 0x01, 0x03, 0x66, 0x6f, 0x6f, // path = "foo" + 0x04, 0x03, 0x66, 0x5c, 0x6f, // authority = "f\o" }; stream.Receive(absl::string_view(setup, sizeof(setup)), false); parser.ReadAndDispatchMessages();
diff --git a/quiche/quic/moqt/moqt_session.cc b/quiche/quic/moqt/moqt_session.cc index c5db83c..1e2ea27 100644 --- a/quiche/quic/moqt/moqt_session.cc +++ b/quiche/quic/moqt/moqt_session.cc
@@ -15,7 +15,6 @@ #include <vector> -#include "absl/algorithm/container.h" #include "absl/base/nullability.h" #include "absl/container/btree_map.h" #include "absl/container/flat_hash_map.h" @@ -121,7 +120,7 @@ next_incoming_request_id_ = 1; } QUICHE_DCHECK(parameters_.moqt_implementation.empty()); - parameters_.moqt_implementation = kVersionString; + parameters_.moqt_implementation = kImplementationName; } MoqtSession::ControlStream* MoqtSession::GetControlStream() { @@ -147,10 +146,15 @@ void MoqtSession::OnSessionReady() { QUICHE_DLOG(INFO) << ENDPOINT << "Underlying session ready"; + std::optional<std::string> version = session_->GetNegotiatedSubprotocol(); + if (version != parameters_.version) { + Error(MoqtError::kVersionNegotiationFailed, + "MOQT peer chose wrong subprotocol"); + return; + } if (parameters_.perspective == Perspective::IS_SERVER) { return; } - webtransport::Stream* control_stream = session_->OpenOutgoingBidirectionalStream(); if (control_stream == nullptr) { @@ -161,7 +165,6 @@ std::make_unique<ControlStream>(this, control_stream)); control_stream_ = control_stream->GetStreamId(); MoqtClientSetup setup = MoqtClientSetup{ - .supported_versions = std::vector<MoqtVersion>{parameters_.version}, .parameters = parameters_, }; SendControlMessage(framer_.SerializeClientSetup(setup)); @@ -990,20 +993,11 @@ "Received CLIENT_SETUP from server"); return; } - if (absl::c_find(message.supported_versions, session_->parameters_.version) == - message.supported_versions.end()) { - // TODO(martinduke): Is this the right error code? See issue #346. - session_->Error(MoqtError::kVersionNegotiationFailed, - absl::StrCat("Version mismatch: expected 0x", - absl::Hex(session_->parameters_.version))); - return; - } session_->peer_supports_object_ack_ = message.parameters.support_object_acks; QUICHE_DLOG(INFO) << ENDPOINT << "Received the SETUP message"; if (session_->parameters_.perspective == Perspective::IS_SERVER) { MoqtServerSetup response; response.parameters = session_->parameters_; - response.selected_version = session_->parameters_.version; SendOrBufferMessage(session_->framer_.SerializeServerSetup(response)); QUIC_DLOG(INFO) << ENDPOINT << "Sent the SETUP message"; } @@ -1019,13 +1013,6 @@ "Received SERVER_SETUP from client"); return; } - if (message.selected_version != session_->parameters_.version) { - // TODO(martinduke): Is this the right error code? See issue #346. - session_->Error(MoqtError::kProtocolViolation, - absl::StrCat("Version mismatch: expected 0x", - absl::Hex(session_->parameters_.version))); - return; - } session_->peer_supports_object_ack_ = message.parameters.support_object_acks; QUIC_DLOG(INFO) << ENDPOINT << "Received the SETUP message"; // TODO: handle path.
diff --git a/quiche/quic/moqt/moqt_session_test.cc b/quiche/quic/moqt/moqt_session_test.cc index 0e6755c..f414556 100644 --- a/quiche/quic/moqt/moqt_session_test.cc +++ b/quiche/quic/moqt/moqt_session_test.cc
@@ -37,6 +37,7 @@ #include "quiche/quic/moqt/test_tools/moqt_session_peer.h" #include "quiche/quic/platform/api/quic_test.h" #include "quiche/quic/test_tools/quic_test_utils.h" +#include "quiche/common/platform/api/quiche_expect_bug.h" #include "quiche/common/quiche_buffer_allocator.h" #include "quiche/common/quiche_mem_slice.h" #include "quiche/common/quiche_stream.h" @@ -134,7 +135,7 @@ kDefaultInitialMaxRequestId); ON_CALL(mock_session_, GetStreamById).WillByDefault(Return(&mock_stream_)); EXPECT_EQ(MoqtSessionPeer::GetImplementationString(&session_), - kVersionString); + kImplementationName); } ~MoqtSessionTest() { EXPECT_CALL(session_callbacks_.session_deleted_callback, Call()); @@ -258,6 +259,8 @@ // Verify the session sends CLIENT_SETUP on the control stream. TEST_F(MoqtSessionTest, OnSessionReady) { + EXPECT_CALL(mock_session_, GetNegotiatedSubprotocol) + .WillOnce(Return(std::optional<std::string>(kDefaultMoqtVersion))); EXPECT_CALL(mock_session_, OpenOutgoingBidirectionalStream()) .WillOnce(Return(&mock_stream_)); std::unique_ptr<webtransport::StreamVisitor> visitor; @@ -279,9 +282,7 @@ MoqtSessionPeer::FetchParserVisitorFromWebtransportStreamVisitor( &session_, visitor.get()); // Handle the server setup - MoqtServerSetup setup = { - kDefaultMoqtVersion, - }; + MoqtServerSetup setup; // No fields are set. EXPECT_CALL(session_callbacks_.session_established_callback, Call()).Times(1); stream_input->OnServerSetupMessage(setup); } @@ -294,7 +295,6 @@ std::unique_ptr<MoqtControlParserVisitor> stream_input = MoqtSessionPeer::CreateControlStream(&server_session, &mock_stream_); MoqtClientSetup setup = { - /*supported_versions=*/{kDefaultMoqtVersion}, MoqtSessionParameters(quic::Perspective::IS_CLIENT), }; EXPECT_CALL(mock_stream_, @@ -1927,6 +1927,8 @@ } TEST_F(MoqtSessionTest, OneBidirectionalStreamClient) { + EXPECT_CALL(mock_session_, GetNegotiatedSubprotocol) + .WillOnce(Return(std::optional<std::string>(kDefaultMoqtVersion))); EXPECT_CALL(mock_session_, OpenOutgoingBidirectionalStream()) .WillOnce(Return(&mock_stream_)); std::unique_ptr<webtransport::StreamVisitor> visitor; @@ -1967,7 +1969,6 @@ std::unique_ptr<MoqtControlParserVisitor> stream_input = MoqtSessionPeer::CreateControlStream(&server_session, &mock_stream_); MoqtClientSetup setup = { - /*supported_versions*/ {kDefaultMoqtVersion}, MoqtSessionParameters(), }; EXPECT_CALL(mock_stream_, @@ -4158,6 +4159,23 @@ // Test teardown will destroy session_, triggering removal of "foo". } +TEST_F(MoqtSessionTest, WrongSubprotocol) { + EXPECT_CALL(mock_session_, GetNegotiatedSubprotocol) + .WillOnce( + Return(std::optional<std::string>(kUnrecognizedVersionForTests))); + EXPECT_CALL(mock_session_, CloseSession); + EXPECT_CALL(session_callbacks_.session_terminated_callback, Call); + session_.OnSessionReady(); +} + +TEST_F(MoqtSessionTest, NoSubprotocol) { + EXPECT_CALL(mock_session_, GetNegotiatedSubprotocol) + .WillOnce(Return(std::optional<std::string>())); + EXPECT_CALL(mock_session_, CloseSession); + EXPECT_CALL(session_callbacks_.session_terminated_callback, Call); + session_.OnSessionReady(); +} + // TODO: re-enable this test once this behavior is re-implemented. #if 0 TEST_F(MoqtSessionTest, SubscribeUpdateClosesSubscription) {
diff --git a/quiche/quic/moqt/test_tools/moqt_simulator.cc b/quiche/quic/moqt/test_tools/moqt_simulator.cc index e7f32bd..c1448ec 100644 --- a/quiche/quic/moqt/test_tools/moqt_simulator.cc +++ b/quiche/quic/moqt/test_tools/moqt_simulator.cc
@@ -68,7 +68,7 @@ // value just has to be sufficiently larger than the server link bandwidth. constexpr QuicBandwidth kClientLinkBandwidth = QuicBandwidth::FromBitsPerSecond(10.0e6); -constexpr MoqtVersion kMoqtVersion = kDefaultMoqtVersion; +constexpr absl::string_view kMoqtVersion = kDefaultMoqtVersion; // Track name used by the simulator. FullTrackName TrackName() { return FullTrackName("test", "track"); }
diff --git a/quiche/quic/moqt/test_tools/moqt_simulator_harness.cc b/quiche/quic/moqt/test_tools/moqt_simulator_harness.cc index 43aad64..59d29d8 100644 --- a/quiche/quic/moqt/test_tools/moqt_simulator_harness.cc +++ b/quiche/quic/moqt/test_tools/moqt_simulator_harness.cc
@@ -30,7 +30,7 @@ namespace { MoqtSessionParameters CreateParameters(quic::Perspective perspective, - MoqtVersion version) { + absl::string_view version) { MoqtSessionParameters parameters(perspective, "", ""); parameters.version = version; parameters.deliver_partial_objects = false; @@ -48,14 +48,15 @@ MoqtClientEndpoint::MoqtClientEndpoint(quic::simulator::Simulator* simulator, const std::string& name, const std::string& peer_name, - MoqtVersion version) + absl::string_view version) : QuicEndpointWithConnection(simulator, name, peer_name, quic::Perspective::IS_CLIENT, quic::GetQuicVersionsForGenericSession()), crypto_config_(quic::test::crypto_test_utils::ProofVerifierForTesting()), quic_session_(connection_.get(), false, nullptr, GenerateQuicConfig(), - "test.example.com", 443, "moqt", &session_, - /*visitor_owned=*/false, nullptr, &crypto_config_), + "test.example.com", 443, std::string(kDefaultMoqtVersion), + &session_, /*visitor_owned=*/false, nullptr, + &crypto_config_), session_(&quic_session_, CreateParameters(quic::Perspective::IS_CLIENT, version), std::make_unique<quic::QuicAlarmFactoryProxy>( @@ -67,7 +68,7 @@ MoqtServerEndpoint::MoqtServerEndpoint(quic::simulator::Simulator* simulator, const std::string& name, const std::string& peer_name, - MoqtVersion version) + absl::string_view version) : QuicEndpointWithConnection(simulator, name, peer_name, quic::Perspective::IS_SERVER, quic::GetQuicVersionsForGenericSession()), @@ -78,7 +79,7 @@ quic::test::crypto_test_utils::ProofSourceForTesting(), quic::KeyExchangeSource::Default()), quic_session_(connection_.get(), false, nullptr, GenerateQuicConfig(), - "moqt", &session_, + std::string(kDefaultMoqtVersion), &session_, /*visitor_owned=*/false, nullptr, &crypto_config_, &compressed_certs_cache_), session_(&quic_session_,
diff --git a/quiche/quic/moqt/test_tools/moqt_simulator_harness.h b/quiche/quic/moqt/test_tools/moqt_simulator_harness.h index 3293eb6..3430d36 100644 --- a/quiche/quic/moqt/test_tools/moqt_simulator_harness.h +++ b/quiche/quic/moqt/test_tools/moqt_simulator_harness.h
@@ -8,12 +8,12 @@ #include <optional> #include <string> +#include "absl/strings/string_view.h" #include "quiche/quic/core/crypto/quic_compressed_certs_cache.h" #include "quiche/quic/core/crypto/quic_crypto_client_config.h" #include "quiche/quic/core/crypto/quic_crypto_server_config.h" #include "quiche/quic/core/quic_generic_session.h" #include "quiche/quic/core/quic_time.h" -#include "quiche/quic/moqt/moqt_messages.h" #include "quiche/quic/moqt/moqt_session.h" #include "quiche/quic/test_tools/simulator/simulator.h" #include "quiche/quic/test_tools/simulator/test_harness.h" @@ -25,7 +25,7 @@ public: MoqtClientEndpoint(quic::simulator::Simulator* simulator, const std::string& name, const std::string& peer_name, - MoqtVersion version); + absl::string_view version); MoqtSession* session() { return &session_; } quic::QuicGenericClientSession* quic_session() { return &quic_session_; } @@ -41,7 +41,7 @@ public: MoqtServerEndpoint(quic::simulator::Simulator* simulator, const std::string& name, const std::string& peer_name, - MoqtVersion version); + absl::string_view version); MoqtSession* session() { return &session_; } quic::QuicGenericServerSession* quic_session() { return &quic_session_; }
diff --git a/quiche/quic/moqt/test_tools/moqt_test_message.h b/quiche/quic/moqt/test_tools/moqt_test_message.h index 436f720..842495d 100644 --- a/quiche/quic/moqt/test_tools/moqt_test_message.h +++ b/quiche/quic/moqt/test_tools/moqt_test_message.h
@@ -502,15 +502,15 @@ // Should not send PATH or AUTHORITY. client_setup_.parameters.path = ""; client_setup_.parameters.authority = ""; - raw_packet_[2] = 0x24; // adjust payload length (-17) - raw_packet_[6] = 0x02; // only two parameters + raw_packet_[2] -= 17; // adjust payload length + raw_packet_[3] = 0x02; // only two parameters // Move MaxRequestId up in the packet. - memmove(raw_packet_ + 7, raw_packet_ + 13, 2); + memmove(raw_packet_ + 4, raw_packet_ + 10, 2); // Move MoqtImplementation up in the packet. - memmove(raw_packet_ + 9, raw_packet_ + 26, + memmove(raw_packet_ + 6, raw_packet_ + 23, kTestImplementationString.length() + 2); - raw_packet_[7] = 0x02; // Diff from 0. - raw_packet_[9] = 0x05; // Diff from 2. + raw_packet_[4] = 0x02; // Diff from 0. + raw_packet_[6] = 0x05; // Diff from 2. SetWireImage(raw_packet_, sizeof(raw_packet_) - 17); } else { SetWireImage(raw_packet_, sizeof(raw_packet_)); @@ -519,18 +519,6 @@ bool EqualFieldValues(MessageStructuredData& values) const override { auto cast = std::get<MoqtClientSetup>(values); - if (cast.supported_versions.size() != - client_setup_.supported_versions.size()) { - QUIC_LOG(INFO) << "CLIENT_SETUP number of supported versions mismatch"; - return false; - } - for (uint64_t i = 0; i < cast.supported_versions.size(); ++i) { - // Listed versions are 1 and 2, in that order. - if (cast.supported_versions[i] != client_setup_.supported_versions[i]) { - QUIC_LOG(INFO) << "CLIENT_SETUP supported version mismatch"; - return false; - } - } if (cast.parameters != client_setup_.parameters) { QUIC_LOG(INFO) << "CLIENT_SETUP parameter mismatch"; return false; @@ -540,9 +528,9 @@ void ExpandVarints() override { if (!client_setup_.parameters.path.empty()) { - ExpandVarintsImpl("vvvvvv----vvvv---------vv---------------------------"); + ExpandVarintsImpl("vvv----vvvv---------vv---------------------------"); } else { - ExpandVarintsImpl("vvvvvvvv---------------------------"); + ExpandVarintsImpl("vvvvv---------------------------"); } } @@ -555,9 +543,8 @@ // string parameters in order. Unfortunately, this means that // kMoqtImplementation goes last even though it is always present, while // kPath and KAuthority aren't. - uint8_t raw_packet_[56] = { - 0x20, 0x00, 0x35, // type, length - 0x02, 0x01, 0x02, // versions + uint8_t raw_packet_[53] = { + 0x20, 0x00, 0x32, // type, length 0x04, // 4 parameters 0x01, 0x04, 0x70, 0x61, 0x74, 0x68, // path = "path" 0x01, 0x32, // max_request_id = 50 @@ -568,8 +555,6 @@ 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x54, 0x79, 0x70, 0x65}; MoqtClientSetup client_setup_ = { - /*supported_versions=*/std::vector<MoqtVersion>( - {static_cast<MoqtVersion>(1), static_cast<MoqtVersion>(2)}), MoqtSessionParameters(quic::Perspective::IS_CLIENT, "path", "authority", 50), }; @@ -592,25 +577,22 @@ return true; } - void ExpandVarints() override { ExpandVarintsImpl("vvvv"); } + void ExpandVarints() override { ExpandVarintsImpl("vvv"); } MessageStructuredData structured_data() const override { return TestMessageBase::MessageStructuredData(server_setup_); } private: - uint8_t raw_packet_[37] = {0x21, 0x00, - 0x22, // type - 0x01, - 0x02, // version, two parameters - 0x02, 0x32, // max_subscribe_id = 50 + uint8_t raw_packet_[36] = {0x21, 0x00, 0x21, // type, length + 0x02, // two parameters + 0x02, 0x32, // max_subscribe_id = 50 // moqt_implementation: 0x05, 0x1c, 0x4d, 0x6f, 0x71, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x54, 0x79, 0x70, 0x65}; MoqtServerSetup server_setup_ = { - /*selected_version=*/static_cast<MoqtVersion>(1), MoqtSessionParameters(quic::Perspective::IS_SERVER, 50), }; };
diff --git a/quiche/quic/moqt/tools/moqt_client.cc b/quiche/quic/moqt/tools/moqt_client.cc index 2742431..5140204 100644 --- a/quiche/quic/moqt/tools/moqt_client.cc +++ b/quiche/quic/moqt/tools/moqt_client.cc
@@ -9,7 +9,9 @@ #include <utility> #include "absl/status/status.h" +#include "absl/status/statusor.h" #include "absl/strings/string_view.h" +#include "absl/types/span.h" #include "quiche/quic/core/crypto/proof_verifier.h" #include "quiche/quic/core/http/quic_spdy_client_stream.h" #include "quiche/quic/core/http/web_transport_http3.h" @@ -25,6 +27,7 @@ #include "quiche/quic/tools/quic_name_lookup.h" #include "quiche/common/http/http_header_block.h" #include "quiche/common/platform/api/quiche_logging.h" +#include "quiche/web_transport/web_transport_headers.h" namespace moqt { @@ -83,6 +86,14 @@ headers[":path"] = path; headers[":method"] = "CONNECT"; headers[":protocol"] = "webtransport"; + std::string version = std::string(kDefaultMoqtVersion); + absl::StatusOr<std::string> serialized_version = + webtransport::SerializeSubprotocolRequestHeader( + absl::MakeSpan(&version, 1)); + if (!serialized_version.ok()) { + return serialized_version.status(); + } + headers["wt-available-protocols"] = *serialized_version; stream->SendRequest(std::move(headers), "", false); quic::WebTransportHttp3* web_transport = stream->web_transport();
diff --git a/quiche/quic/moqt/tools/moqt_server.cc b/quiche/quic/moqt/tools/moqt_server.cc index ffe3b85..d4e7052 100644 --- a/quiche/quic/moqt/tools/moqt_server.cc +++ b/quiche/quic/moqt/tools/moqt_server.cc
@@ -9,9 +9,11 @@ #include <string> #include <utility> +#include "absl/algorithm/container.h" #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/string_view.h" +#include "absl/types/span.h" #include "quiche/quic/core/crypto/proof_source.h" #include "quiche/quic/core/crypto/quic_crypto_server_config.h" #include "quiche/quic/core/crypto/quic_random.h" @@ -87,6 +89,11 @@ connection_id_generator_) { dispatcher_.parameters().handler_factory = CreateWebTransportCallback(std::move(callback), event_loop_.get()); + dispatcher_.parameters().subprotocol_callback = + +[](absl::Span<const absl::string_view> subprotocols) { + return absl::c_find(subprotocols, kDefaultMoqtVersion) - + subprotocols.begin(); + }; } absl::Status MoqtServer::CreateUDPSocketAndListen(