In QUIC, add a copt to extend idle timeout by PTO on sent packets. Client side only, not protected. PiperOrigin-RevId: 327529464 Change-Id: I6ee548b29ce911da865e21539ad1e710394bc924
diff --git a/quic/core/crypto/crypto_protocol.h b/quic/core/crypto/crypto_protocol.h index 9bd5f17..4ebdd2e 100644 --- a/quic/core/crypto/crypto_protocol.h +++ b/quic/core/crypto/crypto_protocol.h
@@ -295,6 +295,10 @@ const QuicTag kDTOS = TAG('D', 'T', 'O', 'S'); // Enable overshooting // detection. +const QuicTag kFIDT = TAG('F', 'I', 'D', 'T'); // Extend idle timer by PTO + // instead of the whole idle + // timeout. + // Enable path MTU discovery experiment. const QuicTag kMTUH = TAG('M', 'T', 'U', 'H'); // High-target MTU discovery. const QuicTag kMTUL = TAG('M', 'T', 'U', 'L'); // Low-target MTU discovery.
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc index 11b5ad2..7aff953 100644 --- a/quic/core/quic_connection.cc +++ b/quic/core/quic_connection.cc
@@ -578,6 +578,10 @@ } } + if (config.HasClientRequestedIndependentOption(kFIDT, perspective_)) { + idle_network_detector_.enable_shorter_idle_timeout_on_sent_packet(); + } + if (debug_visitor_ != nullptr) { debug_visitor_->OnSetFromConfig(config); } @@ -2858,7 +2862,8 @@ GetNetworkBlackholeDeadline(), GetPathMtuReductionDeadline()); } - idle_network_detector_.OnPacketSent(packet_send_time); + idle_network_detector_.OnPacketSent(packet_send_time, + sent_packet_manager_.GetPtoDelay()); } MaybeSetMtuAlarm(packet_number);
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc index 8c12920..1b8c0c6 100644 --- a/quic/core/quic_connection_test.cc +++ b/quic/core/quic_connection_test.cc
@@ -11766,6 +11766,52 @@ EXPECT_FALSE(connection_.BlackholeDetectionInProgress()); } +TEST_P(QuicConnectionTest, ShorterIdleTimeoutOnSentPackets) { + EXPECT_TRUE(connection_.connected()); + RttStats* rtt_stats = const_cast<RttStats*>(manager_->GetRttStats()); + rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(100), + QuicTime::Delta::Zero(), QuicTime::Zero()); + + EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); + QuicConfig config; + config.SetClientConnectionOptions(QuicTagVector{kFIDT}); + QuicConfigPeer::SetNegotiated(&config, true); + if (GetQuicReloadableFlag(quic_default_enable_5rto_blackhole_detection2)) { + EXPECT_CALL(visitor_, GetHandshakeState()) + .WillRepeatedly(Return(HANDSHAKE_COMPLETE)); + } + if (connection_.version().AuthenticatesHandshakeConnectionIds()) { + QuicConfigPeer::SetReceivedOriginalConnectionId( + &config, connection_.connection_id()); + QuicConfigPeer::SetReceivedInitialSourceConnectionId( + &config, connection_.connection_id()); + } + connection_.SetFromConfig(config); + + ASSERT_TRUE(connection_.GetTimeoutAlarm()->IsSet()); + // Send a packet close to timeout. + QuicTime::Delta timeout = + connection_.GetTimeoutAlarm()->deadline() - clock_.Now(); + clock_.AdvanceTime(timeout - QuicTime::Delta::FromSeconds(1)); + // Send stream data. + SendStreamDataToPeer( + GetNthClientInitiatedStreamId(1, connection_.transport_version()), "foo", + 0, FIN, nullptr); + // Verify this sent packet does not extend idle timeout since 1s is > PTO + // delay. + ASSERT_TRUE(connection_.GetTimeoutAlarm()->IsSet()); + EXPECT_EQ(QuicTime::Delta::FromSeconds(1), + connection_.GetTimeoutAlarm()->deadline() - clock_.Now()); + + // Received an ACK 100ms later. + clock_.AdvanceTime(timeout - QuicTime::Delta::FromMilliseconds(100)); + QuicAckFrame ack = InitAckFrame(1); + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); + ProcessAckPacket(1, &ack); + // Verify idle timeout gets extended. + EXPECT_EQ(clock_.Now() + timeout, connection_.GetTimeoutAlarm()->deadline()); +} + } // namespace } // namespace test } // namespace quic
diff --git a/quic/core/quic_idle_network_detector.cc b/quic/core/quic_idle_network_detector.cc index a033f7a..f9b37cc 100644 --- a/quic/core/quic_idle_network_detector.cc +++ b/quic/core/quic_idle_network_detector.cc
@@ -71,13 +71,18 @@ idle_network_timeout_ = QuicTime::Delta::Infinite(); } -void QuicIdleNetworkDetector::OnPacketSent(QuicTime now) { +void QuicIdleNetworkDetector::OnPacketSent(QuicTime now, + QuicTime::Delta pto_delay) { if (time_of_first_packet_sent_after_receiving_ > time_of_last_received_packet_) { return; } time_of_first_packet_sent_after_receiving_ = std::max(time_of_first_packet_sent_after_receiving_, now); + if (shorter_idle_timeout_on_sent_packet_) { + MaybeSetAlarmOnSentPacket(pto_delay); + return; + } SetAlarm(); } @@ -105,6 +110,22 @@ alarm_->Update(new_deadline, kAlarmGranularity); } +void QuicIdleNetworkDetector::MaybeSetAlarmOnSentPacket( + QuicTime::Delta pto_delay) { + DCHECK(shorter_idle_timeout_on_sent_packet_); + if (!handshake_timeout_.IsInfinite() || !alarm_->IsSet()) { + SetAlarm(); + return; + } + // Make sure connection will be alive for another PTO. + const QuicTime deadline = alarm_->deadline(); + const QuicTime min_deadline = last_network_activity_time() + pto_delay; + if (deadline > min_deadline) { + return; + } + alarm_->Update(min_deadline, kAlarmGranularity); +} + QuicTime QuicIdleNetworkDetector::GetIdleNetworkDeadline() const { if (idle_network_timeout_.IsInfinite()) { return QuicTime::Zero();
diff --git a/quic/core/quic_idle_network_detector.h b/quic/core/quic_idle_network_detector.h index 1537222..7d7797e 100644 --- a/quic/core/quic_idle_network_detector.h +++ b/quic/core/quic_idle_network_detector.h
@@ -49,11 +49,15 @@ void StopDetection(); // Called when a packet gets sent. - void OnPacketSent(QuicTime now); + void OnPacketSent(QuicTime now, QuicTime::Delta pto_delay); // Called when a packet gets received. void OnPacketReceived(QuicTime now); + void enable_shorter_idle_timeout_on_sent_packet() { + shorter_idle_timeout_on_sent_packet_ = true; + } + QuicTime::Delta handshake_timeout() const { return handshake_timeout_; } QuicTime time_of_last_received_packet() const { @@ -75,6 +79,8 @@ void SetAlarm(); + void MaybeSetAlarmOnSentPacket(QuicTime::Delta pto_delay); + Delegate* delegate_; // Not owned. // Start time of the detector, handshake deadline = start_time_ + @@ -98,6 +104,8 @@ QuicTime::Delta idle_network_timeout_; QuicArenaScopedPtr<QuicAlarm> alarm_; + + bool shorter_idle_timeout_on_sent_packet_ = false; }; } // namespace quic
diff --git a/quic/core/quic_idle_network_detector_test.cc b/quic/core/quic_idle_network_detector_test.cc index 0d40d64..3fa4ebe 100644 --- a/quic/core/quic_idle_network_detector_test.cc +++ b/quic/core/quic_idle_network_detector_test.cc
@@ -123,14 +123,14 @@ // Sent packets after 200ms. clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(200)); - detector_->OnPacketSent(clock_.Now()); + detector_->OnPacketSent(clock_.Now(), QuicTime::Delta::Zero()); const QuicTime packet_sent_time = clock_.Now(); EXPECT_EQ(packet_sent_time + QuicTime::Delta::FromSeconds(600), alarm_->deadline()); // Sent another packet after 200ms clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(200)); - detector_->OnPacketSent(clock_.Now()); + detector_->OnPacketSent(clock_.Now(), QuicTime::Delta::Zero()); // Verify idle network deadline does not extend. EXPECT_EQ(packet_sent_time + QuicTime::Delta::FromSeconds(600), alarm_->deadline()); @@ -142,6 +142,45 @@ alarm_->Fire(); } +TEST_F(QuicIdleNetworkDetectorTest, ShorterIdleTimeoutOnSentPacket) { + detector_->enable_shorter_idle_timeout_on_sent_packet(); + detector_->SetTimeouts( + /*handshake_timeout=*/QuicTime::Delta::Infinite(), + /*idle_network_timeout=*/QuicTime::Delta::FromSeconds(30)); + EXPECT_TRUE(alarm_->IsSet()); + const QuicTime deadline = alarm_->deadline(); + EXPECT_EQ(clock_.Now() + QuicTime::Delta::FromSeconds(30), deadline); + + // Send a packet after 15s and 2s PTO delay. + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(15)); + detector_->OnPacketSent(clock_.Now(), QuicTime::Delta::FromSeconds(2)); + EXPECT_TRUE(alarm_->IsSet()); + // Verify alarm does not get extended because deadline is > PTO delay. + EXPECT_EQ(deadline, alarm_->deadline()); + + // Send another packet near timeout and 2 s PTO delay. + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(14)); + detector_->OnPacketSent(clock_.Now(), QuicTime::Delta::FromSeconds(2)); + EXPECT_TRUE(alarm_->IsSet()); + // Verify alarm does not get extended although it is shorter than PTO. + EXPECT_EQ(deadline, alarm_->deadline()); + + // Receive a packet after 1s. + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1)); + detector_->OnPacketReceived(clock_.Now()); + EXPECT_TRUE(alarm_->IsSet()); + // Verify idle timeout gets extended by 30s. + EXPECT_EQ(clock_.Now() + QuicTime::Delta::FromSeconds(30), + alarm_->deadline()); + + // Send a packet near timeout.. + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(29)); + detector_->OnPacketSent(clock_.Now(), QuicTime::Delta::FromSeconds(2)); + EXPECT_TRUE(alarm_->IsSet()); + // Verify idle timeout gets extended by 1s. + EXPECT_EQ(clock_.Now() + QuicTime::Delta::FromSeconds(2), alarm_->deadline()); +} + } // namespace } // namespace test
diff --git a/quic/core/quic_sent_packet_manager.cc b/quic/core/quic_sent_packet_manager.cc index a9204d6..4746eae 100644 --- a/quic/core/quic_sent_packet_manager.cc +++ b/quic/core/quic_sent_packet_manager.cc
@@ -1583,10 +1583,12 @@ } bool QuicSentPacketManager::IsLessThanThreePTOs(QuicTime::Delta timeout) const { - const QuicTime::Delta retransmission_delay = - pto_enabled_ ? GetProbeTimeoutDelay(APPLICATION_DATA) - : GetRetransmissionDelay(); - return timeout < 3 * retransmission_delay; + return timeout < 3 * GetPtoDelay(); +} + +QuicTime::Delta QuicSentPacketManager::GetPtoDelay() const { + return pto_enabled_ ? GetProbeTimeoutDelay(APPLICATION_DATA) + : GetRetransmissionDelay(); } #undef ENDPOINT // undef for jumbo builds
diff --git a/quic/core/quic_sent_packet_manager.h b/quic/core/quic_sent_packet_manager.h index 584d0fe..9e5bd96 100644 --- a/quic/core/quic_sent_packet_manager.h +++ b/quic/core/quic_sent_packet_manager.h
@@ -417,6 +417,9 @@ // Returns true if |timeout| is less than 3 * RTO/PTO delay. bool IsLessThanThreePTOs(QuicTime::Delta timeout) const; + // Returns current PTO delay. + QuicTime::Delta GetPtoDelay() const; + bool supports_multiple_packet_number_spaces() const { return unacked_packets_.supports_multiple_packet_number_spaces(); }