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,