Reject null items and empty members during HTTP structured headers serialization. * Fail serialization cleanly when encountering a default-constructed `ParameterizedMember` instead of crashing via `QUICHE_CHECK(false)`. * Reject null `Item` parameter values during serialization instead of serializing them as boolean `true` (a holdover from Draft 09). Neither case can be produced by the RFC 8941 (`kFinal`) parsers (`ParseItem`, `ParseList`, `ParseDictionary`), and an audit of all Google3 callers of `SerializeItem`, `SerializeList`, and `SerializeDictionary` confirms that no caller constructs or serializes either case. PiperOrigin-RevId: 1000923381
diff --git a/quiche/common/structured_headers.cc b/quiche/common/structured_headers.cc index f7a24a5..671a67e 100644 --- a/quiche/common/structured_headers.cc +++ b/quiche/common/structured_headers.cc
@@ -738,10 +738,7 @@ return value.Visit(absl::Overload{ [&](const ParameterizedItem& value) { return WriteItem(value); }, [&](const InnerList& value) { return WriteInnerList(value); }, - [](std::monostate) { - QUICHE_CHECK(false); - return false; - }, + [](std::monostate) { return false; }, }); } @@ -763,7 +760,7 @@ for (const auto& [param_name, param_value] : value) { output_.push_back(';'); if (!WriteKey(param_name)) return false; - if (param_value.is_null() || IsBooleanTrue(param_value)) continue; + if (IsBooleanTrue(param_value)) continue; output_.push_back('='); if (!WriteBareItem(param_value)) return false; }
diff --git a/quiche/common/structured_headers_test.cc b/quiche/common/structured_headers_test.cc index 84f13ff..ca88e12 100644 --- a/quiche/common/structured_headers_test.cc +++ b/quiche/common/structured_headers_test.cc
@@ -660,6 +660,7 @@ const char* name; const List value; } bad_lists[] = { + {"Default-constructed member", {ParameterizedMember()}}, {"Null item as member", {{Item(), {}}}}, {"Unserializable item as member", {{Token("\n"), {}}}}, {"Key is empty", {{Token("abc"), {Param("", 1)}}}}, @@ -669,6 +670,7 @@ {{Token("abc"), {Param("a\x7f", 1)}}}}, {"Key contains disallowed characters", {{Token("abc"), {Param("a:", 1)}}}}, + {"Param value is null", {{Token("abc"), {NullParam("a")}}}}, {"Param value is unserializable", {{Token("abc"), {{"a", Token("\n")}}}}}, {"Inner list contains unserializable item", {{std::vector<ParameterizedItem>{{Token("\n"), {}}}, {}}}}, @@ -784,9 +786,13 @@ const char* name; const Dictionary value; } bad_dictionaries[] = { + {"Default-constructed member", + Dictionary{{{"abc", ParameterizedMember()}}}}, {"Unserializable dict key", Dictionary{{{"ABC", {Token("abc"), {}}}}}}, {"Dictionary item is unserializable", Dictionary{{{"abc", {Token("abc="), {}}}}}}, + {"Param value is null", + Dictionary{{{"abc", {Token("abc"), {NullParam("a")}}}}}}, {"Param value is unserializable", Dictionary{{{"abc", {Token("abc"), {{"a", Token("\n")}}}}}}}, {"Dictionary inner-list contains unserializable item",