Include encoder stream bytes in written header size.

Include the total size of dynamic table insertion instructions (including
duplications) sent on the encoder stream triggered by encoding a header list in
the return value of QuicSpdyStream::WriteHeadersImpl().

gfe-relnote: n/a, change to QUIC v99-only code.  Protected by existing disabled gfe2_reloadable_flag_quic_enable_version_99.
PiperOrigin-RevId: 268203243
Change-Id: I5aacf67592862747670192a598c0aa877cb0c658
diff --git a/quic/core/http/end_to_end_test.cc b/quic/core/http/end_to_end_test.cc
index a59100a..9e94a10 100644
--- a/quic/core/http/end_to_end_test.cc
+++ b/quic/core/http/end_to_end_test.cc
@@ -2475,7 +2475,7 @@
     qpack_encoder.set_qpack_stream_sender_delegate(
         &encoder_stream_sender_delegate);
     std::string encoded_headers =
-        qpack_encoder.EncodeHeaderList(/* stream_id = */ 0, headers);
+        qpack_encoder.EncodeHeaderList(/* stream_id = */ 0, headers, nullptr);
     header_size = encoded_headers.size();
   }
 
diff --git a/quic/core/http/quic_spdy_session.cc b/quic/core/http/quic_spdy_session.cc
index 2c32891..53ec3cc 100644
--- a/quic/core/http/quic_spdy_session.cc
+++ b/quic/core/http/quic_spdy_session.cc
@@ -555,7 +555,7 @@
 
   // Encode header list.
   std::string encoded_headers =
-      qpack_encoder_->EncodeHeaderList(original_stream_id, headers);
+      qpack_encoder_->EncodeHeaderList(original_stream_id, headers, nullptr);
   PushPromiseFrame frame;
   frame.push_id = promised_stream_id;
   frame.headers = encoded_headers;
diff --git a/quic/core/http/quic_spdy_stream.cc b/quic/core/http/quic_spdy_stream.cc
index 223442c..e702e85 100644
--- a/quic/core/http/quic_spdy_stream.cc
+++ b/quic/core/http/quic_spdy_stream.cc
@@ -1010,8 +1010,10 @@
   }
 
   // Encode header list.
+  QuicByteCount encoder_stream_sent_byte_count;
   std::string encoded_headers =
-      spdy_session_->qpack_encoder()->EncodeHeaderList(id(), header_block);
+      spdy_session_->qpack_encoder()->EncodeHeaderList(
+          id(), header_block, &encoder_stream_sent_byte_count);
 
   // Write HEADERS frame.
   std::unique_ptr<char[]> headers_frame_header;
@@ -1034,7 +1036,7 @@
                   << encoded_headers.length();
   WriteOrBufferData(encoded_headers, fin, nullptr);
 
-  return encoded_headers.size();
+  return encoded_headers.size() + encoder_stream_sent_byte_count;
 }
 
 void QuicSpdyStream::PopulatePriorityFrame(PriorityFrame* frame) {
diff --git a/quic/core/http/quic_spdy_stream.h b/quic/core/http/quic_spdy_stream.h
index 42f76bb..4849705 100644
--- a/quic/core/http/quic_spdy_stream.h
+++ b/quic/core/http/quic_spdy_stream.h
@@ -110,7 +110,9 @@
   virtual void OnBodyAvailable() = 0;
 
   // Writes the headers contained in |header_block| on the dedicated headers
-  // stream or on this stream, depending on VersionUsesQpack().
+  // stream or on this stream, depending on VersionUsesQpack().  Returns the
+  // number of bytes sent, including data sent on the encoder stream when using
+  // QPACK.
   virtual size_t WriteHeaders(
       spdy::SpdyHeaderBlock header_block,
       bool fin,
@@ -121,7 +123,8 @@
 
   // Writes the trailers contained in |trailer_block| on the dedicated headers
   // stream or on this stream, depending on VersionUsesQpack().  Trailers will
-  // always have the FIN flag set.
+  // always have the FIN flag set.  Returns the number of bytes sent, including
+  // data sent on the encoder stream when using QPACK.
   virtual size_t WriteTrailers(
       spdy::SpdyHeaderBlock trailer_block,
       QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener);
diff --git a/quic/core/http/quic_spdy_stream_test.cc b/quic/core/http/quic_spdy_stream_test.cc
index 3fcf853..5e4dfcb 100644
--- a/quic/core/http/quic_spdy_stream_test.cc
+++ b/quic/core/http/quic_spdy_stream_test.cc
@@ -184,7 +184,8 @@
         &encoder_stream_sender_delegate);
     // QpackEncoder does not use the dynamic table by default,
     // therefore the value of |stream_id| does not matter.
-    return qpack_encoder->EncodeHeaderList(/* stream_id = */ 0, header);
+    return qpack_encoder->EncodeHeaderList(/* stream_id = */ 0, header,
+                                           nullptr);
   }
 
   void Initialize(bool stream_should_process_data) {
diff --git a/quic/core/qpack/fuzzer/qpack_round_trip_fuzzer.cc b/quic/core/qpack/fuzzer/qpack_round_trip_fuzzer.cc
index 8527483..996aa70 100644
--- a/quic/core/qpack/fuzzer/qpack_round_trip_fuzzer.cc
+++ b/quic/core/qpack/fuzzer/qpack_round_trip_fuzzer.cc
@@ -55,7 +55,7 @@
 
   std::string EncodeHeaderList(QuicStreamId stream_id,
                                const spdy::SpdyHeaderBlock& header_list) {
-    return encoder_.EncodeHeaderList(stream_id, header_list);
+    return encoder_.EncodeHeaderList(stream_id, header_list, nullptr);
   }
 
  private:
diff --git a/quic/core/qpack/qpack_encoder.cc b/quic/core/qpack/qpack_encoder.cc
index d1736f8..24f12bb 100644
--- a/quic/core/qpack/qpack_encoder.cc
+++ b/quic/core/qpack/qpack_encoder.cc
@@ -87,9 +87,11 @@
 
 QpackEncoder::Instructions QpackEncoder::FirstPassEncode(
     const spdy::SpdyHeaderBlock& header_list,
-    QpackBlockingManager::IndexSet* referred_indices) {
+    QpackBlockingManager::IndexSet* referred_indices,
+    QuicByteCount* encoder_stream_sent_byte_count) {
   Instructions instructions;
   instructions.reserve(header_list.size());
+  QuicByteCount sent_byte_count = 0;
 
   // The index of the oldest entry that must not be evicted.
   uint64_t smallest_blocking_index =
@@ -144,7 +146,7 @@
                   header_table_.MaxInsertSizeWithoutEvictingGivenEntry(
                       std::min(smallest_blocking_index, index))) {
             // If allowed, duplicate entry and refer to it.
-            encoder_stream_sender_.SendDuplicate(
+            sent_byte_count += encoder_stream_sender_.SendDuplicate(
                 QpackAbsoluteIndexToEncoderStreamRelativeIndex(
                     index, header_table_.inserted_entry_count()));
             auto entry = header_table_.InsertEntry(name, value);
@@ -174,8 +176,9 @@
                   header_table_.MaxInsertSizeWithoutEvictingGivenEntry(
                       smallest_blocking_index)) {
             // If allowed, insert entry into dynamic table and refer to it.
-            encoder_stream_sender_.SendInsertWithNameReference(is_static, index,
-                                                               value);
+            sent_byte_count +=
+                encoder_stream_sender_.SendInsertWithNameReference(
+                    is_static, index, value);
             auto entry = header_table_.InsertEntry(name, value);
             instructions.push_back(EncodeIndexedHeaderField(
                 /* is_static = */ false, entry->InsertionIndex(),
@@ -198,7 +201,7 @@
                 header_table_.MaxInsertSizeWithoutEvictingGivenEntry(
                     std::min(smallest_blocking_index, index))) {
           // If allowed, insert entry with name reference and refer to it.
-          encoder_stream_sender_.SendInsertWithNameReference(
+          sent_byte_count += encoder_stream_sender_.SendInsertWithNameReference(
               is_static,
               QpackAbsoluteIndexToEncoderStreamRelativeIndex(
                   index, header_table_.inserted_entry_count()),
@@ -238,7 +241,9 @@
                 header_table_.MaxInsertSizeWithoutEvictingGivenEntry(
                     smallest_blocking_index)) {
           // If allowed, insert entry and refer to it.
-          encoder_stream_sender_.SendInsertWithoutNameReference(name, value);
+          sent_byte_count +=
+              encoder_stream_sender_.SendInsertWithoutNameReference(name,
+                                                                    value);
           auto entry = header_table_.InsertEntry(name, value);
           instructions.push_back(EncodeIndexedHeaderField(
               /* is_static = */ false, entry->InsertionIndex(),
@@ -256,6 +261,12 @@
     }
   }
 
+  // Use local |sent_byte_count| variable to avoid branching and dereferencing
+  // each time encoder stream data is sent.
+  if (encoder_stream_sent_byte_count) {
+    *encoder_stream_sent_byte_count = sent_byte_count;
+  }
+
   return instructions;
 }
 
@@ -296,13 +307,15 @@
 
 std::string QpackEncoder::EncodeHeaderList(
     QuicStreamId stream_id,
-    const spdy::SpdyHeaderBlock& header_list) {
+    const spdy::SpdyHeaderBlock& header_list,
+    QuicByteCount* encoder_stream_sent_byte_count) {
   // Keep track of all dynamic table indices that this header block refers to so
   // that it can be passed to QpackBlockingManager.
   QpackBlockingManager::IndexSet referred_indices;
 
   // First pass: encode into |instructions|.
-  Instructions instructions = FirstPassEncode(header_list, &referred_indices);
+  Instructions instructions = FirstPassEncode(header_list, &referred_indices,
+                                              encoder_stream_sent_byte_count);
 
   const uint64_t required_insert_count =
       referred_indices.empty()
diff --git a/quic/core/qpack/qpack_encoder.h b/quic/core/qpack/qpack_encoder.h
index 4aa69c1..d64d159 100644
--- a/quic/core/qpack/qpack_encoder.h
+++ b/quic/core/qpack/qpack_encoder.h
@@ -49,9 +49,12 @@
   QpackEncoder(DecoderStreamErrorDelegate* decoder_stream_error_delegate);
   ~QpackEncoder() override;
 
-  // Encode a header list.
+  // Encode a header list.  If |encoder_stream_sent_byte_count| is not null,
+  // |*encoder_stream_sent_byte_count| will be set to the number of bytes sent
+  // on the encoder stream to insert dynamic table entries.
   std::string EncodeHeaderList(QuicStreamId stream_id,
-                               const spdy::SpdyHeaderBlock& header_list);
+                               const spdy::SpdyHeaderBlock& header_list,
+                               QuicByteCount* encoder_stream_sent_byte_count);
 
   // Set maximum dynamic table capacity to |maximum_dynamic_table_capacity|,
   // measured in bytes.  Called when SETTINGS_QPACK_MAX_TABLE_CAPACITY is
@@ -121,13 +124,16 @@
   // |*header_list| as a reference to an existing entry, the name of an existing
   // entry with a literal value, or a literal name and value pair.  Sends
   // necessary instructions on the encoder stream.  Records absolute indices of
-  // referred dynamic table entries in |*referred_indices|.  Returns list of
-  // header field representations, with all dynamic table entries referred to
-  // with absolute indices.  Returned Instructions object may have
-  // QuicStringPieces pointing to strings owned by |*header_list|.
-  Instructions FirstPassEncode(
-      const spdy::SpdyHeaderBlock& header_list,
-      QpackBlockingManager::IndexSet* referred_indices);
+  // referred dynamic table entries in |*referred_indices|.  If
+  // |encoder_stream_sent_byte_count| is not null, then sets
+  // |*encoder_stream_sent_byte_count| to the number of bytes sent on the
+  // encoder stream to insert dynamic table entries.  Returns list of header
+  // field representations, with all dynamic table entries referred to with
+  // absolute indices.  Returned Instructions object may have QuicStringPieces
+  // pointing to strings owned by |*header_list|.
+  Instructions FirstPassEncode(const spdy::SpdyHeaderBlock& header_list,
+                               QpackBlockingManager::IndexSet* referred_indices,
+                               QuicByteCount* encoder_stream_sent_byte_count);
 
   // Performs second pass of two-pass encoding: serializes representations
   // generated in first pass, transforming absolute indices of dynamic table
diff --git a/quic/core/qpack/qpack_encoder_stream_sender.cc b/quic/core/qpack/qpack_encoder_stream_sender.cc
index e6209fd..533e3a1 100644
--- a/quic/core/qpack/qpack_encoder_stream_sender.cc
+++ b/quic/core/qpack/qpack_encoder_stream_sender.cc
@@ -15,7 +15,7 @@
 
 QpackEncoderStreamSender::QpackEncoderStreamSender() : delegate_(nullptr) {}
 
-void QpackEncoderStreamSender::SendInsertWithNameReference(
+QuicByteCount QpackEncoderStreamSender::SendInsertWithNameReference(
     bool is_static,
     uint64_t name_index,
     QuicStringPiece value) {
@@ -27,9 +27,10 @@
   instruction_encoder_.Encode(InsertWithNameReferenceInstruction(), values_,
                               &output);
   delegate_->WriteStreamData(output);
+  return output.size();
 }
 
-void QpackEncoderStreamSender::SendInsertWithoutNameReference(
+QuicByteCount QpackEncoderStreamSender::SendInsertWithoutNameReference(
     QuicStringPiece name,
     QuicStringPiece value) {
   values_.name = name;
@@ -39,23 +40,27 @@
   instruction_encoder_.Encode(InsertWithoutNameReferenceInstruction(), values_,
                               &output);
   delegate_->WriteStreamData(output);
+  return output.size();
 }
 
-void QpackEncoderStreamSender::SendDuplicate(uint64_t index) {
+QuicByteCount QpackEncoderStreamSender::SendDuplicate(uint64_t index) {
   values_.varint = index;
 
   std::string output;
   instruction_encoder_.Encode(DuplicateInstruction(), values_, &output);
   delegate_->WriteStreamData(output);
+  return output.size();
 }
 
-void QpackEncoderStreamSender::SendSetDynamicTableCapacity(uint64_t capacity) {
+QuicByteCount QpackEncoderStreamSender::SendSetDynamicTableCapacity(
+    uint64_t capacity) {
   values_.varint = capacity;
 
   std::string output;
   instruction_encoder_.Encode(SetDynamicTableCapacityInstruction(), values_,
                               &output);
   delegate_->WriteStreamData(output);
+  return output.size();
 }
 
 }  // namespace quic
diff --git a/quic/core/qpack/qpack_encoder_stream_sender.h b/quic/core/qpack/qpack_encoder_stream_sender.h
index 3c410d5..c8c7717 100644
--- a/quic/core/qpack/qpack_encoder_stream_sender.h
+++ b/quic/core/qpack/qpack_encoder_stream_sender.h
@@ -9,6 +9,7 @@
 
 #include "net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.h"
 #include "net/third_party/quiche/src/quic/core/qpack/qpack_stream_sender_delegate.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
 
@@ -25,16 +26,16 @@
   // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#rfc.section.5.2
 
   // 5.2.1. Insert With Name Reference
-  void SendInsertWithNameReference(bool is_static,
-                                   uint64_t name_index,
-                                   QuicStringPiece value);
+  QuicByteCount SendInsertWithNameReference(bool is_static,
+                                            uint64_t name_index,
+                                            QuicStringPiece value);
   // 5.2.2. Insert Without Name Reference
-  void SendInsertWithoutNameReference(QuicStringPiece name,
-                                      QuicStringPiece value);
+  QuicByteCount SendInsertWithoutNameReference(QuicStringPiece name,
+                                               QuicStringPiece value);
   // 5.2.3. Duplicate
-  void SendDuplicate(uint64_t index);
+  QuicByteCount SendDuplicate(uint64_t index);
   // 5.2.4. Set Dynamic Table Capacity
-  void SendSetDynamicTableCapacity(uint64_t capacity);
+  QuicByteCount SendSetDynamicTableCapacity(uint64_t capacity);
 
   // delegate must be set if dynamic table capacity is not zero.
   void set_qpack_stream_sender_delegate(QpackStreamSenderDelegate* delegate) {
diff --git a/quic/core/qpack/qpack_encoder_stream_sender_test.cc b/quic/core/qpack/qpack_encoder_stream_sender_test.cc
index 4a15d1f..dcfd5bd 100644
--- a/quic/core/qpack/qpack_encoder_stream_sender_test.cc
+++ b/quic/core/qpack/qpack_encoder_stream_sender_test.cc
@@ -28,80 +28,93 @@
 
 TEST_F(QpackEncoderStreamSenderTest, InsertWithNameReference) {
   // Static, index fits in prefix, empty value.
-  EXPECT_CALL(delegate_, WriteStreamData(Eq(QuicTextUtils::HexDecode("c500"))));
-  stream_.SendInsertWithNameReference(true, 5, "");
+  std::string expected_encoded_data = QuicTextUtils::HexDecode("c500");
+  EXPECT_CALL(delegate_, WriteStreamData(Eq(expected_encoded_data)));
+  EXPECT_EQ(expected_encoded_data.size(),
+            stream_.SendInsertWithNameReference(true, 5, ""));
 
   // Static, index fits in prefix, Huffman encoded value.
-  EXPECT_CALL(delegate_,
-              WriteStreamData(Eq(QuicTextUtils::HexDecode("c28294e7"))));
-  stream_.SendInsertWithNameReference(true, 2, "foo");
+  expected_encoded_data = QuicTextUtils::HexDecode("c28294e7");
+  EXPECT_CALL(delegate_, WriteStreamData(Eq(expected_encoded_data)));
+  EXPECT_EQ(expected_encoded_data.size(),
+            stream_.SendInsertWithNameReference(true, 2, "foo"));
 
   // Not static, index does not fit in prefix, not Huffman encoded value.
-  EXPECT_CALL(delegate_,
-              WriteStreamData(Eq(QuicTextUtils::HexDecode("bf4a03626172"))));
-  stream_.SendInsertWithNameReference(false, 137, "bar");
+  expected_encoded_data = QuicTextUtils::HexDecode("bf4a03626172");
+  EXPECT_CALL(delegate_, WriteStreamData(Eq(expected_encoded_data)));
+  EXPECT_EQ(expected_encoded_data.size(),
+            stream_.SendInsertWithNameReference(false, 137, "bar"));
 
   // Value length does not fit in prefix.
   // 'Z' would be Huffman encoded to 8 bits, so no Huffman encoding is used.
-  EXPECT_CALL(
-      delegate_,
-      WriteStreamData(Eq(QuicTextUtils::HexDecode(
-          "aa7f005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
-          "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
-          "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
-          "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"))));
-  stream_.SendInsertWithNameReference(false, 42, std::string(127, 'Z'));
+  expected_encoded_data = QuicTextUtils::HexDecode(
+      "aa7f005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+      "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+      "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+      "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a");
+  EXPECT_CALL(delegate_, WriteStreamData(Eq(expected_encoded_data)));
+  EXPECT_EQ(
+      expected_encoded_data.size(),
+      stream_.SendInsertWithNameReference(false, 42, std::string(127, 'Z')));
 }
 
 TEST_F(QpackEncoderStreamSenderTest, InsertWithoutNameReference) {
   // Empty name and value.
-  EXPECT_CALL(delegate_, WriteStreamData(Eq(QuicTextUtils::HexDecode("4000"))));
-  stream_.SendInsertWithoutNameReference("", "");
+  std::string expected_encoded_data = QuicTextUtils::HexDecode("4000");
+  EXPECT_CALL(delegate_, WriteStreamData(Eq(expected_encoded_data)));
+  EXPECT_EQ(expected_encoded_data.size(),
+            stream_.SendInsertWithoutNameReference("", ""));
 
   // Huffman encoded short strings.
-  EXPECT_CALL(delegate_, WriteStreamData(
-                             Eq(QuicTextUtils::HexDecode("4362617203626172"))));
-  stream_.SendInsertWithoutNameReference("bar", "bar");
+  expected_encoded_data = QuicTextUtils::HexDecode("4362617203626172");
+  EXPECT_CALL(delegate_, WriteStreamData(Eq(expected_encoded_data)));
+  EXPECT_EQ(expected_encoded_data.size(),
+            stream_.SendInsertWithoutNameReference("bar", "bar"));
 
   // Not Huffman encoded short strings.
-  EXPECT_CALL(delegate_,
-              WriteStreamData(Eq(QuicTextUtils::HexDecode("6294e78294e7"))));
-  stream_.SendInsertWithoutNameReference("foo", "foo");
+  expected_encoded_data = QuicTextUtils::HexDecode("6294e78294e7");
+  EXPECT_CALL(delegate_, WriteStreamData(Eq(expected_encoded_data)));
+  EXPECT_EQ(expected_encoded_data.size(),
+            stream_.SendInsertWithoutNameReference("foo", "foo"));
 
   // Not Huffman encoded long strings; length does not fit on prefix.
   // 'Z' would be Huffman encoded to 8 bits, so no Huffman encoding is used.
-  EXPECT_CALL(
-      delegate_,
-      WriteStreamData(Eq(QuicTextUtils::HexDecode(
-          "5f005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a7f"
-          "005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
-          "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
-          "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
-          "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"))));
-  stream_.SendInsertWithoutNameReference(std::string(31, 'Z'),
-                                         std::string(127, 'Z'));
+  expected_encoded_data = QuicTextUtils::HexDecode(
+      "5f005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a7f"
+      "005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+      "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+      "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+      "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a");
+  EXPECT_CALL(delegate_, WriteStreamData(Eq(expected_encoded_data)));
+  EXPECT_EQ(expected_encoded_data.size(),
+            stream_.SendInsertWithoutNameReference(std::string(31, 'Z'),
+                                                   std::string(127, 'Z')));
 }
 
 TEST_F(QpackEncoderStreamSenderTest, Duplicate) {
   // Small index fits in prefix.
-  EXPECT_CALL(delegate_, WriteStreamData(Eq(QuicTextUtils::HexDecode("11"))));
-  stream_.SendDuplicate(17);
+  std::string expected_encoded_data = QuicTextUtils::HexDecode("11");
+  EXPECT_CALL(delegate_, WriteStreamData(Eq(expected_encoded_data)));
+  EXPECT_EQ(expected_encoded_data.size(), stream_.SendDuplicate(17));
 
   // Large index requires two extension bytes.
-  EXPECT_CALL(delegate_,
-              WriteStreamData(Eq(QuicTextUtils::HexDecode("1fd503"))));
-  stream_.SendDuplicate(500);
+  expected_encoded_data = QuicTextUtils::HexDecode("1fd503");
+  EXPECT_CALL(delegate_, WriteStreamData(Eq(expected_encoded_data)));
+  EXPECT_EQ(expected_encoded_data.size(), stream_.SendDuplicate(500));
 }
 
 TEST_F(QpackEncoderStreamSenderTest, SetDynamicTableCapacity) {
   // Small capacity fits in prefix.
-  EXPECT_CALL(delegate_, WriteStreamData(Eq(QuicTextUtils::HexDecode("31"))));
-  stream_.SendSetDynamicTableCapacity(17);
+  std::string expected_encoded_data = QuicTextUtils::HexDecode("31");
+  EXPECT_CALL(delegate_, WriteStreamData(Eq(expected_encoded_data)));
+  EXPECT_EQ(expected_encoded_data.size(),
+            stream_.SendSetDynamicTableCapacity(17));
 
   // Large capacity requires two extension bytes.
-  EXPECT_CALL(delegate_,
-              WriteStreamData(Eq(QuicTextUtils::HexDecode("3fd503"))));
-  stream_.SendSetDynamicTableCapacity(500);
+  expected_encoded_data = QuicTextUtils::HexDecode("3fd503");
+  EXPECT_CALL(delegate_, WriteStreamData(Eq(expected_encoded_data)));
+  EXPECT_EQ(expected_encoded_data.size(),
+            stream_.SendSetDynamicTableCapacity(500));
 }
 
 }  // namespace
diff --git a/quic/core/qpack/qpack_encoder_test.cc b/quic/core/qpack/qpack_encoder_test.cc
index 0da6f3e..f230942 100644
--- a/quic/core/qpack/qpack_encoder_test.cc
+++ b/quic/core/qpack/qpack_encoder_test.cc
@@ -25,7 +25,9 @@
 
 class QpackEncoderTest : public QuicTest {
  protected:
-  QpackEncoderTest() : encoder_(&decoder_stream_error_delegate_) {
+  QpackEncoderTest()
+      : encoder_(&decoder_stream_error_delegate_),
+        encoder_stream_sent_byte_count_(0) {
     encoder_.set_qpack_stream_sender_delegate(&encoder_stream_sender_delegate_);
     encoder_.SetMaximumBlockedStreams(1);
   }
@@ -33,12 +35,14 @@
   ~QpackEncoderTest() override = default;
 
   std::string Encode(const spdy::SpdyHeaderBlock& header_list) {
-    return encoder_.EncodeHeaderList(/* stream_id = */ 1, header_list);
+    return encoder_.EncodeHeaderList(/* stream_id = */ 1, header_list,
+                                     &encoder_stream_sent_byte_count_);
   }
 
   StrictMock<MockDecoderStreamErrorDelegate> decoder_stream_error_delegate_;
   StrictMock<MockQpackStreamSenderDelegate> encoder_stream_sender_delegate_;
   QpackEncoder encoder_;
+  QuicByteCount encoder_stream_sent_byte_count_;
 };
 
 TEST_F(QpackEncoderTest, Empty) {
@@ -199,24 +203,32 @@
   header_list["cookie"] = "baz";              // name matches static entry
 
   // Insert three entries into the dynamic table.
+  std::string insert_entry1 = QuicTextUtils::HexDecode(
+      "62"          // insert without name reference
+      "94e7"        // Huffman-encoded name "foo"
+      "03626172");  // value "bar"
   EXPECT_CALL(encoder_stream_sender_delegate_,
-              WriteStreamData(Eq(QuicTextUtils::HexDecode(
-                  "62"             // insert without name reference
-                  "94e7"           // Huffman-encoded name "foo"
-                  "03626172"))));  // value "bar"
+              WriteStreamData(Eq(insert_entry1)));
+
+  std::string insert_entry2 = QuicTextUtils::HexDecode(
+      "80"          // insert with name reference, dynamic index 0
+      "0362617a");  // value "baz"
   EXPECT_CALL(encoder_stream_sender_delegate_,
-              WriteStreamData(Eq(QuicTextUtils::HexDecode(
-                  "80"  // insert with name reference, dynamic index 0
-                  "0362617a"))));  // value "baz"
+              WriteStreamData(Eq(insert_entry2)));
+
+  std::string insert_entry3 = QuicTextUtils::HexDecode(
+      "c5"          // insert with name reference, static index 5
+      "0362617a");  // value "baz"
   EXPECT_CALL(encoder_stream_sender_delegate_,
-              WriteStreamData(Eq(QuicTextUtils::HexDecode(
-                  "c5"             // insert with name reference, static index 5
-                  "0362617a"))));  // value "baz"
+              WriteStreamData(Eq(insert_entry3)));
 
   EXPECT_EQ(QuicTextUtils::HexDecode(
                 "0400"      // prefix
                 "828180"),  // dynamic entries with relative index 0, 1, and 2
             Encode(header_list));
+
+  EXPECT_EQ(insert_entry1.size() + insert_entry2.size() + insert_entry3.size(),
+            encoder_stream_sent_byte_count_);
 }
 
 // There is no room in the dynamic table after inserting the first entry.
@@ -237,11 +249,12 @@
   header_list["bar"] = "baz";                 // no match
 
   // Insert one entry into the dynamic table.
+  std::string insert_entry = QuicTextUtils::HexDecode(
+      "62"          // insert without name reference
+      "94e7"        // Huffman-encoded name "foo"
+      "03626172");  // value "bar"
   EXPECT_CALL(encoder_stream_sender_delegate_,
-              WriteStreamData(Eq(QuicTextUtils::HexDecode(
-                  "62"             // insert without name reference
-                  "94e7"           // Huffman-encoded name "foo"
-                  "03626172"))));  // value "bar"
+              WriteStreamData(Eq(insert_entry)));
 
   EXPECT_EQ(QuicTextUtils::HexDecode("0200"  // prefix
                                      "80"    // dynamic entry 0
@@ -252,6 +265,8 @@
                                      "23626172"    // literal name "bar"
                                      "0362617a"),  // with literal value "baz"
             Encode(header_list));
+
+  EXPECT_EQ(insert_entry.size(), encoder_stream_sent_byte_count_);
 }
 
 TEST_F(QpackEncoderTest, BlockedStream) {
@@ -267,15 +282,18 @@
   header_list1["foo"] = "bar";
 
   // Insert one entry into the dynamic table.
+  std::string insert_entry1 = QuicTextUtils::HexDecode(
+      "62"          // insert without name reference
+      "94e7"        // Huffman-encoded name "foo"
+      "03626172");  // value "bar"
   EXPECT_CALL(encoder_stream_sender_delegate_,
-              WriteStreamData(Eq(QuicTextUtils::HexDecode(
-                  "62"             // insert without name reference
-                  "94e7"           // Huffman-encoded name "foo"
-                  "03626172"))));  // value "bar"
+              WriteStreamData(Eq(insert_entry1)));
 
   EXPECT_EQ(QuicTextUtils::HexDecode("0200"  // prefix
                                      "80"),  // dynamic entry 0
-            encoder_.EncodeHeaderList(/* stream_id = */ 1, header_list1));
+            encoder_.EncodeHeaderList(/* stream_id = */ 1, header_list1,
+                                      &encoder_stream_sent_byte_count_));
+  EXPECT_EQ(insert_entry1.size(), encoder_stream_sent_byte_count_);
 
   // Stream 1 is blocked.  Stream 2 is not allowed to block.
   spdy::SpdyHeaderBlock header_list2;
@@ -294,29 +312,40 @@
                                      "0362617a"    // with literal value "baz"
                                      "23626172"    // literal name "bar"
                                      "0362617a"),  // with literal value "baz"
-            encoder_.EncodeHeaderList(/* stream_id = */ 2, header_list2));
+            encoder_.EncodeHeaderList(/* stream_id = */ 2, header_list2,
+                                      &encoder_stream_sent_byte_count_));
+  EXPECT_EQ(0u, encoder_stream_sent_byte_count_);
 
   // Peer acknowledges receipt of one dynamic table entry.
   // Stream 1 is no longer blocked.
   encoder_.OnInsertCountIncrement(1);
 
   // Insert three entries into the dynamic table.
+  std::string insert_entry2 = QuicTextUtils::HexDecode(
+      "80"          // insert with name reference, dynamic index 0
+      "0362617a");  // value "baz"
   EXPECT_CALL(encoder_stream_sender_delegate_,
-              WriteStreamData(Eq(QuicTextUtils::HexDecode(
-                  "80"  // insert with name reference, dynamic index 0
-                  "0362617a"))));  // value "baz"
+              WriteStreamData(Eq(insert_entry2)));
+
+  std::string insert_entry3 = QuicTextUtils::HexDecode(
+      "c5"          // insert with name reference, static index 5
+      "0362617a");  // value "baz"
   EXPECT_CALL(encoder_stream_sender_delegate_,
-              WriteStreamData(Eq(QuicTextUtils::HexDecode(
-                  "c5"             // insert with name reference, static index 5
-                  "0362617a"))));  // value "baz"
+              WriteStreamData(Eq(insert_entry3)));
+
+  std::string insert_entry4 = QuicTextUtils::HexDecode(
+      "43"          // insert without name reference
+      "626172"      // name "bar"
+      "0362617a");  // value "baz"
   EXPECT_CALL(encoder_stream_sender_delegate_,
-              WriteStreamData(Eq(QuicTextUtils::HexDecode(
-                  "43"                       // insert without name reference
-                  "626172"                   // name "bar"
-                  "0362617a"))));            // value "baz"
-  EXPECT_EQ(QuicTextUtils::HexDecode("0500"  // prefix
+              WriteStreamData(Eq(insert_entry4)));
+
+  EXPECT_EQ(QuicTextUtils::HexDecode("0500"        // prefix
                                      "83828180"),  // dynamic entries
-            encoder_.EncodeHeaderList(/* stream_id = */ 3, header_list2));
+            encoder_.EncodeHeaderList(/* stream_id = */ 3, header_list2,
+                                      &encoder_stream_sent_byte_count_));
+  EXPECT_EQ(insert_entry2.size() + insert_entry3.size() + insert_entry4.size(),
+            encoder_stream_sent_byte_count_);
 
   // Stream 3 is blocked.  Stream 4 is not allowed to block, but it can
   // reference already acknowledged dynamic entry 0.
@@ -328,7 +357,9 @@
                                      "0362617a"    // with literal value "baz"
                                      "23626172"    // literal name "bar"
                                      "0362617a"),  // with literal value "baz"
-            encoder_.EncodeHeaderList(/* stream_id = */ 4, header_list2));
+            encoder_.EncodeHeaderList(/* stream_id = */ 4, header_list2,
+                                      &encoder_stream_sent_byte_count_));
+  EXPECT_EQ(0u, encoder_stream_sent_byte_count_);
 
   // Peer acknowledges receipt of two more dynamic table entries.
   // Stream 3 is still blocked.
@@ -340,7 +371,9 @@
                                      "828180"      // dynamic entries
                                      "23626172"    // literal name "bar"
                                      "0362617a"),  // with literal value "baz"
-            encoder_.EncodeHeaderList(/* stream_id = */ 5, header_list2));
+            encoder_.EncodeHeaderList(/* stream_id = */ 5, header_list2,
+                                      &encoder_stream_sent_byte_count_));
+  EXPECT_EQ(0u, encoder_stream_sent_byte_count_);
 
   // Peer acknowledges decoding header block on stream 3.
   // Stream 3 is not blocked any longer.
@@ -348,7 +381,9 @@
 
   EXPECT_EQ(QuicTextUtils::HexDecode("0500"        // prefix
                                      "83828180"),  // dynamic entries
-            encoder_.EncodeHeaderList(/* stream_id = */ 6, header_list2));
+            encoder_.EncodeHeaderList(/* stream_id = */ 6, header_list2,
+                                      &encoder_stream_sent_byte_count_));
+  EXPECT_EQ(0u, encoder_stream_sent_byte_count_);
 }
 
 TEST_F(QpackEncoderTest, Draining) {
diff --git a/quic/core/qpack/qpack_round_trip_test.cc b/quic/core/qpack/qpack_round_trip_test.cc
index 9441b82..beff68e 100644
--- a/quic/core/qpack/qpack_round_trip_test.cc
+++ b/quic/core/qpack/qpack_round_trip_test.cc
@@ -32,7 +32,7 @@
     QpackEncoder encoder(&decoder_stream_error_delegate);
     encoder.set_qpack_stream_sender_delegate(&encoder_stream_sender_delegate);
     std::string encoded_header_block =
-        encoder.EncodeHeaderList(/* stream_id = */ 1, header_list);
+        encoder.EncodeHeaderList(/* stream_id = */ 1, header_list, nullptr);
 
     TestHeadersHandler handler;
     NoopEncoderStreamErrorDelegate encoder_stream_error_delegate;