Add `num_bhttp_chunks` flag and remove `indeterminate_length` flag.

If `num_bhttp_chunks` is not set or is set to -1, it will match the chunked flag mode. If `num_bhttp_chunks` is set to 0, the logic will use known-length BHTTP. If it is more than 0, then it will use indeterminate-length BHTTP and attempt to split the post data (if any) by `num_bhttp_chunks`.

PiperOrigin-RevId: 897754014
diff --git a/quiche/quic/masque/masque_ohttp_client.cc b/quiche/quic/masque/masque_ohttp_client.cc
index 540c7a7..6e8d5d2 100644
--- a/quiche/quic/masque/masque_ohttp_client.cc
+++ b/quiche/quic/masque/masque_ohttp_client.cc
@@ -81,6 +81,24 @@
   return absl::OkStatus();
 }
 
+absl::StatusOr<std::vector<absl::string_view>> SplitIntoChunks(
+    absl::string_view data, int num_chunks) {
+  if (num_chunks <= 0) {
+    return absl::InvalidArgumentError("num_chunks must be greater than 0");
+  }
+  if (data.size() < num_chunks) {
+    return absl::InvalidArgumentError("data is smaller than num_chunks");
+  }
+  std::vector<absl::string_view> chunks;
+  size_t offset = 0;
+  for (size_t i = 0; i < num_chunks; ++i) {
+    size_t end = (data.size() * (i + 1)) / num_chunks;
+    chunks.push_back(data.substr(offset, end - offset));
+    offset = end;
+  }
+  return chunks;
+}
+
 }  // namespace
 
 std::string MasqueOhttpClient::Config::PerRequestConfig::method() const {
@@ -405,10 +423,11 @@
         "Cannot send OHTTP request without OHTTP client");
   }
   std::string encoded_data;
-  const bool use_indeterminate_length =
-      per_request_config.use_indeterminate_length().value_or(
-          per_request_config.use_chunked_ohttp());
-  if (use_indeterminate_length) {
+  int num_bhttp_chunks = per_request_config.num_bhttp_chunks();
+  if (num_bhttp_chunks < 0) {
+    num_bhttp_chunks = (per_request_config.use_chunked_ohttp() ? 1 : 0);
+  }
+  if (num_bhttp_chunks > 0) {
     BinaryHttpRequest::IndeterminateLengthEncoder encoder;
 
     QUICHE_ASSIGN_OR_RETURN(encoded_data,
@@ -422,26 +441,17 @@
                             encoder.EncodeHeaders(absl::MakeSpan(headers)));
     encoded_data += encoded_headers;
     if (!post_data.empty()) {
-      absl::string_view body = post_data;
-      std::vector<absl::string_view> body_chunks;
-      if (body.size() > 1) {
-        // Intentionally split the data into two chunks to test body chunking.
-        body_chunks.push_back(body.substr(0, body.size() / 2));
-        body_chunks.push_back(body.substr(body.size() / 2));
-      } else {
-        body_chunks.push_back(body);
-      }
+      QUICHE_ASSIGN_OR_RETURN(std::vector<absl::string_view> body_chunks,
+                              SplitIntoChunks(post_data, num_bhttp_chunks));
       QUICHE_ASSIGN_OR_RETURN(
           std::string encoded_body,
           encoder.EncodeBodyChunks(absl::MakeSpan(body_chunks),
                                    /*body_chunks_done=*/false));
       encoded_data += encoded_body;
     }
-    std::vector<absl::string_view> empty_body_chunks;
     QUICHE_ASSIGN_OR_RETURN(
         std::string encoded_final_chunk,
-        encoder.EncodeBodyChunks(absl::MakeSpan(empty_body_chunks),
-                                 /*body_chunks_done=*/true));
+        encoder.EncodeBodyChunks({}, /*body_chunks_done=*/true));
     encoded_data += encoded_final_chunk;
     std::vector<quiche::BinaryHttpMessage::FieldView> trailers;
     QUICHE_ASSIGN_OR_RETURN(std::string encoded_trailers,
diff --git a/quiche/quic/masque/masque_ohttp_client.h b/quiche/quic/masque/masque_ohttp_client.h
index 6f4db6a..8a5c02b 100644
--- a/quiche/quic/masque/masque_ohttp_client.h
+++ b/quiche/quic/masque/masque_ohttp_client.h
@@ -54,10 +54,7 @@
       void SetUseChunkedOhttp(bool use_chunked_ohttp) {
         use_chunked_ohttp_ = use_chunked_ohttp;
       }
-      void SetUseIndeterminateLength(
-          std::optional<bool> use_indeterminate_length) {
-        use_indeterminate_length_ = use_indeterminate_length;
-      }
+      void SetNumBhttpChunks(int num_chunks) { num_bhttp_chunks_ = num_chunks; }
       void SetExpectedGatewayError(const std::string& expected_gateway_error) {
         expected_gateway_error_ = expected_gateway_error;
       }
@@ -84,9 +81,7 @@
         return outer_headers_;
       }
       bool use_chunked_ohttp() const { return use_chunked_ohttp_; }
-      std::optional<bool> use_indeterminate_length() const {
-        return use_indeterminate_length_;
-      }
+      int num_bhttp_chunks() const { return num_bhttp_chunks_; }
       std::optional<std::string> expected_gateway_error() const {
         return expected_gateway_error_;
       }
@@ -107,7 +102,7 @@
       std::vector<std::pair<std::string, std::string>> headers_;
       std::vector<std::pair<std::string, std::string>> outer_headers_;
       bool use_chunked_ohttp_ = false;
-      std::optional<bool> use_indeterminate_length_;
+      int num_bhttp_chunks_ = -1;
       std::optional<std::string> expected_gateway_error_;
       std::optional<uint16_t> expected_gateway_status_code_;
       std::optional<uint16_t> expected_encapsulated_status_code_;
diff --git a/quiche/quic/masque/masque_ohttp_client_bin.cc b/quiche/quic/masque/masque_ohttp_client_bin.cc
index 13b9bbb..fab77ab 100644
--- a/quiche/quic/masque/masque_ohttp_client_bin.cc
+++ b/quiche/quic/masque/masque_ohttp_client_bin.cc
@@ -32,12 +32,6 @@
 DEFINE_QUICHE_COMMAND_LINE_FLAG(bool, chunked, false,
                                 "If true, use chunked OHTTP.");
 
-DEFINE_QUICHE_COMMAND_LINE_FLAG(std::optional<bool>, indeterminate_length,
-                                std::nullopt,
-                                "If set, overrides whether to use the "
-                                "indeterminate length binary HTTP encoding. If "
-                                "unset, uses the value of --chunked.");
-
 DEFINE_QUICHE_COMMAND_LINE_FLAG(int, address_family, 0,
                                 "IP address family to use. Must be 0, 4 or 6. "
                                 "Defaults to 0 which means any.");
@@ -64,6 +58,12 @@
     "--post_data or --post_data_file is set.");
 
 DEFINE_QUICHE_COMMAND_LINE_FLAG(
+    int, num_bhttp_chunks, -1,
+    "Number of indeterminate-length BHTTP chunks to split post data into. If "
+    "not set or if set to -1, it will match the chunked flag mode. If set to "
+    "0, the client will use known-length BHTTP.");
+
+DEFINE_QUICHE_COMMAND_LINE_FLAG(
     std::vector<std::string>, header, {},
     "Adds a header field to the encapsulated binary request. Separate the "
     "header name and value with a colon. Can be specified multiple times.");
@@ -122,8 +122,6 @@
       quiche::GetQuicheCommandLineFlag(FLAGS_use_mtls_for_key_fetch);
   const bool use_chunked_ohttp =
       quiche::GetQuicheCommandLineFlag(FLAGS_chunked);
-  const std::optional<bool> indeterminate_length =
-      quiche::GetQuicheCommandLineFlag(FLAGS_indeterminate_length);
   const std::string client_cert_file =
       quiche::GetQuicheCommandLineFlag(FLAGS_client_cert_file);
   const std::string client_cert_key_file =
@@ -156,6 +154,8 @@
   }
   std::optional<std::string> method =
       quiche::GetQuicheCommandLineFlag(FLAGS_method);
+  const int num_bhttp_chunks =
+      quiche::GetQuicheCommandLineFlag(FLAGS_num_bhttp_chunks);
   std::vector<std::string> headers =
       quiche::GetQuicheCommandLineFlag(FLAGS_header);
   std::vector<std::string> key_fetch_headers =
@@ -225,7 +225,7 @@
           per_request_config.AddPrivateToken(generated_private_token));
     }
     per_request_config.SetUseChunkedOhttp(use_chunked_ohttp);
-    per_request_config.SetUseIndeterminateLength(indeterminate_length);
+    per_request_config.SetNumBhttpChunks(num_bhttp_chunks);
     if (expect_gateway_error.has_value()) {
       per_request_config.SetExpectedGatewayError(*expect_gateway_error);
     }