In quic, consider frame expansion when extra padding is needed to meet the minimum plaintext size needed for header protection. protected by gfe2_reloadable_flag_quic_fix_extra_padding_bytes.

PiperOrigin-RevId: 318497490
Change-Id: If5ade70a1992e6daa7c6d116cb778cb88210981c
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index 7fc45ca..e751f7b 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -11366,7 +11366,8 @@
   if (!connection_.version().CanSendCoalescedPackets()) {
     return;
   }
-  if (GetQuicReloadableFlag(quic_fix_min_crypto_frame_size)) {
+  if (GetQuicReloadableFlag(quic_fix_extra_padding_bytes) ||
+      GetQuicReloadableFlag(quic_fix_min_crypto_frame_size)) {
     EXPECT_CALL(visitor_, OnHandshakePacketSent()).Times(1);
   } else {
     EXPECT_CALL(visitor_, OnHandshakePacketSent()).Times(0);
@@ -11382,7 +11383,8 @@
     SendStreamDataToPeer(2, std::string(1286, 'a'), 0, NO_FIN, nullptr);
     connection_.SetDefaultEncryptionLevel(ENCRYPTION_HANDSHAKE);
     // Try to coalesce a HANDSHAKE packet after 1-RTT packet.
-    if (GetQuicReloadableFlag(quic_fix_min_crypto_frame_size)) {
+    if (GetQuicReloadableFlag(quic_fix_extra_padding_bytes) ||
+        GetQuicReloadableFlag(quic_fix_min_crypto_frame_size)) {
       // Verify soft max packet length gets resumed and handshake packet gets
       // successfully sent.
       connection_.SendCryptoDataWithString("a", 0, ENCRYPTION_HANDSHAKE);
diff --git a/quic/core/quic_packet_creator.cc b/quic/core/quic_packet_creator.cc
index a31b179..56c3050 100644
--- a/quic/core/quic_packet_creator.cc
+++ b/quic/core/quic_packet_creator.cc
@@ -432,10 +432,8 @@
   size_t min_frame_size =
       QuicFramer::GetMinCryptoFrameSize(write_length, offset);
   size_t min_plaintext_bytes = min_frame_size;
-  if (fix_min_crypto_frame_size_ && queued_frames_.empty()) {
-    // TODO(fayang): to make this more general, we need to move the checking
-    // when trying to add a frame when GetSerializedFrameLength. Remove soft
-    // limit if current packet needs extra padding and the space is too small.
+  if (!fix_extra_padding_bytes_ && fix_min_crypto_frame_size_ &&
+      queued_frames_.empty()) {
     QUIC_RELOADABLE_FLAG_COUNT(quic_fix_min_crypto_frame_size);
     min_plaintext_bytes =
         std::max(min_frame_size, MinPlaintextPacketSize(framer_->version()));
@@ -1567,6 +1565,43 @@
       EncryptionlevelToLongHeaderType(packet_.encryption_level);
 }
 
+size_t QuicPacketCreator::GetSerializedFrameLength(const QuicFrame& frame) {
+  size_t serialized_frame_length = framer_->GetSerializedFrameLength(
+      frame, BytesFree(), queued_frames_.empty(),
+      /* last_frame_in_packet= */ true, GetPacketNumberLength());
+  if (!fix_extra_padding_bytes_) {
+    return serialized_frame_length;
+  }
+  if (!framer_->version().HasHeaderProtection() ||
+      serialized_frame_length == 0) {
+    return serialized_frame_length;
+  }
+  // Calculate frame bytes and bytes free with this frame added.
+  const size_t frame_bytes = PacketSize() - PacketHeaderSize() +
+                             ExpansionOnNewFrame() + serialized_frame_length;
+  if (frame_bytes >= MinPlaintextPacketSize(framer_->version())) {
+    // No extra bytes is needed.
+    return serialized_frame_length;
+  }
+  QUIC_RELOADABLE_FLAG_COUNT_N(quic_fix_extra_padding_bytes, 1, 3);
+  if (BytesFree() < serialized_frame_length) {
+    QUIC_BUG << ENDPOINT << "Frame does not fit: " << frame;
+    return 0;
+  }
+  // Please note bytes_free does not take |frame|'s expansion into account.
+  size_t bytes_free = BytesFree() - serialized_frame_length;
+  // Extra bytes needed (this is NOT padding needed) should be at least 1
+  // padding + expansion.
+  const size_t extra_bytes_needed =
+      std::max(1 + ExpansionOnNewFrameWithLastFrame(frame),
+               MinPlaintextPacketSize(framer_->version()) - frame_bytes);
+  if (bytes_free < extra_bytes_needed) {
+    // This frame does not fit.
+    return 0;
+  }
+  return serialized_frame_length;
+}
+
 bool QuicPacketCreator::AddFrame(const QuicFrame& frame,
                                  TransmissionType transmission_type) {
   QUIC_DVLOG(1) << ENDPOINT << "Adding frame with transmission type "
@@ -1594,14 +1629,10 @@
     }
   }
 
-  size_t frame_len = framer_->GetSerializedFrameLength(
-      frame, BytesFree(), queued_frames_.empty(),
-      /* last_frame_in_packet= */ true, GetPacketNumberLength());
+  size_t frame_len = GetSerializedFrameLength(frame);
   if (frame_len == 0 && RemoveSoftMaxPacketLength()) {
     // Remove soft max_packet_length and retry.
-    frame_len = framer_->GetSerializedFrameLength(
-        frame, BytesFree(), queued_frames_.empty(),
-        /* last_frame_in_packet= */ true, GetPacketNumberLength());
+    frame_len = GetSerializedFrameLength(frame);
   }
   if (frame_len == 0) {
     QUIC_DVLOG(1) << "Flushing because current open packet is full when adding "
@@ -1655,6 +1686,25 @@
   return true;
 }
 
+void QuicPacketCreator::MaybeAddExtraPaddingForHeaderProtection() {
+  DCHECK(fix_extra_padding_bytes_);
+  if (!framer_->version().HasHeaderProtection() || needs_full_padding_) {
+    return;
+  }
+  const size_t frame_bytes = PacketSize() - PacketHeaderSize();
+  if (frame_bytes >= MinPlaintextPacketSize(framer_->version())) {
+    return;
+  }
+  const QuicByteCount min_header_protection_padding =
+      std::max(1 + ExpansionOnNewFrame(),
+               MinPlaintextPacketSize(framer_->version()) - frame_bytes) -
+      ExpansionOnNewFrame();
+  QUIC_RELOADABLE_FLAG_COUNT_N(quic_fix_extra_padding_bytes, 2, 3);
+  // Update pending_padding_bytes_.
+  pending_padding_bytes_ =
+      std::max(pending_padding_bytes_, min_header_protection_padding);
+}
+
 bool QuicPacketCreator::MaybeCoalesceStreamFrame(const QuicStreamFrame& frame) {
   if (queued_frames_.empty() || queued_frames_.back().type != STREAM_FRAME) {
     return false;
@@ -1736,15 +1786,21 @@
   }
 
   // Header protection requires a minimum plaintext packet size.
+  // TODO(fayang): remove extra_padding_bytes when deprecating
+  // quic_fix_extra_padding_bytes.
   size_t extra_padding_bytes = 0;
-  if (framer_->version().HasHeaderProtection()) {
-    size_t frame_bytes = PacketSize() - PacketHeaderSize();
+  if (fix_extra_padding_bytes_) {
+    MaybeAddExtraPaddingForHeaderProtection();
+  } else {
+    if (framer_->version().HasHeaderProtection()) {
+      size_t frame_bytes = PacketSize() - PacketHeaderSize();
 
-    if (frame_bytes + pending_padding_bytes_ <
-            MinPlaintextPacketSize(framer_->version()) &&
-        !needs_full_padding_) {
-      extra_padding_bytes =
-          MinPlaintextPacketSize(framer_->version()) - frame_bytes;
+      if (frame_bytes + pending_padding_bytes_ <
+              MinPlaintextPacketSize(framer_->version()) &&
+          !needs_full_padding_) {
+        extra_padding_bytes =
+            MinPlaintextPacketSize(framer_->version()) - frame_bytes;
+      }
     }
   }
 
diff --git a/quic/core/quic_packet_creator.h b/quic/core/quic_packet_creator.h
index ddc5e2d..c579e42 100644
--- a/quic/core/quic_packet_creator.h
+++ b/quic/core/quic_packet_creator.h
@@ -542,6 +542,14 @@
   // Returns true if packet under construction has IETF long header.
   bool HasIetfLongHeader() const;
 
+  // Get serialized frame length. Returns 0 if the frame does not fit into
+  // current packet.
+  size_t GetSerializedFrameLength(const QuicFrame& frame);
+
+  // Add extra padding to pending_padding_bytes_ to meet minimum plaintext
+  // packet size required for header protection.
+  void MaybeAddExtraPaddingForHeaderProtection();
+
   // Does not own these delegates or the framer.
   DelegateInterface* delegate_;
   DebugDelegate* debug_delegate_;
@@ -622,7 +630,11 @@
   // When true, this will override the padding generation code to disable it.
   bool disable_padding_override_ = false;
 
-  bool update_packet_size_ = GetQuicReloadableFlag(quic_update_packet_size);
+  const bool update_packet_size_ =
+      GetQuicReloadableFlag(quic_update_packet_size);
+
+  const bool fix_extra_padding_bytes_ =
+      GetQuicReloadableFlag(quic_fix_extra_padding_bytes);
 };
 
 }  // namespace quic
diff --git a/quic/core/quic_packet_creator_test.cc b/quic/core/quic_packet_creator_test.cc
index 394b82e..7c4416d 100644
--- a/quic/core/quic_packet_creator_test.cc
+++ b/quic/core/quic_packet_creator_test.cc
@@ -3793,6 +3793,31 @@
   EXPECT_EQ(TestConnectionId(0x33), creator_.GetSourceConnectionId());
 }
 
+// Regresstion test for b/159812345.
+TEST_F(QuicPacketCreatorMultiplePacketsTest, ExtraPaddingNeeded) {
+  if (!framer_.version().HasHeaderProtection()) {
+    return;
+  }
+  delegate_.SetCanWriteAnything();
+
+  EXPECT_CALL(delegate_, OnSerializedPacket(_))
+      .WillOnce(
+          Invoke(this, &QuicPacketCreatorMultiplePacketsTest::SavePacket));
+  MakeIOVector("a", &iov_);
+  creator_.ConsumeData(QuicUtils::GetFirstBidirectionalStreamId(
+                           framer_.transport_version(), Perspective::IS_CLIENT),
+                       &iov_, 1u, iov_.iov_len, 0, FIN);
+  creator_.Flush();
+  ASSERT_FALSE(packets_[0].nonretransmittable_frames.empty());
+  QuicFrame padding = packets_[0].nonretransmittable_frames[0];
+  if (GetQuicReloadableFlag(quic_fix_extra_padding_bytes)) {
+    // Verify stream frame expansion is excluded.
+    padding.padding_frame.num_padding_bytes = 3;
+  } else {
+    padding.padding_frame.num_padding_bytes = 4;
+  }
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic