Updates header validation behavior when encountering a too-long header field.

It turns out that nghttp2 does not invoke OnInvalidFrame(), and does not signal a user callback failure. Replicating this behavior requires adding some new status enum values and further complicating OgHttp2Session::OnHeaderStatus().

PiperOrigin-RevId: 421043626
diff --git a/http2/adapter/header_validator.cc b/http2/adapter/header_validator.cc
index 3a70628..0c1d873 100644
--- a/http2/adapter/header_validator.cc
+++ b/http2/adapter/header_validator.cc
@@ -65,7 +65,7 @@
       key.size() + value.size() > max_field_size_.value()) {
     QUICHE_VLOG(2) << "Header field size is " << key.size() + value.size()
                    << ", exceeds max size of " << max_field_size_.value();
-    return HEADER_FIELD_INVALID;
+    return HEADER_FIELD_TOO_LONG;
   }
   const absl::string_view validated_key = key[0] == ':' ? key.substr(1) : key;
   if (validated_key.find_first_not_of(kHttp2HeaderNameAllowedChars) !=
diff --git a/http2/adapter/header_validator.h b/http2/adapter/header_validator.h
index fc8c226..b21b299 100644
--- a/http2/adapter/header_validator.h
+++ b/http2/adapter/header_validator.h
@@ -34,6 +34,7 @@
   enum HeaderStatus {
     HEADER_OK,
     HEADER_FIELD_INVALID,
+    HEADER_FIELD_TOO_LONG,
   };
   HeaderStatus ValidateSingleHeader(absl::string_view key,
                                     absl::string_view value);
diff --git a/http2/adapter/header_validator_test.cc b/http2/adapter/header_validator_test.cc
index 09c4d43..5fe05a8 100644
--- a/http2/adapter/header_validator_test.cc
+++ b/http2/adapter/header_validator_test.cc
@@ -28,7 +28,7 @@
   status = v.ValidateSingleHeader(
       "name2",
       "Antidisestablishmentariansism is supercalifragilisticexpialodocious.");
-  EXPECT_EQ(HeaderValidator::HEADER_FIELD_INVALID, status);
+  EXPECT_EQ(HeaderValidator::HEADER_FIELD_TOO_LONG, status);
 }
 
 TEST(HeaderValidatorTest, NameHasInvalidChar) {
diff --git a/http2/adapter/http2_visitor_interface.h b/http2/adapter/http2_visitor_interface.h
index 8e39ef4..0e8619f 100644
--- a/http2/adapter/http2_visitor_interface.h
+++ b/http2/adapter/http2_visitor_interface.h
@@ -127,6 +127,8 @@
     // The headers are a violation of HTTP messaging semantics and will be reset
     // with error code PROTOCOL_ERROR.
     HEADER_HTTP_MESSAGING,
+    // The headers caused a compression context error.
+    HEADER_COMPRESSION_ERROR,
   };
   virtual OnHeaderResult OnHeaderForStream(Http2StreamId stream_id,
                                            absl::string_view key,
diff --git a/http2/adapter/nghttp2_callbacks.cc b/http2/adapter/nghttp2_callbacks.cc
index 2d05bee..d96214d 100644
--- a/http2/adapter/nghttp2_callbacks.cc
+++ b/http2/adapter/nghttp2_callbacks.cc
@@ -174,6 +174,7 @@
     case Http2VisitorInterface::HEADER_OK:
       return 0;
     case Http2VisitorInterface::HEADER_CONNECTION_ERROR:
+    case Http2VisitorInterface::HEADER_COMPRESSION_ERROR:
       return NGHTTP2_ERR_CALLBACK_FAILURE;
     case Http2VisitorInterface::HEADER_RST_STREAM:
     case Http2VisitorInterface::HEADER_FIELD_INVALID:
diff --git a/http2/adapter/oghttp2_adapter_test.cc b/http2/adapter/oghttp2_adapter_test.cc
index 5a82c97..2d87254 100644
--- a/http2/adapter/oghttp2_adapter_test.cc
+++ b/http2/adapter/oghttp2_adapter_test.cc
@@ -3332,9 +3332,8 @@
   EXPECT_CALL(visitor, OnHeaderForStream(1, ":path", "/this/is/request/one"));
   EXPECT_CALL(visitor, OnFrameHeader(1, _, CONTINUATION, 0)).Times(3);
   EXPECT_CALL(visitor, OnFrameHeader(1, _, CONTINUATION, 4));
-  EXPECT_CALL(
-      visitor,
-      OnInvalidFrame(1, Http2VisitorInterface::InvalidFrameError::kHttpHeader));
+  EXPECT_CALL(visitor, OnConnectionError(ConnectionError::kHeaderError));
+  // Further header processing is skipped, as the header field is too large.
 
   const int64_t result = adapter->ProcessBytes(frames);
   EXPECT_EQ(static_cast<int64_t>(frames.size()), result);
@@ -3343,19 +3342,19 @@
 
   EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
   EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
-  EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1));
-  EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0));
-  EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, 1, 4, 0x0));
-  EXPECT_CALL(visitor, OnFrameSent(RST_STREAM, 1, 4, 0x0, 1));
-  EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR));
+  // Since the library opted not to process the header, it generates a GOAWAY
+  // with error code COMPRESSION_ERROR.
+  EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0));
+  EXPECT_CALL(visitor,
+              OnFrameSent(GOAWAY, 0, _, 0x0,
+                          static_cast<int>(Http2ErrorCode::COMPRESSION_ERROR)));
 
   int send_result = adapter->Send();
   // Some bytes should have been serialized.
   EXPECT_EQ(0, send_result);
-  // SETTINGS, SETTINGS ack, and RST_STREAM.
+  // SETTINGS and GOAWAY.
   EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS,
-                                            spdy::SpdyFrameType::SETTINGS,
-                                            spdy::SpdyFrameType::RST_STREAM}));
+                                            spdy::SpdyFrameType::GOAWAY}));
 }
 
 TEST(OgHttp2AdapterServerTest, ServerRejectsStreamData) {
diff --git a/http2/adapter/oghttp2_session.cc b/http2/adapter/oghttp2_session.cc
index a91af7b..09d3d93 100644
--- a/http2/adapter/oghttp2_session.cc
+++ b/http2/adapter/oghttp2_session.cc
@@ -226,6 +226,19 @@
   validator_.StartHeaderBlock();
 }
 
+Http2VisitorInterface::OnHeaderResult InterpretHeaderStatus(
+    HeaderValidator::HeaderStatus status) {
+  switch (status) {
+    case HeaderValidator::HEADER_OK:
+      return Http2VisitorInterface::HEADER_OK;
+    case HeaderValidator::HEADER_FIELD_INVALID:
+      return Http2VisitorInterface::HEADER_FIELD_INVALID;
+    case HeaderValidator::HEADER_FIELD_TOO_LONG:
+      return Http2VisitorInterface::HEADER_COMPRESSION_ERROR;
+  }
+  return Http2VisitorInterface::HEADER_CONNECTION_ERROR;
+}
+
 void OgHttp2Session::PassthroughHeadersHandler::OnHeader(
     absl::string_view key,
     absl::string_view value) {
@@ -233,11 +246,12 @@
     QUICHE_VLOG(2) << "Early return; status not HEADER_OK";
     return;
   }
-  const auto validation_result = validator_.ValidateSingleHeader(key, value);
+  const HeaderValidator::HeaderStatus validation_result =
+      validator_.ValidateSingleHeader(key, value);
   if (validation_result != HeaderValidator::HEADER_OK) {
-    QUICHE_VLOG(2) << "RST_STREAM with result "
+    QUICHE_VLOG(2) << "Header validation failed with result "
                    << static_cast<int>(validation_result);
-    result_ = Http2VisitorInterface::HEADER_FIELD_INVALID;
+    result_ = InterpretHeaderStatus(validation_result);
     return;
   }
   result_ = visitor_.OnHeaderForStream(stream_id_, key, value);
@@ -1287,6 +1301,9 @@
     fatal_visitor_callback_failure_ = true;
     LatchErrorAndNotify(Http2ErrorCode::INTERNAL_ERROR,
                         ConnectionError::kHeaderError);
+  } else if (result == Http2VisitorInterface::HEADER_COMPRESSION_ERROR) {
+    LatchErrorAndNotify(Http2ErrorCode::COMPRESSION_ERROR,
+                        ConnectionError::kHeaderError);
   }
 }