Adds a NoopHeaderValidator, which does not perform any validation.

OgHttp2Session will use NoopHeaderValidator iff the constructor option `validate_http_headers` is set to false.

Protected by new behavior is guarded by a constructor option, default false.

PiperOrigin-RevId: 448102524
diff --git a/quiche/http2/adapter/noop_header_validator.cc b/quiche/http2/adapter/noop_header_validator.cc
new file mode 100644
index 0000000..f39342d
--- /dev/null
+++ b/quiche/http2/adapter/noop_header_validator.cc
@@ -0,0 +1,22 @@
+#include "quiche/http2/adapter/noop_header_validator.h"
+
+#include "absl/strings/escaping.h"
+#include "quiche/common/platform/api/quiche_logging.h"
+
+namespace http2 {
+namespace adapter {
+
+HeaderValidatorBase::HeaderStatus NoopHeaderValidator::ValidateSingleHeader(
+    absl::string_view key, absl::string_view value) {
+  if (key == ":status") {
+    status_ = std::string(value);
+  }
+  return HEADER_OK;
+}
+
+bool NoopHeaderValidator::FinishHeaderBlock(HeaderType /* type */) {
+  return true;
+}
+
+}  // namespace adapter
+}  // namespace http2
diff --git a/quiche/http2/adapter/noop_header_validator.h b/quiche/http2/adapter/noop_header_validator.h
new file mode 100644
index 0000000..52d1791
--- /dev/null
+++ b/quiche/http2/adapter/noop_header_validator.h
@@ -0,0 +1,25 @@
+#ifndef QUICHE_HTTP2_ADAPTER_NOOP_HEADER_VALIDATOR_H_
+#define QUICHE_HTTP2_ADAPTER_NOOP_HEADER_VALIDATOR_H_
+
+#include "absl/strings/string_view.h"
+#include "quiche/http2/adapter/header_validator_base.h"
+#include "quiche/common/platform/api/quiche_export.h"
+
+namespace http2 {
+namespace adapter {
+
+// A validator that does not actually perform any validation.
+class QUICHE_EXPORT_PRIVATE NoopHeaderValidator : public HeaderValidatorBase {
+ public:
+  NoopHeaderValidator() = default;
+
+  HeaderStatus ValidateSingleHeader(absl::string_view key,
+                                    absl::string_view value) override;
+
+  bool FinishHeaderBlock(HeaderType type) override;
+};
+
+}  // namespace adapter
+}  // namespace http2
+
+#endif  // QUICHE_HTTP2_ADAPTER_NOOP_HEADER_VALIDATOR_H_
diff --git a/quiche/http2/adapter/noop_header_validator_test.cc b/quiche/http2/adapter/noop_header_validator_test.cc
new file mode 100644
index 0000000..078a84b
--- /dev/null
+++ b/quiche/http2/adapter/noop_header_validator_test.cc
@@ -0,0 +1,523 @@
+#include "quiche/http2/adapter/noop_header_validator.h"
+
+#include <utility>
+#include <vector>
+
+#include "absl/strings/str_cat.h"
+#include "absl/types/optional.h"
+#include "quiche/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(NoopHeaderValidatorTest, HeaderNameEmpty) {
+  NoopHeaderValidator v;
+  NoopHeaderValidator::HeaderStatus status =
+      v.ValidateSingleHeader("", "value");
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK, status);
+}
+
+TEST(NoopHeaderValidatorTest, HeaderValueEmpty) {
+  NoopHeaderValidator v;
+  NoopHeaderValidator::HeaderStatus status = v.ValidateSingleHeader("name", "");
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK, status);
+}
+
+TEST(NoopHeaderValidatorTest, ExceedsMaxSize) {
+  NoopHeaderValidator v;
+  v.SetMaxFieldSize(64u);
+  NoopHeaderValidator::HeaderStatus status =
+      v.ValidateSingleHeader("name", "value");
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK, status);
+  status = v.ValidateSingleHeader(
+      "name2",
+      "Antidisestablishmentariansism is supercalifragilisticexpialodocious.");
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK, status);
+}
+
+TEST(NoopHeaderValidatorTest, AnyNameCharIsValid) {
+  NoopHeaderValidator v;
+  char pseudo_name[] = ":met hod";
+  char name[] = "na me";
+  for (int i = std::numeric_limits<char>::min();
+       i < std::numeric_limits<char>::max(); ++i) {
+    char c = static_cast<char>(i);
+    // Test a pseudo-header name with this char.
+    pseudo_name[3] = c;
+    auto sv = absl::string_view(pseudo_name, 8);
+    EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+              v.ValidateSingleHeader(sv, "value"));
+    // Test a regular header name with this char.
+    name[2] = c;
+    sv = absl::string_view(name, 5);
+    EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+              v.ValidateSingleHeader(sv, "value"));
+  }
+}
+
+TEST(NoopHeaderValidatorTest, AnyValueCharIsValid) {
+  NoopHeaderValidator v;
+  char value[] = "val ue";
+  for (int i = std::numeric_limits<char>::min();
+       i < std::numeric_limits<char>::max(); ++i) {
+    char c = static_cast<char>(i);
+    value[3] = c;
+    auto sv = absl::string_view(value, 6);
+    EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+              v.ValidateSingleHeader("name", sv));
+  }
+}
+
+TEST(NoopHeaderValidatorTest, AnyStatusIsValid) {
+  NoopHeaderValidator v;
+
+  for (HeaderType type : {HeaderType::RESPONSE, HeaderType::RESPONSE_100}) {
+    v.StartHeaderBlock();
+    EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+              v.ValidateSingleHeader(":status", "bar"));
+    EXPECT_TRUE(v.FinishHeaderBlock(type));
+
+    v.StartHeaderBlock();
+    EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+              v.ValidateSingleHeader(":status", "10"));
+    EXPECT_TRUE(v.FinishHeaderBlock(type));
+
+    v.StartHeaderBlock();
+    EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+              v.ValidateSingleHeader(":status", "9000"));
+    EXPECT_TRUE(v.FinishHeaderBlock(type));
+
+    v.StartHeaderBlock();
+    EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+              v.ValidateSingleHeader(":status", "400"));
+    EXPECT_TRUE(v.FinishHeaderBlock(type));
+  }
+}
+
+TEST(NoopHeaderValidatorTest, AnyAuthorityCharIsValid) {
+  char value[] = "ho st.example.com";
+  for (int i = std::numeric_limits<char>::min();
+       i < std::numeric_limits<char>::max(); ++i) {
+    char c = static_cast<char>(i);
+    value[2] = c;
+    auto sv = absl::string_view(value, 17);
+    for (absl::string_view key : {":authority", "host"}) {
+      NoopHeaderValidator v;
+      v.StartHeaderBlock();
+      EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+                v.ValidateSingleHeader(key, sv));
+    }
+  }
+}
+
+TEST(NoopHeaderValidatorTest, RequestHostAndAuthority) {
+  NoopHeaderValidator v;
+  v.StartHeaderBlock();
+  for (Header to_add : kSampleRequestPseudoheaders) {
+    EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+              v.ValidateSingleHeader(to_add.first, to_add.second));
+  }
+  // If both "host" and ":authority" have the same value, validation succeeds.
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader("host", "www.foo.com"));
+  EXPECT_TRUE(v.FinishHeaderBlock(HeaderType::REQUEST));
+
+  v.StartHeaderBlock();
+  for (Header to_add : kSampleRequestPseudoheaders) {
+    EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+              v.ValidateSingleHeader(to_add.first, to_add.second));
+  }
+  // If "host" and ":authority" have different values, validation still
+  // succeeds.
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader("host", "www.bar.com"));
+}
+
+TEST(NoopHeaderValidatorTest, RequestPseudoHeaders) {
+  NoopHeaderValidator v;
+  for (Header to_skip : kSampleRequestPseudoheaders) {
+    v.StartHeaderBlock();
+    for (Header to_add : kSampleRequestPseudoheaders) {
+      if (to_add != to_skip) {
+        EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+                  v.ValidateSingleHeader(to_add.first, to_add.second));
+      }
+    }
+    // Even if a pseudo-header is missing, final validation will succeed.
+    EXPECT_TRUE(v.FinishHeaderBlock(HeaderType::REQUEST));
+  }
+
+  // When all pseudo-headers are present, final validation will succeed.
+  v.StartHeaderBlock();
+  for (Header to_add : kSampleRequestPseudoheaders) {
+    EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+              v.ValidateSingleHeader(to_add.first, to_add.second));
+  }
+  EXPECT_TRUE(v.FinishHeaderBlock(HeaderType::REQUEST));
+
+  // When an extra pseudo-header is present, final validation will still
+  // succeed.
+  v.StartHeaderBlock();
+  for (Header to_add : kSampleRequestPseudoheaders) {
+    EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+              v.ValidateSingleHeader(to_add.first, to_add.second));
+  }
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader(":extra", "blah"));
+  EXPECT_TRUE(v.FinishHeaderBlock(HeaderType::REQUEST));
+
+  // When a required pseudo-header is repeated, final validation will succeed.
+  for (Header to_repeat : kSampleRequestPseudoheaders) {
+    v.StartHeaderBlock();
+    for (Header to_add : kSampleRequestPseudoheaders) {
+      EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+                v.ValidateSingleHeader(to_add.first, to_add.second));
+      if (to_add == to_repeat) {
+        EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+                  v.ValidateSingleHeader(to_add.first, to_add.second));
+      }
+    }
+    EXPECT_TRUE(v.FinishHeaderBlock(HeaderType::REQUEST));
+  }
+}
+
+TEST(NoopHeaderValidatorTest, WebsocketPseudoHeaders) {
+  NoopHeaderValidator v;
+  v.StartHeaderBlock();
+  for (Header to_add : kSampleRequestPseudoheaders) {
+    EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+              v.ValidateSingleHeader(to_add.first, to_add.second));
+  }
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader(":protocol", "websocket"));
+  // Validation always succeeds.
+  EXPECT_TRUE(v.FinishHeaderBlock(HeaderType::REQUEST));
+
+  // This is a no-op for NoopHeaderValidator.
+  v.AllowConnect();
+
+  v.StartHeaderBlock();
+  for (Header to_add : kSampleRequestPseudoheaders) {
+    EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+              v.ValidateSingleHeader(to_add.first, to_add.second));
+  }
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader(":protocol", "websocket"));
+  // The validator does not check for a CONNECT request.
+  EXPECT_TRUE(v.FinishHeaderBlock(HeaderType::REQUEST));
+
+  v.StartHeaderBlock();
+  for (Header to_add : kSampleRequestPseudoheaders) {
+    if (to_add.first == ":method") {
+      EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+                v.ValidateSingleHeader(to_add.first, "CONNECT"));
+    } else {
+      EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+                v.ValidateSingleHeader(to_add.first, to_add.second));
+    }
+  }
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader(":protocol", "websocket"));
+  // After allowing the method, `:protocol` is acepted for CONNECT requests.
+  EXPECT_TRUE(v.FinishHeaderBlock(HeaderType::REQUEST));
+}
+
+TEST(NoopHeaderValidatorTest, AsteriskPathPseudoHeader) {
+  NoopHeaderValidator v;
+
+  // The validator does not perform any path validation.
+  v.StartHeaderBlock();
+  for (Header to_add : kSampleRequestPseudoheaders) {
+    if (to_add.first == ":path") {
+      EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+                v.ValidateSingleHeader(to_add.first, "*"));
+    } else {
+      EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+                v.ValidateSingleHeader(to_add.first, to_add.second));
+    }
+  }
+  EXPECT_TRUE(v.FinishHeaderBlock(HeaderType::REQUEST));
+
+  v.StartHeaderBlock();
+  for (Header to_add : kSampleRequestPseudoheaders) {
+    if (to_add.first == ":path") {
+      EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+                v.ValidateSingleHeader(to_add.first, "*"));
+    } else if (to_add.first == ":method") {
+      EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+                v.ValidateSingleHeader(to_add.first, "OPTIONS"));
+    } else {
+      EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+                v.ValidateSingleHeader(to_add.first, to_add.second));
+    }
+  }
+  EXPECT_TRUE(v.FinishHeaderBlock(HeaderType::REQUEST));
+}
+
+TEST(NoopHeaderValidatorTest, InvalidPathPseudoHeader) {
+  NoopHeaderValidator v;
+
+  // An empty path is allowed.
+  v.StartHeaderBlock();
+  for (Header to_add : kSampleRequestPseudoheaders) {
+    if (to_add.first == ":path") {
+      EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+                v.ValidateSingleHeader(to_add.first, ""));
+    } else {
+      EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+                v.ValidateSingleHeader(to_add.first, to_add.second));
+    }
+  }
+  EXPECT_TRUE(v.FinishHeaderBlock(HeaderType::REQUEST));
+
+  // A path that does not start with a slash is allowed.
+  v.StartHeaderBlock();
+  for (Header to_add : kSampleRequestPseudoheaders) {
+    if (to_add.first == ":path") {
+      EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+                v.ValidateSingleHeader(to_add.first, "shawarma"));
+    } else {
+      EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+                v.ValidateSingleHeader(to_add.first, to_add.second));
+    }
+  }
+  EXPECT_TRUE(v.FinishHeaderBlock(HeaderType::REQUEST));
+}
+
+TEST(NoopHeaderValidatorTest, ResponsePseudoHeaders) {
+  NoopHeaderValidator v;
+
+  for (HeaderType type : {HeaderType::RESPONSE, HeaderType::RESPONSE_100}) {
+    // When `:status` is missing, validation succeeds.
+    v.StartHeaderBlock();
+    EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+              v.ValidateSingleHeader("foo", "bar"));
+    EXPECT_TRUE(v.FinishHeaderBlock(type));
+
+    // When all pseudo-headers are present, final validation succeeds.
+    v.StartHeaderBlock();
+    EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+              v.ValidateSingleHeader(":status", "199"));
+    EXPECT_TRUE(v.FinishHeaderBlock(type));
+    EXPECT_EQ("199", v.status_header());
+
+    // When `:status` is repeated, validation succeeds.
+    v.StartHeaderBlock();
+    EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+              v.ValidateSingleHeader(":status", "199"));
+    EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+              v.ValidateSingleHeader(":status", "299"));
+    EXPECT_TRUE(v.FinishHeaderBlock(type));
+
+    // When an extra pseudo-header is present, final validation succeeds.
+    v.StartHeaderBlock();
+    EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+              v.ValidateSingleHeader(":status", "199"));
+    EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+              v.ValidateSingleHeader(":extra", "blorp"));
+    EXPECT_TRUE(v.FinishHeaderBlock(type));
+  }
+}
+
+TEST(NoopHeaderValidatorTest, ResponseWithHost) {
+  NoopHeaderValidator v;
+
+  v.StartHeaderBlock();
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader(":status", "200"));
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader("host", "myserver.com"));
+  EXPECT_TRUE(v.FinishHeaderBlock(HeaderType::RESPONSE));
+}
+
+TEST(NoopHeaderValidatorTest, Response204) {
+  NoopHeaderValidator v;
+
+  v.StartHeaderBlock();
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader(":status", "204"));
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader("x-content", "is not present"));
+  EXPECT_TRUE(v.FinishHeaderBlock(HeaderType::RESPONSE));
+}
+
+TEST(NoopHeaderValidatorTest, ResponseWithMultipleIdenticalContentLength) {
+  NoopHeaderValidator v;
+
+  v.StartHeaderBlock();
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader(":status", "200"));
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader("content-length", "13"));
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader("content-length", "13"));
+}
+
+TEST(NoopHeaderValidatorTest, ResponseWithMultipleDifferingContentLength) {
+  NoopHeaderValidator v;
+
+  v.StartHeaderBlock();
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader(":status", "200"));
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader("content-length", "13"));
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader("content-length", "17"));
+}
+
+TEST(NoopHeaderValidatorTest, Response204WithContentLengthZero) {
+  NoopHeaderValidator v;
+
+  v.StartHeaderBlock();
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader(":status", "204"));
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader("x-content", "is not present"));
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader("content-length", "0"));
+  EXPECT_TRUE(v.FinishHeaderBlock(HeaderType::RESPONSE));
+}
+
+TEST(NoopHeaderValidatorTest, Response204WithContentLength) {
+  NoopHeaderValidator v;
+
+  v.StartHeaderBlock();
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader(":status", "204"));
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader("x-content", "is not present"));
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader("content-length", "1"));
+}
+
+TEST(NoopHeaderValidatorTest, Response100) {
+  NoopHeaderValidator v;
+
+  v.StartHeaderBlock();
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader(":status", "100"));
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader("x-content", "is not present"));
+  EXPECT_TRUE(v.FinishHeaderBlock(HeaderType::RESPONSE));
+}
+
+TEST(NoopHeaderValidatorTest, Response100WithContentLengthZero) {
+  NoopHeaderValidator v;
+
+  v.StartHeaderBlock();
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader(":status", "100"));
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader("x-content", "is not present"));
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader("content-length", "0"));
+  EXPECT_TRUE(v.FinishHeaderBlock(HeaderType::RESPONSE));
+}
+
+TEST(NoopHeaderValidatorTest, Response100WithContentLength) {
+  NoopHeaderValidator v;
+
+  v.StartHeaderBlock();
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader(":status", "100"));
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader("x-content", "is not present"));
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader("content-length", "1"));
+}
+
+TEST(NoopHeaderValidatorTest, ResponseTrailerPseudoHeaders) {
+  NoopHeaderValidator v;
+
+  // When no pseudo-headers are present, validation will succeed.
+  v.StartHeaderBlock();
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader("foo", "bar"));
+  EXPECT_TRUE(v.FinishHeaderBlock(HeaderType::RESPONSE_TRAILER));
+
+  // When a pseudo-header is present, validation will succeed.
+  v.StartHeaderBlock();
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader(":status", "200"));
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader("foo", "bar"));
+  EXPECT_TRUE(v.FinishHeaderBlock(HeaderType::RESPONSE_TRAILER));
+}
+
+TEST(NoopHeaderValidatorTest, ValidContentLength) {
+  NoopHeaderValidator v;
+
+  v.StartHeaderBlock();
+  EXPECT_EQ(v.content_length(), absl::nullopt);
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader("content-length", "41"));
+  EXPECT_EQ(v.content_length(), absl::nullopt);
+
+  v.StartHeaderBlock();
+  EXPECT_EQ(v.content_length(), absl::nullopt);
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader("content-length", "42"));
+  EXPECT_EQ(v.content_length(), absl::nullopt);
+}
+
+TEST(NoopHeaderValidatorTest, InvalidContentLength) {
+  NoopHeaderValidator v;
+
+  v.StartHeaderBlock();
+  EXPECT_EQ(v.content_length(), absl::nullopt);
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader("content-length", ""));
+  EXPECT_EQ(v.content_length(), absl::nullopt);
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader("content-length", "nan"));
+  EXPECT_EQ(v.content_length(), absl::nullopt);
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader("content-length", "-42"));
+  EXPECT_EQ(v.content_length(), absl::nullopt);
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader("content-length", "42"));
+  EXPECT_EQ(v.content_length(), absl::nullopt);
+}
+
+TEST(NoopHeaderValidatorTest, TeHeader) {
+  NoopHeaderValidator v;
+
+  v.StartHeaderBlock();
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader("te", "trailers"));
+
+  v.StartHeaderBlock();
+  EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+            v.ValidateSingleHeader("te", "trailers, deflate"));
+}
+
+TEST(NoopHeaderValidatorTest, ConnectionSpecificHeaders) {
+  const std::vector<Header> connection_headers = {
+      {"connection", "keep-alive"}, {"proxy-connection", "keep-alive"},
+      {"keep-alive", "timeout=42"}, {"transfer-encoding", "chunked"},
+      {"upgrade", "h2c"},
+  };
+  for (const auto& [connection_key, connection_value] : connection_headers) {
+    NoopHeaderValidator v;
+    v.StartHeaderBlock();
+    for (const auto& [sample_key, sample_value] : kSampleRequestPseudoheaders) {
+      EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+                v.ValidateSingleHeader(sample_key, sample_value));
+    }
+    EXPECT_EQ(NoopHeaderValidator::HEADER_OK,
+              v.ValidateSingleHeader(connection_key, connection_value));
+  }
+}
+
+}  // namespace test
+}  // namespace adapter
+}  // namespace http2
diff --git a/quiche/http2/adapter/oghttp2_adapter_test.cc b/quiche/http2/adapter/oghttp2_adapter_test.cc
index 606cd22..077e33c 100644
--- a/quiche/http2/adapter/oghttp2_adapter_test.cc
+++ b/quiche/http2/adapter/oghttp2_adapter_test.cc
@@ -7433,6 +7433,129 @@
                             SpdyFrameType::WINDOW_UPDATE}));
 }
 
+// Verifies that NoopHeaderValidator allows several header combinations that
+// would otherwise be invalid.
+TEST(OgHttp2AdapterTest, NoopHeaderValidatorTest) {
+  DataSavingVisitor visitor;
+  OgHttp2Adapter::Options options;
+  options.perspective = Perspective::kServer;
+  options.validate_http_headers = false;
+  auto adapter = OgHttp2Adapter::Create(visitor, options);
+
+  const std::string frames = TestFrameSequence()
+                                 .ClientPreface()
+                                 .Headers(1,
+                                          {{":method", "POST"},
+                                           {":scheme", "https"},
+                                           {":authority", "example.com"},
+                                           {":path", "/1"},
+                                           {"content-length", "7"},
+                                           {"content-length", "7"}},
+                                          /*fin=*/false)
+                                 .Headers(3,
+                                          {{":method", "POST"},
+                                           {":scheme", "https"},
+                                           {":authority", "example.com"},
+                                           {":path", "/3"},
+                                           {"content-length", "11"},
+                                           {"content-length", "13"}},
+                                          /*fin=*/false)
+                                 .Headers(5,
+                                          {{":method", "POST"},
+                                           {":scheme", "https"},
+                                           {":authority", "foo.com"},
+                                           {":path", "/"},
+                                           {"host", "bar.com"}},
+                                          /*fin=*/true)
+                                 .Headers(7,
+                                          {{":method", "POST"},
+                                           {":scheme", "https"},
+                                           {":authority", "example.com"},
+                                           {":path", "/"},
+                                           {"Accept", "uppercase, oh boy!"}},
+                                          /*fin=*/false)
+                                 .Headers(9,
+                                          {{":method", "POST"},
+                                           {":scheme", "https"},
+                                           {":authority", "ex|ample.com"},
+                                           {":path", "/"}},
+                                          /*fin=*/false)
+                                 .Headers(11,
+                                          {{":method", "GET"},
+                                           {":scheme", "https"},
+                                           {":authority", "example.com"},
+                                           {":path", "/"},
+                                           {"content-length", "nan"}},
+                                          /*fin=*/true)
+                                 .Serialize();
+  testing::InSequence s;
+
+  // Client preface (empty SETTINGS)
+  EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+  EXPECT_CALL(visitor, OnSettingsStart());
+  EXPECT_CALL(visitor, OnSettingsEnd());
+  // Stream 1
+  EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4));
+  EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+  EXPECT_CALL(visitor, OnHeaderForStream(1, ":method", "POST"));
+  EXPECT_CALL(visitor, OnHeaderForStream(1, ":scheme", "https"));
+  EXPECT_CALL(visitor, OnHeaderForStream(1, ":authority", "example.com"));
+  EXPECT_CALL(visitor, OnHeaderForStream(1, ":path", "/1"));
+  EXPECT_CALL(visitor, OnHeaderForStream(1, "content-length", "7"));
+  EXPECT_CALL(visitor, OnHeaderForStream(1, "content-length", "7"));
+  EXPECT_CALL(visitor, OnEndHeadersForStream(1));
+  // Stream 3
+  EXPECT_CALL(visitor, OnFrameHeader(3, _, HEADERS, 4));
+  EXPECT_CALL(visitor, OnBeginHeadersForStream(3));
+  EXPECT_CALL(visitor, OnHeaderForStream(3, ":method", "POST"));
+  EXPECT_CALL(visitor, OnHeaderForStream(3, ":scheme", "https"));
+  EXPECT_CALL(visitor, OnHeaderForStream(3, ":authority", "example.com"));
+  EXPECT_CALL(visitor, OnHeaderForStream(3, ":path", "/3"));
+  EXPECT_CALL(visitor, OnHeaderForStream(3, "content-length", "11"));
+  EXPECT_CALL(visitor, OnHeaderForStream(3, "content-length", "13"));
+  EXPECT_CALL(visitor, OnEndHeadersForStream(3));
+  // Stream 5
+  EXPECT_CALL(visitor, OnFrameHeader(5, _, HEADERS, 5));
+  EXPECT_CALL(visitor, OnBeginHeadersForStream(5));
+  EXPECT_CALL(visitor, OnHeaderForStream(5, ":method", "POST"));
+  EXPECT_CALL(visitor, OnHeaderForStream(5, ":scheme", "https"));
+  EXPECT_CALL(visitor, OnHeaderForStream(5, ":authority", "foo.com"));
+  EXPECT_CALL(visitor, OnHeaderForStream(5, ":path", "/"));
+  EXPECT_CALL(visitor, OnHeaderForStream(5, "host", "bar.com"));
+  EXPECT_CALL(visitor, OnEndHeadersForStream(5));
+  EXPECT_CALL(visitor, OnEndStream(5));
+  // Stream 7
+  EXPECT_CALL(visitor, OnFrameHeader(7, _, HEADERS, 4));
+  EXPECT_CALL(visitor, OnBeginHeadersForStream(7));
+  EXPECT_CALL(visitor, OnHeaderForStream(7, ":method", "POST"));
+  EXPECT_CALL(visitor, OnHeaderForStream(7, ":scheme", "https"));
+  EXPECT_CALL(visitor, OnHeaderForStream(7, ":authority", "example.com"));
+  EXPECT_CALL(visitor, OnHeaderForStream(7, ":path", "/"));
+  EXPECT_CALL(visitor, OnHeaderForStream(7, "Accept", "uppercase, oh boy!"));
+  EXPECT_CALL(visitor, OnEndHeadersForStream(7));
+  // Stream 9
+  EXPECT_CALL(visitor, OnFrameHeader(9, _, HEADERS, 4));
+  EXPECT_CALL(visitor, OnBeginHeadersForStream(9));
+  EXPECT_CALL(visitor, OnHeaderForStream(9, ":method", "POST"));
+  EXPECT_CALL(visitor, OnHeaderForStream(9, ":scheme", "https"));
+  EXPECT_CALL(visitor, OnHeaderForStream(9, ":authority", "ex|ample.com"));
+  EXPECT_CALL(visitor, OnHeaderForStream(9, ":path", "/"));
+  EXPECT_CALL(visitor, OnEndHeadersForStream(9));
+  // Stream 11
+  EXPECT_CALL(visitor, OnFrameHeader(11, _, HEADERS, 5));
+  EXPECT_CALL(visitor, OnBeginHeadersForStream(11));
+  EXPECT_CALL(visitor, OnHeaderForStream(11, ":method", "GET"));
+  EXPECT_CALL(visitor, OnHeaderForStream(11, ":scheme", "https"));
+  EXPECT_CALL(visitor, OnHeaderForStream(11, ":authority", "example.com"));
+  EXPECT_CALL(visitor, OnHeaderForStream(11, ":path", "/"));
+  EXPECT_CALL(visitor, OnHeaderForStream(11, "content-length", "nan"));
+  EXPECT_CALL(visitor, OnEndHeadersForStream(11));
+  EXPECT_CALL(visitor, OnEndStream(11));
+
+  const int64_t result = adapter->ProcessBytes(frames);
+  EXPECT_EQ(frames.size(), static_cast<size_t>(result));
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace adapter
diff --git a/quiche/http2/adapter/oghttp2_session.cc b/quiche/http2/adapter/oghttp2_session.cc
index 563b6db..d495ae0 100644
--- a/quiche/http2/adapter/oghttp2_session.cc
+++ b/quiche/http2/adapter/oghttp2_session.cc
@@ -10,6 +10,7 @@
 #include "quiche/http2/adapter/http2_protocol.h"
 #include "quiche/http2/adapter/http2_util.h"
 #include "quiche/http2/adapter/http2_visitor_interface.h"
+#include "quiche/http2/adapter/noop_header_validator.h"
 #include "quiche/http2/adapter/oghttp2_util.h"
 #include "quiche/spdy/core/spdy_protocol.h"
 
@@ -219,7 +220,13 @@
 OgHttp2Session::PassthroughHeadersHandler::PassthroughHeadersHandler(
     OgHttp2Session& session, Http2VisitorInterface& visitor)
     : session_(session), visitor_(visitor) {
-  validator_ = absl::make_unique<HeaderValidator>();
+  if (session_.options_.validate_http_headers) {
+    QUICHE_LOG(INFO) << "birenroy | instantiating regular header validator";
+    validator_ = absl::make_unique<HeaderValidator>();
+  } else {
+    QUICHE_LOG(INFO) << "birenroy | instantiating noop header validator";
+    validator_ = absl::make_unique<NoopHeaderValidator>();
+  }
 }
 
 void OgHttp2Session::PassthroughHeadersHandler::OnHeaderBlockStart() {
@@ -332,6 +339,7 @@
 
 OgHttp2Session::OgHttp2Session(Http2VisitorInterface& visitor, Options options)
     : visitor_(visitor),
+      options_(options),
       event_forwarder_([this]() { return !latched_error_; }, *this),
       receive_logger_(
           &event_forwarder_, TracePerspectiveAsString(options.perspective),
@@ -347,8 +355,7 @@
             SendWindowUpdate(kConnectionStreamId, window_update_delta);
           },
           options.should_window_update_fn,
-          /*update_window_on_notify=*/false),
-      options_(options) {
+          /*update_window_on_notify=*/false) {
   decoder_.set_visitor(&receive_logger_);
   decoder_.set_extension_visitor(this);
   if (options_.max_header_list_bytes) {
diff --git a/quiche/http2/adapter/oghttp2_session.h b/quiche/http2/adapter/oghttp2_session.h
index 5c3caa0..458d0da 100644
--- a/quiche/http2/adapter/oghttp2_session.h
+++ b/quiche/http2/adapter/oghttp2_session.h
@@ -72,6 +72,9 @@
     // Whether to allow `obs-text` (characters from hexadecimal 0x80 to 0xff) in
     // header field values.
     bool allow_obs_text = true;
+    // If true, validates header field names and values according to RFC 7230
+    // and RFC 7540.
+    bool validate_http_headers = true;
   };
 
   OgHttp2Session(Http2VisitorInterface& visitor, Options options);
@@ -426,6 +429,8 @@
   // Receives events when inbound frames are parsed.
   Http2VisitorInterface& visitor_;
 
+  const Options options_;
+
   // Forwards received events to the session if it can accept them.
   EventForwarder event_forwarder_;
 
@@ -512,7 +517,6 @@
       std::numeric_limits<uint32_t>::max();
   uint32_t max_inbound_concurrent_streams_ =
       std::numeric_limits<uint32_t>::max();
-  const Options options_;
 
   // The HPACK encoder header table capacity that will be applied when
   // acking SETTINGS from the peer. Only contains a value if the peer advertises