diff options
Diffstat (limited to 'chromium/net/third_party/quiche/src/http2/adapter/header_validator_test.cc')
-rw-r--r-- | chromium/net/third_party/quiche/src/http2/adapter/header_validator_test.cc | 314 |
1 files changed, 289 insertions, 25 deletions
diff --git a/chromium/net/third_party/quiche/src/http2/adapter/header_validator_test.cc b/chromium/net/third_party/quiche/src/http2/adapter/header_validator_test.cc index 01460c9a94b..52148df9850 100644 --- a/chromium/net/third_party/quiche/src/http2/adapter/header_validator_test.cc +++ b/chromium/net/third_party/quiche/src/http2/adapter/header_validator_test.cc @@ -1,16 +1,25 @@ #include "http2/adapter/header_validator.h" #include "absl/strings/str_cat.h" +#include "absl/types/optional.h" #include "common/platform/api/quiche_test.h" namespace http2 { namespace adapter { namespace test { +using ::testing::Optional; + +using Header = std::pair<absl::string_view, absl::string_view>; +constexpr Header kSampleRequestPseudoheaders[] = {{":authority", "www.foo.com"}, + {":method", "GET"}, + {":path", "/foo"}, + {":scheme", "https"}}; + TEST(HeaderValidatorTest, HeaderNameEmpty) { HeaderValidator v; HeaderValidator::HeaderStatus status = v.ValidateSingleHeader("", "value"); - EXPECT_EQ(HeaderValidator::HEADER_NAME_EMPTY, status); + EXPECT_EQ(HeaderValidator::HEADER_FIELD_INVALID, status); } TEST(HeaderValidatorTest, HeaderValueEmpty) { @@ -19,6 +28,18 @@ TEST(HeaderValidatorTest, HeaderValueEmpty) { EXPECT_EQ(HeaderValidator::HEADER_OK, status); } +TEST(HeaderValidatorTest, ExceedsMaxSize) { + HeaderValidator v; + v.SetMaxFieldSize(64u); + HeaderValidator::HeaderStatus status = + v.ValidateSingleHeader("name", "value"); + EXPECT_EQ(HeaderValidator::HEADER_OK, status); + status = v.ValidateSingleHeader( + "name2", + "Antidisestablishmentariansism is supercalifragilisticexpialodocious."); + EXPECT_EQ(HeaderValidator::HEADER_FIELD_TOO_LONG, status); +} + TEST(HeaderValidatorTest, NameHasInvalidChar) { HeaderValidator v; for (const bool is_pseudo_header : {true, false}) { @@ -37,13 +58,13 @@ TEST(HeaderValidatorTest, NameHasInvalidChar) { : absl::StrCat("na", c, "me"); HeaderValidator::HeaderStatus status = v.ValidateSingleHeader(name, "value"); - EXPECT_EQ(HeaderValidator::HEADER_NAME_INVALID_CHAR, status); + EXPECT_EQ(HeaderValidator::HEADER_FIELD_INVALID, status); } // Uppercase characters in header names should not be allowed. const std::string uc_name = is_pseudo_header ? ":Method" : "Name"; HeaderValidator::HeaderStatus status = v.ValidateSingleHeader(uc_name, "value"); - EXPECT_EQ(HeaderValidator::HEADER_NAME_INVALID_CHAR, status); + EXPECT_EQ(HeaderValidator::HEADER_FIELD_INVALID, status); } } @@ -60,7 +81,7 @@ TEST(HeaderValidatorTest, ValueHasInvalidChar) { for (const char* c : {"\r", "\n"}) { HeaderValidator::HeaderStatus status = v.ValidateSingleHeader("name", absl::StrCat("val", c, "ue")); - EXPECT_EQ(HeaderValidator::HEADER_VALUE_INVALID_CHAR, status); + EXPECT_EQ(HeaderValidator::HEADER_FIELD_INVALID, status); } } @@ -70,19 +91,19 @@ TEST(HeaderValidatorTest, StatusHasInvalidChar) { for (HeaderType type : {HeaderType::RESPONSE, HeaderType::RESPONSE_100}) { // When `:status` has a non-digit value, validation will fail. v.StartHeaderBlock(); - EXPECT_EQ(HeaderValidator::HEADER_VALUE_INVALID_CHAR, + EXPECT_EQ(HeaderValidator::HEADER_FIELD_INVALID, v.ValidateSingleHeader(":status", "bar")); EXPECT_FALSE(v.FinishHeaderBlock(type)); // When `:status` is too short, validation will fail. v.StartHeaderBlock(); - EXPECT_EQ(HeaderValidator::HEADER_VALUE_INVALID_CHAR, + EXPECT_EQ(HeaderValidator::HEADER_FIELD_INVALID, v.ValidateSingleHeader(":status", "10")); EXPECT_FALSE(v.FinishHeaderBlock(type)); // When `:status` is too long, validation will fail. v.StartHeaderBlock(); - EXPECT_EQ(HeaderValidator::HEADER_VALUE_INVALID_CHAR, + EXPECT_EQ(HeaderValidator::HEADER_FIELD_INVALID, v.ValidateSingleHeader(":status", "9000")); EXPECT_FALSE(v.FinishHeaderBlock(type)); @@ -94,16 +115,48 @@ TEST(HeaderValidatorTest, StatusHasInvalidChar) { } } +TEST(HeaderValidatorTest, AuthorityHasInvalidChar) { + HeaderValidator v; + v.StartHeaderBlock(); + + // These characters should be allowed. (Not exhaustive.) + for (const absl::string_view c : {"1", "-", "!", ":", "+", "=", ","}) { + HeaderValidator::HeaderStatus status = v.ValidateSingleHeader( + ":authority", absl::StrCat("ho", c, "st.example.com")); + EXPECT_EQ(HeaderValidator::HEADER_OK, status); + } + // These should not. + for (const absl::string_view c : {"\r", "\n", "|", "\\", "`"}) { + HeaderValidator::HeaderStatus status = v.ValidateSingleHeader( + ":authority", absl::StrCat("ho", c, "st.example.com")); + EXPECT_EQ(HeaderValidator::HEADER_FIELD_INVALID, status); + } + + // IPv4 example + HeaderValidator::HeaderStatus status = + v.ValidateSingleHeader(":authority", "123.45.67.89"); + EXPECT_EQ(HeaderValidator::HEADER_OK, status); + + // IPv6 examples + status = v.ValidateSingleHeader(":authority", + "2001:0db8:85a3:0000:0000:8a2e:0370:7334"); + EXPECT_EQ(HeaderValidator::HEADER_OK, status); + status = v.ValidateSingleHeader(":authority", "[::1]:80"); + EXPECT_EQ(HeaderValidator::HEADER_OK, status); + + // Empty field + status = v.ValidateSingleHeader(":authority", ""); + EXPECT_EQ(HeaderValidator::HEADER_OK, status); +} + TEST(HeaderValidatorTest, RequestPseudoHeaders) { HeaderValidator v; - const absl::string_view headers[] = {":authority", ":method", ":path", - ":scheme"}; - for (absl::string_view to_skip : headers) { + for (Header to_skip : kSampleRequestPseudoheaders) { v.StartHeaderBlock(); - for (absl::string_view to_add : headers) { + for (Header to_add : kSampleRequestPseudoheaders) { if (to_add != to_skip) { EXPECT_EQ(HeaderValidator::HEADER_OK, - v.ValidateSingleHeader(to_add, "foo")); + v.ValidateSingleHeader(to_add.first, to_add.second)); } } // When any pseudo-header is missing, final validation will fail. @@ -112,31 +165,31 @@ TEST(HeaderValidatorTest, RequestPseudoHeaders) { // When all pseudo-headers are present, final validation will succeed. v.StartHeaderBlock(); - for (absl::string_view to_add : headers) { + for (Header to_add : kSampleRequestPseudoheaders) { EXPECT_EQ(HeaderValidator::HEADER_OK, - v.ValidateSingleHeader(to_add, "foo")); + v.ValidateSingleHeader(to_add.first, to_add.second)); } EXPECT_TRUE(v.FinishHeaderBlock(HeaderType::REQUEST)); // When an extra pseudo-header is present, final validation will fail. v.StartHeaderBlock(); - for (absl::string_view to_add : headers) { + for (Header to_add : kSampleRequestPseudoheaders) { EXPECT_EQ(HeaderValidator::HEADER_OK, - v.ValidateSingleHeader(to_add, "foo")); + v.ValidateSingleHeader(to_add.first, to_add.second)); } EXPECT_EQ(HeaderValidator::HEADER_OK, v.ValidateSingleHeader(":extra", "blah")); EXPECT_FALSE(v.FinishHeaderBlock(HeaderType::REQUEST)); // When a required pseudo-header is repeated, final validation will fail. - for (absl::string_view to_repeat : headers) { + for (Header to_repeat : kSampleRequestPseudoheaders) { v.StartHeaderBlock(); - for (absl::string_view to_add : headers) { + for (Header to_add : kSampleRequestPseudoheaders) { EXPECT_EQ(HeaderValidator::HEADER_OK, - v.ValidateSingleHeader(to_add, "foo")); + v.ValidateSingleHeader(to_add.first, to_add.second)); if (to_add == to_repeat) { EXPECT_EQ(HeaderValidator::HEADER_OK, - v.ValidateSingleHeader(to_add, "foo")); + v.ValidateSingleHeader(to_add.first, to_add.second)); } } EXPECT_FALSE(v.FinishHeaderBlock(HeaderType::REQUEST)); @@ -145,16 +198,107 @@ TEST(HeaderValidatorTest, RequestPseudoHeaders) { TEST(HeaderValidatorTest, WebsocketPseudoHeaders) { HeaderValidator v; - const absl::string_view headers[] = {":authority", ":method", ":path", - ":scheme"}; v.StartHeaderBlock(); - for (absl::string_view to_add : headers) { + for (Header to_add : kSampleRequestPseudoheaders) { + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(to_add.first, to_add.second)); + } + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(":protocol", "websocket")); + // At this point, `:protocol` is treated as an extra pseudo-header. + EXPECT_FALSE(v.FinishHeaderBlock(HeaderType::REQUEST)); + + // Future header blocks may send the `:protocol` pseudo-header for CONNECT + // requests. + v.AllowConnect(); + + v.StartHeaderBlock(); + for (Header to_add : kSampleRequestPseudoheaders) { EXPECT_EQ(HeaderValidator::HEADER_OK, - v.ValidateSingleHeader(to_add, "foo")); + v.ValidateSingleHeader(to_add.first, to_add.second)); } EXPECT_EQ(HeaderValidator::HEADER_OK, v.ValidateSingleHeader(":protocol", "websocket")); - // For now, `:protocol` is treated as an extra pseudo-header. + // The method is not "CONNECT", so `:protocol` is still treated as an extra + // pseudo-header. + EXPECT_FALSE(v.FinishHeaderBlock(HeaderType::REQUEST)); + + v.StartHeaderBlock(); + for (Header to_add : kSampleRequestPseudoheaders) { + if (to_add.first == ":method") { + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(to_add.first, "CONNECT")); + } else { + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(to_add.first, to_add.second)); + } + } + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(":protocol", "websocket")); + // After allowing the method, `:protocol` is acepted for CONNECT requests. + EXPECT_TRUE(v.FinishHeaderBlock(HeaderType::REQUEST)); +} + +TEST(HeaderValidatorTest, AsteriskPathPseudoHeader) { + HeaderValidator v; + + // An asterisk :path should not be allowed for non-OPTIONS requests. + v.StartHeaderBlock(); + for (Header to_add : kSampleRequestPseudoheaders) { + if (to_add.first == ":path") { + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(to_add.first, "*")); + } else { + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(to_add.first, to_add.second)); + } + } + EXPECT_FALSE(v.FinishHeaderBlock(HeaderType::REQUEST)); + + // An asterisk :path should be allowed for OPTIONS requests. + v.StartHeaderBlock(); + for (Header to_add : kSampleRequestPseudoheaders) { + if (to_add.first == ":path") { + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(to_add.first, "*")); + } else if (to_add.first == ":method") { + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(to_add.first, "OPTIONS")); + } else { + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(to_add.first, to_add.second)); + } + } + EXPECT_TRUE(v.FinishHeaderBlock(HeaderType::REQUEST)); +} + +TEST(HeaderValidatorTest, InvalidPathPseudoHeader) { + HeaderValidator v; + + // An empty path should fail on single header validation and finish. + v.StartHeaderBlock(); + for (Header to_add : kSampleRequestPseudoheaders) { + if (to_add.first == ":path") { + EXPECT_EQ(HeaderValidator::HEADER_FIELD_INVALID, + v.ValidateSingleHeader(to_add.first, "")); + } else { + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(to_add.first, to_add.second)); + } + } + EXPECT_FALSE(v.FinishHeaderBlock(HeaderType::REQUEST)); + + // A path that does not start with a slash should fail on finish. + v.StartHeaderBlock(); + for (Header to_add : kSampleRequestPseudoheaders) { + if (to_add.first == ":path") { + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(to_add.first, "shawarma")); + } else { + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(to_add.first, to_add.second)); + } + } EXPECT_FALSE(v.FinishHeaderBlock(HeaderType::REQUEST)); } @@ -192,6 +336,78 @@ TEST(HeaderValidatorTest, ResponsePseudoHeaders) { } } +TEST(HeaderValidatorTest, Response204) { + HeaderValidator v; + + v.StartHeaderBlock(); + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(":status", "204")); + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader("x-content", "is not present")); + EXPECT_TRUE(v.FinishHeaderBlock(HeaderType::RESPONSE)); +} + +TEST(HeaderValidatorTest, Response204WithContentLengthZero) { + HeaderValidator v; + + v.StartHeaderBlock(); + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(":status", "204")); + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader("x-content", "is not present")); + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader("content-length", "0")); + EXPECT_TRUE(v.FinishHeaderBlock(HeaderType::RESPONSE)); +} + +TEST(HeaderValidatorTest, Response204WithContentLength) { + HeaderValidator v; + + v.StartHeaderBlock(); + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(":status", "204")); + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader("x-content", "is not present")); + EXPECT_EQ(HeaderValidator::HEADER_FIELD_INVALID, + v.ValidateSingleHeader("content-length", "1")); +} + +TEST(HeaderValidatorTest, Response100) { + HeaderValidator v; + + v.StartHeaderBlock(); + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(":status", "100")); + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader("x-content", "is not present")); + EXPECT_TRUE(v.FinishHeaderBlock(HeaderType::RESPONSE)); +} + +TEST(HeaderValidatorTest, Response100WithContentLengthZero) { + HeaderValidator v; + + v.StartHeaderBlock(); + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(":status", "100")); + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader("x-content", "is not present")); + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader("content-length", "0")); + EXPECT_TRUE(v.FinishHeaderBlock(HeaderType::RESPONSE)); +} + +TEST(HeaderValidatorTest, Response100WithContentLength) { + HeaderValidator v; + + v.StartHeaderBlock(); + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(":status", "100")); + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader("x-content", "is not present")); + EXPECT_EQ(HeaderValidator::HEADER_FIELD_INVALID, + v.ValidateSingleHeader("content-length", "1")); +} + TEST(HeaderValidatorTest, ResponseTrailerPseudoHeaders) { HeaderValidator v; @@ -208,6 +424,54 @@ TEST(HeaderValidatorTest, ResponseTrailerPseudoHeaders) { EXPECT_FALSE(v.FinishHeaderBlock(HeaderType::RESPONSE_TRAILER)); } +TEST(HeaderValidatorTest, ValidContentLength) { + HeaderValidator v; + + v.StartHeaderBlock(); + EXPECT_EQ(v.content_length(), absl::nullopt); + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader("content-length", "41")); + EXPECT_THAT(v.content_length(), Optional(41)); + + v.StartHeaderBlock(); + EXPECT_EQ(v.content_length(), absl::nullopt); + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader("content-length", "42")); + EXPECT_THAT(v.content_length(), Optional(42)); +} + +TEST(HeaderValidatorTest, InvalidContentLength) { + HeaderValidator v; + + v.StartHeaderBlock(); + EXPECT_EQ(v.content_length(), absl::nullopt); + EXPECT_EQ(HeaderValidator::HEADER_FIELD_INVALID, + v.ValidateSingleHeader("content-length", "")); + EXPECT_EQ(v.content_length(), absl::nullopt); + EXPECT_EQ(HeaderValidator::HEADER_FIELD_INVALID, + v.ValidateSingleHeader("content-length", "nan")); + EXPECT_EQ(v.content_length(), absl::nullopt); + EXPECT_EQ(HeaderValidator::HEADER_FIELD_INVALID, + v.ValidateSingleHeader("content-length", "-42")); + EXPECT_EQ(v.content_length(), absl::nullopt); + // End on a positive note. + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader("content-length", "42")); + EXPECT_THAT(v.content_length(), Optional(42)); +} + +TEST(HeaderValidatorTest, TeHeader) { + HeaderValidator v; + + v.StartHeaderBlock(); + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader("te", "trailers")); + + v.StartHeaderBlock(); + EXPECT_EQ(HeaderValidator::HEADER_FIELD_INVALID, + v.ValidateSingleHeader("te", "trailers, deflate")); +} + } // namespace test } // namespace adapter } // namespace http2 |