Validate QUIC request/response headers against invalid token and disallowed headers.
Add empty string to disallow-list.
Split --gfe2_reloadable_flag_quic_verify_request_headers into 2 flags:
--gfe2_reloadable_flag_quic_verify_request_headers_2 to validate QUIC request/response headers against invalid request with ratio monitoring; mark H2 request with empty string header as invalid earlier in H2 stack.
--gfe2_reloadable_flag_quic_act_upon_invalid_header return error response upon any invalid QUIC request header.
Protected by quic_reloadable_flag_quic_verify_request_headers_2 and quic_reloadable_flag_quic_act_upon_invalid_header.
PiperOrigin-RevId: 407654730
diff --git a/http2/http2_constants.cc b/http2/http2_constants.cc
index 7ffdb04..6feea19 100644
--- a/http2/http2_constants.cc
+++ b/http2/http2_constants.cc
@@ -7,6 +7,7 @@
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
+#include "http2/platform/api/http2_flag_utils.h"
#include "http2/platform/api/http2_logging.h"
namespace http2 {
@@ -152,12 +153,24 @@
// Invalid HTTP/2 header names according to
// https://datatracker.ietf.org/doc/html/rfc7540#section-8.1.2.2.
-// TODO(birenroy): Consider adding "upgrade" to this set.
+// TODO(b/78024822): Consider adding "upgrade" to this set.
constexpr char const* kHttp2InvalidHeaderNames[] = {
+ "connection", "host", "keep-alive", "proxy-connection",
+ "transfer-encoding", "",
+};
+
+constexpr char const* kHttp2InvalidHeaderNamesOld[] = {
"connection", "host", "keep-alive", "proxy-connection", "transfer-encoding",
};
const InvalidHeaderSet& GetInvalidHttp2HeaderSet() {
+ if (!GetQuicheReloadableFlag(quic, quic_verify_request_headers_2)) {
+ static const auto* invalid_header_set_old =
+ new InvalidHeaderSet(std::begin(http2::kHttp2InvalidHeaderNamesOld),
+ std::end(http2::kHttp2InvalidHeaderNamesOld));
+ return *invalid_header_set_old;
+ }
+ HTTP2_RELOADABLE_FLAG_COUNT_N(quic_verify_request_headers_2, 3, 3);
static const auto* invalid_header_set =
new InvalidHeaderSet(std::begin(http2::kHttp2InvalidHeaderNames),
std::end(http2::kHttp2InvalidHeaderNames));
diff --git a/quic/core/http/end_to_end_test.cc b/quic/core/http/end_to_end_test.cc
index eae2996..949e703 100644
--- a/quic/core/http/end_to_end_test.cc
+++ b/quic/core/http/end_to_end_test.cc
@@ -6606,7 +6606,8 @@
}
TEST_P(EndToEndTest, InvalidExtendedConnect) {
- SetQuicReloadableFlag(quic_verify_request_headers, true);
+ SetQuicReloadableFlag(quic_verify_request_headers_2, true);
+ SetQuicReloadableFlag(quic_act_upon_invalid_header, true);
ASSERT_TRUE(Initialize());
if (!version_.UsesHttp3()) {
@@ -6626,7 +6627,8 @@
}
TEST_P(EndToEndTest, RejectExtendedConnect) {
- SetQuicReloadableFlag(quic_verify_request_headers, true);
+ SetQuicReloadableFlag(quic_verify_request_headers_2, true);
+ SetQuicReloadableFlag(quic_act_upon_invalid_header, true);
// Disable extended CONNECT.
memory_cache_backend_.set_enable_extended_connect(false);
ASSERT_TRUE(Initialize());
@@ -6657,6 +6659,79 @@
CheckResponseHeaders("404");
}
+TEST_P(EndToEndTest, RejectInvalidRequestHeader) {
+ SetQuicReloadableFlag(quic_verify_request_headers_2, true);
+ SetQuicReloadableFlag(quic_act_upon_invalid_header, true);
+ ASSERT_TRUE(Initialize());
+
+ spdy::SpdyHeaderBlock headers;
+ headers[":scheme"] = "https";
+ headers[":authority"] = "localhost";
+ headers[":method"] = "GET";
+ headers[":path"] = "/echo";
+ // transfer-encoding header is not allowed.
+ headers["transfer-encoding"] = "chunk";
+
+ client_->SendMessage(headers, "", /*fin=*/false);
+ client_->WaitForResponse();
+ CheckResponseHeaders("400");
+}
+
+TEST_P(EndToEndTest, RejectTransferEncodingResponse) {
+ SetQuicReloadableFlag(quic_verify_request_headers_2, true);
+ SetQuicReloadableFlag(quic_act_upon_invalid_header, true);
+ ASSERT_TRUE(Initialize());
+
+ // Add a response with transfer-encoding headers.
+ SpdyHeaderBlock headers;
+ headers[":status"] = "200";
+ headers["transfer-encoding"] = "gzip";
+
+ SpdyHeaderBlock trailers;
+ trailers["some-trailing-header"] = "trailing-header-value";
+
+ memory_cache_backend_.AddResponse(server_hostname_, "/eep",
+ std::move(headers), "", trailers.Clone());
+
+ std::string received_response = client_->SendSynchronousRequest("/eep");
+ EXPECT_THAT(client_->stream_error(),
+ IsStreamError(QUIC_BAD_APPLICATION_PAYLOAD));
+}
+
+TEST_P(EndToEndTest, RejectUpperCaseRequest) {
+ SetQuicReloadableFlag(quic_verify_request_headers_2, true);
+ SetQuicReloadableFlag(quic_act_upon_invalid_header, true);
+ ASSERT_TRUE(Initialize());
+
+ spdy::SpdyHeaderBlock headers;
+ headers[":scheme"] = "https";
+ headers[":authority"] = "localhost";
+ headers[":method"] = "GET";
+ headers[":path"] = "/echo";
+ headers["UpperCaseHeader"] = "foo";
+
+ client_->SendMessage(headers, "", /*fin=*/false);
+ client_->WaitForResponse();
+ CheckResponseHeaders("400");
+}
+
+TEST_P(EndToEndTest, RejectRequestWithInvalidToken) {
+ SetQuicReloadableFlag(quic_verify_request_headers_2, true);
+ SetQuicReloadableFlag(quic_act_upon_invalid_header, true);
+ ASSERT_TRUE(Initialize());
+
+ spdy::SpdyHeaderBlock headers;
+ headers[":scheme"] = "https";
+ headers[":authority"] = "localhost";
+ headers[":method"] = "GET";
+ headers[":path"] = "/echo";
+ headers["invalid,header"] = "foo";
+
+ client_->SendMessage(headers, "", /*fin=*/false);
+ client_->WaitForResponse();
+ CheckResponseHeaders("400");
+}
+
} // namespace
} // namespace test
} // namespace quic
diff --git a/quic/core/http/quic_send_control_stream_test.cc b/quic/core/http/quic_send_control_stream_test.cc
index 7c71abe..fa1e584 100644
--- a/quic/core/http/quic_send_control_stream_test.cc
+++ b/quic/core/http/quic_send_control_stream_test.cc
@@ -130,7 +130,7 @@
"4040" // 0x40 as the reserved frame type
"01" // 1 byte frame length
"61"); // payload "a"
- if ((!GetQuicReloadableFlag(quic_verify_request_headers) ||
+ if ((!GetQuicReloadableFlag(quic_verify_request_headers_2) ||
perspective() == Perspective::IS_CLIENT) &&
QuicSpdySessionPeer::LocalHttpDatagramSupport(&session_) ==
HttpDatagramSupport::kDraft00And04) {
@@ -154,7 +154,7 @@
"01" // 1 byte frame length
"61"); // payload "a"
}
- if (GetQuicReloadableFlag(quic_verify_request_headers) &&
+ if (GetQuicReloadableFlag(quic_verify_request_headers_2) &&
perspective() == Perspective::IS_SERVER &&
QuicSpdySessionPeer::LocalHttpDatagramSupport(&session_) ==
HttpDatagramSupport::kNone) {
@@ -176,7 +176,7 @@
"01" // 1 byte frame length
"61"); // payload "a"
}
- if (GetQuicReloadableFlag(quic_verify_request_headers) &&
+ if (GetQuicReloadableFlag(quic_verify_request_headers_2) &&
perspective() == Perspective::IS_SERVER &&
QuicSpdySessionPeer::LocalHttpDatagramSupport(&session_) !=
HttpDatagramSupport::kNone) {
diff --git a/quic/core/http/quic_spdy_client_stream.cc b/quic/core/http/quic_spdy_client_stream.cc
index 4ba3fc9..2491900 100644
--- a/quic/core/http/quic_spdy_client_stream.cc
+++ b/quic/core/http/quic_spdy_client_stream.cc
@@ -51,6 +51,12 @@
QUICHE_DCHECK(headers_decompressed());
header_bytes_read_ += frame_len;
+ if (rst_sent()) {
+ // QuicSpdyStream::OnInitialHeadersComplete already rejected invalid
+ // response header.
+ return;
+ }
+
if (!SpdyUtils::CopyAndValidateHeaders(header_list, &content_length_,
&response_headers_)) {
QUIC_DLOG(ERROR) << "Failed to parse header list: "
@@ -191,7 +197,7 @@
bool QuicSpdyClientStream::AreHeadersValid(
const QuicHeaderList& header_list) const {
- if (!GetQuicReloadableFlag(quic_verify_request_headers)) {
+ if (!GetQuicReloadableFlag(quic_verify_request_headers_2)) {
return true;
}
if (!QuicSpdyStream::AreHeadersValid(header_list)) {
diff --git a/quic/core/http/quic_spdy_client_stream_test.cc b/quic/core/http/quic_spdy_client_stream_test.cc
index 11f81e5..32a0c0c 100644
--- a/quic/core/http/quic_spdy_client_stream_test.cc
+++ b/quic/core/http/quic_spdy_client_stream_test.cc
@@ -129,7 +129,8 @@
}
TEST_P(QuicSpdyClientStreamTest, InvalidResponseHeader) {
- SetQuicReloadableFlag(quic_verify_request_headers, true);
+ SetQuicReloadableFlag(quic_verify_request_headers_2, true);
+ SetQuicReloadableFlag(quic_act_upon_invalid_header, true);
auto headers = AsHeaderList(std::vector<std::pair<std::string, std::string>>{
{":status", "200"}, {":path", "/foo"}});
EXPECT_CALL(*connection_,
@@ -141,7 +142,8 @@
}
TEST_P(QuicSpdyClientStreamTest, MissingStatusCode) {
- SetQuicReloadableFlag(quic_verify_request_headers, true);
+ SetQuicReloadableFlag(quic_verify_request_headers_2, true);
+ SetQuicReloadableFlag(quic_act_upon_invalid_header, true);
auto headers = AsHeaderList(
std::vector<std::pair<std::string, std::string>>{{"key", "value"}});
EXPECT_CALL(*connection_,
diff --git a/quic/core/http/quic_spdy_server_stream_base.cc b/quic/core/http/quic_spdy_server_stream_base.cc
index 7431d86..8b57fb6 100644
--- a/quic/core/http/quic_spdy_server_stream_base.cc
+++ b/quic/core/http/quic_spdy_server_stream_base.cc
@@ -50,10 +50,10 @@
bool QuicSpdyServerStreamBase::AreHeadersValid(
const QuicHeaderList& header_list) const {
- if (!GetQuicReloadableFlag(quic_verify_request_headers)) {
+ if (!GetQuicReloadableFlag(quic_verify_request_headers_2)) {
return true;
}
- QUIC_RELOADABLE_FLAG_COUNT_N(quic_verify_request_headers, 3, 3);
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_verify_request_headers_2, 2, 3);
if (!QuicSpdyStream::AreHeadersValid(header_list)) {
return false;
}
diff --git a/quic/core/http/quic_spdy_server_stream_base_test.cc b/quic/core/http/quic_spdy_server_stream_base_test.cc
index 92c2d08..ae22781 100644
--- a/quic/core/http/quic_spdy_server_stream_base_test.cc
+++ b/quic/core/http/quic_spdy_server_stream_base_test.cc
@@ -109,7 +109,8 @@
header_list.OnHeader(":scheme", "http");
header_list.OnHeaderBlockEnd(128, 128);
stream_->OnStreamHeaderList(/*fin=*/false, 0, header_list);
- EXPECT_EQ(GetQuicReloadableFlag(quic_verify_request_headers) &&
+ EXPECT_EQ(GetQuicReloadableFlag(quic_verify_request_headers_2) &&
+ GetQuicReloadableFlag(quic_act_upon_invalid_header) &&
!session_.allow_extended_connect(),
stream_->rst_sent());
}
@@ -124,7 +125,8 @@
header_list.OnHeader(":scheme", "http");
header_list.OnHeaderBlockEnd(128, 128);
stream_->OnStreamHeaderList(/*fin=*/false, 0, header_list);
- EXPECT_EQ(GetQuicReloadableFlag(quic_verify_request_headers) &&
+ EXPECT_EQ(GetQuicReloadableFlag(quic_verify_request_headers_2) &&
+ GetQuicReloadableFlag(quic_act_upon_invalid_header) &&
!session_.allow_extended_connect(),
stream_->rst_sent());
}
@@ -133,7 +135,8 @@
if (!session_.version().UsesHttp3()) {
return;
}
- SetQuicReloadableFlag(quic_verify_request_headers, true);
+ SetQuicReloadableFlag(quic_verify_request_headers_2, true);
+ SetQuicReloadableFlag(quic_act_upon_invalid_header, true);
QuicHeaderList header_list;
header_list.OnHeaderBlockStart();
header_list.OnHeader(":authority", "www.google.com:4433");
@@ -162,7 +165,8 @@
}
TEST_F(QuicSpdyServerStreamBaseTest, InvalidVanillaConnect) {
- SetQuicReloadableFlag(quic_verify_request_headers, true);
+ SetQuicReloadableFlag(quic_verify_request_headers_2, true);
+ SetQuicReloadableFlag(quic_act_upon_invalid_header, true);
QuicHeaderList header_list;
header_list.OnHeaderBlockStart();
header_list.OnHeader(":authority", "www.google.com:4433");
@@ -180,7 +184,8 @@
}
TEST_F(QuicSpdyServerStreamBaseTest, InvalidNonConnectWithProtocol) {
- SetQuicReloadableFlag(quic_verify_request_headers, true);
+ SetQuicReloadableFlag(quic_verify_request_headers_2, true);
+ SetQuicReloadableFlag(quic_act_upon_invalid_header, true);
QuicHeaderList header_list;
header_list.OnHeaderBlockStart();
header_list.OnHeader(":authority", "www.google.com:4433");
@@ -200,7 +205,8 @@
}
TEST_F(QuicSpdyServerStreamBaseTest, InvalidRequestWithoutScheme) {
- SetQuicReloadableFlag(quic_verify_request_headers, true);
+ SetQuicReloadableFlag(quic_verify_request_headers_2, true);
+ SetQuicReloadableFlag(quic_act_upon_invalid_header, true);
// A request without :scheme should be rejected.
QuicHeaderList header_list;
header_list.OnHeaderBlockStart();
@@ -219,7 +225,8 @@
}
TEST_F(QuicSpdyServerStreamBaseTest, InvalidRequestWithoutAuthority) {
- SetQuicReloadableFlag(quic_verify_request_headers, true);
+ SetQuicReloadableFlag(quic_verify_request_headers_2, true);
+ SetQuicReloadableFlag(quic_act_upon_invalid_header, true);
// A request without :authority should be rejected.
QuicHeaderList header_list;
header_list.OnHeaderBlockStart();
@@ -238,7 +245,8 @@
}
TEST_F(QuicSpdyServerStreamBaseTest, InvalidRequestWithoutMethod) {
- SetQuicReloadableFlag(quic_verify_request_headers, true);
+ SetQuicReloadableFlag(quic_verify_request_headers_2, true);
+ SetQuicReloadableFlag(quic_act_upon_invalid_header, true);
// A request without :method should be rejected.
QuicHeaderList header_list;
header_list.OnHeaderBlockStart();
@@ -257,7 +265,8 @@
}
TEST_F(QuicSpdyServerStreamBaseTest, InvalidRequestWithoutPath) {
- SetQuicReloadableFlag(quic_verify_request_headers, true);
+ SetQuicReloadableFlag(quic_verify_request_headers_2, true);
+ SetQuicReloadableFlag(quic_act_upon_invalid_header, true);
// A request without :path should be rejected.
QuicHeaderList header_list;
header_list.OnHeaderBlockStart();
@@ -276,7 +285,8 @@
}
TEST_F(QuicSpdyServerStreamBaseTest, InvalidRequestHeader) {
- SetQuicReloadableFlag(quic_verify_request_headers, true);
+ SetQuicReloadableFlag(quic_verify_request_headers_2, true);
+ SetQuicReloadableFlag(quic_act_upon_invalid_header, true);
// A request without :path should be rejected.
QuicHeaderList header_list;
header_list.OnHeaderBlockStart();
@@ -296,7 +306,8 @@
}
TEST_F(QuicSpdyServerStreamBaseTest, EmptyHeaders) {
- SetQuicReloadableFlag(quic_verify_request_headers, true);
+ SetQuicReloadableFlag(quic_verify_request_headers_2, true);
+ SetQuicReloadableFlag(quic_act_upon_invalid_header, true);
spdy::SpdyHeaderBlock empty_header;
quic::test::NoopQpackStreamSenderDelegate encoder_stream_sender_delegate;
quic::test::NoopDecoderStreamErrorDelegate decoder_stream_error_delegate;
diff --git a/quic/core/http/quic_spdy_session.cc b/quic/core/http/quic_spdy_session.cc
index 54fda98..2d7d906 100644
--- a/quic/core/http/quic_spdy_session.cc
+++ b/quic/core/http/quic_spdy_session.cc
@@ -455,7 +455,7 @@
debug_visitor_(nullptr),
destruction_indicator_(123456789),
allow_extended_connect_(
- GetQuicReloadableFlag(quic_verify_request_headers) &&
+ GetQuicReloadableFlag(quic_verify_request_headers_2) &&
perspective() == Perspective::IS_SERVER &&
VersionUsesHttp3(transport_version())) {
h2_deframer_.set_visitor(spdy_framer_visitor_.get());
@@ -527,7 +527,7 @@
settings_.values[SETTINGS_WEBTRANS_DRAFT00] = 1;
}
if (allow_extended_connect()) {
- QUIC_RELOADABLE_FLAG_COUNT_N(quic_verify_request_headers, 1, 3);
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_verify_request_headers_2, 1, 3);
settings_.values[SETTINGS_ENABLE_CONNECT_PROTOCOL] = 1;
}
}
@@ -1758,7 +1758,7 @@
bool QuicSpdySession::SupportsWebTransport() {
return WillNegotiateWebTransport() && SupportsH3Datagram() &&
peer_supports_webtransport_ &&
- (!GetQuicReloadableFlag(quic_verify_request_headers) ||
+ (!GetQuicReloadableFlag(quic_verify_request_headers_2) ||
allow_extended_connect_);
}
@@ -1920,11 +1920,11 @@
// Must not be called after Initialize().
void QuicSpdySession::set_allow_extended_connect(bool allow_extended_connect) {
QUIC_BUG_IF(extended connect wrong version,
- !GetQuicReloadableFlag(quic_verify_request_headers) ||
+ !GetQuicReloadableFlag(quic_verify_request_headers_2) ||
!VersionUsesHttp3(transport_version()))
<< "Try to enable/disable extended CONNECT in Google QUIC";
QUIC_BUG_IF(extended connect on client,
- !GetQuicReloadableFlag(quic_verify_request_headers) ||
+ !GetQuicReloadableFlag(quic_verify_request_headers_2) ||
perspective() == Perspective::IS_CLIENT)
<< "Enabling/disabling extended CONNECT on the client side has no effect";
if (ShouldNegotiateWebTransport()) {
diff --git a/quic/core/http/quic_spdy_session_test.cc b/quic/core/http/quic_spdy_session_test.cc
index 964f6fb..834cd18 100644
--- a/quic/core/http/quic_spdy_session_test.cc
+++ b/quic/core/http/quic_spdy_session_test.cc
@@ -237,6 +237,11 @@
(override));
MOCK_METHOD(bool, HasPendingRetransmission, (), (const, override));
+
+ protected:
+ bool AreHeadersValid(const QuicHeaderList& /*header_list*/) const override {
+ return true;
+ }
};
class TestSession : public QuicSpdySession {
@@ -402,7 +407,7 @@
session_(connection_) {
if (perspective == Perspective::IS_SERVER &&
VersionUsesHttp3(transport_version()) &&
- GetQuicReloadableFlag(quic_verify_request_headers)) {
+ GetQuicReloadableFlag(quic_verify_request_headers_2)) {
session_.set_allow_extended_connect(allow_extended_connect);
}
session_.Initialize();
@@ -3764,7 +3769,8 @@
if (!version().UsesHttp3()) {
return;
}
- SetQuicReloadableFlag(quic_verify_request_headers, true);
+ SetQuicReloadableFlag(quic_verify_request_headers_2, true);
+ SetQuicReloadableFlag(quic_act_upon_invalid_header, true);
EXPECT_FALSE(session_.SupportsWebTransport());
EXPECT_TRUE(session_.ShouldProcessIncomingRequests());
diff --git a/quic/core/http/quic_spdy_stream.cc b/quic/core/http/quic_spdy_stream.cc
index 7cb50c0..8147ce0 100644
--- a/quic/core/http/quic_spdy_stream.cc
+++ b/quic/core/http/quic_spdy_stream.cc
@@ -13,6 +13,7 @@
#include "absl/strings/numbers.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
+#include "http2/http2_constants.h"
#include "quic/core/http/capsule.h"
#include "quic/core/http/http_constants.h"
#include "quic/core/http/http_decoder.h"
@@ -632,12 +633,15 @@
// OnHeadersTooLarge() should have already handled it previously.
if (!header_too_large && !AreHeadersValid(header_list)) {
QUIC_CODE_COUNT_N(quic_validate_request_header, 1, 2);
- OnInvalidHeaders();
- return;
+ if (GetQuicReloadableFlag(quic_act_upon_invalid_header)) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_act_upon_invalid_header);
+ OnInvalidHeaders();
+ return;
+ }
}
QUIC_CODE_COUNT_N(quic_validate_request_header, 2, 2);
- if (!GetQuicReloadableFlag(quic_verify_request_headers) ||
+ if (!GetQuicReloadableFlag(quic_verify_request_headers_2) ||
!header_too_large) {
MaybeProcessReceivedWebTransportHeaders();
if (ShouldUseDatagramContexts()) {
@@ -1865,10 +1869,39 @@
}
}
-bool QuicSpdyStream::AreHeadersValid(
- const QuicHeaderList& /*header_list*/) const {
- // TODO(b/202433856) check each header name to be valid token and isn't a
- // disallowed header.
+namespace {
+// Return true if |c| is not allowed in an HTTP/3 wire-encoded header and
+// pseudo-header names according to
+// https://datatracker.ietf.org/doc/html/draft-ietf-quic-http#section-4.1.1 and
+// https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-semantics-19#section-5.6.2
+constexpr bool isInvalidHeaderNameCharacter(unsigned char c) {
+ if (c == '!' || c == '|' || c == '~' || c == '*' || c == '+' || c == '-' ||
+ c == '.' ||
+ // #, $, %, &, '
+ (c >= '#' && c <= '\'') ||
+ // [0-9], :
+ (c >= '0' && c <= ':') ||
+ // ^, _, `, [a-z]
+ (c >= '^' && c <= 'z')) {
+ return false;
+ }
+ return true;
+}
+} // namespace
+
+bool QuicSpdyStream::AreHeadersValid(const QuicHeaderList& header_list) const {
+ QUICHE_DCHECK(GetQuicReloadableFlag(quic_verify_request_headers_2));
+ for (const std::pair<std::string, std::string>& pair : header_list) {
+ const std::string& name = pair.first;
+ if (std::any_of(name.begin(), name.end(), isInvalidHeaderNameCharacter)) {
+ QUIC_DLOG(ERROR) << "Invalid request header " << name;
+ return false;
+ }
+ if (http2::GetInvalidHttp2HeaderSet().contains(name)) {
+ QUIC_DLOG(ERROR) << name << " header is not allowed";
+ return false;
+ }
+ }
return true;
}
diff --git a/quic/core/http/quic_spdy_stream_test.cc b/quic/core/http/quic_spdy_stream_test.cc
index 2732de2..3bad6ef 100644
--- a/quic/core/http/quic_spdy_stream_test.cc
+++ b/quic/core/http/quic_spdy_stream_test.cc
@@ -26,6 +26,7 @@
#include "quic/core/quic_versions.h"
#include "quic/core/quic_write_blocked_list.h"
#include "quic/platform/api/quic_expect_bug.h"
+#include "quic/platform/api/quic_flags.h"
#include "quic/platform/api/quic_test.h"
#include "quic/test_tools/qpack/qpack_test_utils.h"
#include "quic/test_tools/quic_config_peer.h"
@@ -251,6 +252,11 @@
size_t headers_payload_length() const { return headers_payload_length_; }
+ bool AreHeadersValid(const QuicHeaderList& header_list) const override {
+ return !GetQuicReloadableFlag(quic_verify_request_headers_2) ||
+ QuicSpdyStream::AreHeadersValid(header_list);
+ }
+
private:
bool should_process_data_;
spdy::SpdyHeaderBlock saved_headers_;
diff --git a/quic/core/quic_flags_list.h b/quic/core/quic_flags_list.h
index fb6032b..9ad90f4 100644
--- a/quic/core/quic_flags_list.h
+++ b/quic/core/quic_flags_list.h
@@ -93,10 +93,12 @@
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_pass_path_response_to_validator, true)
// If true, quic dispatcher supports one connection to use multiple connection IDs.
QUIC_FLAG(FLAGS_quic_restart_flag_quic_dispatcher_support_multiple_cid_per_connection_v2, true)
-// If true, quic server will send ENABLE_CONNECT_PROTOCOL setting and and endpoint will validate required request/response headers and extended CONNECT mechanism.
-QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_verify_request_headers, false)
+// If true, quic server will send ENABLE_CONNECT_PROTOCOL setting and and endpoint will validate required request/response headers and extended CONNECT mechanism and update code counts of valid/invalid headers.
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_verify_request_headers_2, false)
// If true, record addresses that server has sent reset to recently, and do not send reset if the address lives in the set.
QUIC_FLAG(FLAGS_quic_restart_flag_quic_use_recent_reset_addresses, true)
+// If true, reject or send error response code upon receiving invalid request or response headers. This flag depends on --gfe2_reloadable_flag_quic_verify_request_headers_2.
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_act_upon_invalid_header, false)
// If true, require handshake confirmation for QUIC connections, functionally disabling 0-rtt handshakes.
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_require_handshake_confirmation, false)
// If true, reset per packet state before processing undecryptable packets.
diff --git a/quic/test_tools/quic_test_server.cc b/quic/test_tools/quic_test_server.cc
index 1d85805..e8aee79 100644
--- a/quic/test_tools/quic_test_server.cc
+++ b/quic/test_tools/quic_test_server.cc
@@ -123,7 +123,7 @@
compressed_certs_cache(), server_backend());
}
if (VersionUsesHttp3(version.transport_version) &&
- GetQuicReloadableFlag(quic_verify_request_headers)) {
+ GetQuicReloadableFlag(quic_verify_request_headers_2)) {
QUICHE_DCHECK(session->allow_extended_connect());
// Do not allow extended CONNECT request if the backend doesn't support
// it.
diff --git a/quic/tools/quic_simple_server_stream.cc b/quic/tools/quic_simple_server_stream.cc
index 84e7071..0ea0e50 100644
--- a/quic/tools/quic_simple_server_stream.cc
+++ b/quic/tools/quic_simple_server_stream.cc
@@ -56,14 +56,13 @@
size_t frame_len,
const QuicHeaderList& header_list) {
QuicSpdyStream::OnInitialHeadersComplete(fin, frame_len, header_list);
- if (!SpdyUtils::CopyAndValidateHeaders(header_list, &content_length_,
+ // QuicSpdyStream::OnInitialHeadersComplete() may have already sent error
+ // response.
+ if (!response_sent_ &&
+ !SpdyUtils::CopyAndValidateHeaders(header_list, &content_length_,
&request_headers_)) {
QUIC_DVLOG(1) << "Invalid headers";
- if (!response_sent_) {
- // QuicSpdyStream::OnInitialHeadersComplete() may have already sent error
- // response.
SendErrorResponse();
- }
}
ConsumeHeaderList();
if (!fin && !response_sent_) {