| // Copyright 2023 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "quiche/web_transport/web_transport_headers.h" | |
| #include <initializer_list> | |
| #include <string> | |
| #include <vector> | |
| #include "absl/status/status.h" | |
| #include "absl/strings/string_view.h" | |
| #include "quiche/common/platform/api/quiche_test.h" | |
| #include "quiche/common/test_tools/quiche_test_utils.h" | |
| namespace webtransport { | |
| namespace { | |
| using ::quiche::test::IsOkAndHolds; | |
| using ::quiche::test::StatusIs; | |
| using ::testing::ElementsAre; | |
| using ::testing::HasSubstr; | |
| TEST(WebTransportHeaders, ParseSubprotocolRequestHeader) { | |
| EXPECT_THAT(ParseSubprotocolRequestHeader("\"test\""), | |
| IsOkAndHolds(ElementsAre("test"))); | |
| EXPECT_THAT( | |
| ParseSubprotocolRequestHeader(R"("moqt-draft01", "moqt-draft02")"), | |
| IsOkAndHolds(ElementsAre("moqt-draft01", "moqt-draft02"))); | |
| EXPECT_THAT( | |
| ParseSubprotocolRequestHeader(R"("moqt-draft01"; a=b, "moqt-draft02")"), | |
| IsOkAndHolds(ElementsAre("moqt-draft01", "moqt-draft02"))); | |
| EXPECT_THAT( | |
| ParseSubprotocolRequestHeader(R"("moqt-draft01", "moqt-draft02"; a=b)"), | |
| IsOkAndHolds(ElementsAre("moqt-draft01", "moqt-draft02"))); | |
| EXPECT_THAT(ParseSubprotocolRequestHeader("test"), | |
| StatusIs(absl::StatusCode::kInvalidArgument, | |
| HasSubstr("found token instead"))); | |
| EXPECT_THAT(ParseSubprotocolRequestHeader("42"), | |
| StatusIs(absl::StatusCode::kInvalidArgument, | |
| HasSubstr("found integer instead"))); | |
| EXPECT_THAT(ParseSubprotocolRequestHeader("\"a\", (b)"), | |
| StatusIs(absl::StatusCode::kInvalidArgument, | |
| HasSubstr("found a nested list instead"))); | |
| EXPECT_THAT(ParseSubprotocolRequestHeader("\"a\", (b c)"), | |
| StatusIs(absl::StatusCode::kInvalidArgument, | |
| HasSubstr("found a nested list instead"))); | |
| EXPECT_THAT(ParseSubprotocolRequestHeader("\"foo\", ?1, bar"), | |
| StatusIs(absl::StatusCode::kInvalidArgument, | |
| HasSubstr("found boolean instead"))); | |
| EXPECT_THAT(ParseSubprotocolRequestHeader("(a"), | |
| StatusIs(absl::StatusCode::kInvalidArgument, | |
| HasSubstr("parse the header as an sf-list"))); | |
| } | |
| TEST(WebTransportHeaders, SerializeSubprotocolRequestHeader) { | |
| EXPECT_THAT(SerializeSubprotocolRequestHeader({"test"}), | |
| IsOkAndHolds(R"("test")")); | |
| EXPECT_THAT(SerializeSubprotocolRequestHeader({"foo", "bar"}), | |
| IsOkAndHolds(R"("foo", "bar")")); | |
| EXPECT_THAT(SerializeSubprotocolRequestHeader({"a\"b", "a/b/c"}), | |
| IsOkAndHolds(R"("a\"b", "a/b/c")")); | |
| EXPECT_THAT(SerializeSubprotocolRequestHeader({"abcd", "\n", "efgh"}), | |
| StatusIs(absl::StatusCode::kInvalidArgument)); | |
| } | |
| 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("\xff"), | |
| StatusIs(absl::StatusCode::kInvalidArgument)); | |
| } | |
| TEST(WebTransportHeader, ParseInitHeader) { | |
| WebTransportInitHeader expected_header; | |
| expected_header.initial_unidi_limit = 100; | |
| expected_header.initial_incoming_bidi_limit = 200; | |
| expected_header.initial_outgoing_bidi_limit = 400; | |
| EXPECT_THAT(ParseInitHeader("br=400, bl=200, u=100"), | |
| IsOkAndHolds(expected_header)); | |
| EXPECT_THAT(ParseInitHeader("br=300, bl=200, u=100, br=400"), | |
| IsOkAndHolds(expected_header)); | |
| EXPECT_THAT(ParseInitHeader("br=400, bl=200; foo=bar, u=100"), | |
| IsOkAndHolds(expected_header)); | |
| EXPECT_THAT(ParseInitHeader("br=400, bl=200, u=100.0"), | |
| StatusIs(absl::StatusCode::kInvalidArgument, | |
| HasSubstr("found decimal instead"))); | |
| EXPECT_THAT(ParseInitHeader("br=400, bl=200, u=?1"), | |
| StatusIs(absl::StatusCode::kInvalidArgument, | |
| HasSubstr("found boolean instead"))); | |
| EXPECT_THAT(ParseInitHeader("br=400, bl=200, u=(a b)"), | |
| StatusIs(absl::StatusCode::kInvalidArgument, | |
| HasSubstr("found a nested list instead"))); | |
| EXPECT_THAT(ParseInitHeader("br=400, bl=200, u=:abcd:"), | |
| StatusIs(absl::StatusCode::kInvalidArgument, | |
| HasSubstr("found byte sequence instead"))); | |
| EXPECT_THAT(ParseInitHeader("br=400, bl=200, u=-1"), | |
| StatusIs(absl::StatusCode::kInvalidArgument, | |
| HasSubstr("negative value"))); | |
| EXPECT_THAT(ParseInitHeader("br=400, bl=200, u=18446744073709551615"), | |
| StatusIs(absl::StatusCode::kInvalidArgument, | |
| HasSubstr("Failed to parse"))); | |
| } | |
| TEST(WebTransportHeaders, SerializeInitHeader) { | |
| EXPECT_THAT(SerializeInitHeader(WebTransportInitHeader{}), | |
| IsOkAndHolds("u=0, bl=0, br=0")); | |
| WebTransportInitHeader test_header; | |
| test_header.initial_unidi_limit = 100; | |
| test_header.initial_incoming_bidi_limit = 200; | |
| test_header.initial_outgoing_bidi_limit = 400; | |
| EXPECT_THAT(SerializeInitHeader(test_header), | |
| IsOkAndHolds("u=100, bl=200, br=400")); | |
| } | |
| // Helper to sidestep the fact that calling ValidateSubprotocolList directly | |
| // with an initializer list is ambiguous. | |
| bool ValidateSubprotocolListHelper( | |
| std::initializer_list<absl::string_view> list) { | |
| return ValidateSubprotocolList(list); | |
| } | |
| TEST(WebTransportHeaders, ValidateSubprotocolName) { | |
| EXPECT_TRUE(ValidateSubprotocolName("test")); | |
| EXPECT_TRUE(ValidateSubprotocolName("123")); | |
| EXPECT_FALSE(ValidateSubprotocolName("\n")); | |
| EXPECT_FALSE(ValidateSubprotocolName("")); | |
| EXPECT_TRUE(ValidateSubprotocolListHelper({})); | |
| EXPECT_TRUE(ValidateSubprotocolListHelper({"a", "b", "c"})); | |
| EXPECT_FALSE(ValidateSubprotocolListHelper({"a", "\n", "c"})); | |
| EXPECT_FALSE(ValidateSubprotocolListHelper({"a", "b", "a"})); | |
| std::vector<std::string> vec = {"a", "b"}; | |
| EXPECT_TRUE(ValidateSubprotocolList(vec)); | |
| vec.push_back("b"); | |
| EXPECT_FALSE(ValidateSubprotocolList(vec)); | |
| } | |
| } // namespace | |
| } // namespace webtransport |