gfe-relnote: Implement QUIC MTU discovery v2. Protected by --gfe2_reloadable_flag_quic_mtu_discovery_v2.

PiperOrigin-RevId: 274290098
Change-Id: I6a55ff6aced1702fd42841f9a130f4d84076c3ec
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index 97119e8..3d923a2 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -1574,10 +1574,16 @@
 
   void set_packets_between_probes_base(
       const QuicPacketCount packets_between_probes_base) {
-    QuicConnectionPeer::SetPacketsBetweenMtuProbes(&connection_,
-                                                   packets_between_probes_base);
-    QuicConnectionPeer::SetNextMtuProbeAt(
-        &connection_, QuicPacketNumber(packets_between_probes_base));
+    if (GetQuicReloadableFlag(quic_mtu_discovery_v2)) {
+      QuicConnectionPeer::ReInitializeMtuDiscoverer(
+          &connection_, packets_between_probes_base,
+          QuicPacketNumber(packets_between_probes_base));
+    } else {
+      QuicConnectionPeer::SetPacketsBetweenMtuProbes(
+          &connection_, packets_between_probes_base);
+      QuicConnectionPeer::SetNextMtuProbeAt(
+          &connection_, QuicPacketNumber(packets_between_probes_base));
+    }
   }
 
   bool IsDefaultTestConfiguration() {
@@ -4933,7 +4939,7 @@
   }
 }
 
-// Tests whether MTU discovery works when the probe gets acknowledged on the
+// Tests whether MTU discovery works when all probes are acknowledged on the
 // first try.
 TEST_P(QuicConnectionTest, MtuDiscoveryEnabled) {
   EXPECT_TRUE(connection_.connected());
@@ -4949,11 +4955,11 @@
   // for ones using TLS.
   connection_.SetEncrypter(ENCRYPTION_INITIAL, nullptr);
 
-  connection_.EnablePathMtuDiscovery(send_algorithm_);
-
   const QuicPacketCount packets_between_probes_base = 5;
   set_packets_between_probes_base(packets_between_probes_base);
 
+  connection_.EnablePathMtuDiscovery(send_algorithm_);
+
   // Send enough packets so that the next one triggers path MTU discovery.
   for (QuicPacketCount i = 0; i < packets_between_probes_base - 1; i++) {
     SendStreamDataToPeer(3, ".", i, NO_FIN, nullptr);
@@ -4968,7 +4974,12 @@
   EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
       .WillOnce(SaveArg<3>(&probe_size));
   connection_.GetMtuDiscoveryAlarm()->Fire();
-  EXPECT_EQ(kMtuDiscoveryTargetPacketSizeHigh, probe_size);
+  if (GetQuicReloadableFlag(quic_mtu_discovery_v2)) {
+    EXPECT_THAT(probe_size, InRange(connection_.max_packet_length(),
+                                    kMtuDiscoveryTargetPacketSizeHigh));
+  } else {
+    EXPECT_EQ(kMtuDiscoveryTargetPacketSizeHigh, probe_size);
+  }
 
   const QuicPacketNumber probe_packet_number =
       FirstSendingPacketNumber() + packets_between_probes_base;
@@ -4977,19 +4988,99 @@
   // Acknowledge all packets sent so far.
   QuicAckFrame probe_ack = InitAckFrame(probe_packet_number);
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
-  EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+  EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _))
+      .Times(AnyNumber());
   ProcessAckPacket(&probe_ack);
-  EXPECT_EQ(kMtuDiscoveryTargetPacketSizeHigh, connection_.max_packet_length());
+  EXPECT_EQ(probe_size, connection_.max_packet_length());
   EXPECT_EQ(0u, connection_.GetBytesInFlight());
 
-  // Send more packets, and ensure that none of them sets the alarm.
-  for (QuicPacketCount i = 0; i < 4 * packets_between_probes_base; i++) {
-    SendStreamDataToPeer(3, ".", packets_between_probes_base + i, NO_FIN,
-                         nullptr);
+  EXPECT_EQ(1u, connection_.mtu_probe_count());
+
+  if (!GetQuicReloadableFlag(quic_mtu_discovery_v2)) {
+    // Send more packets, and ensure that none of them sets the alarm.
+    for (QuicPacketCount i = 0; i < 4 * packets_between_probes_base; i++) {
+      SendStreamDataToPeer(3, ".", packets_between_probes_base + i, NO_FIN,
+                           nullptr);
+      ASSERT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+    }
+
+    return;
+  }
+
+  QuicStreamOffset stream_offset = packets_between_probes_base;
+  for (size_t num_probes = 1; num_probes < kMtuDiscoveryAttempts;
+       ++num_probes) {
+    // Send just enough packets without triggering the next probe.
+    for (QuicPacketCount i = 0;
+         i < (packets_between_probes_base << num_probes) - 1; ++i) {
+      SendStreamDataToPeer(3, ".", stream_offset++, NO_FIN, nullptr);
+      ASSERT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+    }
+
+    // Trigger the next probe.
+    SendStreamDataToPeer(3, "!", stream_offset++, NO_FIN, nullptr);
+    ASSERT_TRUE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+    QuicByteCount new_probe_size;
+    EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+        .WillOnce(SaveArg<3>(&new_probe_size));
+    connection_.GetMtuDiscoveryAlarm()->Fire();
+    EXPECT_THAT(new_probe_size,
+                InRange(probe_size, kMtuDiscoveryTargetPacketSizeHigh));
+    EXPECT_EQ(num_probes + 1, connection_.mtu_probe_count());
+
+    // Acknowledge all packets sent so far.
+    QuicAckFrame probe_ack = InitAckFrame(creator_->packet_number());
+    ProcessAckPacket(&probe_ack);
+    EXPECT_EQ(new_probe_size, connection_.max_packet_length());
+    EXPECT_EQ(0u, connection_.GetBytesInFlight());
+
+    probe_size = new_probe_size;
+  }
+
+  // The last probe size should be equal to the target.
+  EXPECT_EQ(probe_size, kMtuDiscoveryTargetPacketSizeHigh);
+}
+
+// Simulate the case where the first attempt to send a probe is write blocked,
+// and after unblock, the second attempt returns a MSG_TOO_BIG error.
+TEST_P(QuicConnectionTest, MtuDiscoveryWriteBlocked) {
+  EXPECT_TRUE(connection_.connected());
+
+  connection_.SetEncrypter(ENCRYPTION_INITIAL, nullptr);
+
+  const QuicPacketCount packets_between_probes_base = 5;
+  set_packets_between_probes_base(packets_between_probes_base);
+
+  connection_.EnablePathMtuDiscovery(send_algorithm_);
+
+  // Send enough packets so that the next one triggers path MTU discovery.
+  for (QuicPacketCount i = 0; i < packets_between_probes_base - 1; i++) {
+    SendStreamDataToPeer(3, ".", i, NO_FIN, nullptr);
     ASSERT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet());
   }
 
+  QuicByteCount original_max_packet_length = connection_.max_packet_length();
+
+  // Trigger the probe.
+  SendStreamDataToPeer(3, "!", packets_between_probes_base - 1, NO_FIN,
+                       nullptr);
+  ASSERT_TRUE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+  if (GetQuicReloadableFlag(quic_treat_queued_packets_as_sent)) {
+    EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+  }
+  BlockOnNextWrite();
+  EXPECT_EQ(0u, connection_.NumQueuedPackets());
+  connection_.GetMtuDiscoveryAlarm()->Fire();
   EXPECT_EQ(1u, connection_.mtu_probe_count());
+  EXPECT_EQ(1u, connection_.NumQueuedPackets());
+  ASSERT_TRUE(connection_.connected());
+
+  writer_->SetWritable();
+  SimulateNextPacketTooLarge();
+  connection_.OnCanWrite();
+  EXPECT_EQ(0u, connection_.NumQueuedPackets());
+  EXPECT_EQ(original_max_packet_length, connection_.max_packet_length());
+  EXPECT_TRUE(connection_.connected());
 }
 
 // Tests whether MTU discovery works correctly when the probes never get
@@ -4997,17 +5088,18 @@
 TEST_P(QuicConnectionTest, MtuDiscoveryFailed) {
   EXPECT_TRUE(connection_.connected());
 
-  connection_.EnablePathMtuDiscovery(send_algorithm_);
-
-  const QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(100);
-
-  EXPECT_EQ(kPacketsBetweenMtuProbesBase,
-            QuicConnectionPeer::GetPacketsBetweenMtuProbes(&connection_));
   // Lower the number of probes between packets in order to make the test go
   // much faster.
   const QuicPacketCount packets_between_probes_base = 5;
   set_packets_between_probes_base(packets_between_probes_base);
 
+  connection_.EnablePathMtuDiscovery(send_algorithm_);
+
+  const QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(100);
+
+  EXPECT_EQ(packets_between_probes_base,
+            QuicConnectionPeer::GetPacketsBetweenMtuProbes(&connection_));
+
   // This tests sends more packets than strictly necessary to make sure that if
   // the connection was to send more discovery packets than needed, those would
   // get caught as well.
@@ -5074,6 +5166,108 @@
   EXPECT_EQ(kMtuDiscoveryAttempts, connection_.mtu_probe_count());
 }
 
+// Probe 3 times, the first one succeeds, then fails, then succeeds again.
+TEST_P(QuicConnectionTest, MtuDiscoverySecondProbeFailed) {
+  if (!GetQuicReloadableFlag(quic_mtu_discovery_v2)) {
+    return;
+  }
+  EXPECT_TRUE(connection_.connected());
+
+  // QuicFramer::GetMaxPlaintextSize uses the smallest max plaintext size across
+  // all encrypters. The initial encrypter used with IETF QUIC has a 16-byte
+  // overhead, while the NullEncrypter used throughout this test has a 12-byte
+  // overhead. This test tests behavior that relies on computing the packet size
+  // correctly, so by unsetting the initial encrypter, we avoid having a
+  // mismatch between the overheads for the encrypters used. In non-test
+  // scenarios all encrypters used for a given connection have the same
+  // overhead, either 12 bytes for ones using Google QUIC crypto, or 16 bytes
+  // for ones using TLS.
+  connection_.SetEncrypter(ENCRYPTION_INITIAL, nullptr);
+
+  const QuicPacketCount packets_between_probes_base = 5;
+  set_packets_between_probes_base(packets_between_probes_base);
+
+  connection_.EnablePathMtuDiscovery(send_algorithm_);
+
+  // Send enough packets so that the next one triggers path MTU discovery.
+  QuicStreamOffset stream_offset = 0;
+  for (QuicPacketCount i = 0; i < packets_between_probes_base - 1; i++) {
+    SendStreamDataToPeer(3, ".", stream_offset++, NO_FIN, nullptr);
+    ASSERT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+  }
+
+  // Trigger the probe.
+  SendStreamDataToPeer(3, "!", packets_between_probes_base - 1, NO_FIN,
+                       nullptr);
+  ASSERT_TRUE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+  QuicByteCount probe_size;
+  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+      .WillOnce(SaveArg<3>(&probe_size));
+  connection_.GetMtuDiscoveryAlarm()->Fire();
+  EXPECT_THAT(probe_size, InRange(connection_.max_packet_length(),
+                                  kMtuDiscoveryTargetPacketSizeHigh));
+
+  const QuicPacketNumber probe_packet_number =
+      FirstSendingPacketNumber() + packets_between_probes_base;
+  ASSERT_EQ(probe_packet_number, creator_->packet_number());
+
+  // Acknowledge all packets sent so far.
+  QuicAckFrame first_ack = InitAckFrame(probe_packet_number);
+  EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+  EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _))
+      .Times(AnyNumber());
+  ProcessAckPacket(&first_ack);
+  EXPECT_EQ(probe_size, connection_.max_packet_length());
+  EXPECT_EQ(0u, connection_.GetBytesInFlight());
+
+  EXPECT_EQ(1u, connection_.mtu_probe_count());
+
+  // Send just enough packets without triggering the second probe.
+  for (QuicPacketCount i = 0; i < (packets_between_probes_base << 1) - 1; ++i) {
+    SendStreamDataToPeer(3, ".", stream_offset++, NO_FIN, nullptr);
+    ASSERT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+  }
+
+  // Trigger the second probe.
+  SendStreamDataToPeer(3, "!", stream_offset++, NO_FIN, nullptr);
+  ASSERT_TRUE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+  QuicByteCount second_probe_size;
+  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+      .WillOnce(SaveArg<3>(&second_probe_size));
+  connection_.GetMtuDiscoveryAlarm()->Fire();
+  EXPECT_THAT(second_probe_size,
+              InRange(probe_size, kMtuDiscoveryTargetPacketSizeHigh));
+  EXPECT_EQ(2u, connection_.mtu_probe_count());
+
+  // Acknowledge all packets sent so far, except the second probe.
+  QuicPacketNumber second_probe_packet_number = creator_->packet_number();
+  QuicAckFrame second_ack = InitAckFrame(second_probe_packet_number - 1);
+  ProcessAckPacket(&first_ack);
+  EXPECT_EQ(probe_size, connection_.max_packet_length());
+
+  // Send just enough packets without triggering the third probe.
+  for (QuicPacketCount i = 0; i < (packets_between_probes_base << 2) - 1; ++i) {
+    SendStreamDataToPeer(3, "@", stream_offset++, NO_FIN, nullptr);
+    ASSERT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+  }
+
+  // Trigger the third probe.
+  SendStreamDataToPeer(3, "#", stream_offset++, NO_FIN, nullptr);
+  ASSERT_TRUE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+  QuicByteCount third_probe_size;
+  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+      .WillOnce(SaveArg<3>(&third_probe_size));
+  connection_.GetMtuDiscoveryAlarm()->Fire();
+  EXPECT_THAT(third_probe_size, InRange(probe_size, second_probe_size));
+  EXPECT_EQ(3u, connection_.mtu_probe_count());
+
+  // Acknowledge all packets sent so far, except the second probe.
+  QuicAckFrame third_ack =
+      ConstructAckFrame(creator_->packet_number(), second_probe_packet_number);
+  ProcessAckPacket(&third_ack);
+  EXPECT_EQ(third_probe_size, connection_.max_packet_length());
+}
+
 // Tests whether MTU discovery works when the writer has a limit on how large a
 // packet can be.
 TEST_P(QuicConnectionTest, MtuDiscoveryWriterLimited) {
@@ -5092,11 +5286,12 @@
 
   const QuicByteCount mtu_limit = kMtuDiscoveryTargetPacketSizeHigh - 1;
   writer_->set_max_packet_size(mtu_limit);
-  connection_.EnablePathMtuDiscovery(send_algorithm_);
 
   const QuicPacketCount packets_between_probes_base = 5;
   set_packets_between_probes_base(packets_between_probes_base);
 
+  connection_.EnablePathMtuDiscovery(send_algorithm_);
+
   // Send enough packets so that the next one triggers path MTU discovery.
   for (QuicPacketCount i = 0; i < packets_between_probes_base - 1; i++) {
     SendStreamDataToPeer(3, ".", i, NO_FIN, nullptr);
@@ -5111,7 +5306,12 @@
   EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
       .WillOnce(SaveArg<3>(&probe_size));
   connection_.GetMtuDiscoveryAlarm()->Fire();
-  EXPECT_EQ(mtu_limit, probe_size);
+  if (GetQuicReloadableFlag(quic_mtu_discovery_v2)) {
+    EXPECT_THAT(probe_size,
+                InRange(connection_.max_packet_length(), mtu_limit));
+  } else {
+    EXPECT_EQ(mtu_limit, probe_size);
+  }
 
   const QuicPacketNumber probe_sequence_number =
       FirstSendingPacketNumber() + packets_between_probes_base;
@@ -5120,19 +5320,56 @@
   // Acknowledge all packets sent so far.
   QuicAckFrame probe_ack = InitAckFrame(probe_sequence_number);
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
-  EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _));
+  EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _))
+      .Times(AnyNumber());
   ProcessAckPacket(&probe_ack);
-  EXPECT_EQ(mtu_limit, connection_.max_packet_length());
+  EXPECT_EQ(probe_size, connection_.max_packet_length());
   EXPECT_EQ(0u, connection_.GetBytesInFlight());
 
-  // Send more packets, and ensure that none of them sets the alarm.
-  for (QuicPacketCount i = 0; i < 4 * packets_between_probes_base; i++) {
-    SendStreamDataToPeer(3, ".", packets_between_probes_base + i, NO_FIN,
-                         nullptr);
-    ASSERT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+  EXPECT_EQ(1u, connection_.mtu_probe_count());
+
+  if (!GetQuicReloadableFlag(quic_mtu_discovery_v2)) {
+    // Send more packets, and ensure that none of them sets the alarm.
+    for (QuicPacketCount i = 0; i < 4 * packets_between_probes_base; i++) {
+      SendStreamDataToPeer(3, ".", packets_between_probes_base + i, NO_FIN,
+                           nullptr);
+      ASSERT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+    }
+
+    return;
   }
 
-  EXPECT_EQ(1u, connection_.mtu_probe_count());
+  QuicStreamOffset stream_offset = packets_between_probes_base;
+  for (size_t num_probes = 1; num_probes < kMtuDiscoveryAttempts;
+       ++num_probes) {
+    // Send just enough packets without triggering the next probe.
+    for (QuicPacketCount i = 0;
+         i < (packets_between_probes_base << num_probes) - 1; ++i) {
+      SendStreamDataToPeer(3, ".", stream_offset++, NO_FIN, nullptr);
+      ASSERT_FALSE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+    }
+
+    // Trigger the next probe.
+    SendStreamDataToPeer(3, "!", stream_offset++, NO_FIN, nullptr);
+    ASSERT_TRUE(connection_.GetMtuDiscoveryAlarm()->IsSet());
+    QuicByteCount new_probe_size;
+    EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+        .WillOnce(SaveArg<3>(&new_probe_size));
+    connection_.GetMtuDiscoveryAlarm()->Fire();
+    EXPECT_THAT(new_probe_size, InRange(probe_size, mtu_limit));
+    EXPECT_EQ(num_probes + 1, connection_.mtu_probe_count());
+
+    // Acknowledge all packets sent so far.
+    QuicAckFrame probe_ack = InitAckFrame(creator_->packet_number());
+    ProcessAckPacket(&probe_ack);
+    EXPECT_EQ(new_probe_size, connection_.max_packet_length());
+    EXPECT_EQ(0u, connection_.GetBytesInFlight());
+
+    probe_size = new_probe_size;
+  }
+
+  // The last probe size should be equal to the target.
+  EXPECT_EQ(probe_size, mtu_limit);
 }
 
 // Tests whether MTU discovery works when the writer returns an error despite
@@ -5144,11 +5381,12 @@
   const QuicByteCount initial_mtu = connection_.max_packet_length();
   EXPECT_LT(initial_mtu, mtu_limit);
   writer_->set_max_packet_size(mtu_limit);
-  connection_.EnablePathMtuDiscovery(send_algorithm_);
 
   const QuicPacketCount packets_between_probes_base = 5;
   set_packets_between_probes_base(packets_between_probes_base);
 
+  connection_.EnablePathMtuDiscovery(send_algorithm_);
+
   // Send enough packets so that the next one triggers path MTU discovery.
   for (QuicPacketCount i = 0; i < packets_between_probes_base - 1; i++) {
     SendStreamDataToPeer(3, ".", i, NO_FIN, nullptr);
@@ -5192,11 +5430,11 @@
 TEST_P(QuicConnectionTest, NoMtuDiscoveryAfterConnectionClosed) {
   EXPECT_TRUE(connection_.connected());
 
-  connection_.EnablePathMtuDiscovery(send_algorithm_);
-
   const QuicPacketCount packets_between_probes_base = 10;
   set_packets_between_probes_base(packets_between_probes_base);
 
+  connection_.EnablePathMtuDiscovery(send_algorithm_);
+
   // Send enough packets so that the next one triggers path MTU discovery.
   for (QuicPacketCount i = 0; i < packets_between_probes_base - 1; i++) {
     SendStreamDataToPeer(3, ".", i, NO_FIN, nullptr);