Change Http2VisitorInterface::OnInvalidFrame() to accept an InvalidFrameError enum.

This CL introduces a new enum class InvalidFrameError in Http2VisitorInterface
and converts Http2VisitorInterface::OnInvalidFrame() to use an
InvalidFrameError instead of an integer error code. This change improves
clarity in the method and will allow OgHttp2Session to call OnInvalidFrame()
more readily, e.g., in header validation.

PiperOrigin-RevId: 407223283
diff --git a/http2/adapter/callback_visitor.cc b/http2/adapter/callback_visitor.cc
index 99c1223..49a76e7 100644
--- a/http2/adapter/callback_visitor.cc
+++ b/http2/adapter/callback_visitor.cc
@@ -1,6 +1,7 @@
 #include "http2/adapter/callback_visitor.h"
 
 #include "absl/strings/escaping.h"
+#include "http2/adapter/http2_util.h"
 #include "http2/adapter/nghttp2_util.h"
 #include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h"
 #include "common/quiche_endian.h"
@@ -378,12 +379,15 @@
   return 0;
 }
 
-bool CallbackVisitor::OnInvalidFrame(Http2StreamId stream_id, int error_code) {
-  QUICHE_VLOG(1) << "OnInvalidFrame(" << stream_id << ", " << error_code << ")";
+bool CallbackVisitor::OnInvalidFrame(Http2StreamId stream_id,
+                                     InvalidFrameError error) {
+  QUICHE_VLOG(1) << "OnInvalidFrame(" << stream_id << ", "
+                 << InvalidFrameErrorToString(error) << ")";
   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 0 ==
+           callbacks_->on_invalid_frame_recv_callback(
+               nullptr, &current_frame_, ToNgHttp2ErrorCode(error), user_data_);
   }
   return true;
 }
diff --git a/http2/adapter/callback_visitor.h b/http2/adapter/callback_visitor.h
index 4abfe94..3bef25c 100644
--- a/http2/adapter/callback_visitor.h
+++ b/http2/adapter/callback_visitor.h
@@ -58,7 +58,8 @@
                         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;
+  bool OnInvalidFrame(Http2StreamId stream_id,
+                      InvalidFrameError error) override;
   void OnBeginMetadataForStream(Http2StreamId stream_id,
                                 size_t payload_length) override;
   bool OnMetadataForStream(Http2StreamId stream_id,
diff --git a/http2/adapter/http2_util.cc b/http2/adapter/http2_util.cc
index 1ceff5c..138bde7 100644
--- a/http2/adapter/http2_util.cc
+++ b/http2/adapter/http2_util.cc
@@ -5,6 +5,7 @@
 namespace {
 
 using ConnectionError = Http2VisitorInterface::ConnectionError;
+using InvalidFrameError = Http2VisitorInterface::InvalidFrameError;
 
 }  // anonymous namespace
 
@@ -93,5 +94,25 @@
   }
 }
 
+absl::string_view InvalidFrameErrorToString(
+    Http2VisitorInterface::InvalidFrameError error) {
+  switch (error) {
+    case InvalidFrameError::kProtocol:
+      return "Protocol";
+    case InvalidFrameError::kRefusedStream:
+      return "RefusedStream";
+    case InvalidFrameError::kHttpHeader:
+      return "HttpHeader";
+    case InvalidFrameError::kHttpMessaging:
+      return "HttpMessaging";
+    case InvalidFrameError::kFlowControl:
+      return "FlowControl";
+    case InvalidFrameError::kStreamClosed:
+      return "StreamClosed";
+  }
+  QUICHE_LOG(ERROR) << "Unknown InvalidFrameError " << static_cast<int>(error);
+  return "UnknownError";
+}
+
 }  // namespace adapter
 }  // namespace http2
diff --git a/http2/adapter/http2_util.h b/http2/adapter/http2_util.h
index be6b877..3e25b8c 100644
--- a/http2/adapter/http2_util.h
+++ b/http2/adapter/http2_util.h
@@ -17,6 +17,9 @@
 QUICHE_EXPORT_PRIVATE absl::string_view ConnectionErrorToString(
     Http2VisitorInterface::ConnectionError error);
 
+QUICHE_EXPORT_PRIVATE absl::string_view InvalidFrameErrorToString(
+    Http2VisitorInterface::InvalidFrameError error);
+
 }  // namespace adapter
 }  // namespace http2
 
diff --git a/http2/adapter/http2_visitor_interface.h b/http2/adapter/http2_visitor_interface.h
index 7bffb39..5622452 100644
--- a/http2/adapter/http2_visitor_interface.h
+++ b/http2/adapter/http2_visitor_interface.h
@@ -184,11 +184,25 @@
                           size_t length, uint8_t flags,
                           uint32_t error_code) = 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
+  // Called when the connection receives an invalid frame. 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;
+  enum class InvalidFrameError {
+    // The frame contains a general protocol error.
+    kProtocol,
+    // The frame would have caused a new (invalid) stream to be opened.
+    kRefusedStream,
+    // The frame contains an invalid header field.
+    kHttpHeader,
+    // The frame contains a violation in HTTP messaging rules.
+    kHttpMessaging,
+    // The frame causes a flow control error.
+    kFlowControl,
+    // The frame is on an already closed stream or has an invalid stream ID.
+    kStreamClosed,
+  };
+  virtual bool OnInvalidFrame(Http2StreamId stream_id,
+                              InvalidFrameError error) = 0;
 
   // Called when the connection receives the beginning of a METADATA frame
   // (which may itself be the middle of a logical metadata block). The metadata
diff --git a/http2/adapter/mock_http2_visitor.h b/http2/adapter/mock_http2_visitor.h
index a66fe79..7454d2f 100644
--- a/http2/adapter/mock_http2_visitor.h
+++ b/http2/adapter/mock_http2_visitor.h
@@ -103,8 +103,8 @@
                uint8_t flags, uint32_t error_code),
               (override));
 
-  MOCK_METHOD(bool, OnInvalidFrame, (Http2StreamId stream_id, int error_code),
-              (override));
+  MOCK_METHOD(bool, OnInvalidFrame,
+              (Http2StreamId stream_id, InvalidFrameError error), (override));
 
   MOCK_METHOD(void,
               OnBeginMetadataForStream,
diff --git a/http2/adapter/nghttp2_adapter_test.cc b/http2/adapter/nghttp2_adapter_test.cc
index 03e2629..4754050 100644
--- a/http2/adapter/nghttp2_adapter_test.cc
+++ b/http2/adapter/nghttp2_adapter_test.cc
@@ -1,6 +1,7 @@
 #include "http2/adapter/nghttp2_adapter.h"
 
 #include "http2/adapter/http2_protocol.h"
+#include "http2/adapter/http2_visitor_interface.h"
 #include "http2/adapter/mock_http2_visitor.h"
 #include "http2/adapter/nghttp2_test_utils.h"
 #include "http2/adapter/oghttp2_util.h"
@@ -556,7 +557,9 @@
       visitor,
       OnErrorDebug("Invalid HTTP header field was received: frame type: 1, "
                    "stream: 1, name: [:bad-status], value: [9000]"));
-  EXPECT_CALL(visitor, OnInvalidFrame(1, -531));
+  EXPECT_CALL(
+      visitor,
+      OnInvalidFrame(1, Http2VisitorInterface::InvalidFrameError::kHttpHeader));
 
   // 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 c3abeef..8564d6a 100644
--- a/http2/adapter/nghttp2_callbacks.cc
+++ b/http2/adapter/nghttp2_callbacks.cc
@@ -212,8 +212,8 @@
                            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);
+  const bool result = visitor->OnInvalidFrame(
+      frame->hd.stream_id, ToInvalidFrameError(lib_error_code));
   return result ? 0 : NGHTTP2_ERR_CALLBACK_FAILURE;
 }
 
diff --git a/http2/adapter/nghttp2_util.cc b/http2/adapter/nghttp2_util.cc
index 4bfced6..65ee06b 100644
--- a/http2/adapter/nghttp2_util.cc
+++ b/http2/adapter/nghttp2_util.cc
@@ -15,6 +15,8 @@
 
 namespace {
 
+using InvalidFrameError = Http2VisitorInterface::InvalidFrameError;
+
 void DeleteCallbacks(nghttp2_session_callbacks* callbacks) {
   if (callbacks) {
     nghttp2_session_callbacks_del(callbacks);
@@ -122,6 +124,44 @@
   return static_cast<Http2ErrorCode>(wire_error_code);
 }
 
+int ToNgHttp2ErrorCode(InvalidFrameError error) {
+  switch (error) {
+    case InvalidFrameError::kProtocol:
+      return NGHTTP2_ERR_PROTO;
+    case InvalidFrameError::kRefusedStream:
+      return NGHTTP2_ERR_REFUSED_STREAM;
+    case InvalidFrameError::kHttpHeader:
+      return NGHTTP2_ERR_HTTP_HEADER;
+    case InvalidFrameError::kHttpMessaging:
+      return NGHTTP2_ERR_HTTP_MESSAGING;
+    case InvalidFrameError::kFlowControl:
+      return NGHTTP2_ERR_FLOW_CONTROL;
+    case InvalidFrameError::kStreamClosed:
+      return NGHTTP2_ERR_STREAM_CLOSED;
+  }
+  QUICHE_LOG(ERROR) << "Unknown InvalidFrameError " << static_cast<int>(error);
+  return NGHTTP2_ERR_PROTO;
+}
+
+InvalidFrameError ToInvalidFrameError(int error) {
+  switch (error) {
+    case NGHTTP2_ERR_PROTO:
+      return InvalidFrameError::kProtocol;
+    case NGHTTP2_ERR_REFUSED_STREAM:
+      return InvalidFrameError::kRefusedStream;
+    case NGHTTP2_ERR_HTTP_HEADER:
+      return InvalidFrameError::kHttpHeader;
+    case NGHTTP2_ERR_HTTP_MESSAGING:
+      return InvalidFrameError::kHttpMessaging;
+    case NGHTTP2_ERR_FLOW_CONTROL:
+      return InvalidFrameError::kFlowControl;
+    case NGHTTP2_ERR_STREAM_CLOSED:
+      return InvalidFrameError::kStreamClosed;
+  }
+  QUICHE_LOG(ERROR) << "Unknown error " << error;
+  return InvalidFrameError::kProtocol;
+}
+
 class Nghttp2DataFrameSource : public DataFrameSource {
  public:
   Nghttp2DataFrameSource(nghttp2_data_provider provider,
diff --git a/http2/adapter/nghttp2_util.h b/http2/adapter/nghttp2_util.h
index 78d4702..57702a3 100644
--- a/http2/adapter/nghttp2_util.h
+++ b/http2/adapter/nghttp2_util.h
@@ -10,6 +10,7 @@
 #include "absl/types/span.h"
 #include "http2/adapter/data_source.h"
 #include "http2/adapter/http2_protocol.h"
+#include "http2/adapter/http2_visitor_interface.h"
 #include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h"
 #include "spdy/core/spdy_header_block.h"
 
@@ -56,6 +57,11 @@
 // based on the RFC 7540 Section 7 suggestion.
 Http2ErrorCode ToHttp2ErrorCode(uint32_t wire_error_code);
 
+// Converts between the integer error code used by nghttp2 and the corresponding
+// InvalidFrameError value.
+int ToNgHttp2ErrorCode(Http2VisitorInterface::InvalidFrameError error);
+Http2VisitorInterface::InvalidFrameError ToInvalidFrameError(int error);
+
 // Transforms a nghttp2_data_provider into a DataFrameSource. Assumes that
 // |provider| uses the zero-copy nghttp2_data_source_read_callback API. Unsafe
 // otherwise.
diff --git a/http2/adapter/recording_http2_visitor.cc b/http2/adapter/recording_http2_visitor.cc
index 7e12e3d..74db309 100644
--- a/http2/adapter/recording_http2_visitor.cc
+++ b/http2/adapter/recording_http2_visitor.cc
@@ -141,9 +141,9 @@
 }
 
 bool RecordingHttp2Visitor::OnInvalidFrame(Http2StreamId stream_id,
-                                           int error_code) {
-  events_.push_back(
-      absl::StrFormat("OnInvalidFrame %d %d", stream_id, error_code));
+                                           InvalidFrameError error) {
+  events_.push_back(absl::StrFormat("OnInvalidFrame %d %s", stream_id,
+                                    InvalidFrameErrorToString(error)));
   return true;
 }
 
diff --git a/http2/adapter/recording_http2_visitor.h b/http2/adapter/recording_http2_visitor.h
index 4cbc15b..6008725 100644
--- a/http2/adapter/recording_http2_visitor.h
+++ b/http2/adapter/recording_http2_visitor.h
@@ -56,7 +56,8 @@
                         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;
+  bool OnInvalidFrame(Http2StreamId stream_id,
+                      InvalidFrameError error) override;
   void OnBeginMetadataForStream(Http2StreamId stream_id,
                                 size_t payload_length) override;
   bool OnMetadataForStream(Http2StreamId stream_id,