Update parser and framer to draft-01. PiperOrigin-RevId: 569892337
diff --git a/quiche/quic/moqt/moqt_framer.cc b/quiche/quic/moqt/moqt_framer.cc index 986e1ae..5806b7d 100644 --- a/quiche/quic/moqt/moqt_framer.cc +++ b/quiche/quic/moqt/moqt_framer.cc
@@ -220,6 +220,22 @@ return buffer; } +quiche::QuicheBuffer MoqtFramer::SerializeUnsubscribe( + const MoqtUnsubscribe& message) { + size_t message_len = NeededVarIntLen(message.full_track_name.length()) + + message.full_track_name.length(); + size_t buffer_size = + message_len + + NeededVarIntLen(static_cast<uint64_t>(MoqtMessageType::kUnsubscribe)) + + NeededVarIntLen(message_len); + quiche::QuicheBuffer buffer(allocator_, buffer_size); + quic::QuicDataWriter writer(buffer.size(), buffer.data()); + writer.WriteVarInt62(static_cast<uint64_t>(MoqtMessageType::kUnsubscribe)); + writer.WriteVarInt62(message_len); + writer.WriteStringPieceVarInt62(message.full_track_name); + return buffer; +} + quiche::QuicheBuffer MoqtFramer::SerializeAnnounce( const MoqtAnnounce& message) { size_t message_len = NeededVarIntLen(message.track_namespace.length()) + @@ -249,7 +265,8 @@ quiche::QuicheBuffer MoqtFramer::SerializeAnnounceOk( const MoqtAnnounceOk& message) { - size_t message_len = message.track_namespace.length(); + size_t message_len = NeededVarIntLen(message.track_namespace.length()) + + message.track_namespace.length(); size_t buffer_size = message_len + NeededVarIntLen(static_cast<uint64_t>(MoqtMessageType::kAnnounceOk)) + @@ -258,7 +275,7 @@ quic::QuicDataWriter writer(buffer.size(), buffer.data()); writer.WriteVarInt62(static_cast<uint64_t>(MoqtMessageType::kAnnounceOk)); writer.WriteVarInt62(message_len); - writer.WriteStringPiece(message.track_namespace); + writer.WriteStringPieceVarInt62(message.track_namespace); return buffer; } @@ -283,6 +300,22 @@ return buffer; } +quiche::QuicheBuffer MoqtFramer::SerializeUnannounce( + const MoqtUnannounce& message) { + size_t message_len = NeededVarIntLen(message.track_namespace.length()) + + message.track_namespace.length(); + size_t buffer_size = + message_len + + NeededVarIntLen(static_cast<uint64_t>(MoqtMessageType::kUnannounce)) + + NeededVarIntLen(message_len); + quiche::QuicheBuffer buffer(allocator_, buffer_size); + quic::QuicDataWriter writer(buffer.size(), buffer.data()); + writer.WriteVarInt62(static_cast<uint64_t>(MoqtMessageType::kUnannounce)); + writer.WriteVarInt62(message_len); + writer.WriteStringPieceVarInt62(message.track_namespace); + return buffer; +} + quiche::QuicheBuffer MoqtFramer::SerializeGoAway() { size_t buffer_size = NeededVarIntLen(static_cast<uint64_t>(MoqtMessageType::kGoAway)) +
diff --git a/quiche/quic/moqt/moqt_framer.h b/quiche/quic/moqt/moqt_framer.h index ba194a6..d88ecfb 100644 --- a/quiche/quic/moqt/moqt_framer.h +++ b/quiche/quic/moqt/moqt_framer.h
@@ -49,9 +49,11 @@ quiche::QuicheBuffer SerializeSubscribeOk(const MoqtSubscribeOk& message); quiche::QuicheBuffer SerializeSubscribeError( const MoqtSubscribeError& message); + quiche::QuicheBuffer SerializeUnsubscribe(const MoqtUnsubscribe& message); quiche::QuicheBuffer SerializeAnnounce(const MoqtAnnounce& message); quiche::QuicheBuffer SerializeAnnounceOk(const MoqtAnnounceOk& message); quiche::QuicheBuffer SerializeAnnounceError(const MoqtAnnounceError& message); + quiche::QuicheBuffer SerializeUnannounce(const MoqtUnannounce& message); quiche::QuicheBuffer SerializeGoAway(); private:
diff --git a/quiche/quic/moqt/moqt_framer_test.cc b/quiche/quic/moqt/moqt_framer_test.cc index dd3cad4..abfac63 100644 --- a/quiche/quic/moqt/moqt_framer_test.cc +++ b/quiche/quic/moqt/moqt_framer_test.cc
@@ -130,6 +130,10 @@ auto data = std::get<MoqtSubscribeError>(structured_data); return framer_.SerializeSubscribeError(data); } + case MoqtMessageType::kUnsubscribe: { + auto data = std::get<MoqtUnsubscribe>(structured_data); + return framer_.SerializeUnsubscribe(data); + } case MoqtMessageType::kAnnounce: { auto data = std::get<MoqtAnnounce>(structured_data); return framer_.SerializeAnnounce(data); @@ -142,6 +146,10 @@ auto data = std::get<MoqtAnnounceError>(structured_data); return framer_.SerializeAnnounceError(data); } + case MoqtMessageType::kUnannounce: { + auto data = std::get<MoqtUnannounce>(structured_data); + return framer_.SerializeUnannounce(data); + } case moqt::MoqtMessageType::kGoAway: { return framer_.SerializeGoAway(); }
diff --git a/quiche/quic/moqt/moqt_messages.cc b/quiche/quic/moqt/moqt_messages.cc index 9b37dc7..93a4b95 100644 --- a/quiche/quic/moqt/moqt_messages.cc +++ b/quiche/quic/moqt/moqt_messages.cc
@@ -20,12 +20,16 @@ return "SUBSCRIBE_OK"; case MoqtMessageType::kSubscribeError: return "SUBSCRIBE_ERROR"; + case MoqtMessageType::kUnsubscribe: + return "UNSUBSCRIBE"; case MoqtMessageType::kAnnounce: return "ANNOUNCE"; case MoqtMessageType::kAnnounceOk: return "ANNOUNCE_OK"; case MoqtMessageType::kAnnounceError: return "ANNOUNCE_ERROR"; + case MoqtMessageType::kUnannounce: + return "UNANNOUNCE"; case MoqtMessageType::kGoAway: return "GOAWAY"; }
diff --git a/quiche/quic/moqt/moqt_messages.h b/quiche/quic/moqt/moqt_messages.h index 30c1d97..a88f284 100644 --- a/quiche/quic/moqt/moqt_messages.h +++ b/quiche/quic/moqt/moqt_messages.h
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Structured data for message types in draft-ietf-moq-transport-00. +// Structured data for message types in draft-ietf-moq-transport-01. #ifndef QUICHE_QUIC_MOQT_MOQT_MESSAGES_H_ #define QUICHE_QUIC_MOQT_MOQT_MESSAGES_H_ @@ -33,6 +33,8 @@ kAnnounce = 0x06, kAnnounceOk = 0x7, kAnnounceError = 0x08, + kUnannounce = 0x09, + kUnsubscribe = 0x0a, kGoAway = 0x10, }; @@ -88,6 +90,10 @@ absl::string_view reason_phrase; }; +struct QUICHE_EXPORT MoqtUnsubscribe { + absl::string_view full_track_name; +}; + struct QUICHE_EXPORT MoqtAnnounce { absl::string_view track_namespace; absl::optional<absl::string_view> authorization_info; @@ -103,6 +109,10 @@ absl::string_view reason_phrase; }; +struct QUICHE_EXPORT MoqtUnannounce { + absl::string_view track_namespace; +}; + struct QUICHE_EXPORT MoqtGoAway {}; std::string MoqtMessageTypeToString(MoqtMessageType message_type);
diff --git a/quiche/quic/moqt/moqt_parser.cc b/quiche/quic/moqt/moqt_parser.cc index d90af04..3de44cf 100644 --- a/quiche/quic/moqt/moqt_parser.cc +++ b/quiche/quic/moqt/moqt_parser.cc
@@ -284,12 +284,16 @@ return ProcessSubscribeOk(data); case MoqtMessageType::kSubscribeError: return ProcessSubscribeError(data); + case MoqtMessageType::kUnsubscribe: + return ProcessUnsubscribe(data); case MoqtMessageType::kAnnounce: return ProcessAnnounce(data); case MoqtMessageType::kAnnounceOk: return ProcessAnnounceOk(data); case MoqtMessageType::kAnnounceError: return ProcessAnnounceError(data); + case MoqtMessageType::kUnannounce: + return ProcessUnannounce(data); case MoqtMessageType::kGoAway: return ProcessGoAway(data); default: @@ -520,6 +524,18 @@ return reader.PreviouslyReadPayload().length(); } +absl::optional<size_t> MoqtParser::ProcessUnsubscribe(absl::string_view data) { + MoqtUnsubscribe unsubscribe; + quic::QuicDataReader reader(data); + if (!reader.ReadStringPieceVarInt62(&unsubscribe.full_track_name)) { + return absl::nullopt; + } + if (reader.IsDoneReading()) { + visitor_.OnUnsubscribeMessage(unsubscribe); + } + return reader.PreviouslyReadPayload().length(); +} + absl::optional<size_t> MoqtParser::ProcessAnnounce(absl::string_view data) { MoqtAnnounce announce; quic::QuicDataReader reader(data); @@ -585,7 +601,7 @@ absl::optional<size_t> MoqtParser::ProcessAnnounceOk(absl::string_view data) { MoqtAnnounceOk announce_ok; quic::QuicDataReader reader(data); - if (!reader.ReadStringPiece(&announce_ok.track_namespace, data.length())) { + if (!reader.ReadStringPieceVarInt62(&announce_ok.track_namespace)) { return absl::nullopt; } if (reader.IsDoneReading()) { @@ -613,6 +629,18 @@ return reader.PreviouslyReadPayload().length(); } +absl::optional<size_t> MoqtParser::ProcessUnannounce(absl::string_view data) { + MoqtUnannounce unannounce; + quic::QuicDataReader reader(data); + if (!reader.ReadStringPieceVarInt62(&unannounce.track_namespace)) { + return absl::nullopt; + } + if (reader.IsDoneReading()) { + visitor_.OnUnannounceMessage(unannounce); + } + return reader.PreviouslyReadPayload().length(); +} + absl::optional<size_t> MoqtParser::ProcessGoAway(absl::string_view data) { if (!data.empty()) { // GOAWAY can only be followed by end_of_stream. Anything else is an error.
diff --git a/quiche/quic/moqt/moqt_parser.h b/quiche/quic/moqt/moqt_parser.h index d931922..bb23546 100644 --- a/quiche/quic/moqt/moqt_parser.h +++ b/quiche/quic/moqt/moqt_parser.h
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// A parser for draft-ietf-moq-transport-00. +// A parser for draft-ietf-moq-transport-01. #ifndef QUICHE_QUIC_MOQT_MOQT_PARSER_H_ #define QUICHE_QUIC_MOQT_MOQT_PARSER_H_ @@ -40,9 +40,11 @@ const MoqtSubscribeRequest& message) = 0; virtual void OnSubscribeOkMessage(const MoqtSubscribeOk& message) = 0; virtual void OnSubscribeErrorMessage(const MoqtSubscribeError& message) = 0; + virtual void OnUnsubscribeMessage(const MoqtUnsubscribe& message) = 0; virtual void OnAnnounceMessage(const MoqtAnnounce& message) = 0; virtual void OnAnnounceOkMessage(const MoqtAnnounceOk& message) = 0; virtual void OnAnnounceErrorMessage(const MoqtAnnounceError& message) = 0; + virtual void OnUnannounceMessage(const MoqtUnannounce& message) = 0; // In an exception to the above, the parser calls this when it gets two bytes, // whether or not it includes stream FIN. When a zero-length message has // special meaning, a message with an actual length of zero is tricky! @@ -93,9 +95,11 @@ absl::optional<size_t> ProcessSubscribeRequest(absl::string_view data); absl::optional<size_t> ProcessSubscribeOk(absl::string_view data); absl::optional<size_t> ProcessSubscribeError(absl::string_view data); + absl::optional<size_t> ProcessUnsubscribe(absl::string_view data); absl::optional<size_t> ProcessAnnounce(absl::string_view data); absl::optional<size_t> ProcessAnnounceOk(absl::string_view data); absl::optional<size_t> ProcessAnnounceError(absl::string_view data); + absl::optional<size_t> ProcessUnannounce(absl::string_view data); absl::optional<size_t> ProcessGoAway(absl::string_view data); // If the message length field is zero, it runs to the end of the stream.
diff --git a/quiche/quic/moqt/moqt_parser_test.cc b/quiche/quic/moqt/moqt_parser_test.cc index 357be26..3af0767 100644 --- a/quiche/quic/moqt/moqt_parser_test.cc +++ b/quiche/quic/moqt/moqt_parser_test.cc
@@ -125,6 +125,14 @@ subscribe_error.reason_phrase = absl::string_view(string1_); last_message_ = TestMessageBase::MessageStructuredData(subscribe_error); } + void OnUnsubscribeMessage(const MoqtUnsubscribe& message) override { + end_of_message_ = true; + messages_received_++; + MoqtUnsubscribe unsubscribe = message; + string0_ = std::string(unsubscribe.full_track_name); + unsubscribe.full_track_name = absl::string_view(string0_); + last_message_ = TestMessageBase::MessageStructuredData(unsubscribe); + } void OnAnnounceMessage(const MoqtAnnounce& message) override { end_of_message_ = true; messages_received_++; @@ -155,6 +163,14 @@ announce_error.reason_phrase = absl::string_view(string1_); last_message_ = TestMessageBase::MessageStructuredData(announce_error); } + void OnUnannounceMessage(const MoqtUnannounce& message) override { + end_of_message_ = true; + messages_received_++; + MoqtUnannounce unannounce = message; + string0_ = std::string(unannounce.track_namespace); + unannounce.track_namespace = absl::string_view(string0_); + last_message_ = TestMessageBase::MessageStructuredData(unannounce); + } void OnGoAwayMessage() override { got_goaway_ = true; end_of_message_ = true;
diff --git a/quiche/quic/moqt/test_tools/moqt_test_message.h b/quiche/quic/moqt/test_tools/moqt_test_message.h index 5d1ac90..876a619 100644 --- a/quiche/quic/moqt/test_tools/moqt_test_message.h +++ b/quiche/quic/moqt/test_tools/moqt_test_message.h
@@ -35,8 +35,9 @@ MoqtMessageType message_type() const { return message_type_; } typedef absl::variant<MoqtSetup, MoqtObject, MoqtSubscribeRequest, - MoqtSubscribeOk, MoqtSubscribeError, MoqtAnnounce, - MoqtAnnounceOk, MoqtAnnounceError, MoqtGoAway> + MoqtSubscribeOk, MoqtSubscribeError, MoqtUnsubscribe, + MoqtAnnounce, MoqtAnnounceOk, MoqtAnnounceError, + MoqtUnannounce, MoqtGoAway> MessageStructuredData; // The total actual size of the message. @@ -429,6 +430,37 @@ }; }; +class QUICHE_NO_EXPORT UnsubscribeMessage : public TestMessageBase { + public: + UnsubscribeMessage() : TestMessageBase(MoqtMessageType::kUnsubscribe) { + SetWireImage(raw_packet_, sizeof(raw_packet_)); + } + + bool EqualFieldValues(MessageStructuredData& values) const override { + auto cast = std::get<MoqtUnsubscribe>(values); + if (cast.full_track_name != unsubscribe_.full_track_name) { + QUIC_LOG(INFO) << "UNSUBSCRIBE full track name mismatch"; + return false; + } + return true; + } + + void ExpandVarints() override { ExpandVarintsImpl("vvv---"); } + + MessageStructuredData structured_data() const override { + return TestMessageBase::MessageStructuredData(unsubscribe_); + } + + private: + uint8_t raw_packet_[6] = { + 0x0a, 0x04, 0x03, 0x66, 0x6f, 0x6f, // track_name = "foo" + }; + + MoqtUnsubscribe unsubscribe_ = { + /*full_track_name=*/"foo", + }; +}; + class QUICHE_NO_EXPORT AnnounceMessage : public TestMessageBase { public: AnnounceMessage() : TestMessageBase(MoqtMessageType::kAnnounce) { @@ -481,15 +513,15 @@ return true; } - void ExpandVarints() override { ExpandVarintsImpl("vv---"); } + void ExpandVarints() override { ExpandVarintsImpl("vvv---"); } MessageStructuredData structured_data() const override { return TestMessageBase::MessageStructuredData(announce_ok_); } private: - uint8_t raw_packet_[5] = { - 0x07, 0x03, 0x66, 0x6f, 0x6f, // track_namespace = "foo" + uint8_t raw_packet_[6] = { + 0x07, 0x04, 0x03, 0x66, 0x6f, 0x6f, // track_namespace = "foo" }; MoqtAnnounceOk announce_ok_ = { @@ -540,6 +572,37 @@ }; }; +class QUICHE_NO_EXPORT UnannounceMessage : public TestMessageBase { + public: + UnannounceMessage() : TestMessageBase(MoqtMessageType::kUnannounce) { + SetWireImage(raw_packet_, sizeof(raw_packet_)); + } + + bool EqualFieldValues(MessageStructuredData& values) const override { + auto cast = std::get<MoqtUnannounce>(values); + if (cast.track_namespace != unannounce_.track_namespace) { + QUIC_LOG(INFO) << "UNSUBSCRIBE full track name mismatch"; + return false; + } + return true; + } + + void ExpandVarints() override { ExpandVarintsImpl("vvv---"); } + + MessageStructuredData structured_data() const override { + return TestMessageBase::MessageStructuredData(unannounce_); + } + + private: + uint8_t raw_packet_[6] = { + 0x09, 0x04, 0x03, 0x66, 0x6f, 0x6f, // track_namespace + }; + + MoqtUnannounce unannounce_ = { + /*track_namespace=*/"foo", + }; +}; + class QUICHE_NO_EXPORT GoAwayMessage : public TestMessageBase { public: GoAwayMessage() : TestMessageBase(MoqtMessageType::kGoAway) {