Plumb max_datagram_frame_size

In QUIC_CRYPTO, the maximum allowed size of a DATAGRAM/MESSAGE frame is unlimited. However, in QUIC+TLS, it's negotiated during the handshake using the max_datagram_frame_size IETF QUIC transport parameter. This CL adds this limit to QuicPacketCreator and plumbs it through during the handshake. Since all of our uses of the DATAGRAM/MESSAGE frame today only support QUIC_CRYPTO, this CL does not change any existing behavior.

Plumb max_datagram_frame_size, no behavior change, not flag protected

PiperOrigin-RevId: 310456458
Change-Id: Ib40d6e7e950865e496809ba5c880dc47cd243be9
diff --git a/quic/core/quic_config.cc b/quic/core/quic_config.cc
index d0f42b7..865aef8 100644
--- a/quic/core/quic_config.cc
+++ b/quic/core/quic_config.cc
@@ -1188,7 +1188,6 @@
   if (params.max_datagram_frame_size.IsValid()) {
     max_datagram_frame_size_.SetReceivedValue(
         params.max_datagram_frame_size.value());
-    // TODO(dschinazi) act on this.
   }
 
   initial_session_flow_control_window_bytes_.SetReceivedValue(
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index 6445fce..7922fdd 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -506,6 +506,10 @@
     packet_creator_.SetMaxPacketLength(
         GetLimitedMaxPacketSize(packet_creator_.max_packet_length()));
   }
+  if (config.HasReceivedMaxDatagramFrameSize()) {
+    packet_creator_.SetMaxDatagramFrameSize(
+        config.ReceivedMaxDatagramFrameSize());
+  }
 
   supports_release_time_ =
       writer_ != nullptr && writer_->SupportsReleaseTime() &&
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index 1822d0c..f3bb4e1 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -9150,6 +9150,13 @@
   if (!VersionSupportsMessageFrames(connection_.transport_version())) {
     return;
   }
+  if (connection_.version().UsesTls()) {
+    QuicConfig config;
+    QuicConfigPeer::SetReceivedMaxDatagramFrameSize(
+        &config, kMaxAcceptedDatagramFrameSize);
+    EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+    connection_.SetFromConfig(config);
+  }
   std::string message(connection_.GetCurrentLargestMessagePayload() * 2, 'a');
   quiche::QuicheStringPiece message_data(message);
   QuicMemSliceStorage storage(nullptr, 0, nullptr, 0);
@@ -9192,6 +9199,94 @@
                 false));
 }
 
+TEST_P(QuicConnectionTest, GetCurrentLargestMessagePayload) {
+  if (!connection_.version().SupportsMessageFrames()) {
+    return;
+  }
+  // Force use of this encrypter to simplify test expectations by making sure
+  // that the encryption overhead is constant across versions.
+  connection_.SetEncrypter(ENCRYPTION_INITIAL,
+                           std::make_unique<TaggingEncrypter>(0x00));
+  QuicPacketLength expected_largest_payload = 1319;
+  if (connection_.version().SendsVariableLengthPacketNumberInLongHeader()) {
+    expected_largest_payload += 3;
+  }
+  if (connection_.version().HasLongHeaderLengths()) {
+    expected_largest_payload -= 2;
+  }
+  if (connection_.version().HasLengthPrefixedConnectionIds()) {
+    expected_largest_payload -= 1;
+  }
+  if (connection_.version().UsesTls()) {
+    // QUIC+TLS disallows DATAGRAM/MESSAGE frames before the handshake.
+    EXPECT_EQ(connection_.GetCurrentLargestMessagePayload(), 0);
+    QuicConfig config;
+    QuicConfigPeer::SetReceivedMaxDatagramFrameSize(
+        &config, kMaxAcceptedDatagramFrameSize);
+    EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+    connection_.SetFromConfig(config);
+    // Verify the value post-handshake.
+    EXPECT_EQ(connection_.GetCurrentLargestMessagePayload(),
+              expected_largest_payload);
+  } else {
+    EXPECT_EQ(connection_.GetCurrentLargestMessagePayload(),
+              expected_largest_payload);
+  }
+}
+
+TEST_P(QuicConnectionTest, GetGuaranteedLargestMessagePayload) {
+  if (!connection_.version().SupportsMessageFrames()) {
+    return;
+  }
+  // Force use of this encrypter to simplify test expectations by making sure
+  // that the encryption overhead is constant across versions.
+  connection_.SetEncrypter(ENCRYPTION_INITIAL,
+                           std::make_unique<TaggingEncrypter>(0x00));
+  QuicPacketLength expected_largest_payload = 1319;
+  if (connection_.version().HasLongHeaderLengths()) {
+    expected_largest_payload -= 2;
+  }
+  if (connection_.version().HasLengthPrefixedConnectionIds()) {
+    expected_largest_payload -= 1;
+  }
+  if (connection_.version().UsesTls()) {
+    // QUIC+TLS disallows DATAGRAM/MESSAGE frames before the handshake.
+    EXPECT_EQ(connection_.GetGuaranteedLargestMessagePayload(), 0);
+    QuicConfig config;
+    QuicConfigPeer::SetReceivedMaxDatagramFrameSize(
+        &config, kMaxAcceptedDatagramFrameSize);
+    EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+    connection_.SetFromConfig(config);
+    // Verify the value post-handshake.
+    EXPECT_EQ(connection_.GetGuaranteedLargestMessagePayload(),
+              expected_largest_payload);
+  } else {
+    EXPECT_EQ(connection_.GetGuaranteedLargestMessagePayload(),
+              expected_largest_payload);
+  }
+}
+
+TEST_P(QuicConnectionTest, LimitedLargestMessagePayload) {
+  if (!connection_.version().SupportsMessageFrames() ||
+      !connection_.version().UsesTls()) {
+    return;
+  }
+  constexpr QuicPacketLength kFrameSizeLimit = 1000;
+  constexpr QuicPacketLength kPayloadSizeLimit =
+      kFrameSizeLimit - kQuicFrameTypeSize;
+  // QUIC+TLS disallows DATAGRAM/MESSAGE frames before the handshake.
+  EXPECT_EQ(connection_.GetCurrentLargestMessagePayload(), 0);
+  EXPECT_EQ(connection_.GetGuaranteedLargestMessagePayload(), 0);
+  QuicConfig config;
+  QuicConfigPeer::SetReceivedMaxDatagramFrameSize(&config, kFrameSizeLimit);
+  EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+  connection_.SetFromConfig(config);
+  // Verify the value post-handshake.
+  EXPECT_EQ(connection_.GetCurrentLargestMessagePayload(), kPayloadSizeLimit);
+  EXPECT_EQ(connection_.GetGuaranteedLargestMessagePayload(),
+            kPayloadSizeLimit);
+}
+
 // Test to check that the path challenge/path response logic works
 // correctly. This test is only for version-99
 TEST_P(QuicConnectionTest, PathChallengeResponse) {
@@ -10575,6 +10670,10 @@
   connection_options.push_back(kPTOS);
   connection_options.push_back(k1PTO);
   config.SetConnectionOptionsToSend(connection_options);
+  if (connection_.version().UsesTls()) {
+    QuicConfigPeer::SetReceivedMaxDatagramFrameSize(
+        &config, kMaxAcceptedDatagramFrameSize);
+  }
   EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
   connection_.SetFromConfig(config);
   EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
diff --git a/quic/core/quic_packet_creator.cc b/quic/core/quic_packet_creator.cc
index cea1a1b..a9d7076 100644
--- a/quic/core/quic_packet_creator.cc
+++ b/quic/core/quic_packet_creator.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <cstddef>
 #include <cstdint>
+#include <limits>
 #include <string>
 #include <utility>
 
@@ -130,8 +131,15 @@
       next_transmission_type_(NOT_RETRANSMISSION),
       flusher_attached_(false),
       fully_pad_crypto_handshake_packets_(true),
-      latched_hard_max_packet_length_(0) {
+      latched_hard_max_packet_length_(0),
+      max_datagram_frame_size_(0) {
   SetMaxPacketLength(kDefaultMaxPacketSize);
+  if (!framer_->version().UsesTls()) {
+    // QUIC+TLS negotiates the maximum datagram frame size via the
+    // IETF QUIC max_datagram_frame_size transport parameter.
+    // QUIC_CRYPTO however does not negotiate this so we set its value here.
+    SetMaxDatagramFrameSize(kMaxAcceptedDatagramFrameSize);
+  }
 }
 
 QuicPacketCreator::~QuicPacketCreator() {
@@ -165,6 +173,21 @@
       << "Attempted to set max packet length too small";
 }
 
+void QuicPacketCreator::SetMaxDatagramFrameSize(
+    QuicByteCount max_datagram_frame_size) {
+  constexpr QuicByteCount upper_bound =
+      std::min<QuicByteCount>(std::numeric_limits<QuicPacketLength>::max(),
+                              std::numeric_limits<size_t>::max());
+  if (max_datagram_frame_size > upper_bound) {
+    // A value of |max_datagram_frame_size| that is equal or greater than
+    // 2^16-1 is effectively infinite because QUIC packets cannot be that large.
+    // We therefore clamp the value here to allow us to safely cast
+    // |max_datagram_frame_size_| to QuicPacketLength or size_t.
+    max_datagram_frame_size = upper_bound;
+  }
+  max_datagram_frame_size_ = max_datagram_frame_size;
+}
+
 void QuicPacketCreator::SetSoftMaxPacketLength(QuicByteCount length) {
   DCHECK(CanSetMaxPacketLength());
   if (length > max_packet_length_) {
@@ -326,6 +349,10 @@
 bool QuicPacketCreator::HasRoomForMessageFrame(QuicByteCount length) {
   const size_t message_frame_size = QuicFramer::GetMessageFrameSize(
       framer_->transport_version(), /*last_frame_in_packet=*/true, length);
+  if (static_cast<QuicByteCount>(message_frame_size) >
+      max_datagram_frame_size_) {
+    return false;
+  }
   if (BytesFree() >= message_frame_size) {
     return true;
   }
@@ -1705,8 +1732,12 @@
       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);
+  size_t largest_frame =
+      max_plaintext_size - std::min(max_plaintext_size, packet_header_size);
+  if (static_cast<QuicByteCount>(largest_frame) > max_datagram_frame_size_) {
+    largest_frame = static_cast<size_t>(max_datagram_frame_size_);
+  }
+  return largest_frame - std::min(largest_frame, kQuicFrameTypeSize);
 }
 
 QuicPacketLength QuicPacketCreator::GetGuaranteedLargestMessagePayload() const {
@@ -1739,9 +1770,13 @@
       latched_hard_max_packet_length_ == 0
           ? max_plaintext_size_
           : framer_->GetMaxPlaintextSize(latched_hard_max_packet_length_);
+  size_t largest_frame =
+      max_plaintext_size - std::min(max_plaintext_size, packet_header_size);
+  if (static_cast<QuicByteCount>(largest_frame) > max_datagram_frame_size_) {
+    largest_frame = static_cast<size_t>(max_datagram_frame_size_);
+  }
   const QuicPacketLength largest_payload =
-      max_plaintext_size -
-      std::min(max_plaintext_size, packet_header_size + kQuicFrameTypeSize);
+      largest_frame - std::min(largest_frame, 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 508cd56..fbe046f 100644
--- a/quic/core/quic_packet_creator.h
+++ b/quic/core/quic_packet_creator.h
@@ -278,6 +278,9 @@
   // Sets the maximum packet length.
   void SetMaxPacketLength(QuicByteCount length);
 
+  // Sets the maximum DATAGRAM/MESSAGE frame size we can send.
+  void SetMaxDatagramFrameSize(QuicByteCount max_datagram_frame_size);
+
   // 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.
@@ -589,6 +592,11 @@
   // SetSoftMaxPacketLength is called and max_packet_length_ gets
   // set to a soft value.
   QuicByteCount latched_hard_max_packet_length_;
+
+  // The maximum length of a MESSAGE/DATAGRAM frame that our peer is willing to
+  // accept. There is no limit for QUIC_CRYPTO connections, but QUIC+TLS
+  // negotiates this during the handshake.
+  QuicByteCount max_datagram_frame_size_;
 };
 
 }  // namespace quic
diff --git a/quic/core/quic_packet_creator_test.cc b/quic/core/quic_packet_creator_test.cc
index 19fc5cb..b11417d 100644
--- a/quic/core/quic_packet_creator_test.cc
+++ b/quic/core/quic_packet_creator_test.cc
@@ -1778,6 +1778,9 @@
   if (!VersionSupportsMessageFrames(client_framer_.transport_version())) {
     return;
   }
+  if (client_framer_.version().UsesTls()) {
+    creator_.SetMaxDatagramFrameSize(kMaxAcceptedDatagramFrameSize);
+  }
   creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE);
   EXPECT_CALL(delegate_, OnSerializedPacket(_))
       .Times(3)
@@ -1830,6 +1833,9 @@
   if (!VersionSupportsMessageFrames(client_framer_.transport_version())) {
     return;
   }
+  if (client_framer_.version().UsesTls()) {
+    creator_.SetMaxDatagramFrameSize(kMaxAcceptedDatagramFrameSize);
+  }
   std::string message_data(kDefaultMaxPacketSize, 'a');
   quiche::QuicheStringPiece message_buffer(message_data);
   QuicMemSliceStorage storage(nullptr, 0, nullptr, 0);
@@ -1871,21 +1877,93 @@
   }
 }
 
-// Regression test for bugfix of GetPacketHeaderSize.
 TEST_P(QuicPacketCreatorTest, GetGuaranteedLargestMessagePayload) {
-  QuicTransportVersion version = creator_.transport_version();
-  if (!VersionSupportsMessageFrames(version)) {
+  ParsedQuicVersion version = GetParam().version;
+  if (!version.SupportsMessageFrames()) {
     return;
   }
+  if (version.UsesTls()) {
+    creator_.SetMaxDatagramFrameSize(kMaxAcceptedDatagramFrameSize);
+  }
   QuicPacketLength expected_largest_payload = 1319;
-  if (QuicVersionHasLongHeaderLengths(version)) {
+  if (version.HasLongHeaderLengths()) {
     expected_largest_payload -= 2;
   }
-  if (GetParam().version.HasLengthPrefixedConnectionIds()) {
+  if (version.HasLengthPrefixedConnectionIds()) {
     expected_largest_payload -= 1;
   }
   EXPECT_EQ(expected_largest_payload,
             creator_.GetGuaranteedLargestMessagePayload());
+  EXPECT_TRUE(creator_.HasRoomForMessageFrame(
+      creator_.GetGuaranteedLargestMessagePayload()));
+
+  // Now test whether SetMaxDatagramFrameSize works.
+  creator_.SetMaxDatagramFrameSize(expected_largest_payload + 1 +
+                                   kQuicFrameTypeSize);
+  EXPECT_EQ(expected_largest_payload,
+            creator_.GetGuaranteedLargestMessagePayload());
+  EXPECT_TRUE(creator_.HasRoomForMessageFrame(
+      creator_.GetGuaranteedLargestMessagePayload()));
+
+  creator_.SetMaxDatagramFrameSize(expected_largest_payload +
+                                   kQuicFrameTypeSize);
+  EXPECT_EQ(expected_largest_payload,
+            creator_.GetGuaranteedLargestMessagePayload());
+  EXPECT_TRUE(creator_.HasRoomForMessageFrame(
+      creator_.GetGuaranteedLargestMessagePayload()));
+
+  creator_.SetMaxDatagramFrameSize(expected_largest_payload - 1 +
+                                   kQuicFrameTypeSize);
+  EXPECT_EQ(expected_largest_payload - 1,
+            creator_.GetGuaranteedLargestMessagePayload());
+  EXPECT_TRUE(creator_.HasRoomForMessageFrame(
+      creator_.GetGuaranteedLargestMessagePayload()));
+
+  constexpr QuicPacketLength kFrameSizeLimit = 1000;
+  constexpr QuicPacketLength kPayloadSizeLimit =
+      kFrameSizeLimit - kQuicFrameTypeSize;
+  creator_.SetMaxDatagramFrameSize(kFrameSizeLimit);
+  EXPECT_EQ(creator_.GetGuaranteedLargestMessagePayload(), kPayloadSizeLimit);
+  EXPECT_TRUE(creator_.HasRoomForMessageFrame(kPayloadSizeLimit));
+  EXPECT_FALSE(creator_.HasRoomForMessageFrame(kPayloadSizeLimit + 1));
+}
+
+TEST_P(QuicPacketCreatorTest, GetCurrentLargestMessagePayload) {
+  ParsedQuicVersion version = GetParam().version;
+  if (!version.SupportsMessageFrames()) {
+    return;
+  }
+  if (version.UsesTls()) {
+    creator_.SetMaxDatagramFrameSize(kMaxAcceptedDatagramFrameSize);
+  }
+  QuicPacketLength expected_largest_payload = 1319;
+  if (version.SendsVariableLengthPacketNumberInLongHeader()) {
+    expected_largest_payload += 3;
+  }
+  if (version.HasLongHeaderLengths()) {
+    expected_largest_payload -= 2;
+  }
+  if (version.HasLengthPrefixedConnectionIds()) {
+    expected_largest_payload -= 1;
+  }
+  EXPECT_EQ(expected_largest_payload,
+            creator_.GetCurrentLargestMessagePayload());
+
+  // Now test whether SetMaxDatagramFrameSize works.
+  creator_.SetMaxDatagramFrameSize(expected_largest_payload + 1 +
+                                   kQuicFrameTypeSize);
+  EXPECT_EQ(expected_largest_payload,
+            creator_.GetCurrentLargestMessagePayload());
+
+  creator_.SetMaxDatagramFrameSize(expected_largest_payload +
+                                   kQuicFrameTypeSize);
+  EXPECT_EQ(expected_largest_payload,
+            creator_.GetCurrentLargestMessagePayload());
+
+  creator_.SetMaxDatagramFrameSize(expected_largest_payload - 1 +
+                                   kQuicFrameTypeSize);
+  EXPECT_EQ(expected_largest_payload - 1,
+            creator_.GetCurrentLargestMessagePayload());
 }
 
 TEST_P(QuicPacketCreatorTest, PacketTransmissionType) {
@@ -2189,6 +2267,9 @@
   // Same for message frame.
   if (VersionSupportsMessageFrames(client_framer_.transport_version())) {
     creator_.SetSoftMaxPacketLength(overhead);
+    if (client_framer_.version().UsesTls()) {
+      creator_.SetMaxDatagramFrameSize(kMaxAcceptedDatagramFrameSize);
+    }
     // Verify GetCurrentLargestMessagePayload is based on the actual
     // max_packet_length.
     EXPECT_LT(1u, creator_.GetCurrentLargestMessagePayload());
@@ -3677,6 +3758,9 @@
   if (!VersionSupportsMessageFrames(framer_.transport_version())) {
     return;
   }
+  if (framer_.version().UsesTls()) {
+    creator_.SetMaxDatagramFrameSize(kMaxAcceptedDatagramFrameSize);
+  }
   quic::QuicMemSliceStorage storage(nullptr, 0, nullptr, 0);
   delegate_.SetCanWriteAnything();
   EXPECT_CALL(delegate_, OnSerializedPacket(_))
diff --git a/quic/test_tools/quic_config_peer.cc b/quic/test_tools/quic_config_peer.cc
index f8f3f42..3e75215 100644
--- a/quic/test_tools/quic_config_peer.cc
+++ b/quic/test_tools/quic_config_peer.cc
@@ -110,5 +110,12 @@
   config->received_original_connection_id_ = original_connection_id;
 }
 
+// static
+void QuicConfigPeer::SetReceivedMaxDatagramFrameSize(
+    QuicConfig* config,
+    uint64_t max_datagram_frame_size) {
+  config->max_datagram_frame_size_.SetReceivedValue(max_datagram_frame_size);
+}
+
 }  // namespace test
 }  // namespace quic
diff --git a/quic/test_tools/quic_config_peer.h b/quic/test_tools/quic_config_peer.h
index 94f30b4..c435f22 100644
--- a/quic/test_tools/quic_config_peer.h
+++ b/quic/test_tools/quic_config_peer.h
@@ -65,6 +65,9 @@
   static void SetReceivedOriginalConnectionId(
       QuicConfig* config,
       const QuicConnectionId& original_connection_id);
+
+  static void SetReceivedMaxDatagramFrameSize(QuicConfig* config,
+                                              uint64_t max_datagram_frame_size);
 };
 
 }  // namespace test