When the HTTP/2 decoder encounters an error, OgHttp2Session latches the error state, and returns a negative integer from ProcessBytes.

Returning a negative value makes errors more readily apparent to users of the library.

PiperOrigin-RevId: 400815381
diff --git a/http2/adapter/oghttp2_adapter_test.cc b/http2/adapter/oghttp2_adapter_test.cc
index f663fb7..413db77 100644
--- a/http2/adapter/oghttp2_adapter_test.cc
+++ b/http2/adapter/oghttp2_adapter_test.cc
@@ -289,9 +289,8 @@
   EXPECT_CALL(visitor, OnConnectionError());
 
   const int64_t stream_result = adapter->ProcessBytes(stream_frames);
-  // Unlike nghttp2, this adapter implementation returns the number of bytes
-  // actually processed.
-  EXPECT_LT(static_cast<size_t>(stream_result), stream_frames.size());
+  // Negative integer returned to indicate an error.
+  EXPECT_LT(stream_result, 0);
 
   EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1));
   EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0));
@@ -450,7 +449,7 @@
   EXPECT_CALL(visitor, OnDataForStream(1, "This is the response body."));
 
   const int64_t stream_result = adapter->ProcessBytes(stream_frames);
-  EXPECT_EQ(stream_frames.size(), static_cast<size_t>(stream_result));
+  EXPECT_LT(stream_result, 0);
 
   EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
   EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
@@ -522,7 +521,7 @@
   EXPECT_CALL(visitor, OnDataForStream(1, "This is the response body."));
 
   const int64_t stream_result = adapter->ProcessBytes(stream_frames);
-  EXPECT_EQ(static_cast<size_t>(stream_result), stream_frames.size());
+  EXPECT_LT(stream_result, 0);
 
   EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
   EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
@@ -681,8 +680,7 @@
   EXPECT_CALL(visitor, OnConnectionError());
 
   const int64_t stream_result = adapter->ProcessBytes(stream_frames);
-  EXPECT_GT(stream_result, 0);
-  EXPECT_LT(stream_result, static_cast<int64_t>(stream_frames.size()));
+  EXPECT_LT(stream_result, 0);
 
   EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
   EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
@@ -1267,8 +1265,7 @@
   EXPECT_CALL(visitor, OnConnectionError());
 
   const int64_t result = adapter->ProcessBytes(frames);
-  EXPECT_GT(result, 0);
-  EXPECT_LT(result, static_cast<int64_t>(frames.size()));
+  EXPECT_LT(result, 0);
 
   EXPECT_TRUE(adapter->want_write());
 
@@ -1317,8 +1314,7 @@
   EXPECT_CALL(visitor, OnConnectionError());
 
   const int64_t result = adapter->ProcessBytes(frames);
-  EXPECT_GT(result, 0);
-  EXPECT_LT(result, static_cast<int64_t>(frames.size()));
+  EXPECT_LT(result, 0);
 
   EXPECT_TRUE(adapter->want_write());
 
@@ -1378,8 +1374,7 @@
   EXPECT_CALL(visitor, OnConnectionError());
 
   const int64_t result = adapter->ProcessBytes(frames);
-  EXPECT_GT(result, 0);
-  EXPECT_LT(result, static_cast<int64_t>(frames.size()));
+  EXPECT_LT(result, 0);
 
   EXPECT_TRUE(adapter->want_write());
 
@@ -1439,8 +1434,7 @@
   EXPECT_CALL(visitor, OnConnectionError());
 
   const int64_t result = adapter->ProcessBytes(frames);
-  EXPECT_GT(result, 0);
-  EXPECT_LT(result, static_cast<int64_t>(frames.size()));
+  EXPECT_LT(result, 0);
 
   EXPECT_TRUE(adapter->want_write());
 
diff --git a/http2/adapter/oghttp2_session.cc b/http2/adapter/oghttp2_session.cc
index eee9c3c..450a56d 100644
--- a/http2/adapter/oghttp2_session.cc
+++ b/http2/adapter/oghttp2_session.cc
@@ -258,7 +258,7 @@
       QUICHE_DLOG(INFO) << "Preface doesn't match! Expected: ["
                         << absl::CEscape(remaining_preface_) << "], actual: ["
                         << absl::CEscape(bytes) << "]";
-      visitor_.OnConnectionError();
+      LatchErrorAndNotify();
       return -1;
     }
     remaining_preface_.remove_prefix(min_size);
@@ -271,7 +271,13 @@
     preface_consumed = min_size;
   }
   int64_t result = decoder_.ProcessInput(bytes.data(), bytes.size());
-  return result < 0 ? result : result + preface_consumed;
+  if (latched_error_) {
+    QUICHE_VLOG(2) << "ProcessBytes encountered an error.";
+    return -1;
+  }
+  const int64_t ret = result < 0 ? result : result + preface_consumed;
+  QUICHE_VLOG(2) << "ProcessBytes returning: " << ret;
+  return ret;
 }
 
 int OgHttp2Session::Consume(Http2StreamId stream_id, size_t num_bytes) {
@@ -323,7 +329,7 @@
     }
   }
   if (result < 0) {
-    visitor_.OnConnectionError();
+    LatchErrorAndNotify();
     return result;
   } else if (!serialized_prefix_.empty()) {
     return 0;
@@ -359,7 +365,7 @@
     spdy::SpdySerializedFrame frame = framer_.SerializeFrame(*frame_ptr);
     const int64_t result = visitor_.OnReadyToSend(absl::string_view(frame));
     if (result < 0) {
-      visitor_.OnConnectionError();
+      LatchErrorAndNotify();
       return false;
     } else if (result == 0) {
       // Write blocked.
@@ -601,7 +607,7 @@
   QUICHE_VLOG(1) << "Error: "
                  << http2::Http2DecoderAdapter::SpdyFramerErrorToString(error)
                  << " details: " << detailed_error;
-  visitor_.OnConnectionError();
+  LatchErrorAndNotify();
 }
 
 void OgHttp2Session::OnCommonHeader(spdy::SpdyStreamId stream_id,
@@ -793,7 +799,7 @@
           stream_id, spdy::ERROR_CODE_INTERNAL_ERROR));
     }
   } else if (result == Http2VisitorInterface::HEADER_CONNECTION_ERROR) {
-    visitor_.OnConnectionError();
+    LatchErrorAndNotify();
   }
 }
 
@@ -953,5 +959,10 @@
   return stream_map_.size() < max_outbound_concurrent_streams_;
 }
 
+void OgHttp2Session::LatchErrorAndNotify() {
+  latched_error_ = true;
+  visitor_.OnConnectionError();
+}
+
 }  // namespace adapter
 }  // namespace http2
diff --git a/http2/adapter/oghttp2_session.h b/http2/adapter/oghttp2_session.h
index a8beb0b..558b36c 100644
--- a/http2/adapter/oghttp2_session.h
+++ b/http2/adapter/oghttp2_session.h
@@ -259,6 +259,8 @@
   // Returns true if the session can create a new stream.
   bool CanCreateStream() const;
 
+  void LatchErrorAndNotify();
+
   // Receives events when inbound frames are parsed.
   Http2VisitorInterface& visitor_;
 
@@ -321,6 +323,7 @@
 
   // Replace this with a stream ID, for multiple GOAWAY support.
   bool queued_goaway_ = false;
+  bool latched_error_ = false;
 };
 
 }  // namespace adapter