gfe-relnote: In QUIC, call NeuterHandshakePackets() at most once per connection. Protected by gfe2_reloadable_flag_quic_neuter_handshake_packets_once2 which replaces gfe2_reloadable_flag_quic_neuter_handshake_packets_once.

Also fix the bug where RTO and PTO try to retransmit not in flight packets.

PiperOrigin-RevId: 279118870
Change-Id: I1235372277109c18fded6c8d162a2147424d3dbd
diff --git a/quic/core/frames/quic_stream_frame.cc b/quic/core/frames/quic_stream_frame.cc
index c7626d3..d0e65d2 100644
--- a/quic/core/frames/quic_stream_frame.cc
+++ b/quic/core/frames/quic_stream_frame.cc
@@ -49,4 +49,8 @@
          offset == rhs.offset;
 }
 
+bool QuicStreamFrame::operator!=(const QuicStreamFrame& rhs) const {
+  return !(*this == rhs);
+}
+
 }  // namespace quic
diff --git a/quic/core/frames/quic_stream_frame.h b/quic/core/frames/quic_stream_frame.h
index 5e7439d..5c5323b 100644
--- a/quic/core/frames/quic_stream_frame.h
+++ b/quic/core/frames/quic_stream_frame.h
@@ -33,6 +33,8 @@
 
   bool operator==(const QuicStreamFrame& rhs) const;
 
+  bool operator!=(const QuicStreamFrame& rhs) const;
+
   bool fin;
   QuicPacketLength data_length;
   QuicStreamId stream_id;
diff --git a/quic/core/quic_sent_packet_manager.cc b/quic/core/quic_sent_packet_manager.cc
index c8c870b..cdc5a1c 100644
--- a/quic/core/quic_sent_packet_manager.cc
+++ b/quic/core/quic_sent_packet_manager.cc
@@ -107,7 +107,7 @@
       pto_exponential_backoff_start_point_(0),
       pto_rttvar_multiplier_(4),
       neuter_handshake_packets_once_(
-          GetQuicReloadableFlag(quic_neuter_handshake_packets_once)) {
+          GetQuicReloadableFlag(quic_neuter_handshake_packets_once2)) {
   SetSendAlgorithm(congestion_control_type);
 }
 
@@ -342,7 +342,7 @@
 void QuicSentPacketManager::SetHandshakeConfirmed() {
   if (!neuter_handshake_packets_once_ || !handshake_confirmed_) {
     if (neuter_handshake_packets_once_) {
-      QUIC_RELOADABLE_FLAG_COUNT(quic_neuter_handshake_packets_once);
+      QUIC_RELOADABLE_FLAG_COUNT_N(quic_neuter_handshake_packets_once2, 1, 3);
     }
     handshake_confirmed_ = true;
     NeuterHandshakePackets();
@@ -454,7 +454,7 @@
 
 void QuicSentPacketManager::NeuterUnencryptedPackets() {
   QuicPacketNumber packet_number = unacked_packets_.GetLeastUnacked();
-  for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
+  for (QuicUnackedPacketMap::iterator it = unacked_packets_.begin();
        it != unacked_packets_.end(); ++it, ++packet_number) {
     if (!it->retransmittable_frames.empty() &&
         it->encryption_level == ENCRYPTION_INITIAL) {
@@ -462,18 +462,31 @@
       // will be sent. The data has been abandoned in the cryto stream. Remove
       // it from in flight.
       unacked_packets_.RemoveFromInFlight(packet_number);
+      if (neuter_handshake_packets_once_) {
+        QUIC_RELOADABLE_FLAG_COUNT_N(quic_neuter_handshake_packets_once2, 2, 3);
+        it->state = NEUTERED;
+        DCHECK(!unacked_packets_.HasRetransmittableFrames(*it));
+      }
     }
   }
 }
 
 void QuicSentPacketManager::NeuterHandshakePackets() {
   QuicPacketNumber packet_number = unacked_packets_.GetLeastUnacked();
-  for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
+  for (QuicUnackedPacketMap::iterator it = unacked_packets_.begin();
        it != unacked_packets_.end(); ++it, ++packet_number) {
     if (!it->retransmittable_frames.empty() &&
         unacked_packets_.GetPacketNumberSpace(it->encryption_level) ==
             HANDSHAKE_DATA) {
       unacked_packets_.RemoveFromInFlight(packet_number);
+      if (neuter_handshake_packets_once_) {
+        // Notify session that the data has been delivered (but do not notify
+        // send algorithm).
+        QUIC_RELOADABLE_FLAG_COUNT_N(quic_neuter_handshake_packets_once2, 3, 3);
+        it->state = NEUTERED;
+        unacked_packets_.NotifyFramesAcked(*it, QuicTime::Delta::Zero(),
+                                           QuicTime::Zero());
+      }
     }
   }
 }
@@ -765,6 +778,7 @@
     if (it->state == OUTSTANDING &&
         unacked_packets_.HasRetransmittableFrames(*it) &&
         pending_timer_transmission_count_ < max_rto_packets_) {
+      DCHECK(!neuter_handshake_packets_once_ || it->in_flight);
       retransmissions.push_back(packet_number);
       ++pending_timer_transmission_count_;
     }
@@ -797,6 +811,7 @@
        it != unacked_packets_.end(); ++it, ++packet_number) {
     if (it->state == OUTSTANDING &&
         unacked_packets_.HasRetransmittableFrames(*it)) {
+      DCHECK(!neuter_handshake_packets_once_ || it->in_flight);
       probing_packets.push_back(packet_number);
       if (probing_packets.size() == pending_timer_transmission_count_) {
         break;
diff --git a/quic/core/quic_sent_packet_manager.h b/quic/core/quic_sent_packet_manager.h
index 95b9ee2..67ded33 100644
--- a/quic/core/quic_sent_packet_manager.h
+++ b/quic/core/quic_sent_packet_manager.h
@@ -646,7 +646,7 @@
   // The multiplier of rttvar when calculating PTO timeout.
   int pto_rttvar_multiplier_;
 
-  // Latched value of quic_neuter_handshake_packets_once.
+  // Latched value of quic_neuter_handshake_packets_once2.
   const bool neuter_handshake_packets_once_;
 };
 
diff --git a/quic/core/quic_sent_packet_manager_test.cc b/quic/core/quic_sent_packet_manager_test.cc
index 539ce97..1bd08d9 100644
--- a/quic/core/quic_sent_packet_manager_test.cc
+++ b/quic/core/quic_sent_packet_manager_test.cc
@@ -1125,9 +1125,9 @@
 
   // Now neuter all unacked unencrypted packets, which occurs when the
   // connection goes forward secure.
-  manager_.NeuterUnencryptedPackets();
   EXPECT_CALL(notifier_, HasUnackedCryptoData()).WillRepeatedly(Return(false));
   EXPECT_CALL(notifier_, IsFrameOutstanding(_)).WillRepeatedly(Return(false));
+  manager_.NeuterUnencryptedPackets();
   EXPECT_FALSE(manager_.HasUnackedCryptoPackets());
   uint64_t unacked[] = {1, 2, 3};
   VerifyUnackedPackets(unacked, QUIC_ARRAYSIZE(unacked));
@@ -3055,6 +3055,58 @@
             manager_.GetRetransmissionTime());
 }
 
+// Regression test for b/143962153
+TEST_F(QuicSentPacketManagerTest, RtoNotInFlightPacket) {
+  QuicSentPacketManagerPeer::SetMaxTailLossProbes(&manager_, 2);
+  // Send SHLO.
+  QuicStreamFrame crypto_frame(1, false, 0, QuicStringPiece());
+  SendCryptoPacket(1);
+  // Send data packet.
+  SendDataPacket(2, ENCRYPTION_FORWARD_SECURE);
+
+  // Successfully decrypt a forward secure packet.
+  if (GetQuicReloadableFlag(quic_neuter_handshake_packets_once2)) {
+    EXPECT_CALL(notifier_, OnFrameAcked(_, _, _)).Times(1);
+  } else {
+    EXPECT_CALL(notifier_, OnFrameAcked(_, _, _)).Times(0);
+  }
+  manager_.SetHandshakeConfirmed();
+
+  // 1st TLP.
+  manager_.OnRetransmissionTimeout();
+  EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+      .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) {
+        RetransmitDataPacket(3, type, ENCRYPTION_FORWARD_SECURE);
+      })));
+  manager_.MaybeRetransmitTailLossProbe();
+
+  // 2nd TLP.
+  manager_.OnRetransmissionTimeout();
+  EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+      .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) {
+        RetransmitDataPacket(4, type, ENCRYPTION_FORWARD_SECURE);
+      })));
+  manager_.MaybeRetransmitTailLossProbe();
+
+  // RTO retransmits SHLO although it is not in flight.
+  size_t num_rto_packets = 2;
+  if (GetQuicReloadableFlag(quic_neuter_handshake_packets_once2)) {
+    num_rto_packets = 1;
+  }
+  EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+      .Times(num_rto_packets)
+      .WillOnce(
+          WithArgs<0>(Invoke([this, &crypto_frame](const QuicFrames& frames) {
+            EXPECT_EQ(1u, frames.size());
+            if (GetQuicReloadableFlag(quic_neuter_handshake_packets_once2)) {
+              EXPECT_NE(crypto_frame, frames[0].stream_frame);
+            } else {
+              EXPECT_EQ(crypto_frame, frames[0].stream_frame);
+            }
+          })));
+  manager_.OnRetransmissionTimeout();
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/quic/core/quic_types.h b/quic/core/quic_types.h
index 972e57c..06931dc 100644
--- a/quic/core/quic_types.h
+++ b/quic/core/quic_types.h
@@ -446,7 +446,7 @@
 };
 
 enum SentPacketState : uint8_t {
-  // The packet has been sent and waiting to be acked.
+  // The packet is in flight and waiting to be acked.
   OUTSTANDING,
   FIRST_PACKET_STATE = OUTSTANDING,
   // The packet was never sent.
@@ -455,6 +455,8 @@
   ACKED,
   // This packet is not expected to be acked.
   UNACKABLE,
+  // This packet has been delivered or unneeded.
+  NEUTERED,
 
   // States below are corresponding to retransmission types in TransmissionType.
 
diff --git a/quic/core/quic_utils.cc b/quic/core/quic_utils.cc
index a6f6577..eed0e37 100644
--- a/quic/core/quic_utils.cc
+++ b/quic/core/quic_utils.cc
@@ -161,6 +161,7 @@
     RETURN_STRING_LITERAL(NEVER_SENT);
     RETURN_STRING_LITERAL(ACKED);
     RETURN_STRING_LITERAL(UNACKABLE);
+    RETURN_STRING_LITERAL(NEUTERED);
     RETURN_STRING_LITERAL(HANDSHAKE_RETRANSMITTED);
     RETURN_STRING_LITERAL(LOST);
     RETURN_STRING_LITERAL(TLP_RETRANSMITTED);
diff --git a/quic/test_tools/simple_session_notifier.cc b/quic/test_tools/simple_session_notifier.cc
index 5ba12c6..9e67c89 100644
--- a/quic/test_tools/simple_session_notifier.cc
+++ b/quic/test_tools/simple_session_notifier.cc
@@ -126,8 +126,13 @@
 }
 
 void SimpleSessionNotifier::NeuterUnencryptedData() {
-  // TODO(nharper): Handle CRYPTO frame case.
   if (QuicVersionUsesCryptoFrames(connection_->transport_version())) {
+    for (const auto& interval : crypto_bytes_transferred_[ENCRYPTION_INITIAL]) {
+      QuicCryptoFrame crypto_frame(ENCRYPTION_INITIAL, interval.min(),
+                                   interval.max() - interval.min());
+      OnFrameAcked(QuicFrame(&crypto_frame), QuicTime::Delta::Zero(),
+                   QuicTime::Zero());
+    }
     return;
   }
   for (const auto& interval : crypto_bytes_transferred_[ENCRYPTION_INITIAL]) {