Allows "classic" HTTP/2 CONNECT request headers. PiperOrigin-RevId: 450671963
diff --git a/quiche/http2/adapter/header_validator.cc b/quiche/http2/adapter/header_validator.cc index 23d4225..972b0c6 100644 --- a/quiche/http2/adapter/header_validator.cc +++ b/quiche/http2/adapter/header_validator.cc
@@ -81,17 +81,29 @@ } bool ValidateRequestHeaders(const std::vector<std::string>& pseudo_headers, + absl::optional<std::string>& authority, absl::string_view method, absl::string_view path, bool allow_extended_connect) { QUICHE_VLOG(2) << "Request pseudo-headers: [" << absl::StrJoin(pseudo_headers, ", ") << "], allow_extended_connect: " << allow_extended_connect + << ", authority: " + << (authority ? authority.value() : "<nullopt>") << ", method: " << method << ", path: " << path; - if (allow_extended_connect && method == "CONNECT") { - static const std::vector<std::string>* kConnectHeaders = - new std::vector<std::string>( - {":authority", ":method", ":path", ":protocol", ":scheme"}); - return pseudo_headers == *kConnectHeaders; + if (method == "CONNECT") { + if (allow_extended_connect) { + // See RFC 8441. + static const std::vector<std::string>* kExtendedConnectHeaders = + new std::vector<std::string>( + {":authority", ":method", ":path", ":protocol", ":scheme"}); + return pseudo_headers == *kExtendedConnectHeaders; + } else { + // See RFC 7540 Section 8.3. + static const std::vector<std::string>* kConnectHeaders = + new std::vector<std::string>({":authority", ":method"}); + return authority.has_value() && !authority.value().empty() && + pseudo_headers == *kConnectHeaders; + } } if (path.empty()) { @@ -220,7 +232,7 @@ std::sort(pseudo_headers_.begin(), pseudo_headers_.end()); switch (type) { case HeaderType::REQUEST: - return ValidateRequestHeaders(pseudo_headers_, method_, path_, + return ValidateRequestHeaders(pseudo_headers_, authority_, method_, path_, allow_extended_connect_); case HeaderType::REQUEST_TRAILER: return ValidateRequestTrailers(pseudo_headers_);
diff --git a/quiche/http2/adapter/header_validator_test.cc b/quiche/http2/adapter/header_validator_test.cc index 31ba369..fc3e207 100644 --- a/quiche/http2/adapter/header_validator_test.cc +++ b/quiche/http2/adapter/header_validator_test.cc
@@ -266,6 +266,45 @@ } } +TEST(HeaderValidatorTest, ConnectHeaders) { + // Too few headers. + HeaderValidator v; + v.StartHeaderBlock(); + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(":authority", "athena.dialup.mit.edu:23")); + EXPECT_FALSE(v.FinishHeaderBlock(HeaderType::REQUEST)); + + v.StartHeaderBlock(); + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(":method", "CONNECT")); + EXPECT_FALSE(v.FinishHeaderBlock(HeaderType::REQUEST)); + + // Too many headers. + v.StartHeaderBlock(); + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(":authority", "athena.dialup.mit.edu:23")); + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(":method", "CONNECT")); + EXPECT_EQ(HeaderValidator::HEADER_OK, v.ValidateSingleHeader(":path", "/")); + EXPECT_FALSE(v.FinishHeaderBlock(HeaderType::REQUEST)); + + // Empty :authority + v.StartHeaderBlock(); + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(":authority", "")); + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(":method", "CONNECT")); + EXPECT_FALSE(v.FinishHeaderBlock(HeaderType::REQUEST)); + + // Just right. + v.StartHeaderBlock(); + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(":authority", "athena.dialup.mit.edu:23")); + EXPECT_EQ(HeaderValidator::HEADER_OK, + v.ValidateSingleHeader(":method", "CONNECT")); + EXPECT_TRUE(v.FinishHeaderBlock(HeaderType::REQUEST)); +} + TEST(HeaderValidatorTest, WebsocketPseudoHeaders) { HeaderValidator v; v.StartHeaderBlock();