In QUIC, do not send PING when PING alarm fires but ShouldKeepConnectionAlive returns false. Client side only, protected by ENABLED gfe2_reloadable_flag_quic_fix_on_ping_timeout. This is used to fix a bug where PING alarm is not cancelled when application finish reading data (draining streams get closed). PiperOrigin-RevId: 326053367 Change-Id: I06aea85dad633c40e1d8486f0cb790c644b0e0e9
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc index f442b1b..4bd2195 100644 --- a/quic/core/quic_connection.cc +++ b/quic/core/quic_connection.cc
@@ -3091,9 +3091,14 @@ } void QuicConnection::OnPingTimeout() { - if (!retransmission_alarm_->IsSet()) { - visitor_->SendPing(); + if (retransmission_alarm_->IsSet()) { + return; } + if (GetQuicReloadableFlag(quic_fix_on_ping_timeout) && + !visitor_->ShouldKeepConnectionAlive()) { + return; + } + visitor_->SendPing(); } void QuicConnection::SendAck() {
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc index 49c5c4d..5340233 100644 --- a/quic/core/quic_connection_test.cc +++ b/quic/core/quic_connection_test.cc
@@ -11646,6 +11646,58 @@ } } +TEST_P(QuicConnectionTest, DonotSendPing) { + connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + connection_.OnHandshakeComplete(); + EXPECT_TRUE(connection_.connected()); + EXPECT_CALL(visitor_, ShouldKeepConnectionAlive()) + .WillRepeatedly(Return(true)); + EXPECT_FALSE(connection_.GetPingAlarm()->IsSet()); + EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); + + SendStreamDataToPeer( + GetNthClientInitiatedStreamId(0, connection_.transport_version()), + "GET /", 0, FIN, nullptr); + EXPECT_TRUE(connection_.GetPingAlarm()->IsSet()); + EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet()); + EXPECT_EQ(QuicTime::Delta::FromSeconds(15), + connection_.GetPingAlarm()->deadline() - clock_.ApproximateNow()); + + // Now recevie an ACK and response of the previous packet, which will move the + // ping alarm forward. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + QuicFrames frames; + QuicAckFrame ack_frame = InitAckFrame(1); + frames.push_back(QuicFrame(&ack_frame)); + frames.push_back(QuicFrame(QuicStreamFrame( + GetNthClientInitiatedStreamId(0, connection_.transport_version()), true, + 0u, quiche::QuicheStringPiece()))); + EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); + ProcessFramesPacketAtLevel(1, frames, ENCRYPTION_FORWARD_SECURE); + EXPECT_TRUE(connection_.GetPingAlarm()->IsSet()); + EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); + // The ping timer is set slightly less than 15 seconds in the future, because + // of the 1s ping timer alarm granularity. + EXPECT_EQ( + QuicTime::Delta::FromSeconds(15) - QuicTime::Delta::FromMilliseconds(5), + connection_.GetPingAlarm()->deadline() - clock_.ApproximateNow()); + + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(15)); + // Suppose now ShouldKeepConnectionAlive returns false. + EXPECT_CALL(visitor_, ShouldKeepConnectionAlive()) + .WillRepeatedly(Return(false)); + if (GetQuicReloadableFlag(quic_fix_on_ping_timeout)) { + // Verify PING does not get sent. + EXPECT_CALL(visitor_, SendPing()).Times(0); + } else { + // PING get sent even ShouldKeepConnectionAlive returns false. + EXPECT_CALL(visitor_, SendPing()).Times(1); + } + connection_.GetPingAlarm()->Fire(); +} + } // namespace } // namespace test } // namespace quic