Refactor BHTTP content terminated sections decoding.

Split the `DecodeContentTerminatedSection` member method into two free functions: `DecodeContentTerminatedFieldSection` for headers and trailers, and `DecodeContentTerminatedBodyChunkSection` for body chunks.

These functions are only used for the request decoder, but will be re-used for handling the informational responses, final headers, body, and trailers when implementing the response decoder.

PiperOrigin-RevId: 854289053
diff --git a/quiche/BUILD.bazel b/quiche/BUILD.bazel
index c71830d..ec08d5e 100644
--- a/quiche/BUILD.bazel
+++ b/quiche/BUILD.bazel
@@ -65,6 +65,7 @@
         "@com_google_absl//absl/base:core_headers",
         "@com_google_absl//absl/base:nullability",
         "@com_google_absl//absl/container:flat_hash_map",
+        "@com_google_absl//absl/functional:bind_front",
         "@com_google_absl//absl/status",
         "@com_google_absl//absl/status:statusor",
         "@com_google_absl//absl/strings",
diff --git a/quiche/binary_http/binary_http_message.cc b/quiche/binary_http/binary_http_message.cc
index b91b3b1..e10e5a9 100644
--- a/quiche/binary_http/binary_http_message.cc
+++ b/quiche/binary_http/binary_http_message.cc
@@ -1,10 +1,8 @@
 #include "quiche/binary_http/binary_http_message.h"
 
 #include <algorithm>
+#include <cstddef>
 #include <cstdint>
-#include <functional>
-#include <iterator>
-#include <memory>
 #include <optional>
 #include <ostream>
 #include <string>
@@ -12,7 +10,7 @@
 #include <vector>
 
 #include "absl/base/attributes.h"
-#include "absl/container/flat_hash_map.h"
+#include "absl/functional/bind_front.h"
 #include "absl/status/status.h"
 #include "absl/status/statusor.h"
 #include "absl/strings/ascii.h"
@@ -21,9 +19,11 @@
 #include "absl/strings/str_join.h"
 #include "absl/strings/string_view.h"
 #include "absl/types/span.h"
+#include "quiche/common/platform/api/quiche_logging.h"
 #include "quiche/common/quiche_callbacks.h"
 #include "quiche/common/quiche_data_reader.h"
 #include "quiche/common/quiche_data_writer.h"
+#include "quiche/common/quiche_status_utils.h"
 
 namespace quiche {
 namespace {
@@ -239,6 +239,95 @@
   return data;
 }
 
+// Initializes the checkpoint based on the provided data and any buffered data.
+// If the buffer has data, the new data is appended to the buffer.
+absl::string_view InitializeChunkedDecodingCheckpoint(absl::string_view data,
+                                                      std::string& buffer) {
+  absl::string_view checkpoint = data;
+  // Prepend buffered data if present.
+  if (!buffer.empty()) {
+    absl::StrAppend(&buffer, data);
+    checkpoint = buffer;
+  }
+  return checkpoint;
+}
+
+// Updates the checkpoint based on the current position of the reader.
+void UpdateChunkedDecodingCheckpoint(const QuicheDataReader& reader,
+                                     absl::string_view& checkpoint) {
+  checkpoint = reader.PeekRemainingPayload();
+}
+
+// Buffers the checkpoint.
+void BufferChunkedDecodingCheckpoint(absl::string_view checkpoint,
+                                     std::string& buffer) {
+  if (buffer != checkpoint) {
+    buffer.assign(checkpoint);
+  }
+}
+
+// Decodes the fields in the reader. Calls the field_handler for each field
+// until the reader is done or the content terminator is encountered.
+absl::Status DecodeContentTerminatedFieldSection(
+    QuicheDataReader& reader, absl::string_view& checkpoint,
+    quiche::UnretainedCallback<absl::Status(absl::string_view,
+                                            absl::string_view)>
+        field_handler) {
+  uint64_t length_or_content_terminator = kContentTerminator;
+  do {
+    if (!reader.ReadVarInt62(&length_or_content_terminator)) {
+      return absl::OutOfRangeError("Not enough data to read section.");
+    }
+    if (length_or_content_terminator != kContentTerminator) {
+      const absl::StatusOr<BinaryHttpMessage::FieldView> field =
+          DecodeField(reader, length_or_content_terminator);
+      if (!field.ok()) {
+        return field.status();
+      }
+      const absl::Status section_status =
+          field_handler(field->name, field->value);
+      if (!section_status.ok()) {
+        return absl::InternalError(absl::StrCat("Failed to handle header: ",
+                                                section_status.message()));
+      }
+    }
+    // Either a field was successfully decoded or a content terminator was
+    // encountered, update the checkpoint.
+    UpdateChunkedDecodingCheckpoint(reader, checkpoint);
+  } while (length_or_content_terminator != kContentTerminator);
+  return absl::OkStatus();
+}
+
+// Decodes the body chunks in the reader. Calls the body_chunk_handler for
+// each body chunk until the reader is done or the content terminator is
+// encountered.
+absl::Status DecodeContentTerminatedBodyChunkSection(
+    QuicheDataReader& reader, absl::string_view& checkpoint,
+    quiche::UnretainedCallback<absl::Status(absl::string_view)>
+        body_chunk_handler) {
+  uint64_t length_or_content_terminator = kContentTerminator;
+  do {
+    if (!reader.ReadVarInt62(&length_or_content_terminator)) {
+      return absl::OutOfRangeError("Not enough data to read section.");
+    }
+    if (length_or_content_terminator != kContentTerminator) {
+      absl::string_view body_chunk;
+      if (!reader.ReadStringPiece(&body_chunk, length_or_content_terminator)) {
+        return absl::OutOfRangeError("Failed to read body chunk.");
+      }
+      const absl::Status section_status = body_chunk_handler(body_chunk);
+      if (!section_status.ok()) {
+        return absl::InternalError(absl::StrCat("Failed to handle body chunk: ",
+                                                section_status.message()));
+      }
+    }
+    // Either a body chunk was successfully decoded or a content terminator was
+    // encountered, update the checkpoint.
+    UpdateChunkedDecodingCheckpoint(reader, checkpoint);
+  } while (length_or_content_terminator != kContentTerminator);
+  return absl::OkStatus();
+}
+
 }  // namespace
 
 absl::StatusOr<uint64_t> GetFieldSectionLength(
@@ -279,33 +368,6 @@
   return absl::OkStatus();
 }
 
-// Initializes the checkpoint based on the provided data and any buffered data.
-// If the buffer has data, the new data is appended to the buffer.
-absl::string_view InitializeChunkedDecodingCheckpoint(absl::string_view data,
-                                                      std::string& buffer) {
-  absl::string_view checkpoint = data;
-  // Prepend buffered data if present.
-  if (!buffer.empty()) {
-    absl::StrAppend(&buffer, data);
-    checkpoint = buffer;
-  }
-  return checkpoint;
-}
-
-// Updates the checkpoint based on the current position of the reader.
-void UpdateChunkedDecodingCheckpoint(const QuicheDataReader& reader,
-                                     absl::string_view& checkpoint) {
-  checkpoint = reader.PeekRemainingPayload();
-}
-
-// Buffers the checkpoint.
-void BufferChunkedDecodingCheckpoint(absl::string_view checkpoint,
-                                     std::string& buffer) {
-  if (buffer != checkpoint) {
-    buffer.assign(checkpoint);
-  }
-}
-
 void BinaryHttpMessage::Fields::AddField(BinaryHttpMessage::Field field) {
   fields_.push_back(std::move(field));
 }
@@ -527,70 +589,6 @@
       absl::StrCat("Unsupported framing type ", framing));
 }
 
-absl::Status
-BinaryHttpRequest::IndeterminateLengthDecoder::DecodeContentTerminatedSection(
-    QuicheDataReader& reader, absl::string_view& checkpoint) {
-  uint64_t length_or_content_terminator = kContentTerminator;
-  do {
-    if (!reader.ReadVarInt62(&length_or_content_terminator)) {
-      return absl::OutOfRangeError("Not enough data to read section.");
-    }
-    if (length_or_content_terminator != kContentTerminator) {
-      switch (current_section_) {
-        case IndeterminateLengthMessageSection::kHeader: {
-          const absl::StatusOr<FieldView> field =
-              DecodeField(reader, length_or_content_terminator);
-          if (!field.ok()) {
-            return field.status();
-          }
-          const absl::Status section_status =
-              message_section_handler_.OnHeader(field->name, field->value);
-          if (!section_status.ok()) {
-            return absl::InternalError(absl::StrCat("Failed to handle header: ",
-                                                    section_status.message()));
-          }
-          break;
-        }
-        case IndeterminateLengthMessageSection::kBody: {
-          absl::string_view body_chunk;
-          if (!reader.ReadStringPiece(&body_chunk,
-                                      length_or_content_terminator)) {
-            return absl::OutOfRangeError("Failed to read body chunk.");
-          }
-          const absl::Status section_status =
-              message_section_handler_.OnBodyChunk(body_chunk);
-          if (!section_status.ok()) {
-            return absl::InternalError(absl::StrCat(
-                "Failed to handle body chunk: ", section_status.message()));
-          }
-          break;
-        }
-        case IndeterminateLengthMessageSection::kTrailer: {
-          const absl::StatusOr<FieldView> field =
-              DecodeField(reader, length_or_content_terminator);
-          if (!field.ok()) {
-            return field.status();
-          }
-          const absl::Status section_status =
-              message_section_handler_.OnTrailer(field->name, field->value);
-          if (!section_status.ok()) {
-            return absl::InternalError(absl::StrCat(
-                "Failed to handle trailer: ", section_status.message()));
-          }
-          break;
-        }
-        default:
-          return absl::InternalError(
-              "Unexpected section in DecodeContentTerminatedSection.");
-      }
-    }
-    // Either a section was successfully decoded or a content terminator was
-    // encountered, update the checkpoint.
-    UpdateChunkedDecodingCheckpoint(reader, checkpoint);
-  } while (length_or_content_terminator != kContentTerminator);
-  return absl::OkStatus();
-}
-
 // Returns Ok status only if the decoding processes the Padding section
 // successfully or if the message is truncated properly. All other points of
 // return are errors.
@@ -629,11 +627,10 @@
     }
       ABSL_FALLTHROUGH_INTENDED;
     case IndeterminateLengthMessageSection::kHeader: {
-      const absl::Status status =
-          DecodeContentTerminatedSection(reader, checkpoint);
-      if (!status.ok()) {
-        return status;
-      }
+      QUICHE_RETURN_IF_ERROR(DecodeContentTerminatedFieldSection(
+          reader, checkpoint,
+          absl::bind_front(&MessageSectionHandler::OnHeader,
+                           &message_section_handler_)));
       const absl::Status section_status =
           message_section_handler_.OnHeadersDone();
       if (!section_status.ok()) {
@@ -662,12 +659,12 @@
         return absl::OkStatus();
       }
 
-      absl::Status section_status =
-          DecodeContentTerminatedSection(reader, checkpoint);
-      if (!section_status.ok()) {
-        return section_status;
-      }
-      section_status = message_section_handler_.OnBodyChunksDone();
+      QUICHE_RETURN_IF_ERROR(DecodeContentTerminatedBodyChunkSection(
+          reader, checkpoint,
+          absl::bind_front(&MessageSectionHandler::OnBodyChunk,
+                           &message_section_handler_)));
+      const absl::Status section_status =
+          message_section_handler_.OnBodyChunksDone();
       if (!section_status.ok()) {
         return absl::InternalError(absl::StrCat(
             "Failed to handle body chunks done: ", section_status.message()));
@@ -689,12 +686,12 @@
         return absl::OkStatus();
       }
 
-      absl::Status section_status =
-          DecodeContentTerminatedSection(reader, checkpoint);
-      if (!section_status.ok()) {
-        return section_status;
-      }
-      section_status = message_section_handler_.OnTrailersDone();
+      QUICHE_RETURN_IF_ERROR(DecodeContentTerminatedFieldSection(
+          reader, checkpoint,
+          absl::bind_front(&MessageSectionHandler::OnTrailer,
+                           &message_section_handler_)));
+      const absl::Status section_status =
+          message_section_handler_.OnTrailersDone();
       if (!section_status.ok()) {
         return absl::InternalError(absl::StrCat(
             "Failed to handle trailers done: ", section_status.message()));
diff --git a/quiche/binary_http/binary_http_message.h b/quiche/binary_http/binary_http_message.h
index 921249c..d18bbfa 100644
--- a/quiche/binary_http/binary_http_message.h
+++ b/quiche/binary_http/binary_http_message.h
@@ -274,10 +274,6 @@
   // section. When a section is fully decoded, the checkpoint is updated.
   absl::Status DecodeCheckpointData(bool end_stream,
                                     absl::string_view& checkpoint);
-  // Decodes a section 0 or more times until a content terminator is
-  // encountered.
-  absl::Status DecodeContentTerminatedSection(QuicheDataReader& reader,
-                                              absl::string_view& checkpoint);
 
   // The handler to invoke when a section is decoded successfully. The
   // handler can return an error if the decoded data cannot be processed