Feed header block in fragments in QPACK roundtrip fuzzer.

gfe-relnote: n/a, test-only change.
PiperOrigin-RevId: 264498801
Change-Id: I3082cb1e038f697a64bf99f35dde8077f84eea31
diff --git a/quic/core/qpack/fuzzer/qpack_round_trip_fuzzer.cc b/quic/core/qpack/fuzzer/qpack_round_trip_fuzzer.cc
index 164b05d..53cd279 100644
--- a/quic/core/qpack/fuzzer/qpack_round_trip_fuzzer.cc
+++ b/quic/core/qpack/fuzzer/qpack_round_trip_fuzzer.cc
@@ -107,7 +107,7 @@
       it = header_blocks_.insert(it, {stream_id, {}});
     }
     CHECK_EQ(stream_id, it->first);
-    it->second.push(std::move(encoded_header_block));
+    it->second.push(HeaderBlock(std::move(encoded_header_block)));
   }
 
   // Release some (possibly none) header block data.
@@ -129,13 +129,27 @@
     }
 
     auto& header_block_queue = it->second;
-    visitor_->OnHeaderBlockStart(stream_id);
-    visitor_->OnHeaderBlockFragment(stream_id, header_block_queue.front());
-    visitor_->OnHeaderBlockEnd(stream_id);
+    HeaderBlock& header_block = header_block_queue.front();
 
-    header_block_queue.pop();
-    if (header_block_queue.empty()) {
-      header_blocks_.erase(it);
+    if (header_block.ConsumedLength() == 0) {
+      visitor_->OnHeaderBlockStart(stream_id);
+    }
+
+    DCHECK_NE(0u, header_block.RemainingLength());
+
+    size_t length = provider_->ConsumeIntegralInRange<size_t>(
+        1, header_block.RemainingLength());
+    visitor_->OnHeaderBlockFragment(stream_id, header_block.Consume(length));
+
+    DCHECK_NE(0u, header_block.ConsumedLength());
+
+    if (header_block.RemainingLength() == 0) {
+      visitor_->OnHeaderBlockEnd(stream_id);
+
+      header_block_queue.pop();
+      if (header_block_queue.empty()) {
+        header_blocks_.erase(it);
+      }
     }
   }
 
@@ -146,12 +160,25 @@
     while (!header_blocks_.empty()) {
       auto it = header_blocks_.begin();
       const QuicStreamId stream_id = it->first;
-      CHECK(!visitor_->IsDecodingInProgressOnStream(stream_id));
 
       auto& header_block_queue = it->second;
-      visitor_->OnHeaderBlockStart(stream_id);
-      visitor_->OnHeaderBlockFragment(stream_id, header_block_queue.front());
+      HeaderBlock& header_block = header_block_queue.front();
+
+      if (header_block.ConsumedLength() == 0) {
+        CHECK(!visitor_->IsDecodingInProgressOnStream(stream_id));
+        visitor_->OnHeaderBlockStart(stream_id);
+      }
+
+      DCHECK_NE(0u, header_block.RemainingLength());
+
+      visitor_->OnHeaderBlockFragment(stream_id,
+                                      header_block.ConsumeRemaining());
+
+      DCHECK_NE(0u, header_block.ConsumedLength());
+      DCHECK_EQ(0u, header_block.RemainingLength());
+
       visitor_->OnHeaderBlockEnd(stream_id);
+      CHECK(!visitor_->IsDecodingInProgressOnStream(stream_id));
 
       header_block_queue.pop();
       if (header_block_queue.empty()) {
@@ -161,11 +188,42 @@
   }
 
  private:
+  // Helper class that allows the header block to be consumed in parts.
+  class HeaderBlock {
+   public:
+    explicit HeaderBlock(std::string data)
+        : data_(std::move(data)), offset_(0) {
+      // Valid QPACK header block cannot be empty.
+      DCHECK(!data_.empty());
+    }
+
+    size_t ConsumedLength() const { return offset_; }
+
+    size_t RemainingLength() const { return data_.length() - offset_; }
+
+    QuicStringPiece Consume(size_t length) {
+      DCHECK_NE(0u, length);
+      DCHECK_LE(length, RemainingLength());
+
+      QuicStringPiece consumed = QuicStringPiece(&data_[offset_], length);
+      offset_ += length;
+      return consumed;
+    }
+
+    QuicStringPiece ConsumeRemaining() { return Consume(RemainingLength()); }
+
+   private:
+    // Complete header block.
+    const std::string data_;
+
+    // Offset of the part not consumed yet.  Same as number of consumed bytes.
+    size_t offset_;
+  };
+
   Visitor* const visitor_;
   QuicFuzzedDataProvider* const provider_;
 
-  // TODO(bnc): Break up header blocks into fragments.
-  std::map<QuicStreamId, std::queue<std::string>> header_blocks_;
+  std::map<QuicStreamId, std::queue<HeaderBlock>> header_blocks_;
 };
 
 // Class to decode and verify a header block, and in case of blocked decoding,