Shorten PTO when a HANDSHAKE or 1-RTT packet is received prior to the INITIAL. Client only.
PiperOrigin-RevId: 338475279
Change-Id: Idde6ef503847b1c9e0a5fb4c3e1e336102d06fdb
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index cdb93a3..7f7089b 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -3744,6 +3744,9 @@
}
QUIC_DVLOG(1) << ENDPOINT << "Queueing undecryptable packet.";
undecryptable_packets_.emplace_back(packet, decryption_level);
+ if (perspective_ == Perspective::IS_CLIENT) {
+ SetRetransmissionAlarm();
+ }
}
void QuicConnection::MaybeProcessUndecryptablePackets() {
@@ -3831,6 +3834,9 @@
}
undecryptable_packets_.clear();
}
+ if (perspective_ == Perspective::IS_CLIENT) {
+ SetRetransmissionAlarm();
+ }
}
void QuicConnection::QueueCoalescedPacket(const QuicEncryptedPacket& packet) {
@@ -4123,8 +4129,7 @@
return;
}
- retransmission_alarm_->Update(sent_packet_manager_.GetRetransmissionTime(),
- kAlarmGranularity);
+ retransmission_alarm_->Update(GetRetransmissionDeadline(), kAlarmGranularity);
}
void QuicConnection::MaybeSetMtuAlarm(QuicPacketNumber sent_packet_number) {
@@ -5385,6 +5390,20 @@
return num_rtos_for_blackhole_detection_ > 0;
}
+QuicTime QuicConnection::GetRetransmissionDeadline() const {
+ if (perspective_ == Perspective::IS_CLIENT &&
+ SupportsMultiplePacketNumberSpaces() && !IsHandshakeConfirmed() &&
+ stats_.pto_count == 0 &&
+ !framer_.HasDecrypterOfEncryptionLevel(ENCRYPTION_HANDSHAKE) &&
+ !undecryptable_packets_.empty()) {
+ // Retransmits ClientHello quickly when a Handshake or 1-RTT packet is
+ // received prior to having Handshake keys. Adding kAlarmGranulary will
+ // avoid spurious retransmissions in the case of small-scale reordering.
+ return clock_->ApproximateNow() + kAlarmGranularity;
+ }
+ return sent_packet_manager_.GetRetransmissionTime();
+}
+
void QuicConnection::SendPathChallenge(QuicPathFrameBuffer* data_buffer,
const QuicSocketAddress& self_address,
const QuicSocketAddress& peer_address,
diff --git a/quic/core/quic_connection.h b/quic/core/quic_connection.h
index 59fa80d..376c623 100644
--- a/quic/core/quic_connection.h
+++ b/quic/core/quic_connection.h
@@ -1450,6 +1450,9 @@
// Returns true if network blackhole should be detected.
bool ShouldDetectBlackhole() const;
+ // Returns retransmission deadline.
+ QuicTime GetRetransmissionDeadline() const;
+
// Validate connection IDs used during the handshake. Closes the connection
// on validation failure.
bool ValidateConfigConnectionIds(const QuicConfig& config);
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index 7d736b3..0b94236 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -12861,6 +12861,67 @@
QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs));
}
+TEST_P(QuicConnectionTest, FastRecoveryOfLostServerHello) {
+ if (!connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ QuicConfig config;
+ connection_.SetFromConfig(config);
+
+ use_tagging_decrypter();
+ connection_.SetEncrypter(ENCRYPTION_INITIAL,
+ std::make_unique<TaggingEncrypter>(0x01));
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
+ connection_.SendCryptoStreamData();
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(20));
+
+ // Assume ServerHello gets lost.
+ peer_framer_.SetEncrypter(ENCRYPTION_HANDSHAKE,
+ std::make_unique<TaggingEncrypter>(0x02));
+ ProcessCryptoPacketAtLevel(2, ENCRYPTION_HANDSHAKE);
+ ASSERT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
+ // Shorten PTO for fast recovery from lost ServerHello.
+ EXPECT_EQ(clock_.ApproximateNow() + kAlarmGranularity,
+ connection_.GetRetransmissionAlarm()->deadline());
+}
+
+TEST_P(QuicConnectionTest, ServerHelloGetsReordered) {
+ if (!connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ QuicConfig config;
+ connection_.SetFromConfig(config);
+ EXPECT_CALL(visitor_, OnCryptoFrame(_))
+ .WillRepeatedly(Invoke([=](const QuicCryptoFrame& frame) {
+ if (frame.level == ENCRYPTION_INITIAL) {
+ // Install handshake read keys.
+ SetDecrypter(ENCRYPTION_HANDSHAKE,
+ std::make_unique<StrictTaggingDecrypter>(0x02));
+ connection_.SetEncrypter(ENCRYPTION_HANDSHAKE,
+ std::make_unique<TaggingEncrypter>(0x02));
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_HANDSHAKE);
+ }
+ }));
+
+ use_tagging_decrypter();
+ connection_.SetEncrypter(ENCRYPTION_INITIAL,
+ std::make_unique<TaggingEncrypter>(0x01));
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
+ connection_.SendCryptoStreamData();
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(20));
+
+ // Assume ServerHello gets reordered.
+ peer_framer_.SetEncrypter(ENCRYPTION_HANDSHAKE,
+ std::make_unique<TaggingEncrypter>(0x02));
+ ProcessCryptoPacketAtLevel(2, ENCRYPTION_HANDSHAKE);
+ ProcessCryptoPacketAtLevel(1, ENCRYPTION_INITIAL);
+ // Verify fast recovery is not enabled.
+ EXPECT_EQ(connection_.sent_packet_manager().GetRetransmissionTime(),
+ connection_.GetRetransmissionAlarm()->deadline());
+}
+
} // namespace
} // namespace test
} // namespace quic