Handle PINGs in QUIC connection.

Currently, sending and retransmission of PINGs are handled by control frame manager (and session).

Protected by FLAGS_quic_reloadable_flag_quic_let_connection_handle_pings.

PiperOrigin-RevId: 335072367
Change-Id: I79f20404656beee3d9404c1ccaa8ef2f00af567c
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index da0cdaf..a2f32b6 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -3233,7 +3233,11 @@
       !visitor_->ShouldKeepConnectionAlive()) {
     return;
   }
-  visitor_->SendPing();
+  if (packet_creator_.let_connection_handle_pings()) {
+    SendPingAtLevel(encryption_level_);
+  } else {
+    visitor_->SendPing();
+  }
 }
 
 void QuicConnection::SendAck() {
@@ -3329,7 +3333,19 @@
                     << "No packet gets sent when timer fires in mode "
                     << retransmission_mode << ", send PING";
     DCHECK_LT(0u, sent_packet_manager_.pending_timer_transmission_count());
-    visitor_->SendPing();
+    if (packet_creator_.let_connection_handle_pings()) {
+      EncryptionLevel level = encryption_level_;
+      PacketNumberSpace packet_number_space = NUM_PACKET_NUMBER_SPACES;
+      if (SupportsMultiplePacketNumberSpaces() &&
+          sent_packet_manager_
+              .GetEarliestPacketSentTimeForPto(&packet_number_space)
+              .IsInitialized()) {
+        level = QuicUtils::GetEncryptionLevel(packet_number_space);
+      }
+      SendPingAtLevel(level);
+    } else {
+      visitor_->SendPing();
+    }
   }
   if (retransmission_mode == QuicSentPacketManager::PTO_MODE) {
     sent_packet_manager_.AdjustPendingTimerTransmissions();
@@ -3935,6 +3951,24 @@
             !connection_->packet_creator_.PacketFlusherAttached());
 }
 
+QuicConnection::ScopedEncryptionLevelContext::ScopedEncryptionLevelContext(
+    QuicConnection* connection,
+    EncryptionLevel encryption_level)
+    : connection_(connection), latched_encryption_level_(ENCRYPTION_INITIAL) {
+  if (connection_ == nullptr) {
+    return;
+  }
+  latched_encryption_level_ = connection_->encryption_level_;
+  connection_->SetDefaultEncryptionLevel(encryption_level);
+}
+
+QuicConnection::ScopedEncryptionLevelContext::~ScopedEncryptionLevelContext() {
+  if (connection_ == nullptr || !connection_->connected_) {
+    return;
+  }
+  connection_->SetDefaultEncryptionLevel(latched_encryption_level_);
+}
+
 QuicConnection::BufferedPacket::BufferedPacket(
     const SerializedPacket& packet,
     const QuicSocketAddress& self_address,
@@ -5078,5 +5112,11 @@
   packet_creator_.SetDefaultPeerAddress(peer_address);
 }
 
+void QuicConnection::SendPingAtLevel(EncryptionLevel level) {
+  DCHECK(packet_creator_.let_connection_handle_pings());
+  ScopedEncryptionLevelContext context(this, level);
+  SendControlFrame(QuicFrame(QuicPingFrame()));
+}
+
 #undef ENDPOINT  // undef for jumbo builds
 }  // namespace quic
diff --git a/quic/core/quic_connection.h b/quic/core/quic_connection.h
index 0617478..1abb1f7 100644
--- a/quic/core/quic_connection.h
+++ b/quic/core/quic_connection.h
@@ -831,6 +831,18 @@
     const bool handshake_packet_sent_;
   };
 
+  class QUIC_EXPORT_PRIVATE ScopedEncryptionLevelContext {
+   public:
+    ScopedEncryptionLevelContext(QuicConnection* connection,
+                                 EncryptionLevel level);
+    ~ScopedEncryptionLevelContext();
+
+   private:
+    QuicConnection* connection_;
+    // Latched current write encryption level on creation of this context.
+    EncryptionLevel latched_encryption_level_;
+  };
+
   QuicPacketWriter* writer() { return writer_; }
   const QuicPacketWriter* writer() const { return writer_; }
 
@@ -1422,6 +1434,9 @@
   // Update both connection's and packet creator's peer address.
   void UpdatePeerAddress(QuicSocketAddress peer_address);
 
+  // Send PING at encryption level.
+  void SendPingAtLevel(EncryptionLevel level);
+
   QuicFramer framer_;
 
   // Contents received in the current packet, especially used to identify
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index b18b367..06488e5 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -4024,7 +4024,11 @@
             connection_.GetPingAlarm()->deadline() - clock_.ApproximateNow());
 
   // Simulate firing of the retransmittable on wire and send a PING.
-  EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() { SendPing(); }));
+  if (!GetQuicReloadableFlag(quic_let_connection_handle_pings)) {
+    EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() {
+      SendPing();
+    }));
+  }
   clock_.AdvanceTime(retransmittable_on_wire_timeout);
   connection_.GetPingAlarm()->Fire();
 
@@ -4038,13 +4042,21 @@
       QuicTime::Delta::FromMilliseconds(kMinRetransmissionTimeMs);
   srtt = manager_->GetRttStats()->SmoothedOrInitialRtt();
 
-  // First TLP without unacked stream data will no longer use TLPR.
-  expected_delay = std::max(2 * srtt, 1.5 * srtt + 0.5 * min_rto_timeout);
+  if (GetQuicReloadableFlag(quic_let_connection_handle_pings)) {
+    // Arm RTO mode since there is only PING in flight.
+    expected_delay = manager_->GetPtoDelay();
+  } else {
+    // First TLP without unacked stream data will no longer use TLPR.
+    expected_delay = std::max(2 * srtt, 1.5 * srtt + 0.5 * min_rto_timeout);
+  }
   EXPECT_EQ(expected_delay,
             connection_.GetRetransmissionAlarm()->deadline() - clock_.Now());
 
   // Verify the path degrading delay = TLP delay + 1st RTO + 2nd RTO.
   // Add 1st RTO.
+  if (GetQuicReloadableFlag(quic_let_connection_handle_pings)) {
+    expected_delay = std::max(2 * srtt, 1.5 * srtt + 0.5 * min_rto_timeout);
+  }
   retransmission_delay =
       std::max(manager_->GetRttStats()->smoothed_rtt() +
                    4 * manager_->GetRttStats()->mean_deviation(),
@@ -4071,7 +4083,12 @@
 
   // Verify the retransmission delay.
   // First TLP without unacked stream data will no longer use TLPR.
-  expected_delay = std::max(2 * srtt, 1.5 * srtt + 0.5 * min_rto_timeout);
+  if (GetQuicReloadableFlag(quic_let_connection_handle_pings)) {
+    // Arm RTO mode since there is only PING in flight.
+    expected_delay = manager_->GetPtoDelay();
+  } else {
+    expected_delay = std::max(2 * srtt, 1.5 * srtt + 0.5 * min_rto_timeout);
+  }
   expected_delay = expected_delay - QuicTime::Delta::FromMilliseconds(5);
   EXPECT_EQ(expected_delay,
             connection_.GetRetransmissionAlarm()->deadline() - clock_.Now());
@@ -4597,7 +4614,11 @@
 
   writer_->Reset();
   clock_.AdvanceTime(QuicTime::Delta::FromSeconds(15));
-  EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() { SendPing(); }));
+  if (!GetQuicReloadableFlag(quic_let_connection_handle_pings)) {
+    EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() {
+      SendPing();
+    }));
+  }
   connection_.GetPingAlarm()->Fire();
   size_t padding_frame_count = writer_->padding_frames().size();
   EXPECT_EQ(padding_frame_count + 1u, writer_->frame_count());
@@ -4651,9 +4672,11 @@
 
   writer_->Reset();
   clock_.AdvanceTime(QuicTime::Delta::FromSeconds(10));
-  EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() {
-    connection_.SendControlFrame(QuicFrame(QuicPingFrame(1)));
-  }));
+  if (!GetQuicReloadableFlag(quic_let_connection_handle_pings)) {
+    EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() {
+      connection_.SendControlFrame(QuicFrame(QuicPingFrame(1)));
+    }));
+  }
   connection_.GetPingAlarm()->Fire();
   size_t padding_frame_count = writer_->padding_frames().size();
   EXPECT_EQ(padding_frame_count + 1u, writer_->frame_count());
@@ -7159,9 +7182,11 @@
 
   // Simulate firing the ping alarm and sending a PING.
   clock_.AdvanceTime(retransmittable_on_wire_timeout);
-  EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() {
-    connection_.SendControlFrame(QuicFrame(QuicPingFrame(1)));
-  }));
+  if (!GetQuicReloadableFlag(quic_let_connection_handle_pings)) {
+    EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() {
+      connection_.SendControlFrame(QuicFrame(QuicPingFrame(1)));
+    }));
+  }
   connection_.GetPingAlarm()->Fire();
 
   // Now there's a retransmittable packet (PING) on the wire, so the path
@@ -7862,9 +7887,11 @@
   EXPECT_EQ(prev_deadline, connection_.GetPingAlarm()->deadline());
 
   // Simulate the alarm firing and check that a PING is sent.
-  EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() {
-    connection_.SendControlFrame(QuicFrame(QuicPingFrame(1)));
-  }));
+  if (!GetQuicReloadableFlag(quic_let_connection_handle_pings)) {
+    EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() {
+      connection_.SendControlFrame(QuicFrame(QuicPingFrame(1)));
+    }));
+  }
   connection_.GetPingAlarm()->Fire();
   size_t padding_frame_count = writer_->padding_frames().size();
   if (GetParam().no_stop_waiting) {
@@ -7935,9 +7962,11 @@
 
   // Simulate the alarm firing and check that a PING is sent.
   writer_->Reset();
-  EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() {
-    connection_.SendControlFrame(QuicFrame(QuicPingFrame(1)));
-  }));
+  if (!GetQuicReloadableFlag(quic_let_connection_handle_pings)) {
+    EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() {
+      connection_.SendControlFrame(QuicFrame(QuicPingFrame(1)));
+    }));
+  }
   connection_.GetPingAlarm()->Fire();
   size_t padding_frame_count = writer_->padding_frames().size();
   if (GetParam().no_stop_waiting) {
@@ -7997,9 +8026,11 @@
               connection_.GetPingAlarm()->deadline() - clock_.ApproximateNow());
     // Simulate the alarm firing and check that a PING is sent.
     writer_->Reset();
-    EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() {
-      SendPing();
-    }));
+    if (!GetQuicReloadableFlag(quic_let_connection_handle_pings)) {
+      EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() {
+        SendPing();
+      }));
+    }
     clock_.AdvanceTime(initial_retransmittable_on_wire_timeout);
     connection_.GetPingAlarm()->Fire();
   }
@@ -8024,9 +8055,11 @@
 
     // Simulate the alarm firing and check that a PING is sent.
     writer_->Reset();
-    EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() {
-      SendPing();
-    }));
+    if (!GetQuicReloadableFlag(quic_let_connection_handle_pings)) {
+      EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() {
+        SendPing();
+      }));
+    }
     clock_.AdvanceTime(retransmittable_on_wire_timeout);
     connection_.GetPingAlarm()->Fire();
   }
@@ -8092,7 +8125,11 @@
 
   // Simulate the alarm firing and check that a PING is sent.
   writer_->Reset();
-  EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() { SendPing(); }));
+  if (!GetQuicReloadableFlag(quic_let_connection_handle_pings)) {
+    EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() {
+      SendPing();
+    }));
+  }
   clock_.AdvanceTime(initial_retransmittable_on_wire_timeout);
   connection_.GetPingAlarm()->Fire();
 
@@ -8128,9 +8165,11 @@
               connection_.GetPingAlarm()->deadline() - clock_.ApproximateNow());
     // Simulate the alarm firing and check that a PING is sent.
     writer_->Reset();
-    EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() {
-      SendPing();
-    }));
+    if (!GetQuicReloadableFlag(quic_let_connection_handle_pings)) {
+      EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() {
+        SendPing();
+      }));
+    }
     clock_.AdvanceTime(initial_retransmittable_on_wire_timeout);
     connection_.GetPingAlarm()->Fire();
     // Advance 5ms to receive next packet.
@@ -8148,7 +8187,11 @@
             connection_.GetPingAlarm()->deadline() - clock_.ApproximateNow());
 
   writer_->Reset();
-  EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() { SendPing(); }));
+  if (!GetQuicReloadableFlag(quic_let_connection_handle_pings)) {
+    EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() {
+      SendPing();
+    }));
+  }
   clock_.AdvanceTime(2 * initial_retransmittable_on_wire_timeout);
   connection_.GetPingAlarm()->Fire();
 
@@ -9110,7 +9153,11 @@
 
   // RTO fires, verify a PING packet gets sent because there is no data to send.
   EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(3), _, _));
-  EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() { SendPing(); }));
+  if (!GetQuicReloadableFlag(quic_let_connection_handle_pings)) {
+    EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() {
+      SendPing();
+    }));
+  }
   clock_.AdvanceTime(retransmission_time - clock_.Now());
   connection_.GetRetransmissionAlarm()->Fire();
   EXPECT_EQ(1u, connection_.GetStats().tlp_count);
@@ -9330,7 +9377,11 @@
                                ? QuicPacketNumber(2)
                                : QuicPacketNumber(3),
                            _, _));
-  EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() { SendPing(); }));
+  if (!GetQuicReloadableFlag(quic_let_connection_handle_pings)) {
+    EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() {
+      SendPing();
+    }));
+  }
   connection_.GetRetransmissionAlarm()->Fire();
   EXPECT_EQ(1u, connection_.GetStats().pto_count);
   EXPECT_EQ(1u, connection_.GetStats().crypto_retransmit_count);
@@ -9627,7 +9678,11 @@
   // Fires TLP, verify a PING gets sent because packet 3 is marked
   // RTO_RETRANSMITTED.
   EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(70), _, _));
-  EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() { SendPing(); }));
+  if (!GetQuicReloadableFlag(quic_let_connection_handle_pings)) {
+    EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() {
+      SendPing();
+    }));
+  }
   connection_.GetRetransmissionAlarm()->Fire();
 }
 
@@ -10218,7 +10273,11 @@
   // PTO fires, verify a PING packet gets sent because there is no data to
   // send.
   EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(3), _, _));
-  EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() { SendPing(); }));
+  if (!GetQuicReloadableFlag(quic_let_connection_handle_pings)) {
+    EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() {
+      SendPing();
+    }));
+  }
   connection_.GetRetransmissionAlarm()->Fire();
   EXPECT_EQ(1u, connection_.GetStats().pto_count);
   EXPECT_EQ(0u, connection_.GetStats().crypto_retransmit_count);
@@ -11003,7 +11062,11 @@
   EXPECT_CALL(visitor_, ShouldKeepConnectionAlive())
       .WillRepeatedly(Return(false));
   // Verify PING does not get sent.
-  EXPECT_CALL(visitor_, SendPing()).Times(0);
+  if (GetQuicReloadableFlag(quic_let_connection_handle_pings)) {
+    EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
+  } else {
+    EXPECT_CALL(visitor_, SendPing()).Times(0);
+  }
   connection_.GetPingAlarm()->Fire();
 }
 
@@ -11581,6 +11644,10 @@
     if (!GetQuicReloadableFlag(quic_fix_missing_initial_keys2)) {
       EXPECT_CALL(visitor_, OnHandshakePacketSent()).Times(1);
     }
+  } else if (GetQuicReloadableFlag(quic_let_connection_handle_pings)) {
+    if (!GetQuicReloadableFlag(quic_fix_missing_initial_keys2)) {
+      EXPECT_CALL(visitor_, OnHandshakePacketSent()).Times(1);
+    }
   } else {
     EXPECT_CALL(visitor_, OnHandshakePacketSent()).Times(0);
     EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() {
@@ -11592,10 +11659,15 @@
     // Verify a handshake packet gets PTOed and 1-RTT packet gets coalesced.
     EXPECT_EQ(0x03030303u, writer_->final_bytes_of_last_packet());
   } else {
-    // Verify an 1-RTT PING gets sent because there is nothing to PTO, bummer,
-    // since this 1-RTT PING cannot be processed by peer and there is a
-    // deadlock.
-    EXPECT_EQ(0x03030303u, writer_->final_bytes_of_last_packet());
+    if (GetQuicReloadableFlag(quic_let_connection_handle_pings)) {
+      // Verify PING is sent in the right encryption level.
+      EXPECT_EQ(0x02020202u, writer_->final_bytes_of_last_packet());
+    } else {
+      // Verify an 1-RTT PING gets sent because there is nothing to PTO, bummer,
+      // since this 1-RTT PING cannot be processed by peer and there is a
+      // deadlock.
+      EXPECT_EQ(0x03030303u, writer_->final_bytes_of_last_packet());
+    }
     EXPECT_FALSE(writer_->ping_frames().empty());
   }
 }
@@ -11684,7 +11756,11 @@
   EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
 
   // Fire retransmission alarm.
-  EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() { SendPing(); }));
+  if (!GetQuicReloadableFlag(quic_let_connection_handle_pings)) {
+    EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() {
+      SendPing();
+    }));
+  }
   connection_.GetRetransmissionAlarm()->Fire();
 
   QuicFrames frames1;
diff --git a/quic/core/quic_packet_creator.cc b/quic/core/quic_packet_creator.cc
index 30b911f..af9d079 100644
--- a/quic/core/quic_packet_creator.cc
+++ b/quic/core/quic_packet_creator.cc
@@ -137,6 +137,9 @@
   if (close_connection_on_serialization_failure_) {
     QUIC_RELOADABLE_FLAG_COUNT(quic_close_connection_on_serialization_failure);
   }
+  if (let_connection_handle_pings_) {
+    QUIC_RELOADABLE_FLAG_COUNT(quic_let_connection_handle_pings);
+  }
   SetMaxPacketLength(kDefaultMaxPacketSize);
   if (!framer_->version().UsesTls()) {
     // QUIC+TLS negotiates the maximum datagram frame size via the
@@ -1303,7 +1306,8 @@
 
 bool QuicPacketCreator::ConsumeRetransmittableControlFrame(
     const QuicFrame& frame) {
-  QUIC_BUG_IF(IsControlFrame(frame.type) && !GetControlFrameId(frame))
+  QUIC_BUG_IF(IsControlFrame(frame.type) && !GetControlFrameId(frame) &&
+              (!let_connection_handle_pings_ || frame.type != PING_FRAME))
       << "Adding a control frame with no control frame id: " << frame;
   DCHECK(QuicUtils::IsRetransmittableFrame(frame.type)) << frame;
   MaybeBundleAckOpportunistically();
diff --git a/quic/core/quic_packet_creator.h b/quic/core/quic_packet_creator.h
index b025d06..b65bd27 100644
--- a/quic/core/quic_packet_creator.h
+++ b/quic/core/quic_packet_creator.h
@@ -469,6 +469,10 @@
   // different from the current one, flush all the queue frames first.
   void SetDefaultPeerAddress(QuicSocketAddress address);
 
+  bool let_connection_handle_pings() const {
+    return let_connection_handle_pings_;
+  }
+
  private:
   friend class test::QuicPacketCreatorPeer;
 
@@ -665,6 +669,9 @@
 
   const bool close_connection_on_serialization_failure_ =
       GetQuicReloadableFlag(quic_close_connection_on_serialization_failure);
+
+  const bool let_connection_handle_pings_ =
+      GetQuicReloadableFlag(quic_let_connection_handle_pings);
 };
 
 }  // namespace quic
diff --git a/quic/core/quic_sent_packet_manager.h b/quic/core/quic_sent_packet_manager.h
index 6e36da9..b332034 100644
--- a/quic/core/quic_sent_packet_manager.h
+++ b/quic/core/quic_sent_packet_manager.h
@@ -442,6 +442,11 @@
 
   void OnUserAgentIdKnown() { loss_algorithm_->OnUserAgentIdKnown(); }
 
+  // Gets the earliest in flight packet sent time to calculate PTO. Also
+  // updates |packet_number_space| if a PTO timer should be armed.
+  QuicTime GetEarliestPacketSentTimeForPto(
+      PacketNumberSpace* packet_number_space) const;
+
   bool give_sent_packet_to_debug_visitor_after_sent() const {
     return give_sent_packet_to_debug_visitor_after_sent_;
   }
@@ -540,11 +545,6 @@
   // timeout.
   bool ShouldAddMaxAckDelay(PacketNumberSpace space) const;
 
-  // Gets the earliest in flight packet sent time to calculate PTO. Also
-  // updates |packet_number_space| if a PTO timer should be armed.
-  QuicTime GetEarliestPacketSentTimeForPto(
-      PacketNumberSpace* packet_number_space) const;
-
   // Returns true if application data should be used to arm PTO. Only used when
   // multiple packet number space is enabled.
   bool ShouldArmPtoForApplicationData() const;