Fix a bug where MoqtParser would spuriously return an "unexpected FIN" error.

The bug is caused by the fact that the code assumes that the size of the peeked chunk is the total size of the data remaining in the receive buffer; this does not work if there is more than one chunk remaining.

PiperOrigin-RevId: 819474521
diff --git a/quiche/quic/moqt/moqt_parser.cc b/quiche/quic/moqt/moqt_parser.cc
index 6a463a5..3c1fc9f 100644
--- a/quiche/quic/moqt/moqt_parser.cc
+++ b/quiche/quic/moqt/moqt_parser.cc
@@ -1601,7 +1601,7 @@
         bool done = payload_length_remaining_ == 0;
         if (next_input_ == kData) {
           no_more_data_ = peek_result.all_data_received &&
-                          chunk_size == peek_result.peeked_data.size();
+                          chunk_size == stream_.ReadableBytes();
           if (!done && no_more_data_) {
             ParseError("FIN received at an unexpected point in the stream");
             return;
diff --git a/quiche/quic/moqt/moqt_parser_test.cc b/quiche/quic/moqt/moqt_parser_test.cc
index a9d0687..c16c1a8 100644
--- a/quiche/quic/moqt/moqt_parser_test.cc
+++ b/quiche/quic/moqt/moqt_parser_test.cc
@@ -225,6 +225,23 @@
   }
 }
 
+// In OneByteAtATime, the message is received one byte at a time, and
+// immediately processed; here, it is received all at once, but the stream
+// receive buffer is represented as a sequence of one-byte chunks.
+TEST_P(MoqtParserTest, OneByteAtATimePeek) {
+  control_stream_.set_peek_one_byte_at_a_time(true);
+  data_stream_.set_peek_one_byte_at_a_time(true);
+  std::unique_ptr<TestMessageBase> message = MakeMessage();
+  message->MakeObjectEndOfStream();
+  ProcessData(message->PacketSample(), true);
+  ASSERT_EQ(visitor_.messages_received_, 1);
+  EXPECT_TRUE(message->EqualFieldValues(*visitor_.last_message_));
+  EXPECT_TRUE(visitor_.end_of_message_);
+  if (IsDataStream()) {
+    EXPECT_EQ(visitor_.object_payload(), "foo");
+  }
+}
+
 TEST_P(MoqtParserTest, OneByteAtATimeLongerVarints) {
   std::unique_ptr<TestMessageBase> message = MakeMessage();
   message->ExpandVarints();
diff --git a/quiche/web_transport/test_tools/in_memory_stream.cc b/quiche/web_transport/test_tools/in_memory_stream.cc
index 724f4f8..3b28a60 100644
--- a/quiche/web_transport/test_tools/in_memory_stream.cc
+++ b/quiche/web_transport/test_tools/in_memory_stream.cc
@@ -43,6 +43,9 @@
     return PeekResult{"", fin_received_, fin_received_};
   }
   absl::string_view next_chunk = *buffer_.Chunks().begin();
+  if (peek_one_byte_at_a_time_) {
+    return PeekResult{next_chunk.substr(0, 1), false, fin_received_};
+  }
   return PeekResult{next_chunk, false, fin_received_};
 }
 
diff --git a/quiche/web_transport/test_tools/in_memory_stream.h b/quiche/web_transport/test_tools/in_memory_stream.h
index 078ca94..50fa7a6 100644
--- a/quiche/web_transport/test_tools/in_memory_stream.h
+++ b/quiche/web_transport/test_tools/in_memory_stream.h
@@ -70,6 +70,12 @@
   // and executing the visitor callback.
   void Receive(absl::string_view data, bool fin = false);
 
+  // If set to true, PeekNextReadableRegion() will return a single one-byte
+  // readable region at a time.
+  void set_peek_one_byte_at_a_time(bool peek_one_byte_at_a_time) {
+    peek_one_byte_at_a_time_ = peek_one_byte_at_a_time;
+  }
+
  private:
   void Terminate();
 
@@ -79,6 +85,7 @@
   absl::Cord buffer_;
   bool fin_received_ = false;
   bool abruptly_terminated_ = false;
+  bool peek_one_byte_at_a_time_ = false;
 };
 
 }  // namespace webtransport::test