gfe-relnote: In QUIC, add SetSoftMaxPacketLength to set a soft max packet length in creator. If a single frame is failed to be serialized, restore the actual max packet length. Not used yet. Not protected.

PiperOrigin-RevId: 278424350
Change-Id: Ie89ba741b6798dee0683b4678edc9242f37f7672
diff --git a/quic/core/crypto/aead_base_encrypter.cc b/quic/core/crypto/aead_base_encrypter.cc
index 405292e..a3173f5 100644
--- a/quic/core/crypto/aead_base_encrypter.cc
+++ b/quic/core/crypto/aead_base_encrypter.cc
@@ -168,7 +168,7 @@
 }
 
 size_t AeadBaseEncrypter::GetMaxPlaintextSize(size_t ciphertext_size) const {
-  return ciphertext_size - auth_tag_size_;
+  return ciphertext_size - std::min(ciphertext_size, auth_tag_size_);
 }
 
 size_t AeadBaseEncrypter::GetCiphertextSize(size_t plaintext_size) const {
diff --git a/quic/core/crypto/aes_128_gcm_12_encrypter_test.cc b/quic/core/crypto/aes_128_gcm_12_encrypter_test.cc
index 5ccc44d..d529d5d 100644
--- a/quic/core/crypto/aes_128_gcm_12_encrypter_test.cc
+++ b/quic/core/crypto/aes_128_gcm_12_encrypter_test.cc
@@ -228,6 +228,7 @@
   EXPECT_EQ(1000u, encrypter.GetMaxPlaintextSize(1012));
   EXPECT_EQ(100u, encrypter.GetMaxPlaintextSize(112));
   EXPECT_EQ(10u, encrypter.GetMaxPlaintextSize(22));
+  EXPECT_EQ(0u, encrypter.GetMaxPlaintextSize(11));
 }
 
 TEST_F(Aes128Gcm12EncrypterTest, GetCiphertextSize) {
diff --git a/quic/core/crypto/null_encrypter.cc b/quic/core/crypto/null_encrypter.cc
index 4ad9b2a..1fe0fdf 100644
--- a/quic/core/crypto/null_encrypter.cc
+++ b/quic/core/crypto/null_encrypter.cc
@@ -75,7 +75,7 @@
 }
 
 size_t NullEncrypter::GetMaxPlaintextSize(size_t ciphertext_size) const {
-  return ciphertext_size - GetHashLength();
+  return ciphertext_size - std::min(ciphertext_size, GetHashLength());
 }
 
 size_t NullEncrypter::GetCiphertextSize(size_t plaintext_size) const {
diff --git a/quic/core/crypto/null_encrypter_test.cc b/quic/core/crypto/null_encrypter_test.cc
index fd95cc6..c6a89ef 100644
--- a/quic/core/crypto/null_encrypter_test.cc
+++ b/quic/core/crypto/null_encrypter_test.cc
@@ -87,6 +87,7 @@
   EXPECT_EQ(1000u, encrypter.GetMaxPlaintextSize(1012));
   EXPECT_EQ(100u, encrypter.GetMaxPlaintextSize(112));
   EXPECT_EQ(10u, encrypter.GetMaxPlaintextSize(22));
+  EXPECT_EQ(0u, encrypter.GetMaxPlaintextSize(11));
 }
 
 TEST_F(NullEncrypterTest, GetCiphertextSize) {
diff --git a/quic/core/quic_packet_creator.cc b/quic/core/quic_packet_creator.cc
index fa45cf9..9f7f160 100644
--- a/quic/core/quic_packet_creator.cc
+++ b/quic/core/quic_packet_creator.cc
@@ -124,6 +124,7 @@
       next_transmission_type_(NOT_RETRANSMISSION),
       flusher_attached_(false),
       fully_pad_crypto_handshake_packets_(true),
+      latched_hard_max_packet_length_(0),
       combine_generator_and_creator_(
           GetQuicReloadableFlag(quic_combine_generator_and_creator)),
       populate_nonretransmittable_frames_(
@@ -168,6 +169,25 @@
       << "Attempted to set max packet length too small";
 }
 
+void QuicPacketCreator::SetSoftMaxPacketLength(QuicByteCount length) {
+  DCHECK(CanSetMaxPacketLength());
+  if (length > max_packet_length_) {
+    QUIC_BUG << ENDPOINT
+             << "Try to increase max_packet_length_ in "
+                "SetSoftMaxPacketLength, use SetMaxPacketLength instead.";
+    return;
+  }
+  if (framer_->GetMaxPlaintextSize(length) <
+      PacketHeaderSize() + MinPlaintextPacketSize(framer_->version())) {
+    QUIC_DLOG(INFO) << length << " is too small to fit packet header";
+    return;
+  }
+  QUIC_DVLOG(1) << "Setting soft max packet length to: " << length;
+  latched_hard_max_packet_length_ = max_packet_length_;
+  max_packet_length_ = length;
+  max_plaintext_size_ = framer_->GetMaxPlaintextSize(length);
+}
+
 // Stops serializing version of the protocol in packets sent after this call.
 // A packet that is already open might send kQuicVersionSize bytes less than the
 // maximum packet size if we stop sending version before it is serialized.
@@ -295,14 +315,28 @@
 bool QuicPacketCreator::HasRoomForStreamFrame(QuicStreamId id,
                                               QuicStreamOffset offset,
                                               size_t data_size) {
-  return BytesFree() >
-         QuicFramer::GetMinStreamFrameSize(framer_->transport_version(), id,
-                                           offset, true, data_size);
+  const size_t min_stream_frame_size = QuicFramer::GetMinStreamFrameSize(
+      framer_->transport_version(), id, offset, /*last_frame_in_packet=*/true,
+      data_size);
+  if (BytesFree() > min_stream_frame_size) {
+    return true;
+  }
+  if (!RemoveSoftMaxPacketLength()) {
+    return false;
+  }
+  return BytesFree() > min_stream_frame_size;
 }
 
 bool QuicPacketCreator::HasRoomForMessageFrame(QuicByteCount length) {
-  return BytesFree() >= QuicFramer::GetMessageFrameSize(
-                            framer_->transport_version(), true, length);
+  const size_t message_frame_size = QuicFramer::GetMessageFrameSize(
+      framer_->transport_version(), /*last_frame_in_packet=*/true, length);
+  if (BytesFree() >= message_frame_size) {
+    return true;
+  }
+  if (!RemoveSoftMaxPacketLength()) {
+    return false;
+  }
+  return BytesFree() >= message_frame_size;
 }
 
 // TODO(fkastenholz): this method should not use constant values for
@@ -388,7 +422,8 @@
                                           QuicFrame* frame) {
   size_t min_frame_size =
       QuicFramer::GetMinCryptoFrameSize(write_length, offset);
-  if (BytesFree() <= min_frame_size) {
+  if (BytesFree() <= min_frame_size &&
+      (!RemoveSoftMaxPacketLength() || BytesFree() <= min_frame_size)) {
     return false;
   }
   size_t max_write_length = BytesFree() - min_frame_size;
@@ -423,6 +458,7 @@
 
   SerializedPacket packet(std::move(packet_));
   ClearPacket();
+  RemoveSoftMaxPacketLength();
   delegate_->OnSerializedPacket(&packet);
 }
 
@@ -730,6 +766,7 @@
 QuicPacketCreator::SerializeConnectivityProbingPacket() {
   QUIC_BUG_IF(VersionHasIetfQuicFrames(framer_->transport_version()))
       << "Must not be version 99 to serialize padded ping connectivity probe";
+  RemoveSoftMaxPacketLength();
   QuicPacketHeader header;
   // FillPacketHeader increments packet_number_.
   FillPacketHeader(&header);
@@ -765,6 +802,7 @@
       << "Must be version 99 to serialize path challenge connectivity probe, "
          "is version "
       << framer_->transport_version();
+  RemoveSoftMaxPacketLength();
   QuicPacketHeader header;
   // FillPacketHeader increments packet_number_.
   FillPacketHeader(&header);
@@ -801,6 +839,7 @@
       << "Must be version 99 to serialize path response connectivity probe, is "
          "version "
       << framer_->transport_version();
+  RemoveSoftMaxPacketLength();
   QuicPacketHeader header;
   // FillPacketHeader increments packet_number_.
   FillPacketHeader(&header);
@@ -1115,7 +1154,8 @@
   // the slow path loop.
   bool run_fast_path =
       !has_handshake && state != FIN_AND_PADDING && !HasPendingFrames() &&
-      write_length - total_bytes_consumed > kMaxOutgoingPacketSize;
+      write_length - total_bytes_consumed > kMaxOutgoingPacketSize &&
+      latched_hard_max_packet_length_ == 0;
 
   while (!run_fast_path && delegate_->ShouldGeneratePacket(
                                HAS_RETRANSMITTABLE_DATA,
@@ -1154,7 +1194,8 @@
 
     run_fast_path =
         !has_handshake && state != FIN_AND_PADDING && !HasPendingFrames() &&
-        write_length - total_bytes_consumed > kMaxOutgoingPacketSize;
+        write_length - total_bytes_consumed > kMaxOutgoingPacketSize &&
+        latched_hard_max_packet_length_ == 0;
   }
 
   if (run_fast_path) {
@@ -1449,6 +1490,12 @@
   size_t frame_len = framer_->GetSerializedFrameLength(
       frame, BytesFree(), queued_frames_.empty(),
       /* last_frame_in_packet= */ true, GetPacketNumberLength());
+  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());
+  }
   if (frame_len == 0) {
     // Current open packet is full.
     FlushCurrentPacket();
@@ -1528,6 +1575,21 @@
   return true;
 }
 
+bool QuicPacketCreator::RemoveSoftMaxPacketLength() {
+  if (latched_hard_max_packet_length_ == 0) {
+    return false;
+  }
+  if (!CanSetMaxPacketLength()) {
+    return false;
+  }
+  QUIC_DVLOG(1) << "Restoring max packet length to: "
+                << latched_hard_max_packet_length_;
+  SetMaxPacketLength(latched_hard_max_packet_length_);
+  // Reset latched_max_packet_length_.
+  latched_hard_max_packet_length_ = 0;
+  return true;
+}
+
 void QuicPacketCreator::MaybeAddPadding() {
   // The current packet should have no padding bytes because padding is only
   // added when this method is called just before the packet is serialized.
@@ -1639,8 +1701,12 @@
       VARIABLE_LENGTH_INTEGER_LENGTH_0, 0, GetLengthLength());
   // This is the largest possible message payload when the length field is
   // omitted.
-  return max_plaintext_size_ -
-         std::min(max_plaintext_size_, packet_header_size + kQuicFrameTypeSize);
+  size_t max_plaintext_size =
+      latched_hard_max_packet_length_ == 0
+          ? max_plaintext_size_
+          : framer_->GetMaxPlaintextSize(latched_hard_max_packet_length_);
+  return max_plaintext_size -
+         std::min(max_plaintext_size, packet_header_size + kQuicFrameTypeSize);
 }
 
 QuicPacketLength QuicPacketCreator::GetGuaranteedLargestMessagePayload() const {
@@ -1669,9 +1735,13 @@
       VARIABLE_LENGTH_INTEGER_LENGTH_0, 0, length_length);
   // This is the largest possible message payload when the length field is
   // omitted.
+  size_t max_plaintext_size =
+      latched_hard_max_packet_length_ == 0
+          ? max_plaintext_size_
+          : framer_->GetMaxPlaintextSize(latched_hard_max_packet_length_);
   const QuicPacketLength largest_payload =
-      max_plaintext_size_ -
-      std::min(max_plaintext_size_, packet_header_size + kQuicFrameTypeSize);
+      max_plaintext_size -
+      std::min(max_plaintext_size, packet_header_size + kQuicFrameTypeSize);
   // This must always be less than or equal to GetCurrentLargestMessagePayload.
   DCHECK_LE(largest_payload, GetCurrentLargestMessagePayload());
   return largest_payload;
diff --git a/quic/core/quic_packet_creator.h b/quic/core/quic_packet_creator.h
index 73e69c1..b8240c1 100644
--- a/quic/core/quic_packet_creator.h
+++ b/quic/core/quic_packet_creator.h
@@ -270,6 +270,11 @@
   // Sets the maximum packet length.
   void SetMaxPacketLength(QuicByteCount length);
 
+  // Set a soft maximum packet length in the creator. If a packet cannot be
+  // successfully created, creator will remove the soft limit and use the actual
+  // max packet length.
+  void SetSoftMaxPacketLength(QuicByteCount length);
+
   // Increases pending_padding_bytes by |size|. Pending padding will be sent by
   // MaybeAddPadding().
   void AddPendingPadding(QuicByteCount size);
@@ -476,6 +481,13 @@
   // Returns true on success.
   bool MaybeCoalesceStreamFrame(const QuicStreamFrame& frame);
 
+  // Called to remove the soft max_packet_length and restores
+  // latched_hard_max_packet_length_ if the packet cannot accommodate a single
+  // frame. Returns true if the soft limit is successfully removed. Returns
+  // false if either there is no current soft limit or there are queued frames
+  // (such that the packet length cannot be changed).
+  bool RemoveSoftMaxPacketLength();
+
   // Returns true if a diversification nonce should be included in the current
   // packet's header.
   bool IncludeNonceInPublicHeader() const;
@@ -575,6 +587,11 @@
   // flusher detaches.
   QuicPacketNumber write_start_packet_number_;
 
+  // If not 0, this latches the actual max_packet_length when
+  // SetSoftMaxPacketLength is called and max_packet_length_ gets
+  // set to a soft value.
+  QuicByteCount latched_hard_max_packet_length_;
+
   // Latched value of quic_combine_generator_and_creator.
   const bool combine_generator_and_creator_;
 
diff --git a/quic/core/quic_packet_creator_test.cc b/quic/core/quic_packet_creator_test.cc
index 5a0c9db..0ba7bd1 100644
--- a/quic/core/quic_packet_creator_test.cc
+++ b/quic/core/quic_packet_creator_test.cc
@@ -2167,6 +2167,75 @@
   }
 }
 
+TEST_P(QuicPacketCreatorTest, SoftMaxPacketLength) {
+  creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE);
+  QuicByteCount previous_max_packet_length = creator_.max_packet_length();
+  const size_t overhead =
+      GetPacketHeaderOverhead(client_framer_.transport_version()) +
+      QuicPacketCreator::MinPlaintextPacketSize(client_framer_.version()) +
+      GetEncryptionOverhead();
+  // Make sure a length which cannot accommodate header (includes header
+  // protection minimal length) gets rejected.
+  creator_.SetSoftMaxPacketLength(overhead - 1);
+  EXPECT_EQ(previous_max_packet_length, creator_.max_packet_length());
+
+  creator_.SetSoftMaxPacketLength(overhead);
+  EXPECT_EQ(overhead, creator_.max_packet_length());
+
+  // Verify creator has room for stream frame because max_packet_length_ gets
+  // restored.
+  ASSERT_TRUE(creator_.HasRoomForStreamFrame(GetNthClientInitiatedStreamId(1),
+                                             kMaxIetfVarInt, kMaxIetfVarInt));
+  EXPECT_EQ(previous_max_packet_length, creator_.max_packet_length());
+
+  // Same for message frame.
+  if (VersionSupportsMessageFrames(client_framer_.transport_version())) {
+    creator_.SetSoftMaxPacketLength(overhead);
+    // Verify GetCurrentLargestMessagePayload is based on the actual
+    // max_packet_length.
+    EXPECT_LT(1u, creator_.GetCurrentLargestMessagePayload());
+    EXPECT_EQ(overhead, creator_.max_packet_length());
+    ASSERT_TRUE(creator_.HasRoomForMessageFrame(
+        creator_.GetCurrentLargestMessagePayload()));
+    EXPECT_EQ(previous_max_packet_length, creator_.max_packet_length());
+  }
+
+  // Verify creator can consume crypto data because max_packet_length_ gets
+  // restored.
+  creator_.SetSoftMaxPacketLength(overhead);
+  EXPECT_EQ(overhead, creator_.max_packet_length());
+  std::string data = "crypto data";
+  MakeIOVector(data, &iov_);
+  QuicFrame frame;
+  if (!QuicVersionUsesCryptoFrames(client_framer_.transport_version())) {
+    ASSERT_TRUE(creator_.ConsumeDataToFillCurrentPacket(
+        QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), &iov_,
+        1u, iov_.iov_len, 0u, kOffset, false, true, NOT_RETRANSMISSION,
+        &frame));
+    size_t bytes_consumed = frame.stream_frame.data_length;
+    EXPECT_LT(0u, bytes_consumed);
+  } else {
+    producer_.SaveCryptoData(ENCRYPTION_INITIAL, kOffset, data);
+    ASSERT_TRUE(creator_.ConsumeCryptoDataToFillCurrentPacket(
+        ENCRYPTION_INITIAL, data.length(), kOffset,
+        /*needs_full_padding=*/true, NOT_RETRANSMISSION, &frame));
+    size_t bytes_consumed = frame.crypto_frame->data_length;
+    EXPECT_LT(0u, bytes_consumed);
+  }
+  EXPECT_TRUE(creator_.HasPendingFrames());
+  EXPECT_CALL(delegate_, OnSerializedPacket(_))
+      .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket));
+  creator_.FlushCurrentPacket();
+
+  // Verify ACK frame can be consumed.
+  creator_.SetSoftMaxPacketLength(overhead);
+  EXPECT_EQ(overhead, creator_.max_packet_length());
+  QuicAckFrame ack_frame(InitAckFrame(10u));
+  EXPECT_TRUE(
+      creator_.AddSavedFrame(QuicFrame(&ack_frame), NOT_RETRANSMISSION));
+  EXPECT_TRUE(creator_.HasPendingFrames());
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic