summaryrefslogtreecommitdiff
path: root/chromium/net/third_party/quiche/src/http2/adapter/header_validator_test.cc
diff options
context:
space:
mode:
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.cc314
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