Send Insert Count Increment instruction after decoding a header block if possible.
See also https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#new-table-entries
for justification of not delaying sending Insert Count Increment instructions too long.
gfe-relnote: n/a, change to QUIC v99-only code. Protected by existing disabled gfe2_reloadable_flag_quic_enable_version_99.
PiperOrigin-RevId: 273510751
Change-Id: Ic33afd24c41bc92577b9b75d00f8ecdd94058d60
diff --git a/quic/core/qpack/qpack_decoder.cc b/quic/core/qpack/qpack_decoder.cc
index e2e9d28..d4e5d91 100644
--- a/quic/core/qpack/qpack_decoder.cc
+++ b/quic/core/qpack/qpack_decoder.cc
@@ -16,7 +16,8 @@
EncoderStreamErrorDelegate* encoder_stream_error_delegate)
: encoder_stream_error_delegate_(encoder_stream_error_delegate),
encoder_stream_receiver_(this),
- maximum_blocked_streams_(maximum_blocked_streams) {
+ maximum_blocked_streams_(maximum_blocked_streams),
+ known_received_count_(0) {
DCHECK(encoder_stream_error_delegate_);
header_table_.SetMaximumDynamicTableCapacity(maximum_dynamic_table_capacity);
@@ -41,6 +42,27 @@
DCHECK_EQ(1u, result);
}
+void QpackDecoder::OnDecodingCompleted(QuicStreamId stream_id,
+ uint64_t required_insert_count) {
+ if (required_insert_count > 0) {
+ decoder_stream_sender_.SendHeaderAcknowledgement(stream_id);
+
+ if (known_received_count_ < required_insert_count) {
+ known_received_count_ = required_insert_count;
+ }
+ }
+
+ // Send an Insert Count Increment instruction if not all dynamic table entries
+ // have been acknowledged yet. This is necessary for efficient compression in
+ // case the encoder chooses not to reference unacknowledged dynamic table
+ // entries, otherwise inserted entries would never be acknowledged.
+ if (known_received_count_ < header_table_.inserted_entry_count()) {
+ decoder_stream_sender_.SendInsertCountIncrement(
+ header_table_.inserted_entry_count() - known_received_count_);
+ known_received_count_ = header_table_.inserted_entry_count();
+ }
+}
+
void QpackDecoder::OnInsertWithNameReference(bool is_static,
uint64_t name_index,
QuicStringPiece value) {
@@ -128,8 +150,8 @@
std::unique_ptr<QpackProgressiveDecoder> QpackDecoder::CreateProgressiveDecoder(
QuicStreamId stream_id,
QpackProgressiveDecoder::HeadersHandlerInterface* handler) {
- return std::make_unique<QpackProgressiveDecoder>(
- stream_id, this, &header_table_, &decoder_stream_sender_, handler);
+ return std::make_unique<QpackProgressiveDecoder>(stream_id, this, this,
+ &header_table_, handler);
}
} // namespace quic
diff --git a/quic/core/qpack/qpack_decoder.h b/quic/core/qpack/qpack_decoder.h
index 102fbc4..113c6ea 100644
--- a/quic/core/qpack/qpack_decoder.h
+++ b/quic/core/qpack/qpack_decoder.h
@@ -24,7 +24,8 @@
// list to be encoded.
class QUIC_EXPORT_PRIVATE QpackDecoder
: public QpackEncoderStreamReceiver::Delegate,
- public QpackProgressiveDecoder::BlockedStreamLimitEnforcer {
+ public QpackProgressiveDecoder::BlockedStreamLimitEnforcer,
+ public QpackProgressiveDecoder::DecodingCompletedVisitor {
public:
// Interface for receiving notification that an error has occurred on the
// encoder stream. This MUST be treated as a connection error of type
@@ -63,6 +64,10 @@
bool OnStreamBlocked(QuicStreamId stream_id) override;
void OnStreamUnblocked(QuicStreamId stream_id) override;
+ // QpackProgressiveDecoder::DecodingCompletedVisitor implementation.
+ void OnDecodingCompleted(QuicStreamId stream_id,
+ uint64_t required_insert_count) override;
+
// Factory method to create a QpackProgressiveDecoder for decoding a header
// block. |handler| must remain valid until the returned
// QpackProgressiveDecoder instance is destroyed or the decoder calls
@@ -97,6 +102,13 @@
QpackHeaderTable header_table_;
std::set<QuicStreamId> blocked_streams_;
const uint64_t maximum_blocked_streams_;
+
+ // Known Received Count is the number of insertions the encoder has received
+ // acknowledgement for (through Header Acknowledgement and Insert Count
+ // Increment instructions). The encoder must keep track of it in order to be
+ // able to send Insert Count Increment instructions. See
+ // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#known-received-count.
+ uint64_t known_received_count_;
};
} // namespace quic
diff --git a/quic/core/qpack/qpack_decoder_test.cc b/quic/core/qpack/qpack_decoder_test.cc
index 7071913..faa0f9d 100644
--- a/quic/core/qpack/qpack_decoder_test.cc
+++ b/quic/core/qpack/qpack_decoder_test.cc
@@ -816,7 +816,6 @@
// At most three non-empty entries fit in the dynamic table.
DecodeEncoderStreamData(QuicTextUtils::HexDecode("3f61"));
- StartDecoding();
DecodeHeaderBlock(QuicTextUtils::HexDecode(
"0700" // Required Insert Count 6 and Delta Base 0.
// Base is 6 + 0 = 6.
@@ -854,6 +853,29 @@
progressive_decoder2->Decode(data);
}
+TEST_P(QpackDecoderTest, InsertCountIncrement) {
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode(
+ "3fe107" // Set dynamic table capacity to 1024.
+ "6294e703626172" // Add literal entry with name "foo" and value "bar".
+ "00")); // Duplicate entry.
+
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar")));
+ EXPECT_CALL(handler_, OnDecodingCompleted());
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteStreamData(Eq(kHeaderAcknowledgement)));
+
+ // Decoder received two insertions, but Header Acknowledgement only increases
+ // Known Insert Count to one. Decoder should send an Insert Count Increment
+ // instruction with increment of one to update Known Insert Count to two.
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteStreamData(Eq(QuicTextUtils::HexDecode("01"))));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0200" // Required Insert Count 1 and Delta Base 0.
+ // Base is 1 + 0 = 1.
+ "80")); // Dynamic table entry with relative index 0, absolute index 0.
+}
+
} // namespace
} // namespace test
} // namespace quic
diff --git a/quic/core/qpack/qpack_progressive_decoder.cc b/quic/core/qpack/qpack_progressive_decoder.cc
index 34eb2b3..0a43963 100644
--- a/quic/core/qpack/qpack_progressive_decoder.cc
+++ b/quic/core/qpack/qpack_progressive_decoder.cc
@@ -18,8 +18,8 @@
QpackProgressiveDecoder::QpackProgressiveDecoder(
QuicStreamId stream_id,
BlockedStreamLimitEnforcer* enforcer,
+ DecodingCompletedVisitor* visitor,
QpackHeaderTable* header_table,
- QpackDecoderStreamSender* decoder_stream_sender,
HeadersHandlerInterface* handler)
: stream_id_(stream_id),
prefix_decoder_(
@@ -27,8 +27,8 @@
this)),
instruction_decoder_(QpackRequestStreamLanguage(), this),
enforcer_(enforcer),
+ visitor_(visitor),
header_table_(header_table),
- decoder_stream_sender_(decoder_stream_sender),
handler_(handler),
required_insert_count_(0),
base_(0),
@@ -336,10 +336,7 @@
return;
}
- if (required_insert_count_ > 0) {
- decoder_stream_sender_->SendHeaderAcknowledgement(stream_id_);
- }
-
+ visitor_->OnDecodingCompleted(stream_id_, required_insert_count_);
handler_->OnDecodingCompleted();
}
diff --git a/quic/core/qpack/qpack_progressive_decoder.h b/quic/core/qpack/qpack_progressive_decoder.h
index 91b1e8c..2f306c8 100644
--- a/quic/core/qpack/qpack_progressive_decoder.h
+++ b/quic/core/qpack/qpack_progressive_decoder.h
@@ -9,7 +9,6 @@
#include <memory>
#include <string>
-#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.h"
#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.h"
#include "net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h"
#include "net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.h"
@@ -66,11 +65,23 @@
virtual void OnStreamUnblocked(QuicStreamId stream_id) = 0;
};
+ // Visitor to be notified when decoding is completed.
+ class QUIC_EXPORT_PRIVATE DecodingCompletedVisitor {
+ public:
+ virtual ~DecodingCompletedVisitor() = default;
+
+ // Called when decoding is completed, with Required Insert Count of the
+ // decoded header block. Required Insert Count is defined at
+ // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#blocked-streams.
+ virtual void OnDecodingCompleted(QuicStreamId stream_id,
+ uint64_t required_insert_count) = 0;
+ };
+
QpackProgressiveDecoder() = delete;
QpackProgressiveDecoder(QuicStreamId stream_id,
BlockedStreamLimitEnforcer* enforcer,
+ DecodingCompletedVisitor* visitor,
QpackHeaderTable* header_table,
- QpackDecoderStreamSender* decoder_stream_sender,
HeadersHandlerInterface* handler);
QpackProgressiveDecoder(const QpackProgressiveDecoder&) = delete;
QpackProgressiveDecoder& operator=(const QpackProgressiveDecoder&) = delete;
@@ -117,8 +128,8 @@
QpackInstructionDecoder instruction_decoder_;
BlockedStreamLimitEnforcer* const enforcer_;
+ DecodingCompletedVisitor* const visitor_;
QpackHeaderTable* const header_table_;
- QpackDecoderStreamSender* const decoder_stream_sender_;
HeadersHandlerInterface* const handler_;
// Required Insert Count and Base are decoded from the Header Data Prefix.