Propagates DATA padding length to the visitor in OgHttp2Adapter and CallbackVisitor.

This fixes //gfe/gfe2/http2/e2e:end_to_end_test_http2_gfe3:
http://sponge2/90e283dc-9ab1-4495-a4f1-92cd421368d0

Re-enabling the test will have to wait for the next QUICHE update and Envoy import, though.

PiperOrigin-RevId: 424728304
diff --git a/http2/adapter/callback_visitor.cc b/http2/adapter/callback_visitor.cc
index 4b2f078..1dd082e 100644
--- a/http2/adapter/callback_visitor.cc
+++ b/http2/adapter/callback_visitor.cc
@@ -201,12 +201,23 @@
   return true;
 }
 
+bool CallbackVisitor::OnDataPaddingLength(Http2StreamId /*stream_id*/,
+                                          size_t padding_length) {
+  QUICHE_DCHECK_GE(remaining_data_, padding_length);
+  current_frame_.data.padlen = padding_length;
+  remaining_data_ -= padding_length;
+  if (remaining_data_ == 0 && callbacks_->on_frame_recv_callback != nullptr) {
+    const int result = callbacks_->on_frame_recv_callback(
+        nullptr, &current_frame_, user_data_);
+    return result == 0;
+  }
+  return true;
+}
+
 bool CallbackVisitor::OnBeginDataForStream(Http2StreamId /*stream_id*/,
                                            size_t payload_length) {
-  // TODO(b/181586191): Interpret padding, subtract padding from
-  // |remaining_data_|.
   remaining_data_ = payload_length;
-  if (remaining_data_ == 0 && callbacks_->on_frame_recv_callback) {
+  if (remaining_data_ == 0 && callbacks_->on_frame_recv_callback != nullptr) {
     const int result = callbacks_->on_frame_recv_callback(
         nullptr, &current_frame_, user_data_);
     return result == 0;
diff --git a/http2/adapter/callback_visitor.h b/http2/adapter/callback_visitor.h
index 6d1915c..fa0c770 100644
--- a/http2/adapter/callback_visitor.h
+++ b/http2/adapter/callback_visitor.h
@@ -35,6 +35,8 @@
                                    absl::string_view name,
                                    absl::string_view value) override;
   bool OnEndHeadersForStream(Http2StreamId stream_id) override;
+  bool OnDataPaddingLength(Http2StreamId stream_id,
+                           size_t padding_length) override;
   bool OnBeginDataForStream(Http2StreamId stream_id,
                             size_t payload_length) override;
   bool OnDataForStream(Http2StreamId stream_id,
diff --git a/http2/adapter/callback_visitor_test.cc b/http2/adapter/callback_visitor_test.cc
index f7b982e..9ed28f0 100644
--- a/http2/adapter/callback_visitor_test.cc
+++ b/http2/adapter/callback_visitor_test.cc
@@ -291,6 +291,72 @@
   visitor.OnCloseStream(3, Http2ErrorCode::INTERNAL_ERROR);
 }
 
+TEST(ServerCallbackVisitorUnitTest, DataWithPadding) {
+  testing::StrictMock<MockNghttp2Callbacks> callbacks;
+  CallbackVisitor visitor(Perspective::kServer,
+                          *MockNghttp2Callbacks::GetCallbacks(), &callbacks);
+
+  const size_t kPaddingLength = 39;
+  const uint8_t kFlags = NGHTTP2_FLAG_PADDED | NGHTTP2_FLAG_END_STREAM;
+
+  testing::InSequence seq;
+
+  // DATA on stream 1
+  EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(1, DATA, kFlags)));
+  EXPECT_TRUE(visitor.OnFrameHeader(1, 25 + kPaddingLength, DATA, kFlags));
+
+  EXPECT_TRUE(visitor.OnBeginDataForStream(1, 25 + kPaddingLength));
+
+  // Padding before data.
+  EXPECT_TRUE(visitor.OnDataPaddingLength(1, kPaddingLength));
+
+  EXPECT_CALL(callbacks,
+              OnDataChunkRecv(kFlags, 1, "This is the request body."));
+  EXPECT_CALL(callbacks, OnFrameRecv(IsData(1, _, kFlags, kPaddingLength)));
+  EXPECT_TRUE(visitor.OnDataForStream(1, "This is the request body."));
+  visitor.OnEndStream(1);
+
+  EXPECT_CALL(callbacks, OnStreamClose(1, NGHTTP2_NO_ERROR));
+  visitor.OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR);
+
+  // DATA on stream 3
+  EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(3, DATA, kFlags)));
+  EXPECT_TRUE(visitor.OnFrameHeader(3, 25 + kPaddingLength, DATA, kFlags));
+
+  EXPECT_TRUE(visitor.OnBeginDataForStream(3, 25 + kPaddingLength));
+
+  // Data before padding.
+  EXPECT_CALL(callbacks,
+              OnDataChunkRecv(kFlags, 3, "This is the request body."));
+  EXPECT_TRUE(visitor.OnDataForStream(3, "This is the request body."));
+
+  EXPECT_CALL(callbacks, OnFrameRecv(IsData(3, _, kFlags, kPaddingLength)));
+  EXPECT_TRUE(visitor.OnDataPaddingLength(3, kPaddingLength));
+  visitor.OnEndStream(3);
+
+  EXPECT_CALL(callbacks, OnStreamClose(3, NGHTTP2_NO_ERROR));
+  visitor.OnCloseStream(3, Http2ErrorCode::HTTP2_NO_ERROR);
+
+  // DATA on stream 5
+  EXPECT_CALL(callbacks, OnBeginFrame(HasFrameHeader(5, DATA, kFlags)));
+  EXPECT_TRUE(visitor.OnFrameHeader(5, 25 + kPaddingLength, DATA, kFlags));
+
+  EXPECT_TRUE(visitor.OnBeginDataForStream(5, 25 + kPaddingLength));
+
+  // Error during padding.
+  EXPECT_CALL(callbacks,
+              OnDataChunkRecv(kFlags, 5, "This is the request body."));
+  EXPECT_TRUE(visitor.OnDataForStream(5, "This is the request body."));
+
+  EXPECT_CALL(callbacks, OnFrameRecv(IsData(5, _, kFlags, kPaddingLength)))
+      .WillOnce(testing::Return(NGHTTP2_ERR_CALLBACK_FAILURE));
+  EXPECT_FALSE(visitor.OnDataPaddingLength(5, kPaddingLength));
+  visitor.OnEndStream(3);
+
+  EXPECT_CALL(callbacks, OnStreamClose(5, NGHTTP2_NO_ERROR));
+  visitor.OnCloseStream(5, Http2ErrorCode::HTTP2_NO_ERROR);
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace adapter
diff --git a/http2/adapter/http2_visitor_interface.h b/http2/adapter/http2_visitor_interface.h
index 0e8619f..574bac4 100644
--- a/http2/adapter/http2_visitor_interface.h
+++ b/http2/adapter/http2_visitor_interface.h
@@ -144,6 +144,12 @@
   virtual bool OnBeginDataForStream(Http2StreamId stream_id,
                                     size_t payload_length) = 0;
 
+  // Called when the optional padding length field is parsed as part of a DATA
+  // frame payload. `padding_length` represents the total amount of padding for
+  // this frame, including the length byte itself.
+  virtual bool OnDataPaddingLength(Http2StreamId stream_id,
+                                   size_t padding_length) = 0;
+
   // Called when the connection receives some |data| (as part of a DATA frame
   // payload) for a stream.
   virtual bool OnDataForStream(Http2StreamId stream_id,
diff --git a/http2/adapter/mock_http2_visitor.h b/http2/adapter/mock_http2_visitor.h
index 7454d2f..96f0978 100644
--- a/http2/adapter/mock_http2_visitor.h
+++ b/http2/adapter/mock_http2_visitor.h
@@ -20,6 +20,7 @@
         .WillByDefault(testing::Return(true));
     ON_CALL(*this, OnHeaderForStream).WillByDefault(testing::Return(HEADER_OK));
     ON_CALL(*this, OnEndHeadersForStream).WillByDefault(testing::Return(true));
+    ON_CALL(*this, OnDataPaddingLength).WillByDefault(testing::Return(true));
     ON_CALL(*this, OnBeginDataForStream).WillByDefault(testing::Return(true));
     ON_CALL(*this, OnDataForStream).WillByDefault(testing::Return(true));
     ON_CALL(*this, OnGoAway).WillByDefault(testing::Return(true));
@@ -50,6 +51,9 @@
   MOCK_METHOD(bool, OnEndHeadersForStream, (Http2StreamId stream_id),
               (override));
 
+  MOCK_METHOD(bool, OnDataPaddingLength,
+              (Http2StreamId strema_id, size_t padding_length), (override));
+
   MOCK_METHOD(bool, OnBeginDataForStream,
               (Http2StreamId stream_id, size_t payload_length), (override));
 
diff --git a/http2/adapter/nghttp2_adapter_test.cc b/http2/adapter/nghttp2_adapter_test.cc
index a2fd68f..0b570c3 100644
--- a/http2/adapter/nghttp2_adapter_test.cc
+++ b/http2/adapter/nghttp2_adapter_test.cc
@@ -2547,6 +2547,63 @@
                                             spdy::SpdyFrameType::PING}));
 }
 
+TEST(NgHttp2AdapterTest, ServerHandlesDataWithPadding) {
+  DataSavingVisitor visitor;
+  auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor);
+
+  const std::string frames = TestFrameSequence()
+                                 .ClientPreface()
+                                 .Headers(1,
+                                          {{":method", "POST"},
+                                           {":scheme", "https"},
+                                           {":authority", "example.com"},
+                                           {":path", "/this/is/request/one"}},
+                                          /*fin=*/false)
+                                 .Data(1, "This is the request body.",
+                                       /*fin=*/true, /*padding_length=*/39)
+                                 .Headers(3,
+                                          {{":method", "GET"},
+                                           {":scheme", "http"},
+                                           {":authority", "example.com"},
+                                           {":path", "/this/is/request/two"}},
+                                          /*fin=*/true)
+                                 .Serialize();
+  testing::InSequence s;
+
+  // Client preface (empty SETTINGS)
+  EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+  EXPECT_CALL(visitor, OnSettingsStart());
+  EXPECT_CALL(visitor, OnSettingsEnd());
+
+  EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4));
+  EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+  EXPECT_CALL(visitor, OnHeaderForStream(1, _, _)).Times(4);
+  EXPECT_CALL(visitor, OnEndHeadersForStream(1));
+  EXPECT_CALL(visitor, OnFrameHeader(1, 25 + 39, DATA, 0x9));
+  EXPECT_CALL(visitor, OnBeginDataForStream(1, 25 + 39));
+  EXPECT_CALL(visitor, OnDataForStream(1, "This is the request body."));
+  // Note: nghttp2 passes padding information after the actual data.
+  EXPECT_CALL(visitor, OnDataPaddingLength(1, 39));
+  EXPECT_CALL(visitor, OnEndStream(1));
+  EXPECT_CALL(visitor, OnFrameHeader(3, _, HEADERS, 5));
+  EXPECT_CALL(visitor, OnBeginHeadersForStream(3));
+  EXPECT_CALL(visitor, OnHeaderForStream(3, _, _)).Times(4);
+  EXPECT_CALL(visitor, OnEndHeadersForStream(3));
+  EXPECT_CALL(visitor, OnEndStream(3));
+
+  const int64_t result = adapter->ProcessBytes(frames);
+  EXPECT_EQ(frames.size(), result);
+
+  EXPECT_TRUE(adapter->want_write());
+
+  EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
+  EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
+
+  int send_result = adapter->Send();
+  EXPECT_EQ(0, send_result);
+  EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS}));
+}
+
 // Tests the case where the response body is in the progress of being sent while
 // trailers are queued.
 TEST(NgHttp2AdapterTest, ServerSubmitsTrailersWhileDataDeferred) {
diff --git a/http2/adapter/nghttp2_callbacks.cc b/http2/adapter/nghttp2_callbacks.cc
index d96214d..fee8ffb 100644
--- a/http2/adapter/nghttp2_callbacks.cc
+++ b/http2/adapter/nghttp2_callbacks.cc
@@ -63,6 +63,9 @@
     // callbacks. This callback handles the point at which the entire logical
     // frame has been received and processed.
     case NGHTTP2_DATA:
+      if ((frame->hd.flags & NGHTTP2_FLAG_PADDED) != 0) {
+        visitor->OnDataPaddingLength(stream_id, frame->data.padlen);
+      }
       if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
         visitor->OnEndStream(stream_id);
       }
diff --git a/http2/adapter/nghttp2_test_utils.cc b/http2/adapter/nghttp2_test_utils.cc
index d9a04f2..6e4b5b2 100644
--- a/http2/adapter/nghttp2_test_utils.cc
+++ b/http2/adapter/nghttp2_test_utils.cc
@@ -101,8 +101,12 @@
  public:
   DataMatcher(const testing::Matcher<uint32_t> stream_id,
               const testing::Matcher<size_t> length,
-              const testing::Matcher<int> flags)
-      : stream_id_(stream_id), length_(length), flags_(flags) {}
+              const testing::Matcher<int> flags,
+              const testing::Matcher<size_t> padding)
+      : stream_id_(stream_id),
+        length_(length),
+        flags_(flags),
+        padding_(padding) {}
 
   bool MatchAndExplain(const nghttp2_frame* frame,
                        testing::MatchResultListener* listener) const override {
@@ -121,6 +125,9 @@
     if (!flags_.MatchAndExplain(frame->hd.flags, listener)) {
       matched = false;
     }
+    if (!padding_.MatchAndExplain(frame->data.padlen, listener)) {
+      matched = false;
+    }
     return matched;
   }
 
@@ -142,6 +149,7 @@
   const testing::Matcher<uint32_t> stream_id_;
   const testing::Matcher<size_t> length_;
   const testing::Matcher<int> flags_;
+  const testing::Matcher<size_t> padding_;
 };
 
 class HeadersMatcher : public testing::MatcherInterface<const nghttp2_frame*> {
@@ -405,8 +413,9 @@
 
 testing::Matcher<const nghttp2_frame*> IsData(
     const testing::Matcher<uint32_t> stream_id,
-    const testing::Matcher<size_t> length, const testing::Matcher<int> flags) {
-  return MakeMatcher(new DataMatcher(stream_id, length, flags));
+    const testing::Matcher<size_t> length, const testing::Matcher<int> flags,
+    const testing::Matcher<size_t> padding) {
+  return MakeMatcher(new DataMatcher(stream_id, length, flags, padding));
 }
 
 testing::Matcher<const nghttp2_frame*> IsHeaders(
diff --git a/http2/adapter/nghttp2_test_utils.h b/http2/adapter/nghttp2_test_utils.h
index 92632dc..24493d7 100644
--- a/http2/adapter/nghttp2_test_utils.h
+++ b/http2/adapter/nghttp2_test_utils.h
@@ -66,7 +66,8 @@
 
 testing::Matcher<const nghttp2_frame*> IsData(
     const testing::Matcher<uint32_t> stream_id,
-    const testing::Matcher<size_t> length, const testing::Matcher<int> flags);
+    const testing::Matcher<size_t> length, const testing::Matcher<int> flags,
+    const testing::Matcher<size_t> padding = testing::_);
 
 testing::Matcher<const nghttp2_frame*> IsHeaders(
     const testing::Matcher<uint32_t> stream_id,
diff --git a/http2/adapter/oghttp2_adapter_test.cc b/http2/adapter/oghttp2_adapter_test.cc
index 7917bf2..474a86c 100644
--- a/http2/adapter/oghttp2_adapter_test.cc
+++ b/http2/adapter/oghttp2_adapter_test.cc
@@ -3351,6 +3351,67 @@
   EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS}));
 }
 
+TEST(OgHttp2AdapterServerTest, ServerHandlesDataWithPadding) {
+  DataSavingVisitor visitor;
+  OgHttp2Adapter::Options options{.perspective = Perspective::kServer};
+  auto adapter = OgHttp2Adapter::Create(visitor, options);
+
+  const std::string frames = TestFrameSequence()
+                                 .ClientPreface()
+                                 .Headers(1,
+                                          {{":method", "POST"},
+                                           {":scheme", "https"},
+                                           {":authority", "example.com"},
+                                           {":path", "/this/is/request/one"}},
+                                          /*fin=*/false)
+                                 .Data(1, "This is the request body.",
+                                       /*fin=*/true, /*padding_length=*/39)
+                                 .Headers(3,
+                                          {{":method", "GET"},
+                                           {":scheme", "http"},
+                                           {":authority", "example.com"},
+                                           {":path", "/this/is/request/two"}},
+                                          /*fin=*/true)
+                                 .Serialize();
+  testing::InSequence s;
+
+  // Client preface (empty SETTINGS)
+  EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+  EXPECT_CALL(visitor, OnSettingsStart());
+  EXPECT_CALL(visitor, OnSettingsEnd());
+
+  EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4));
+  EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+  EXPECT_CALL(visitor, OnHeaderForStream(1, _, _)).Times(4);
+  EXPECT_CALL(visitor, OnEndHeadersForStream(1));
+  EXPECT_CALL(visitor, OnFrameHeader(1, 25 + 39, DATA, 0x9));
+  EXPECT_CALL(visitor, OnBeginDataForStream(1, 25 + 39));
+  // Note: oghttp2 passes padding information before the actual data.
+  EXPECT_CALL(visitor, OnDataPaddingLength(1, 39));
+  EXPECT_CALL(visitor, OnDataForStream(1, "This is the request body."));
+  EXPECT_CALL(visitor, OnEndStream(1));
+  EXPECT_CALL(visitor, OnFrameHeader(3, _, HEADERS, 5));
+  EXPECT_CALL(visitor, OnBeginHeadersForStream(3));
+  EXPECT_CALL(visitor, OnHeaderForStream(3, _, _)).Times(4);
+  EXPECT_CALL(visitor, OnEndHeadersForStream(3));
+  EXPECT_CALL(visitor, OnEndStream(3));
+
+  const int64_t result = adapter->ProcessBytes(frames);
+  EXPECT_EQ(static_cast<int64_t>(frames.size()), result);
+
+  EXPECT_TRUE(adapter->want_write());
+
+  EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
+  EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
+  EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
+  EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
+
+  int send_result = adapter->Send();
+  EXPECT_EQ(0, send_result);
+  EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS,
+                                            spdy::SpdyFrameType::SETTINGS}));
+}
+
 // Tests the case where the response body is in the progress of being sent while
 // trailers are queued.
 TEST(OgHttp2AdapterServerTest, ServerSubmitsTrailersWhileDataDeferred) {
diff --git a/http2/adapter/oghttp2_session.cc b/http2/adapter/oghttp2_session.cc
index 5aea9f7..a0c2b6f 100644
--- a/http2/adapter/oghttp2_session.cc
+++ b/http2/adapter/oghttp2_session.cc
@@ -1106,8 +1106,12 @@
 
 void OgHttp2Session::OnStreamPadLength(spdy::SpdyStreamId stream_id,
                                        size_t value) {
+  bool result = visitor_.OnDataPaddingLength(stream_id, 1 + value);
+  if (!result) {
+    fatal_visitor_callback_failure_ = true;
+    decoder_.StopProcessing();
+  }
   MarkDataBuffered(stream_id, 1 + value);
-  // TODO(181586191): Pass padding to the visitor?
 }
 
 void OgHttp2Session::OnStreamPadding(spdy::SpdyStreamId /*stream_id*/, size_t
diff --git a/http2/adapter/recording_http2_visitor.cc b/http2/adapter/recording_http2_visitor.cc
index 74db309..b9cbcfb 100644
--- a/http2/adapter/recording_http2_visitor.cc
+++ b/http2/adapter/recording_http2_visitor.cc
@@ -60,6 +60,13 @@
   return true;
 }
 
+bool RecordingHttp2Visitor::OnDataPaddingLength(Http2StreamId stream_id,
+                                                size_t padding_length) {
+  events_.push_back(
+      absl::StrFormat("OnDataPaddingLength %d %d", stream_id, padding_length));
+  return true;
+}
+
 bool RecordingHttp2Visitor::OnBeginDataForStream(Http2StreamId stream_id,
                                                  size_t payload_length) {
   events_.push_back(
diff --git a/http2/adapter/recording_http2_visitor.h b/http2/adapter/recording_http2_visitor.h
index 6008725..3ded946 100644
--- a/http2/adapter/recording_http2_visitor.h
+++ b/http2/adapter/recording_http2_visitor.h
@@ -33,6 +33,8 @@
                                    absl::string_view name,
                                    absl::string_view value) override;
   bool OnEndHeadersForStream(Http2StreamId stream_id) override;
+  bool OnDataPaddingLength(Http2StreamId stream_id,
+                           size_t padding_length) override;
   bool OnBeginDataForStream(Http2StreamId stream_id,
                             size_t payload_length) override;
   bool OnDataForStream(Http2StreamId stream_id,