Adds Http2VisitorInterface::OnInvalidFrame() and associate plumbing. This method notifies the visitor that the library has processed a frame that it considers invalid.

PiperOrigin-RevId: 381287141
diff --git a/http2/adapter/callback_visitor.cc b/http2/adapter/callback_visitor.cc
index fa1e0f7..a86e795 100644
--- a/http2/adapter/callback_visitor.cc
+++ b/http2/adapter/callback_visitor.cc
@@ -279,6 +279,17 @@
   QUICHE_LOG(FATAL) << "Not implemented";
 }
 
+bool CallbackVisitor::OnInvalidFrame(Http2StreamId stream_id, int error_code) {
+  QUICHE_LOG(INFO) << "OnInvalidFrame(" << stream_id << ", " << error_code
+                   << ")";
+  QUICHE_DCHECK_EQ(stream_id, current_frame_.hd.stream_id);
+  if (callbacks_->on_invalid_frame_recv_callback) {
+    return 0 == callbacks_->on_invalid_frame_recv_callback(
+                    nullptr, &current_frame_, error_code, user_data_);
+  }
+  return true;
+}
+
 void CallbackVisitor::OnReadyToSendMetadataForStream(Http2StreamId stream_id,
                                                      char* buffer,
                                                      size_t length,
diff --git a/http2/adapter/callback_visitor.h b/http2/adapter/callback_visitor.h
index fe728ba..f00307d 100644
--- a/http2/adapter/callback_visitor.h
+++ b/http2/adapter/callback_visitor.h
@@ -62,6 +62,7 @@
                                   size_t length,
                                   ssize_t* written,
                                   bool* end_stream) override;
+  bool OnInvalidFrame(Http2StreamId stream_id, int error_code) override;
   void OnReadyToSendMetadataForStream(Http2StreamId stream_id,
                                       char* buffer,
                                       size_t length,
diff --git a/http2/adapter/http2_visitor_interface.h b/http2/adapter/http2_visitor_interface.h
index 4250a5f..a707568 100644
--- a/http2/adapter/http2_visitor_interface.h
+++ b/http2/adapter/http2_visitor_interface.h
@@ -163,6 +163,12 @@
                                              ssize_t* written,
                                              bool* end_stream) = 0;
 
+  // Called when the connection receives an invalid frame. |error_code| is a
+  // negative integer error code generated by the library. A return value of
+  // false will result in the connection entering an error state, with no
+  // further frame processing possible.
+  virtual bool OnInvalidFrame(Http2StreamId stream_id, int error_code) = 0;
+
   // Called when the connection is ready to write metadata for |stream_id| to
   // the wire. The implementation should write at most |length| bytes of the
   // serialized metadata payload to the |buffer| and set |written| to the number
diff --git a/http2/adapter/mock_http2_visitor.h b/http2/adapter/mock_http2_visitor.h
index f3ab9ec..b07f8e3 100644
--- a/http2/adapter/mock_http2_visitor.h
+++ b/http2/adapter/mock_http2_visitor.h
@@ -13,6 +13,7 @@
  public:
   MockHttp2Visitor() {
     ON_CALL(*this, OnHeaderForStream).WillByDefault(testing::Return(true));
+    ON_CALL(*this, OnInvalidFrame).WillByDefault(testing::Return(true));
   }
 
   MOCK_METHOD(ssize_t,
@@ -97,11 +98,15 @@
               (uint8_t frame_type, Http2StreamId stream_id, size_t length,
                uint8_t flags),
               (override));
+
   MOCK_METHOD(int, OnFrameSent,
               (uint8_t frame_type, Http2StreamId stream_id, size_t length,
                uint8_t flags, uint32_t error_code),
               (override));
 
+  MOCK_METHOD(bool, OnInvalidFrame, (Http2StreamId stream_id, int error_code),
+              (override));
+
   MOCK_METHOD(void,
               OnReadyToSendDataForStream,
               (Http2StreamId stream_id,
diff --git a/http2/adapter/nghttp2_adapter_test.cc b/http2/adapter/nghttp2_adapter_test.cc
index 4ca1fab..9e5308b 100644
--- a/http2/adapter/nghttp2_adapter_test.cc
+++ b/http2/adapter/nghttp2_adapter_test.cc
@@ -397,6 +397,7 @@
       visitor,
       OnErrorDebug("Invalid HTTP header field was received: frame type: 1, "
                    "stream: 1, name: [:bad-status], value: [9000]"));
+  EXPECT_CALL(visitor, OnInvalidFrame(1, -531));
 
   // Bad status trailer will cause a PROTOCOL_ERROR. The header is never
   // delivered in an OnHeaderForStream callback.
diff --git a/http2/adapter/nghttp2_callbacks.cc b/http2/adapter/nghttp2_callbacks.cc
index c88dd8b..fd7b786 100644
--- a/http2/adapter/nghttp2_callbacks.cc
+++ b/http2/adapter/nghttp2_callbacks.cc
@@ -188,6 +188,16 @@
                               frame->hd.length, frame->hd.flags, error_code);
 }
 
+int OnInvalidFrameReceived(nghttp2_session* /* session */,
+                           const nghttp2_frame* frame, int lib_error_code,
+                           void* user_data) {
+  QUICHE_CHECK_NE(user_data, nullptr);
+  auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
+  const bool result =
+      visitor->OnInvalidFrame(frame->hd.stream_id, lib_error_code);
+  return result ? 0 : NGHTTP2_ERR_CALLBACK_FAILURE;
+}
+
 int OnDataChunk(nghttp2_session* /* session */,
                 uint8_t flags,
                 Http2StreamId stream_id,
@@ -238,9 +248,12 @@
   nghttp2_session_callbacks_set_before_frame_send_callback(callbacks,
                                                            &OnBeforeFrameSent);
   nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, &OnFrameSent);
+  nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(
+      callbacks, &OnInvalidFrameReceived);
   nghttp2_session_callbacks_set_error_callback2(callbacks, &OnError);
   nghttp2_session_callbacks_set_send_data_callback(
       callbacks, &DataFrameSourceSendCallback);
+
   return MakeCallbacksPtr(callbacks);
 }
 
diff --git a/http2/adapter/nghttp2_callbacks.h b/http2/adapter/nghttp2_callbacks.h
index faa8f9a..37b4536 100644
--- a/http2/adapter/nghttp2_callbacks.h
+++ b/http2/adapter/nghttp2_callbacks.h
@@ -46,6 +46,10 @@
 int OnFrameSent(nghttp2_session* session, const nghttp2_frame* frame,
                 void* user_data);
 
+// Invoked when an invalid frame is received.
+int OnInvalidFrameReceived(nghttp2_session* session, const nghttp2_frame* frame,
+                           int lib_error_code, void* user_data);
+
 // Invoked when a chunk of data (from a DATA frame payload) has been received.
 int OnDataChunk(nghttp2_session* session, uint8_t flags,
                 Http2StreamId stream_id, const uint8_t* data, size_t len,
diff --git a/http2/adapter/oghttp2_adapter_test.cc b/http2/adapter/oghttp2_adapter_test.cc
index 82b70b0..40387cc 100644
--- a/http2/adapter/oghttp2_adapter_test.cc
+++ b/http2/adapter/oghttp2_adapter_test.cc
@@ -134,7 +134,8 @@
 }
 
 // TODO(birenroy): Validate headers and re-enable this test. The library should
-// invoke OnErrorDebug() with an error message for the invalid header.
+// invoke OnErrorDebug() with an error message for the invalid header. The
+// library should also invoke OnInvalidFrame() for the invalid HEADERS frame.
 TEST(OgHttp2AdapterClientTest, DISABLED_ClientHandlesInvalidTrailers) {
   DataSavingVisitor visitor;
   OgHttp2Adapter::Options options{.perspective = Perspective::kClient};
diff --git a/http2/adapter/recording_http2_visitor.cc b/http2/adapter/recording_http2_visitor.cc
index a752e52..1ccc68e 100644
--- a/http2/adapter/recording_http2_visitor.cc
+++ b/http2/adapter/recording_http2_visitor.cc
@@ -134,6 +134,13 @@
   return 0;
 }
 
+bool RecordingHttp2Visitor::OnInvalidFrame(Http2StreamId stream_id,
+                                           int error_code) {
+  events_.push_back(
+      absl::StrFormat("OnInvalidFrame %d %d", stream_id, error_code));
+  return true;
+}
+
 void RecordingHttp2Visitor::OnReadyToSendDataForStream(Http2StreamId stream_id,
                                                        char* destination_buffer,
                                                        size_t length,
diff --git a/http2/adapter/recording_http2_visitor.h b/http2/adapter/recording_http2_visitor.h
index d286112..e3e40e1 100644
--- a/http2/adapter/recording_http2_visitor.h
+++ b/http2/adapter/recording_http2_visitor.h
@@ -55,6 +55,7 @@
                         size_t length, uint8_t flags) override;
   int OnFrameSent(uint8_t frame_type, Http2StreamId stream_id, size_t length,
                   uint8_t flags, uint32_t error_code) override;
+  bool OnInvalidFrame(Http2StreamId stream_id, int error_code) override;
   void OnReadyToSendDataForStream(Http2StreamId stream_id,
                                   char* destination_buffer,
                                   size_t length,