Passes information about CONTINUATION frames through CallbackVisitor.

This is required for HTTP/2 frame flood detection.

PiperOrigin-RevId: 387882196
diff --git a/http2/adapter/callback_visitor.cc b/http2/adapter/callback_visitor.cc
index 7701cc6..3ecc064 100644
--- a/http2/adapter/callback_visitor.cc
+++ b/http2/adapter/callback_visitor.cc
@@ -72,11 +72,25 @@
                                     size_t length,
                                     uint8_t type,
                                     uint8_t flags) {
+  QUICHE_VLOG(1) << "CallbackVisitor::OnFrameHeader(stream_id: " << stream_id
+                 << ", len: " << length << ", type: " << int(type)
+                 << ", flags: " << int(flags) << ")";
   if (static_cast<FrameType>(type) == FrameType::CONTINUATION) {
     // Treat CONTINUATION as HEADERS
     QUICHE_DCHECK_EQ(current_frame_.hd.stream_id, stream_id);
     current_frame_.hd.length += length;
     current_frame_.hd.flags |= flags;
+    QUICHE_DLOG_IF(ERROR, length == 0) << "Empty CONTINUATION!";
+    // Still need to deliver the CONTINUATION to the begin frame callback.
+    nghttp2_frame_hd hd;
+    memset(&hd, 0, sizeof(hd));
+    hd.stream_id = stream_id;
+    hd.length = length;
+    hd.type = type;
+    hd.flags = flags;
+    if (callbacks_->on_begin_frame_callback) {
+      callbacks_->on_begin_frame_callback(nullptr, &hd, user_data_);
+    }
     return;
   }
   // The general strategy is to clear |current_frame_| at the start of a new
@@ -88,9 +102,6 @@
   current_frame_.hd.type = type;
   current_frame_.hd.flags = flags;
   if (callbacks_->on_begin_frame_callback) {
-    QUICHE_VLOG(1) << "CallbackVisitor::OnFrameHeader(stream_id: " << stream_id
-                   << ", len: " << length << ", type: " << int(type)
-                   << ", flags: " << int(flags) << ")";
     callbacks_->on_begin_frame_callback(nullptr, &current_frame_.hd,
                                         user_data_);
   }
diff --git a/http2/adapter/callback_visitor_test.cc b/http2/adapter/callback_visitor_test.cc
index ddf1f93..2cb31ed 100644
--- a/http2/adapter/callback_visitor_test.cc
+++ b/http2/adapter/callback_visitor_test.cc
@@ -22,6 +22,7 @@
   PING,
   GOAWAY,
   WINDOW_UPDATE,
+  CONTINUATION,
 };
 
 // Tests connection-level events.
@@ -178,6 +179,41 @@
   EXPECT_EQ(absl::string_view(metadata_dest, written), kExampleFrame);
 }
 
+TEST(ClientCallbackVisitorUnitTest, HeadersWithContinuation) {
+  testing::StrictMock<MockNghttp2Callbacks> callbacks;
+  CallbackVisitor visitor(Perspective::kClient,
+                          *MockNghttp2Callbacks::GetCallbacks(), &callbacks);
+
+  testing::InSequence seq;
+
+  // HEADERS on stream 1
+  EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(1, HEADERS, 0x0)));
+  visitor.OnFrameHeader(1, 23, HEADERS, 0x0);
+
+  EXPECT_CALL(callbacks,
+              OnBeginHeaders(IsHeaders(1, _, NGHTTP2_HCAT_RESPONSE)));
+  visitor.OnBeginHeadersForStream(1);
+
+  EXPECT_CALL(callbacks, OnHeader(_, ":status", "200", _));
+  visitor.OnHeaderForStream(1, ":status", "200");
+
+  EXPECT_CALL(callbacks, OnHeader(_, "server", "my-fake-server", _));
+  visitor.OnHeaderForStream(1, "server", "my-fake-server");
+
+  EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(1, CONTINUATION, 0x4)));
+  visitor.OnFrameHeader(1, 23, CONTINUATION, 0x4);
+
+  EXPECT_CALL(callbacks,
+              OnHeader(_, "date", "Tue, 6 Apr 2021 12:54:01 GMT", _));
+  visitor.OnHeaderForStream(1, "date", "Tue, 6 Apr 2021 12:54:01 GMT");
+
+  EXPECT_CALL(callbacks, OnHeader(_, "trailer", "x-server-status", _));
+  visitor.OnHeaderForStream(1, "trailer", "x-server-status");
+
+  EXPECT_CALL(callbacks, OnFrameRecv(IsHeaders(1, _, NGHTTP2_HCAT_RESPONSE)));
+  visitor.OnEndHeadersForStream(1);
+}
+
 TEST(ServerCallbackVisitorUnitTest, ConnectionFrames) {
   testing::StrictMock<MockNghttp2Callbacks> callbacks;
   CallbackVisitor visitor(Perspective::kServer,
diff --git a/http2/adapter/nghttp2_adapter.cc b/http2/adapter/nghttp2_adapter.cc
index 7eb57f8..b786daa 100644
--- a/http2/adapter/nghttp2_adapter.cc
+++ b/http2/adapter/nghttp2_adapter.cc
@@ -98,6 +98,7 @@
 int NgHttp2Adapter::Send() {
   const int result = nghttp2_session_send(session_->raw_ptr());
   if (result != 0) {
+    QUICHE_VLOG(1) << "nghttp2_session_send returned " << result;
     visitor_.OnConnectionError();
   }
   return result;