Report the correct error when parameter length mismatches, and pipe error type code through the ParsingError() functions.
PiperOrigin-RevId: 601568034
diff --git a/quiche/quic/moqt/moqt_parser.cc b/quiche/quic/moqt/moqt_parser.cc
index cde099b..2b9e15f 100644
--- a/quiche/quic/moqt/moqt_parser.cc
+++ b/quiche/quic/moqt/moqt_parser.cc
@@ -92,7 +92,8 @@
size_t message_len = ProcessMessage(reader->PeekRemainingPayload(), fin);
if (message_len == 0) {
if (reader->BytesRemaining() > kMaxMessageHeaderSize) {
- ParseError("Cannot parse non-OBJECT messages > 2KB");
+ ParseError(MoqtError::kGenericError,
+ "Cannot parse non-OBJECT messages > 2KB");
return;
}
if (fin) {
@@ -537,12 +538,16 @@
}
void MoqtParser::ParseError(absl::string_view reason) {
+ ParseError(MoqtError::kProtocolViolation, reason);
+}
+
+void MoqtParser::ParseError(MoqtError error_code, absl::string_view reason) {
if (parsing_error_) {
return; // Don't send multiple parse errors.
}
no_more_data_ = true;
parsing_error_ = true;
- visitor_.OnParsingError(reason);
+ visitor_.OnParsingError(error_code, reason);
}
bool MoqtParser::ReadVarIntPieceVarInt62(quic::QuicDataReader& reader,
@@ -604,7 +609,8 @@
bool MoqtParser::StringViewToVarInt(absl::string_view& sv, uint64_t& vi) {
quic::QuicDataReader reader(sv);
if (static_cast<size_t>(reader.PeekVarInt62Length()) != sv.length()) {
- ParseError("Parameter length does not match varint encoding");
+ ParseError(MoqtError::kParameterLengthMismatch,
+ "Parameter length does not match varint encoding");
return false;
}
reader.ReadVarInt62(&vi);
diff --git a/quiche/quic/moqt/moqt_parser.h b/quiche/quic/moqt/moqt_parser.h
index 9dd63b5..2674885 100644
--- a/quiche/quic/moqt/moqt_parser.h
+++ b/quiche/quic/moqt/moqt_parser.h
@@ -47,7 +47,7 @@
virtual void OnUnannounceMessage(const MoqtUnannounce& message) = 0;
virtual void OnGoAwayMessage(const MoqtGoAway& message) = 0;
- virtual void OnParsingError(absl::string_view reason) = 0;
+ virtual void OnParsingError(MoqtError code, absl::string_view reason) = 0;
};
class QUICHE_EXPORT MoqtParser {
@@ -90,7 +90,9 @@
size_t ProcessUnannounce(quic::QuicDataReader& reader);
size_t ProcessGoAway(quic::QuicDataReader& reader);
+ // If |error| is not provided, assumes kProtocolViolation.
void ParseError(absl::string_view reason);
+ void ParseError(MoqtError error, absl::string_view reason);
// Reads an integer whose length is specified by a preceding VarInt62 and
// returns it in |result|. Returns false if parsing fails.
diff --git a/quiche/quic/moqt/moqt_parser_test.cc b/quiche/quic/moqt/moqt_parser_test.cc
index 32643d4..d5d8f45 100644
--- a/quiche/quic/moqt/moqt_parser_test.cc
+++ b/quiche/quic/moqt/moqt_parser_test.cc
@@ -225,15 +225,17 @@
goaway.new_session_uri = absl::string_view(string0_);
last_message_ = TestMessageBase::MessageStructuredData(goaway);
}
- void OnParsingError(absl::string_view reason) override {
+ void OnParsingError(MoqtError code, absl::string_view reason) override {
QUIC_LOG(INFO) << "Parsing error: " << reason;
parsing_error_ = reason;
+ parsing_error_code_ = code;
}
std::optional<absl::string_view> object_payload_;
bool end_of_message_ = false;
bool got_goaway_ = false;
std::optional<absl::string_view> parsing_error_;
+ MoqtError parsing_error_code_;
uint64_t messages_received_ = 0;
std::optional<TestMessageBase::MessageStructuredData> last_message_;
// Stored strings for last_message_. The visitor API does not promise the
@@ -409,6 +411,7 @@
EXPECT_EQ(visitor_.messages_received_, 0);
EXPECT_TRUE(visitor_.parsing_error_.has_value());
EXPECT_EQ(*visitor_.parsing_error_, "End of stream before complete message");
+ EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kProtocolViolation);
}
// Tests for message-specific error cases, and behaviors for a single message
@@ -517,6 +520,7 @@
EXPECT_EQ(visitor_.messages_received_, 0);
EXPECT_TRUE(visitor_.parsing_error_.has_value());
EXPECT_EQ(*visitor_.parsing_error_, "ROLE parameter appears twice in SETUP");
+ EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kProtocolViolation);
}
TEST_F(MoqtMessageSpecificTest, SetupRoleIsMissing) {
@@ -531,6 +535,25 @@
EXPECT_TRUE(visitor_.parsing_error_.has_value());
EXPECT_EQ(*visitor_.parsing_error_,
"ROLE parameter missing from CLIENT_SETUP message");
+ EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kProtocolViolation);
+}
+
+TEST_F(MoqtMessageSpecificTest, SetupRoleVarintLengthIsWrong) {
+ MoqtParser parser(kRawQuic, visitor_);
+ char setup[] = {
+ 0x40, 0x40, // type
+ 0x02, 0x01, 0x02, // versions
+ 0x02, // 2 parameters
+ 0x00, 0x02, 0x03, // role = both, but length is 2
+ 0x01, 0x03, 0x66, 0x6f, 0x6f // path = "foo"
+ };
+ parser.ProcessData(absl::string_view(setup, sizeof(setup)), false);
+ EXPECT_EQ(visitor_.messages_received_, 0);
+ EXPECT_TRUE(visitor_.parsing_error_.has_value());
+ EXPECT_EQ(*visitor_.parsing_error_,
+ "Parameter length does not match varint encoding");
+
+ EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kParameterLengthMismatch);
}
TEST_F(MoqtMessageSpecificTest, SetupPathFromServer) {
@@ -545,6 +568,7 @@
EXPECT_EQ(visitor_.messages_received_, 0);
EXPECT_TRUE(visitor_.parsing_error_.has_value());
EXPECT_EQ(*visitor_.parsing_error_, "PATH parameter in SERVER_SETUP");
+ EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kProtocolViolation);
}
TEST_F(MoqtMessageSpecificTest, SetupPathAppearsTwice) {
@@ -561,6 +585,7 @@
EXPECT_TRUE(visitor_.parsing_error_.has_value());
EXPECT_EQ(*visitor_.parsing_error_,
"PATH parameter appears twice in CLIENT_SETUP");
+ EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kProtocolViolation);
}
TEST_F(MoqtMessageSpecificTest, SetupPathOverWebtrans) {
@@ -576,6 +601,7 @@
EXPECT_TRUE(visitor_.parsing_error_.has_value());
EXPECT_EQ(*visitor_.parsing_error_,
"WebTransport connection is using PATH parameter in SETUP");
+ EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kProtocolViolation);
}
TEST_F(MoqtMessageSpecificTest, SetupPathMissing) {
@@ -590,6 +616,7 @@
EXPECT_TRUE(visitor_.parsing_error_.has_value());
EXPECT_EQ(*visitor_.parsing_error_,
"PATH SETUP parameter missing from Client message over QUIC");
+ EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kProtocolViolation);
}
TEST_F(MoqtMessageSpecificTest, SubscribeRequestAuthorizationInfoTwice) {
@@ -611,6 +638,7 @@
EXPECT_TRUE(visitor_.parsing_error_.has_value());
EXPECT_EQ(*visitor_.parsing_error_,
"AUTHORIZATION_INFO parameter appears twice in SUBSCRIBE_REQUEST");
+ EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kProtocolViolation);
}
TEST_F(MoqtMessageSpecificTest, AnnounceAuthorizationInfoTwice) {
@@ -626,6 +654,7 @@
EXPECT_TRUE(visitor_.parsing_error_.has_value());
EXPECT_EQ(*visitor_.parsing_error_,
"AUTHORIZATION_INFO parameter appears twice in ANNOUNCE");
+ EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kProtocolViolation);
}
TEST_F(MoqtMessageSpecificTest, FinMidPayload) {
@@ -637,6 +666,7 @@
EXPECT_EQ(visitor_.messages_received_, 1);
EXPECT_TRUE(visitor_.parsing_error_.has_value());
EXPECT_EQ(*visitor_.parsing_error_, "Received FIN mid-payload");
+ EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kProtocolViolation);
}
TEST_F(MoqtMessageSpecificTest, PartialPayloadThenFin) {
@@ -650,6 +680,7 @@
EXPECT_TRUE(visitor_.parsing_error_.has_value());
EXPECT_EQ(*visitor_.parsing_error_,
"End of stream before complete OBJECT PAYLOAD");
+ EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kProtocolViolation);
}
TEST_F(MoqtMessageSpecificTest, DataAfterFin) {
@@ -658,6 +689,7 @@
parser.ProcessData("foo", false);
EXPECT_TRUE(visitor_.parsing_error_.has_value());
EXPECT_EQ(*visitor_.parsing_error_, "Data after end of stream");
+ EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kProtocolViolation);
}
TEST_F(MoqtMessageSpecificTest, Setup2KB) {
@@ -676,6 +708,7 @@
EXPECT_EQ(visitor_.messages_received_, 0);
EXPECT_TRUE(visitor_.parsing_error_.has_value());
EXPECT_EQ(*visitor_.parsing_error_, "Cannot parse non-OBJECT messages > 2KB");
+ EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kGenericError);
}
TEST_F(MoqtMessageSpecificTest, UnknownMessageType) {
diff --git a/quiche/quic/moqt/moqt_session.cc b/quiche/quic/moqt/moqt_session.cc
index c79e515..7adb7ee 100644
--- a/quiche/quic/moqt/moqt_session.cc
+++ b/quiche/quic/moqt/moqt_session.cc
@@ -648,9 +648,9 @@
session_->pending_outgoing_announces_.erase(it);
}
-void MoqtSession::Stream::OnParsingError(absl::string_view reason) {
- session_->Error(MoqtError::kProtocolViolation,
- absl::StrCat("Parse error: ", reason));
+void MoqtSession::Stream::OnParsingError(MoqtError error_code,
+ absl::string_view reason) {
+ session_->Error(error_code, absl::StrCat("Parse error: ", reason));
}
bool MoqtSession::Stream::CheckIfIsControlStream() {
diff --git a/quiche/quic/moqt/moqt_session.h b/quiche/quic/moqt/moqt_session.h
index 6620b3c..01f6fcf 100644
--- a/quiche/quic/moqt/moqt_session.h
+++ b/quiche/quic/moqt/moqt_session.h
@@ -161,7 +161,8 @@
void OnAnnounceErrorMessage(const MoqtAnnounceError& message) override;
void OnUnannounceMessage(const MoqtUnannounce& /*message*/) override {}
void OnGoAwayMessage(const MoqtGoAway& /*message*/) override {}
- void OnParsingError(absl::string_view reason) override;
+ void OnParsingError(MoqtError error_code,
+ absl::string_view reason) override;
quic::Perspective perspective() const {
return session_->parameters_.perspective;