Add a function to parse WebTransport subprotocol negotiation response.
I completely forgot that parsing response requires logic too.
PiperOrigin-RevId: 583947370
diff --git a/quiche/web_transport/web_transport_headers.cc b/quiche/web_transport/web_transport_headers.cc
index 9d4b4f8..fc46536 100644
--- a/quiche/web_transport/web_transport_headers.cc
+++ b/quiche/web_transport/web_transport_headers.cc
@@ -32,20 +32,23 @@
using ::quiche::structured_headers::ParameterizedItem;
using ::quiche::structured_headers::ParameterizedMember;
-absl::Status CheckItemType(const ParameterizedMember& member,
+absl::Status CheckItemType(const ParameterizedItem& item,
Item::ItemType expected_type) {
+ if (item.item.Type() != expected_type) {
+ return absl::InvalidArgumentError(absl::StrCat(
+ "Expected all members to be of type ", ItemTypeToString(expected_type),
+ ", found ", ItemTypeToString(item.item.Type()), " instead"));
+ }
+ return absl::OkStatus();
+}
+absl::Status CheckMemberType(const ParameterizedMember& member,
+ Item::ItemType expected_type) {
if (member.member_is_inner_list || member.member.size() != 1) {
return absl::InvalidArgumentError(absl::StrCat(
"Expected all members to be of type", ItemTypeToString(expected_type),
", found a nested list instead"));
}
- if (member.member[0].item.Type() != expected_type) {
- return absl::InvalidArgumentError(absl::StrCat(
- "Expected all members to be of type ", ItemTypeToString(expected_type),
- ", found ", ItemTypeToString(member.member[0].item.Type()),
- " instead"));
- }
- return absl::OkStatus();
+ return CheckItemType(member.member[0], expected_type);
}
ABSL_CONST_INIT std::array kInitHeaderFields{
@@ -66,7 +69,7 @@
std::vector<std::string> result;
result.reserve(parsed->size());
for (ParameterizedMember& member : *parsed) {
- QUICHE_RETURN_IF_ERROR(CheckItemType(member, Item::kTokenType));
+ QUICHE_RETURN_IF_ERROR(CheckMemberType(member, Item::kTokenType));
result.push_back(std::move(member.member[0].item).TakeString());
}
return result;
@@ -84,6 +87,25 @@
return absl::StrJoin(subprotocols, ", ");
}
+absl::StatusOr<std::string> ParseSubprotocolResponseHeader(
+ absl::string_view value) {
+ std::optional<ParameterizedItem> parsed =
+ quiche::structured_headers::ParseItem(value);
+ if (!parsed.has_value()) {
+ return absl::InvalidArgumentError("Failed to parse sf-item");
+ }
+ QUICHE_RETURN_IF_ERROR(CheckItemType(*parsed, Item::kTokenType));
+ return std::move(parsed->item).TakeString();
+}
+
+absl::StatusOr<std::string> SerializeSubprotocolResponseHeader(
+ absl::string_view subprotocol) {
+ if (!quiche::structured_headers::IsValidToken(subprotocol)) {
+ return absl::InvalidArgumentError("Invalid token value supplied");
+ }
+ return std::string(subprotocol);
+}
+
absl::StatusOr<WebTransportInitHeader> ParseInitHeader(
absl::string_view header) {
std::optional<Dictionary> parsed =
@@ -98,7 +120,7 @@
if (field_name_a != field_name_b) {
continue;
}
- QUICHE_RETURN_IF_ERROR(CheckItemType(field_value, Item::kIntegerType));
+ QUICHE_RETURN_IF_ERROR(CheckMemberType(field_value, Item::kIntegerType));
int64_t value = field_value.member[0].item.GetInteger();
if (value < 0) {
return absl::InvalidArgumentError(
diff --git a/quiche/web_transport/web_transport_headers.h b/quiche/web_transport/web_transport_headers.h
index 10f6d2f..6cf16ee 100644
--- a/quiche/web_transport/web_transport_headers.h
+++ b/quiche/web_transport/web_transport_headers.h
@@ -25,6 +25,10 @@
ParseSubprotocolRequestHeader(absl::string_view value);
QUICHE_EXPORT absl::StatusOr<std::string> SerializeSubprotocolRequestHeader(
absl::Span<const std::string> subprotocols);
+QUICHE_EXPORT absl::StatusOr<std::string> ParseSubprotocolResponseHeader(
+ absl::string_view value);
+QUICHE_EXPORT absl::StatusOr<std::string> SerializeSubprotocolResponseHeader(
+ absl::string_view subprotocol);
inline constexpr absl::string_view kInitHeader = "WebTransport-Init";
diff --git a/quiche/web_transport/web_transport_headers_test.cc b/quiche/web_transport/web_transport_headers_test.cc
index 056303a..c41dc8d 100644
--- a/quiche/web_transport/web_transport_headers_test.cc
+++ b/quiche/web_transport/web_transport_headers_test.cc
@@ -57,6 +57,25 @@
StatusIs(absl::StatusCode::kInvalidArgument, "Invalid token: 0123"));
}
+TEST(WebTransportHeader, ParseSubprotocolResponseHeader) {
+ EXPECT_THAT(ParseSubprotocolResponseHeader("foo"), IsOkAndHolds("foo"));
+ EXPECT_THAT(ParseSubprotocolResponseHeader("foo; a=b"), IsOkAndHolds("foo"));
+ EXPECT_THAT(
+ ParseSubprotocolResponseHeader("1234"),
+ StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("found integer")));
+ EXPECT_THAT(
+ ParseSubprotocolResponseHeader("(a"),
+ StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("parse sf-item")));
+}
+
+TEST(WebTransportHeader, SerializeSubprotocolResponseHeader) {
+ EXPECT_THAT(SerializeSubprotocolResponseHeader("foo"), IsOkAndHolds("foo"));
+ EXPECT_THAT(SerializeSubprotocolResponseHeader("moqt-draft01"),
+ IsOkAndHolds("moqt-draft01"));
+ EXPECT_THAT(SerializeSubprotocolResponseHeader("123abc"),
+ StatusIs(absl::StatusCode::kInvalidArgument));
+}
+
TEST(WebTransportHeader, ParseInitHeader) {
WebTransportInitHeader expected_header;
expected_header.initial_unidi_limit = 100;