Blocked decoding part 3: QpackDecodedHeadersAccumulator and QuicSpdyStream.
gfe-relnote: n/a, QUIC v99-only change.
PiperOrigin-RevId: 257172998
Change-Id: I93ba049c437383a29dd98c9c9d44695aa7754fbf
diff --git a/quic/core/http/quic_spdy_session.cc b/quic/core/http/quic_spdy_session.cc
index 0c26de7..9e4178f 100644
--- a/quic/core/http/quic_spdy_session.cc
+++ b/quic/core/http/quic_spdy_session.cc
@@ -369,6 +369,10 @@
QuicMakeUnique<QpackEncoder>(this, &encoder_stream_sender_delegate_);
qpack_decoder_ =
QuicMakeUnique<QpackDecoder>(this, &decoder_stream_sender_delegate_);
+ // TODO(b/112770235): Send SETTINGS_QPACK_MAX_TABLE_CAPACITY with value
+ // kDefaultQpackMaxDynamicTableCapacity.
+ qpack_decoder_->SetMaximumDynamicTableCapacity(
+ kDefaultQpackMaxDynamicTableCapacity);
}
headers_stream_ = QuicMakeUnique<QuicHeadersStream>((this));
diff --git a/quic/core/http/quic_spdy_stream.cc b/quic/core/http/quic_spdy_stream.cc
index 96be207..0748d45 100644
--- a/quic/core/http/quic_spdy_stream.cc
+++ b/quic/core/http/quic_spdy_stream.cc
@@ -170,7 +170,6 @@
trailers_consumed_(false),
priority_sent_(false),
headers_bytes_to_be_marked_consumed_(0),
- pretend_blocked_decoding_for_tests_(false),
http_decoder_visitor_(new HttpDecoderVisitor(this)),
body_buffer_(sequencer()),
sequencer_offset_(0),
@@ -207,7 +206,6 @@
trailers_consumed_(false),
priority_sent_(false),
headers_bytes_to_be_marked_consumed_(0),
- pretend_blocked_decoding_for_tests_(false),
http_decoder_visitor_(new HttpDecoderVisitor(this)),
body_buffer_(sequencer()),
sequencer_offset_(sequencer()->NumBytesConsumed()),
@@ -867,8 +865,7 @@
qpack_decoded_headers_accumulator_ =
QuicMakeUnique<QpackDecodedHeadersAccumulator>(
id(), spdy_session_->qpack_decoder(), this,
- spdy_session_->max_inbound_header_list_size(),
- pretend_blocked_decoding_for_tests_);
+ spdy_session_->max_inbound_header_list_size());
// Do not call MaybeMarkHeadersBytesConsumed() yet, because
// HEADERS frame header bytes might not have been parsed completely.
diff --git a/quic/core/http/quic_spdy_stream.h b/quic/core/http/quic_spdy_stream.h
index 0c2ba2d..452109a 100644
--- a/quic/core/http/quic_spdy_stream.h
+++ b/quic/core/http/quic_spdy_stream.h
@@ -304,9 +304,6 @@
HttpEncoder encoder_;
// Http decoder for processing raw incoming stream frames.
HttpDecoder decoder_;
- // TODO(b/112770235): Remove once blocked decoding is implemented
- // and can be tested with delayed encoder stream data.
- bool pretend_blocked_decoding_for_tests_;
// Headers accumulator for decoding HEADERS frame payload.
std::unique_ptr<QpackDecodedHeadersAccumulator>
qpack_decoded_headers_accumulator_;
diff --git a/quic/core/http/quic_spdy_stream_test.cc b/quic/core/http/quic_spdy_stream_test.cc
index b2fbc70..43a14d9 100644
--- a/quic/core/http/quic_spdy_stream_test.cc
+++ b/quic/core/http/quic_spdy_stream_test.cc
@@ -38,6 +38,7 @@
using testing::ElementsAre;
using testing::Invoke;
using testing::InvokeWithoutArgs;
+using testing::MatchesRegex;
using testing::Pair;
using testing::Return;
using testing::StrictMock;
@@ -265,8 +266,7 @@
*connection_,
CloseConnection(
QUIC_HEADERS_STREAM_DATA_DECOMPRESS_FAILURE,
- testing::MatchesRegex("Too large headers received on stream \\d+"),
- _));
+ MatchesRegex("Too large headers received on stream \\d+"), _));
} else {
EXPECT_CALL(*session_,
SendRstStream(stream_->id(), QUIC_HEADERS_TOO_LARGE, 0));
@@ -1830,11 +1830,11 @@
EXPECT_CALL(
*connection_,
- CloseConnection(QUIC_DECOMPRESSION_FAILURE,
- testing::MatchesRegex(
- "Error decompressing header block on stream \\d+: "
- "Incomplete header block."),
- _))
+ CloseConnection(
+ QUIC_DECOMPRESSION_FAILURE,
+ MatchesRegex("Error decompressing header block on stream \\d+: "
+ "Incomplete header block."),
+ _))
.WillOnce(
(Invoke([this](QuicErrorCode error, const std::string& error_details,
ConnectionCloseBehavior connection_close_behavior) {
@@ -1852,59 +1852,95 @@
stream_->OnStreamFrame(frame);
}
+TEST_P(QuicSpdyStreamTest, ImmediateHeaderDecodingWithDynamicTableEntries) {
+ if (!VersionUsesQpack(GetParam().transport_version)) {
+ return;
+ }
+
+ Initialize(kShouldProcessData);
+
+ // Deliver dynamic table entry to decoder.
+ session_->qpack_decoder()->OnInsertWithoutNameReference("foo", "bar");
+
+ // HEADERS frame referencing first dynamic table entry.
+ std::string headers = HeadersFrame(QuicTextUtils::HexDecode("020080"));
+ stream_->OnStreamFrame(QuicStreamFrame(stream_->id(), false, 0, headers));
+
+ // Headers can be decoded immediately.
+ EXPECT_TRUE(stream_->headers_decompressed());
+
+ // Verify headers.
+ EXPECT_THAT(stream_->header_list(), ElementsAre(Pair("foo", "bar")));
+ stream_->ConsumeHeaderList();
+
+ // DATA frame.
+ std::string data = DataFrame(kDataFramePayload);
+ stream_->OnStreamFrame(QuicStreamFrame(stream_->id(), false, /* offset = */
+ headers.length(), data));
+ EXPECT_EQ(kDataFramePayload, stream_->data());
+
+ // Deliver second dynamic table entry to decoder.
+ session_->qpack_decoder()->OnInsertWithoutNameReference("trailing", "foobar");
+
+ // Trailing HEADERS frame referencing second dynamic table entry.
+ std::string trailers = HeadersFrame(QuicTextUtils::HexDecode("030080"));
+ stream_->OnStreamFrame(QuicStreamFrame(stream_->id(), true, /* offset = */
+ headers.length() + data.length(),
+ trailers));
+
+ // Trailers can be decoded immediately.
+ EXPECT_TRUE(stream_->trailers_decompressed());
+
+ // Verify trailers.
+ EXPECT_THAT(stream_->received_trailers(),
+ ElementsAre(Pair("trailing", "foobar")));
+ stream_->MarkTrailersConsumed();
+}
+
TEST_P(QuicSpdyStreamTest, BlockedHeaderDecoding) {
if (!VersionUsesQpack(GetParam().transport_version)) {
return;
}
Initialize(kShouldProcessData);
- QuicSpdyStreamPeer::pretend_blocked_decoding(stream_);
- // HEADERS frame with QPACK encoded single header field "foo: bar".
- std::string headers =
- HeadersFrame(QuicTextUtils::HexDecode("00002a94e703626172"));
+ // HEADERS frame referencing first dynamic table entry.
+ std::string headers = HeadersFrame(QuicTextUtils::HexDecode("020080"));
stream_->OnStreamFrame(QuicStreamFrame(stream_->id(), false, 0, headers));
- // Even though entire header block is received, it cannot be decoded.
+ // Decoding is blocked because dynamic table entry has not been received yet.
EXPECT_FALSE(stream_->headers_decompressed());
- // Signal to QpackDecodedHeadersAccumulator that the header block has been
- // decoded. (OnDecodingCompleted() has already been called by the QPACK
- // decoder, so technically quic_header_list_.OnHeaderBlockEnd() is called
- // twice, which is a no-op.)
- QuicSpdyStreamPeer::qpack_decoded_headers_accumulator(stream_)
- ->OnDecodingCompleted();
-
+ // Deliver dynamic table entry to decoder.
+ session_->qpack_decoder()->OnInsertWithoutNameReference("foo", "bar");
EXPECT_TRUE(stream_->headers_decompressed());
+ // Verify headers.
EXPECT_THAT(stream_->header_list(), ElementsAre(Pair("foo", "bar")));
stream_->ConsumeHeaderList();
+ // DATA frame.
std::string data = DataFrame(kDataFramePayload);
stream_->OnStreamFrame(QuicStreamFrame(stream_->id(), false, /* offset = */
headers.length(), data));
EXPECT_EQ(kDataFramePayload, stream_->data());
- // Trailing HEADERS frame with QPACK encoded
- // single header field "custom-key: custom-value".
- std::string trailers = HeadersFrame(
- QuicTextUtils::HexDecode("00002f0125a849e95ba97d7f8925a849e95bb8e8b4bf"));
+ // Trailing HEADERS frame referencing second dynamic table entry.
+ std::string trailers = HeadersFrame(QuicTextUtils::HexDecode("030080"));
stream_->OnStreamFrame(QuicStreamFrame(stream_->id(), true, /* offset = */
headers.length() + data.length(),
trailers));
- // Even though entire header block is received, it cannot be decoded.
+ // Decoding is blocked because dynamic table entry has not been received yet.
EXPECT_FALSE(stream_->trailers_decompressed());
- // Signal to QpackDecodedHeadersAccumulator that the header block has been
- // decoded.
- QuicSpdyStreamPeer::qpack_decoded_headers_accumulator(stream_)
- ->OnDecodingCompleted();
+ // Deliver second dynamic table entry to decoder.
+ session_->qpack_decoder()->OnInsertWithoutNameReference("trailing", "foobar");
EXPECT_TRUE(stream_->trailers_decompressed());
// Verify trailers.
EXPECT_THAT(stream_->received_trailers(),
- ElementsAre(Pair("custom-key", "custom-value")));
+ ElementsAre(Pair("trailing", "foobar")));
stream_->MarkTrailersConsumed();
}
@@ -1914,26 +1950,29 @@
}
Initialize(kShouldProcessData);
- QuicSpdyStreamPeer::pretend_blocked_decoding(stream_);
- // HEADERS frame with QPACK encoded single header field "foo: bar".
- std::string headers =
- HeadersFrame(QuicTextUtils::HexDecode("00002a94e703626172"));
+ // HEADERS frame only referencing entry with absolute index 0 but with
+ // Required Insert Count = 2, which is incorrect.
+ std::string headers = HeadersFrame(QuicTextUtils::HexDecode("030081"));
stream_->OnStreamFrame(QuicStreamFrame(stream_->id(), false, 0, headers));
- // Even though entire header block is received, it cannot be decoded.
+ // Even though entire header block is received and every referenced entry is
+ // available, decoding is blocked until insert count reaches the Required
+ // Insert Count value advertised in the header block prefix.
EXPECT_FALSE(stream_->headers_decompressed());
- // Signal a decoding error to QpackDecodedHeadersAccumulator.
- std::string expected_error_message =
- QuicStrCat("Error during async decoding of headers on stream ",
- stream_->id(), ": unexpected error");
EXPECT_CALL(
*connection_,
- CloseConnection(QUIC_DECOMPRESSION_FAILURE, expected_error_message,
- ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET));
- QuicSpdyStreamPeer::qpack_decoded_headers_accumulator(stream_)
- ->OnDecodingErrorDetected("unexpected error");
+ CloseConnection(
+ QUIC_DECOMPRESSION_FAILURE,
+ MatchesRegex("Error during async decoding of headers on stream \\d+: "
+ "Required Insert Count too large."),
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET));
+
+ // Deliver two dynamic table entries to decoder
+ // to trigger decoding of header block.
+ session_->qpack_decoder()->OnInsertWithoutNameReference("foo", "bar");
+ session_->qpack_decoder()->OnInsertWithoutNameReference("foo", "bar");
}
TEST_P(QuicSpdyStreamTest, AsyncErrorDecodingTrailers) {
@@ -1942,54 +1981,51 @@
}
Initialize(kShouldProcessData);
- QuicSpdyStreamPeer::pretend_blocked_decoding(stream_);
- // HEADERS frame with QPACK encoded single header field "foo: bar".
- std::string headers =
- HeadersFrame(QuicTextUtils::HexDecode("00002a94e703626172"));
+ // HEADERS frame referencing first dynamic table entry.
+ std::string headers = HeadersFrame(QuicTextUtils::HexDecode("020080"));
stream_->OnStreamFrame(QuicStreamFrame(stream_->id(), false, 0, headers));
- // Even though entire header block is received, it cannot be decoded.
+ // Decoding is blocked because dynamic table entry has not been received yet.
EXPECT_FALSE(stream_->headers_decompressed());
- // Signal to QpackDecodedHeadersAccumulator that the header block has been
- // decoded. (OnDecodingCompleted() has already been called by the QPACK
- // decoder, so technically quic_header_list_.OnHeaderBlockEnd() is called
- // twice, which is a no-op.)
- QuicSpdyStreamPeer::qpack_decoded_headers_accumulator(stream_)
- ->OnDecodingCompleted();
-
+ // Deliver dynamic table entry to decoder.
+ session_->qpack_decoder()->OnInsertWithoutNameReference("foo", "bar");
EXPECT_TRUE(stream_->headers_decompressed());
+ // Verify headers.
EXPECT_THAT(stream_->header_list(), ElementsAre(Pair("foo", "bar")));
stream_->ConsumeHeaderList();
+ // DATA frame.
std::string data = DataFrame(kDataFramePayload);
stream_->OnStreamFrame(QuicStreamFrame(stream_->id(), false, /* offset = */
headers.length(), data));
EXPECT_EQ(kDataFramePayload, stream_->data());
- // Trailing HEADERS frame with QPACK encoded
- // single header field "custom-key: custom-value".
- std::string trailers = HeadersFrame(
- QuicTextUtils::HexDecode("00002f0125a849e95ba97d7f8925a849e95bb8e8b4bf"));
+ // Trailing HEADERS frame only referencing entry with absolute index 0 but
+ // with Required Insert Count = 2, which is incorrect.
+ std::string trailers = HeadersFrame(QuicTextUtils::HexDecode("030081"));
stream_->OnStreamFrame(QuicStreamFrame(stream_->id(), true, /* offset = */
headers.length() + data.length(),
trailers));
- // Even though entire header block is received, it cannot be decoded.
+ // Even though entire header block is received and every referenced entry is
+ // available, decoding is blocked until insert count reaches the Required
+ // Insert Count value advertised in the header block prefix.
EXPECT_FALSE(stream_->trailers_decompressed());
- // Signal a decoding error to QpackDecodedHeadersAccumulator.
- std::string expected_error_message =
- QuicStrCat("Error during async decoding of trailers on stream ",
- stream_->id(), ": unexpected error");
- EXPECT_CALL(
- *connection_,
- CloseConnection(QUIC_DECOMPRESSION_FAILURE, expected_error_message,
- ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET));
- QuicSpdyStreamPeer::qpack_decoded_headers_accumulator(stream_)
- ->OnDecodingErrorDetected("unexpected error");
+ EXPECT_CALL(*connection_,
+ CloseConnection(
+ QUIC_DECOMPRESSION_FAILURE,
+ MatchesRegex(
+ "Error during async decoding of trailers on stream \\d+: "
+ "Required Insert Count too large."),
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET));
+
+ // Deliver second dynamic table entry to decoder
+ // to trigger decoding of trailing header block.
+ session_->qpack_decoder()->OnInsertWithoutNameReference("trailing", "foobar");
}
class QuicSpdyStreamIncrementalConsumptionTest : public QuicSpdyStreamTest {
diff --git a/quic/core/qpack/qpack_decoded_headers_accumulator.cc b/quic/core/qpack/qpack_decoded_headers_accumulator.cc
index 8ca28c9..9cce573 100644
--- a/quic/core/qpack/qpack_decoded_headers_accumulator.cc
+++ b/quic/core/qpack/qpack_decoded_headers_accumulator.cc
@@ -12,14 +12,13 @@
QuicStreamId id,
QpackDecoder* qpack_decoder,
Visitor* visitor,
- size_t max_header_list_size,
- bool pretend_blocked_decoding_for_tests)
+ size_t max_header_list_size)
: decoder_(qpack_decoder->CreateProgressiveDecoder(id, this)),
visitor_(visitor),
uncompressed_header_bytes_(0),
compressed_header_bytes_(0),
+ headers_decoded_(false),
blocked_(false),
- pretend_blocked_decoding_for_tests_(pretend_blocked_decoding_for_tests),
error_detected_(false) {
quic_header_list_.set_max_header_list_size(max_header_list_size);
quic_header_list_.OnHeaderBlockStart();
@@ -34,6 +33,10 @@
}
void QpackDecodedHeadersAccumulator::OnDecodingCompleted() {
+ DCHECK(!headers_decoded_);
+ DCHECK(!error_detected_);
+
+ headers_decoded_ = true;
quic_header_list_.OnHeaderBlockEnd(uncompressed_header_bytes_,
compressed_header_bytes_);
@@ -45,6 +48,7 @@
void QpackDecodedHeadersAccumulator::OnDecodingErrorDetected(
QuicStringPiece error_message) {
DCHECK(!error_detected_);
+ DCHECK(!headers_decoded_);
error_detected_ = true;
// Copy error message to ensure it remains valid for the lifetime of |this|.
@@ -67,15 +71,21 @@
QpackDecodedHeadersAccumulator::Status
QpackDecodedHeadersAccumulator::EndHeaderBlock() {
DCHECK(!error_detected_);
+ DCHECK(!headers_decoded_);
decoder_->EndHeaderBlock();
- if (pretend_blocked_decoding_for_tests_) {
- blocked_ = true;
- return Status::kBlocked;
+ if (error_detected_) {
+ DCHECK(!headers_decoded_);
+ return Status::kError;
}
- return error_detected_ ? Status::kError : Status::kSuccess;
+ if (headers_decoded_) {
+ return Status::kSuccess;
+ }
+
+ blocked_ = true;
+ return Status::kBlocked;
}
const QuicHeaderList& QpackDecodedHeadersAccumulator::quic_header_list() const {
diff --git a/quic/core/qpack/qpack_decoded_headers_accumulator.h b/quic/core/qpack/qpack_decoded_headers_accumulator.h
index 59480db..c649322 100644
--- a/quic/core/qpack/qpack_decoded_headers_accumulator.h
+++ b/quic/core/qpack/qpack_decoded_headers_accumulator.h
@@ -51,8 +51,7 @@
QpackDecodedHeadersAccumulator(QuicStreamId id,
QpackDecoder* qpack_decoder,
Visitor* visitor,
- size_t max_header_list_size,
- bool pretend_blocked_decoding_for_tests);
+ size_t max_header_list_size);
virtual ~QpackDecodedHeadersAccumulator() = default;
// QpackProgressiveDecoder::HeadersHandlerInterface implementation.
@@ -91,11 +90,10 @@
QuicHeaderList quic_header_list_;
size_t uncompressed_header_bytes_;
size_t compressed_header_bytes_;
+ // Set to true when OnDecodingCompleted() is called.
+ bool headers_decoded_;
// Set to true when EndHeaderBlock() returns kBlocked.
bool blocked_;
- // TODO(b/112770235): Remove once blocked decoding is implemented
- // and can be tested with delayed encoder stream data.
- bool pretend_blocked_decoding_for_tests_;
bool error_detected_;
std::string error_message_;
};
diff --git a/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc b/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc
index 6f424e4..0898327 100644
--- a/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc
+++ b/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc
@@ -28,6 +28,9 @@
// Limit on header list size.
const size_t kMaxHeaderListSize = 100;
+// Maximum dynamic table capacity.
+const size_t kMaxDynamicTableCapacity = 100;
+
// Header Acknowledgement decoder stream instruction with stream_id = 1.
const char* const kHeaderAcknowledgement = "\x81";
@@ -48,8 +51,7 @@
accumulator_(kTestStreamId,
&qpack_decoder_,
&visitor_,
- kMaxHeaderListSize,
- false) {}
+ kMaxHeaderListSize) {}
NoopEncoderStreamErrorDelegate encoder_stream_error_delegate_;
StrictMock<MockQpackStreamSenderDelegate> decoder_stream_sender_delegate_;
@@ -129,18 +131,39 @@
EXPECT_TRUE(accumulator_.quic_header_list().empty());
}
-// TODO(b/112770235): Remove once blocked decoding is implemented
-// and can be tested with delayed encoder stream data.
-TEST_F(QpackDecodedHeadersAccumulatorTest, PretendBlockedEncoding) {
- QpackDecodedHeadersAccumulator accumulator(
- kTestStreamId, &qpack_decoder_, &visitor_, kMaxHeaderListSize,
- /* pretend_blocked_decoding_for_tests = */ true);
+TEST_F(QpackDecodedHeadersAccumulatorTest, BlockedDecoding) {
+ qpack_decoder_.SetMaximumDynamicTableCapacity(kMaxDynamicTableCapacity);
+ // Reference to dynamic table entry not yet received.
+ EXPECT_TRUE(accumulator_.Decode(QuicTextUtils::HexDecode("020080")));
+ EXPECT_EQ(Status::kBlocked, accumulator_.EndHeaderBlock());
+
+ // Adding dynamic table entry unblocks decoding.
EXPECT_CALL(decoder_stream_sender_delegate_,
WriteStreamData(Eq(kHeaderAcknowledgement)));
+ qpack_decoder_.OnInsertWithoutNameReference("foo", "bar");
- EXPECT_TRUE(accumulator.Decode(QuicTextUtils::HexDecode("0000")));
- EXPECT_EQ(Status::kBlocked, accumulator.EndHeaderBlock());
+ EXPECT_THAT(accumulator_.quic_header_list(), ElementsAre(Pair("foo", "bar")));
+}
+
+TEST_F(QpackDecodedHeadersAccumulatorTest,
+ BlockedDecodingUnblockedBeforeEndOfHeaderBlock) {
+ qpack_decoder_.SetMaximumDynamicTableCapacity(kMaxDynamicTableCapacity);
+
+ // Reference to dynamic table entry not yet received.
+ EXPECT_TRUE(accumulator_.Decode(QuicTextUtils::HexDecode("020080")));
+
+ // Adding dynamic table entry unblocks decoding.
+ qpack_decoder_.OnInsertWithoutNameReference("foo", "bar");
+
+ // Rest of header block: same entry again.
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteStreamData(Eq(kHeaderAcknowledgement)));
+ EXPECT_TRUE(accumulator_.Decode(QuicTextUtils::HexDecode("80")));
+ EXPECT_EQ(Status::kSuccess, accumulator_.EndHeaderBlock());
+
+ EXPECT_THAT(accumulator_.quic_header_list(),
+ ElementsAre(Pair("foo", "bar"), Pair("foo", "bar")));
}
} // namespace test
diff --git a/quic/core/quic_constants.h b/quic/core/quic_constants.h
index 913c063..e718602 100644
--- a/quic/core/quic_constants.h
+++ b/quic/core/quic_constants.h
@@ -74,9 +74,16 @@
const QuicByteCount kStreamReceiveWindowLimit = 16 * 1024 * 1024; // 16 MB
const QuicByteCount kSessionReceiveWindowLimit = 24 * 1024 * 1024; // 24 MB
-// Default limit on the size of uncompressed headers.
+// Default limit on the size of uncompressed headers,
+// communicated via SETTINGS_MAX_HEADER_LIST_SIZE.
+// TODO(bnc): Move this constant to quic/core/http/.
const QuicByteCount kDefaultMaxUncompressedHeaderSize = 16 * 1024; // 16 KB
+// Default maximum dynamic table capacity, communicated via
+// SETTINGS_QPACK_MAX_TABLE_CAPACITY.
+// TODO(bnc): Move this constant to quic/core/http/.
+const QuicByteCount kDefaultQpackMaxDynamicTableCapacity = 64 * 1024; // 64 KB
+
// Minimum size of the CWND, in packets, when doing bandwidth resumption.
const QuicPacketCount kMinCongestionWindowForBandwidthResumption = 10;