Add GetLargestGuaranteedMessagePayload to QuicSession/Connection/Generator/Creator to expose the largest message payload that's guaranteed to fit in any packet containing application data.

gfe-relnote: n/a (Only used in Quartc)
PiperOrigin-RevId: 242135946
Change-Id: I52cc763af88d22fc240210e00cf4d4062d0b9c59
diff --git a/quic/core/http/end_to_end_test.cc b/quic/core/http/end_to_end_test.cc
index c3f50ef..6935dee 100644
--- a/quic/core/http/end_to_end_test.cc
+++ b/quic/core/http/end_to_end_test.cc
@@ -3552,8 +3552,8 @@
   }
 
   SetPacketLossPercentage(30);
-  ASSERT_GT(kMaxPacketSize, client_session->GetLargestMessagePayload());
-  ASSERT_LT(0, client_session->GetLargestMessagePayload());
+  ASSERT_GT(kMaxPacketSize, client_session->GetCurrentLargestMessagePayload());
+  ASSERT_LT(0, client_session->GetCurrentLargestMessagePayload());
 
   std::string message_string(kMaxPacketSize, 'a');
   QuicStringPiece message_buffer(message_string);
@@ -3564,20 +3564,23 @@
     QuicConnection::ScopedPacketFlusher flusher(
         client_session->connection(), QuicConnection::SEND_ACK_IF_PENDING);
     // Verify the largest message gets successfully sent.
-    EXPECT_EQ(MessageResult(MESSAGE_STATUS_SUCCESS, 1),
-              client_session->SendMessage(MakeSpan(
-                  client_session->connection()
-                      ->helper()
-                      ->GetStreamSendBufferAllocator(),
-                  QuicStringPiece(message_buffer.data(),
-                                  client_session->GetLargestMessagePayload()),
-                  &storage)));
+    EXPECT_EQ(
+        MessageResult(MESSAGE_STATUS_SUCCESS, 1),
+        client_session->SendMessage(MakeSpan(
+            client_session->connection()
+                ->helper()
+                ->GetStreamSendBufferAllocator(),
+            QuicStringPiece(message_buffer.data(),
+                            client_session->GetCurrentLargestMessagePayload()),
+            &storage)));
     // Send more messages with size (0, largest_payload] until connection is
     // write blocked.
     const int kTestMaxNumberOfMessages = 100;
     for (size_t i = 2; i <= kTestMaxNumberOfMessages; ++i) {
       size_t message_length =
-          random->RandUint64() % client_session->GetLargestMessagePayload() + 1;
+          random->RandUint64() %
+              client_session->GetCurrentLargestMessagePayload() +
+          1;
       MessageResult result = client_session->SendMessage(MakeSpan(
           client_session->connection()
               ->helper()
@@ -3592,17 +3595,17 @@
   }
 
   client_->WaitForDelayedAcks();
-  EXPECT_EQ(
-      MESSAGE_STATUS_TOO_LARGE,
-      client_session
-          ->SendMessage(MakeSpan(
-              client_session->connection()
-                  ->helper()
-                  ->GetStreamSendBufferAllocator(),
-              QuicStringPiece(message_buffer.data(),
-                              client_session->GetLargestMessagePayload() + 1),
-              &storage))
-          .status);
+  EXPECT_EQ(MESSAGE_STATUS_TOO_LARGE,
+            client_session
+                ->SendMessage(MakeSpan(
+                    client_session->connection()
+                        ->helper()
+                        ->GetStreamSendBufferAllocator(),
+                    QuicStringPiece(
+                        message_buffer.data(),
+                        client_session->GetCurrentLargestMessagePayload() + 1),
+                    &storage))
+                .status);
   EXPECT_EQ(QUIC_NO_ERROR, client_->connection_error());
 }
 
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index 68567b7..c6f939e 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -3875,7 +3875,7 @@
              << transport_version();
     return MESSAGE_STATUS_UNSUPPORTED;
   }
-  if (message.total_length() > GetLargestMessagePayload()) {
+  if (message.total_length() > GetCurrentLargestMessagePayload()) {
     return MESSAGE_STATUS_TOO_LARGE;
   }
   if (!CanWrite(HAS_RETRANSMITTABLE_DATA)) {
@@ -3885,8 +3885,12 @@
   return packet_generator_.AddMessageFrame(message_id, message);
 }
 
-QuicPacketLength QuicConnection::GetLargestMessagePayload() const {
-  return packet_generator_.GetLargestMessagePayload();
+QuicPacketLength QuicConnection::GetCurrentLargestMessagePayload() const {
+  return packet_generator_.GetCurrentLargestMessagePayload();
+}
+
+QuicPacketLength QuicConnection::GetGuaranteedLargestMessagePayload() const {
+  return packet_generator_.GetGuaranteedLargestMessagePayload();
 }
 
 bool QuicConnection::ShouldSetAckAlarm() const {
diff --git a/quic/core/quic_connection.h b/quic/core/quic_connection.h
index edd132b..8176934 100644
--- a/quic/core/quic_connection.h
+++ b/quic/core/quic_connection.h
@@ -757,7 +757,13 @@
                                     QuicMemSliceSpan message);
 
   // Returns the largest payload that will fit into a single MESSAGE frame.
-  QuicPacketLength GetLargestMessagePayload() const;
+  // Because overhead can vary during a connection, this method should be
+  // checked for every message.
+  QuicPacketLength GetCurrentLargestMessagePayload() const;
+  // Returns the largest payload that will fit into a single MESSAGE frame at
+  // any point during the connection.  This assumes the version and
+  // connection ID lengths do not change.
+  QuicPacketLength GetGuaranteedLargestMessagePayload() const;
 
   // Return the id of the cipher of the primary decrypter of the framer.
   uint32_t cipher_id() const { return framer_.decrypter()->cipher_id(); }
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index 3d1e483..f5a840a 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -8257,7 +8257,7 @@
       connection_.SupportsMultiplePacketNumberSpaces()) {
     return;
   }
-  std::string message(connection_.GetLargestMessagePayload() * 2, 'a');
+  std::string message(connection_.GetCurrentLargestMessagePayload() * 2, 'a');
   QuicStringPiece message_data(message);
   QuicMemSliceStorage storage(nullptr, 0, nullptr, 0);
   {
@@ -8272,8 +8272,9 @@
         MESSAGE_STATUS_SUCCESS,
         connection_.SendMessage(
             1, MakeSpan(connection_.helper()->GetStreamSendBufferAllocator(),
-                        QuicStringPiece(message_data.data(),
-                                        connection_.GetLargestMessagePayload()),
+                        QuicStringPiece(
+                            message_data.data(),
+                            connection_.GetCurrentLargestMessagePayload()),
                         &storage)));
   }
   // Fail to send a message if connection is congestion control blocked.
@@ -8289,11 +8290,11 @@
   EXPECT_EQ(
       MESSAGE_STATUS_TOO_LARGE,
       connection_.SendMessage(
-          3,
-          MakeSpan(connection_.helper()->GetStreamSendBufferAllocator(),
-                   QuicStringPiece(message_data.data(),
-                                   connection_.GetLargestMessagePayload() + 1),
-                   &storage)));
+          3, MakeSpan(connection_.helper()->GetStreamSendBufferAllocator(),
+                      QuicStringPiece(
+                          message_data.data(),
+                          connection_.GetCurrentLargestMessagePayload() + 1),
+                      &storage)));
 }
 
 // Test to check that the path challenge/path response logic works
diff --git a/quic/core/quic_packet_creator.cc b/quic/core/quic_packet_creator.cc
index 224aadf..4d5bd14 100644
--- a/quic/core/quic_packet_creator.cc
+++ b/quic/core/quic_packet_creator.cc
@@ -10,9 +10,11 @@
 
 #include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
 #include "net/third_party/quiche/src/quic/core/quic_connection_id.h"
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
 #include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
 #include "net/third_party/quiche/src/quic/core/quic_types.h"
 #include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_aligned.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
@@ -984,7 +986,7 @@
   }
 }
 
-QuicPacketLength QuicPacketCreator::GetLargestMessagePayload() const {
+QuicPacketLength QuicPacketCreator::GetCurrentLargestMessagePayload() const {
   if (framer_->transport_version() <= QUIC_VERSION_44) {
     return 0;
   }
@@ -992,13 +994,44 @@
       framer_->transport_version(), GetDestinationConnectionIdLength(),
       GetSourceConnectionIdLength(), IncludeVersionInHeader(),
       IncludeNonceInPublicHeader(), GetPacketNumberLength(),
-      GetRetryTokenLengthLength(), GetRetryToken().length(), GetLengthLength());
+      // No Retry token on packets containing application data.
+      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);
 }
 
+QuicPacketLength QuicPacketCreator::GetGuaranteedLargestMessagePayload() const {
+  if (framer_->transport_version() <= QUIC_VERSION_44) {
+    return 0;
+  }
+  // QUIC Crypto server packets may include a diversification nonce.
+  const bool may_include_nonce =
+      framer_->version().handshake_protocol == PROTOCOL_QUIC_CRYPTO &&
+      framer_->perspective() == Perspective::IS_SERVER;
+  // IETF QUIC long headers include a length on client 0RTT packets.
+  QuicVariableLengthIntegerLength length_length =
+      framer_->perspective() == Perspective::IS_CLIENT
+          ? VARIABLE_LENGTH_INTEGER_LENGTH_2
+          : VARIABLE_LENGTH_INTEGER_LENGTH_0;
+  const size_t packet_header_size = GetPacketHeaderSize(
+      framer_->transport_version(), GetDestinationConnectionIdLength(),
+      // Assume CID lengths don't change, but version may be present.
+      GetSourceConnectionIdLength(), kIncludeVersion, may_include_nonce,
+      PACKET_4BYTE_PACKET_NUMBER,
+      // No Retry token on packets containing application data.
+      VARIABLE_LENGTH_INTEGER_LENGTH_0, 0, length_length);
+  // This is the largest possible message payload when the length field is
+  // omitted.
+  const QuicPacketLength largest_payload =
+      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;
+}
+
 bool QuicPacketCreator::HasIetfLongHeader() const {
   return framer_->transport_version() > QUIC_VERSION_43 &&
          packet_.encryption_level < ENCRYPTION_FORWARD_SECURE;
diff --git a/quic/core/quic_packet_creator.h b/quic/core/quic_packet_creator.h
index 7c8f496..ea93552 100644
--- a/quic/core/quic_packet_creator.h
+++ b/quic/core/quic_packet_creator.h
@@ -255,7 +255,11 @@
   void SetRetryToken(QuicStringPiece retry_token);
 
   // Returns the largest payload that will fit into a single MESSAGE frame.
-  QuicPacketLength GetLargestMessagePayload() const;
+  QuicPacketLength GetCurrentLargestMessagePayload() const;
+  // Returns the largest payload that will fit into a single MESSAGE frame at
+  // any point during the connection.  This assumes the version and
+  // connection ID lengths do not change.
+  QuicPacketLength GetGuaranteedLargestMessagePayload() const;
 
   void set_debug_delegate(DebugDelegate* debug_delegate) {
     debug_delegate_ = debug_delegate;
diff --git a/quic/core/quic_packet_creator_test.cc b/quic/core/quic_packet_creator_test.cc
index aea0540..aa2f6f7 100644
--- a/quic/core/quic_packet_creator_test.cc
+++ b/quic/core/quic_packet_creator_test.cc
@@ -1606,9 +1606,9 @@
           Invoke(this, &QuicPacketCreatorTest::ClearSerializedPacketForTests));
   QuicMemSliceStorage storage(nullptr, 0, nullptr, 0);
   // Verify that there is enough room for the largest message payload.
-  EXPECT_TRUE(
-      creator_.HasRoomForMessageFrame(creator_.GetLargestMessagePayload()));
-  std::string message(creator_.GetLargestMessagePayload(), 'a');
+  EXPECT_TRUE(creator_.HasRoomForMessageFrame(
+      creator_.GetCurrentLargestMessagePayload()));
+  std::string message(creator_.GetCurrentLargestMessagePayload(), 'a');
   QuicMessageFrame* message_frame =
       new QuicMessageFrame(1, MakeSpan(&allocator_, message, &storage));
   EXPECT_TRUE(
@@ -1639,8 +1639,8 @@
   EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(frame4), NOT_RETRANSMISSION));
   EXPECT_TRUE(creator_.HasPendingFrames());
   // Verify there is not enough room for largest payload.
-  EXPECT_FALSE(
-      creator_.HasRoomForMessageFrame(creator_.GetLargestMessagePayload()));
+  EXPECT_FALSE(creator_.HasRoomForMessageFrame(
+      creator_.GetCurrentLargestMessagePayload()));
   // Add largest message will causes the flush of the stream frame.
   QuicMessageFrame frame5(5, MakeSpan(&allocator_, message, &storage));
   EXPECT_FALSE(creator_.AddSavedFrame(QuicFrame(&frame5), NOT_RETRANSMISSION));
@@ -1654,31 +1654,40 @@
   std::string message_data(kDefaultMaxPacketSize, 'a');
   QuicStringPiece message_buffer(message_data);
   QuicMemSliceStorage storage(nullptr, 0, nullptr, 0);
-  // Test all possible size of message frames.
-  for (size_t message_size = 0;
-       message_size <= creator_.GetLargestMessagePayload(); ++message_size) {
-    QuicMessageFrame* frame = new QuicMessageFrame(
-        0, MakeSpan(&allocator_,
-                    QuicStringPiece(message_buffer.data(), message_size),
-                    &storage));
-    EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(frame), NOT_RETRANSMISSION));
-    EXPECT_TRUE(creator_.HasPendingFrames());
+  // Test all possible encryption levels of message frames.
+  for (EncryptionLevel level :
+       {ENCRYPTION_ZERO_RTT, ENCRYPTION_FORWARD_SECURE}) {
+    creator_.set_encryption_level(level);
+    // Test all possible sizes of message frames.
+    for (size_t message_size = 0;
+         message_size <= creator_.GetCurrentLargestMessagePayload();
+         ++message_size) {
+      QuicMessageFrame* frame = new QuicMessageFrame(
+          0, MakeSpan(&allocator_,
+                      QuicStringPiece(message_buffer.data(), message_size),
+                      &storage));
+      EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(frame), NOT_RETRANSMISSION));
+      EXPECT_TRUE(creator_.HasPendingFrames());
 
-    size_t expansion_bytes = message_size >= 64 ? 2 : 1;
-    EXPECT_EQ(expansion_bytes, creator_.ExpansionOnNewFrame());
-    // Verify BytesFree returns bytes available for the next frame, which should
-    // subtract the message length.
-    size_t expected_bytes_free =
-        creator_.GetLargestMessagePayload() - message_size < expansion_bytes
-            ? 0
-            : creator_.GetLargestMessagePayload() - expansion_bytes -
-                  message_size;
-    EXPECT_EQ(expected_bytes_free, creator_.BytesFree());
-    EXPECT_CALL(delegate_, OnSerializedPacket(_))
-        .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket));
-    creator_.Flush();
-    ASSERT_TRUE(serialized_packet_.encrypted_buffer);
-    DeleteSerializedPacket();
+      size_t expansion_bytes = message_size >= 64 ? 2 : 1;
+      EXPECT_EQ(expansion_bytes, creator_.ExpansionOnNewFrame());
+      // Verify BytesFree returns bytes available for the next frame, which
+      // should subtract the message length.
+      size_t expected_bytes_free =
+          creator_.GetCurrentLargestMessagePayload() - message_size <
+                  expansion_bytes
+              ? 0
+              : creator_.GetCurrentLargestMessagePayload() - expansion_bytes -
+                    message_size;
+      EXPECT_EQ(expected_bytes_free, creator_.BytesFree());
+      EXPECT_LE(creator_.GetGuaranteedLargestMessagePayload(),
+                creator_.GetCurrentLargestMessagePayload());
+      EXPECT_CALL(delegate_, OnSerializedPacket(_))
+          .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket));
+      creator_.Flush();
+      ASSERT_TRUE(serialized_packet_.encrypted_buffer);
+      DeleteSerializedPacket();
+    }
   }
 }
 
diff --git a/quic/core/quic_packet_generator.cc b/quic/core/quic_packet_generator.cc
index 8267ade..831689b 100644
--- a/quic/core/quic_packet_generator.cc
+++ b/quic/core/quic_packet_generator.cc
@@ -472,7 +472,7 @@
     MaybeBundleAckOpportunistically();
   }
   const QuicByteCount message_length = message.total_length();
-  if (message_length > GetLargestMessagePayload()) {
+  if (message_length > GetCurrentLargestMessagePayload()) {
     return MESSAGE_STATUS_TOO_LARGE;
   }
   SendQueuedFrames(/*flush=*/false);
@@ -530,8 +530,13 @@
   return true;
 }
 
-QuicPacketLength QuicPacketGenerator::GetLargestMessagePayload() const {
-  return packet_creator_.GetLargestMessagePayload();
+QuicPacketLength QuicPacketGenerator::GetCurrentLargestMessagePayload() const {
+  return packet_creator_.GetCurrentLargestMessagePayload();
+}
+
+QuicPacketLength QuicPacketGenerator::GetGuaranteedLargestMessagePayload()
+    const {
+  return packet_creator_.GetGuaranteedLargestMessagePayload();
 }
 
 void QuicPacketGenerator::SetConnectionId(QuicConnectionId connection_id) {
diff --git a/quic/core/quic_packet_generator.h b/quic/core/quic_packet_generator.h
index 4c780f5..dc191a0 100644
--- a/quic/core/quic_packet_generator.h
+++ b/quic/core/quic_packet_generator.h
@@ -227,7 +227,8 @@
   bool FlushAckFrame(const QuicFrames& frames);
 
   // Returns the largest payload that will fit into a single MESSAGE frame.
-  QuicPacketLength GetLargestMessagePayload() const;
+  QuicPacketLength GetCurrentLargestMessagePayload() const;
+  QuicPacketLength GetGuaranteedLargestMessagePayload() const;
 
   // Update the connection ID used in outgoing packets.
   void SetConnectionId(QuicConnectionId connection_id);
diff --git a/quic/core/quic_packet_generator_test.cc b/quic/core/quic_packet_generator_test.cc
index bc20bfe..09abb31 100644
--- a/quic/core/quic_packet_generator_test.cc
+++ b/quic/core/quic_packet_generator_test.cc
@@ -1531,9 +1531,10 @@
   EXPECT_EQ(
       MESSAGE_STATUS_SUCCESS,
       generator_.AddMessageFrame(
-          2, MakeSpan(&allocator_,
-                      std::string(generator_.GetLargestMessagePayload(), 'a'),
-                      &storage)));
+          2, MakeSpan(
+                 &allocator_,
+                 std::string(generator_.GetCurrentLargestMessagePayload(), 'a'),
+                 &storage)));
   EXPECT_TRUE(generator_.HasRetransmittableFrames());
 
   // Failed to send messages which cannot fit into one packet.
@@ -1542,7 +1543,8 @@
       generator_.AddMessageFrame(
           3,
           MakeSpan(&allocator_,
-                   std::string(generator_.GetLargestMessagePayload() + 10, 'a'),
+                   std::string(
+                       generator_.GetCurrentLargestMessagePayload() + 10, 'a'),
                    &storage)));
 }
 
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc
index 763d208..01d6feb 100644
--- a/quic/core/quic_session.cc
+++ b/quic/core/quic_session.cc
@@ -1623,8 +1623,12 @@
   return connection_->session_decides_what_to_write();
 }
 
-QuicPacketLength QuicSession::GetLargestMessagePayload() const {
-  return connection_->GetLargestMessagePayload();
+QuicPacketLength QuicSession::GetCurrentLargestMessagePayload() const {
+  return connection_->GetCurrentLargestMessagePayload();
+}
+
+QuicPacketLength QuicSession::GetGuaranteedLargestMessagePayload() const {
+  return connection_->GetGuaranteedLargestMessagePayload();
 }
 
 void QuicSession::SendStopSending(uint16_t code, QuicStreamId stream_id) {
diff --git a/quic/core/quic_session.h b/quic/core/quic_session.h
index 776da8e..b13f627 100644
--- a/quic/core/quic_session.h
+++ b/quic/core/quic_session.h
@@ -320,7 +320,12 @@
   // Returns the largest payload that will fit into a single MESSAGE frame.
   // Because overhead can vary during a connection, this method should be
   // checked for every message.
-  QuicPacketLength GetLargestMessagePayload() const;
+  QuicPacketLength GetCurrentLargestMessagePayload() const;
+
+  // Returns the largest payload that will fit into a single MESSAGE frame at
+  // any point during the connection.  This assumes the version and
+  // connection ID lengths do not change.
+  QuicPacketLength GetGuaranteedLargestMessagePayload() const;
 
   bool goaway_sent() const { return goaway_sent_; }