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();
}