In quic, bundle server initial crypto data with initial ack. protected by gfe2_reloadable_flag_quic_bundle_crypto_data_with_initial_ack.
This is used to speed up handshake since INITIAL ACK will be padded to full anyway.
PiperOrigin-RevId: 317176505
Change-Id: I5444372466c64cdc662792333916d74b08e8b594
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index 03f6922..b69edfe 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -4237,13 +4237,39 @@
return ENCRYPTION_INITIAL;
}
+void QuicConnection::MaybeBundleCryptoDataWithInitialAck() {
+ DCHECK(SupportsMultiplePacketNumberSpaces());
+ const QuicTime initial_ack_timeout =
+ uber_received_packet_manager_.GetAckTimeout(INITIAL_DATA);
+ if (!initial_ack_timeout.IsInitialized() ||
+ (initial_ack_timeout > clock_->ApproximateNow() &&
+ initial_ack_timeout >
+ uber_received_packet_manager_.GetEarliestAckTimeout())) {
+ // Not going to send initial ACK.
+ return;
+ }
+ // Initial ACK will be padded to full anyway, so try to bundle INITIAL crypto
+ // data.
+ sent_packet_manager_.RetransmitInitialDataIfAny();
+}
+
void QuicConnection::SendAllPendingAcks() {
DCHECK(SupportsMultiplePacketNumberSpaces());
QUIC_DVLOG(1) << ENDPOINT << "Trying to send all pending ACKs";
ack_alarm_->Cancel();
- const QuicTime earliest_ack_timeout =
+ QuicTime earliest_ack_timeout =
uber_received_packet_manager_.GetEarliestAckTimeout();
QUIC_BUG_IF(!earliest_ack_timeout.IsInitialized());
+ if (GetQuicReloadableFlag(quic_bundle_crypto_data_with_initial_ack) &&
+ perspective() == Perspective::IS_SERVER) {
+ MaybeBundleCryptoDataWithInitialAck();
+ earliest_ack_timeout =
+ uber_received_packet_manager_.GetEarliestAckTimeout();
+ if (!earliest_ack_timeout.IsInitialized()) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_bundle_crypto_data_with_initial_ack);
+ return;
+ }
+ }
// Latches current encryption level.
const EncryptionLevel current_encryption_level = encryption_level_;
for (int8_t i = INITIAL_DATA; i <= APPLICATION_DATA; ++i) {
@@ -4342,13 +4368,15 @@
if (coalesced_packet_.length() == 0) {
return true;
}
- QUIC_DVLOG(1) << ENDPOINT << "Sending coalesced packet";
+
char buffer[kMaxOutgoingPacketSize];
const size_t length = packet_creator_.SerializeCoalescedPacket(
coalesced_packet_, buffer, coalesced_packet_.max_packet_length());
if (length == 0) {
return false;
}
+ QUIC_DVLOG(1) << ENDPOINT << "Sending coalesced packet "
+ << coalesced_packet_.ToString(length);
if (!buffered_packets_.empty() || HandleWriteBlocked()) {
QUIC_DVLOG(1) << ENDPOINT
diff --git a/quic/core/quic_connection.h b/quic/core/quic_connection.h
index 48d16a2..6cf01f0 100644
--- a/quic/core/quic_connection.h
+++ b/quic/core/quic_connection.h
@@ -1318,6 +1318,9 @@
bool ValidateConfigConnectionIds(const QuicConfig& config);
bool ValidateConfigConnectionIdsOld(const QuicConfig& config);
+ // Called when ACK alarm goes off. Try to bundle INITIAL data with the ACK.
+ void MaybeBundleCryptoDataWithInitialAck();
+
// Returns true if an undecryptable packet of |decryption_level| should be
// buffered (such that connection can try to decrypt it later).
bool ShouldEnqueueUnDecryptablePacket(EncryptionLevel decryption_level,
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index d273bbe..41e1974 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -11259,6 +11259,57 @@
EXPECT_EQ(0u, QuicConnectionPeer::NumUndecryptablePackets(&connection_));
}
+TEST_P(QuicConnectionTest, BundleInitialDataWithInitialAck) {
+ if (!connection_.SupportsMultiplePacketNumberSpaces()) {
+ return;
+ }
+ set_perspective(Perspective::IS_SERVER);
+ if (QuicVersionUsesCryptoFrames(connection_.transport_version())) {
+ EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(AnyNumber());
+ }
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber());
+ use_tagging_decrypter();
+ // Receives packet 1000 in initial data.
+ ProcessCryptoPacketAtLevel(1000, ENCRYPTION_INITIAL);
+ EXPECT_TRUE(connection_.HasPendingAcks());
+
+ connection_.SetEncrypter(ENCRYPTION_INITIAL,
+ std::make_unique<TaggingEncrypter>(0x01));
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
+ connection_.SendCryptoDataWithString("foo", 0, ENCRYPTION_INITIAL);
+ QuicTime expected_pto_time =
+ connection_.sent_packet_manager().GetRetransmissionTime();
+
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ connection_.SetEncrypter(ENCRYPTION_HANDSHAKE,
+ std::make_unique<TaggingEncrypter>(0x02));
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_HANDSHAKE);
+ EXPECT_CALL(visitor_, OnHandshakePacketSent()).Times(1);
+ connection_.SendCryptoDataWithString("foo", 0, ENCRYPTION_HANDSHAKE);
+ // Verify PTO time does not change.
+ EXPECT_EQ(expected_pto_time,
+ connection_.sent_packet_manager().GetRetransmissionTime());
+
+ // Receives packet 1001 in initial data.
+ ProcessCryptoPacketAtLevel(1001, ENCRYPTION_INITIAL);
+ EXPECT_TRUE(connection_.HasPendingAcks());
+ // Receives packet 1002 in initial data.
+ ProcessCryptoPacketAtLevel(1002, ENCRYPTION_INITIAL);
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ if (GetQuicReloadableFlag(quic_bundle_crypto_data_with_initial_ack)) {
+ // Verify CRYPTO frame is bundled with INITIAL ACK.
+ EXPECT_FALSE(writer_->crypto_frames().empty());
+ // Verify PTO time changes.
+ EXPECT_NE(expected_pto_time,
+ connection_.sent_packet_manager().GetRetransmissionTime());
+ } else {
+ EXPECT_TRUE(writer_->crypto_frames().empty());
+ // Verify PTO time does not change.
+ EXPECT_EQ(expected_pto_time,
+ connection_.sent_packet_manager().GetRetransmissionTime());
+ }
+}
+
} // namespace
} // namespace test
} // namespace quic
diff --git a/quic/core/quic_sent_packet_manager.cc b/quic/core/quic_sent_packet_manager.cc
index 31ac454..d74a487 100644
--- a/quic/core/quic_sent_packet_manager.cc
+++ b/quic/core/quic_sent_packet_manager.cc
@@ -912,6 +912,27 @@
pto_exponential_backoff_start_point_ = exponential_backoff_start_point;
}
+void QuicSentPacketManager::RetransmitInitialDataIfAny() {
+ DCHECK(supports_multiple_packet_number_spaces());
+ if (!unacked_packets_.GetLastInFlightPacketSentTime(INITIAL_DATA)
+ .IsInitialized()) {
+ // No in flight initial data.
+ return;
+ }
+ QuicPacketNumber packet_number = unacked_packets_.GetLeastUnacked();
+ for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ it != unacked_packets_.end(); ++it, ++packet_number) {
+ if (it->state == OUTSTANDING &&
+ unacked_packets_.HasRetransmittableFrames(*it) &&
+ unacked_packets_.GetPacketNumberSpace(it->encryption_level) ==
+ INITIAL_DATA) {
+ DCHECK(it->in_flight);
+ MarkForRetransmission(packet_number, PTO_RETRANSMISSION);
+ return;
+ }
+ }
+}
+
QuicSentPacketManager::RetransmissionTimeoutMode
QuicSentPacketManager::GetRetransmissionMode() const {
DCHECK(unacked_packets_.HasInFlightPackets() ||
diff --git a/quic/core/quic_sent_packet_manager.h b/quic/core/quic_sent_packet_manager.h
index b0a8352..af32eba 100644
--- a/quic/core/quic_sent_packet_manager.h
+++ b/quic/core/quic_sent_packet_manager.h
@@ -394,6 +394,9 @@
void StartExponentialBackoffAfterNthPto(
size_t exponential_backoff_start_point);
+ // Called to retransmit in flight INITIAL packet if any.
+ void RetransmitInitialDataIfAny();
+
bool supports_multiple_packet_number_spaces() const {
return unacked_packets_.supports_multiple_packet_number_spaces();
}
diff --git a/quic/core/quic_sent_packet_manager_test.cc b/quic/core/quic_sent_packet_manager_test.cc
index cdec2d0..90c7e15 100644
--- a/quic/core/quic_sent_packet_manager_test.cc
+++ b/quic/core/quic_sent_packet_manager_test.cc
@@ -4044,6 +4044,58 @@
"retransmissions");
}
+TEST_F(QuicSentPacketManagerTest, MaybeRetransmitInitialData) {
+ manager_.EnableMultiplePacketNumberSpacesSupport();
+ EXPECT_CALL(*send_algorithm_, PacingRate(_))
+ .WillRepeatedly(Return(QuicBandwidth::Zero()));
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow())
+ .WillRepeatedly(Return(10 * kDefaultTCPMSS));
+ RttStats* rtt_stats = const_cast<RttStats*>(manager_.GetRttStats());
+ rtt_stats->UpdateRtt(QuicTime::Delta::FromMilliseconds(100),
+ QuicTime::Delta::Zero(), QuicTime::Zero());
+ QuicTime::Delta srtt = rtt_stats->smoothed_rtt();
+
+ // Send packet 1.
+ SendDataPacket(1, ENCRYPTION_INITIAL);
+ QuicTime packet1_sent_time = clock_.Now();
+
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
+ // Send packets 2 and 3.
+ SendDataPacket(2, ENCRYPTION_HANDSHAKE);
+ QuicTime packet2_sent_time = clock_.Now();
+ SendDataPacket(3, ENCRYPTION_HANDSHAKE);
+ // Verify PTO is correctly set based on packet 1.
+ int pto_rttvar_multiplier =
+ GetQuicReloadableFlag(quic_default_on_pto) ? 2 : 4;
+ QuicTime::Delta expected_pto_delay =
+ srtt + pto_rttvar_multiplier * rtt_stats->mean_deviation() +
+ QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs);
+ EXPECT_EQ(packet1_sent_time + expected_pto_delay,
+ manager_.GetRetransmissionTime());
+
+ // Assume connection is going to send INITIAL ACK.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) {
+ RetransmitDataPacket(4, type, ENCRYPTION_INITIAL);
+ })));
+ manager_.RetransmitInitialDataIfAny();
+ // Verify PTO is re-armed based on packet 2.
+ EXPECT_EQ(packet2_sent_time + expected_pto_delay,
+ manager_.GetRetransmissionTime());
+
+ // Connection is going to send another INITIAL ACK.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
+ EXPECT_CALL(notifier_, RetransmitFrames(_, _))
+ .WillOnce(WithArgs<1>(Invoke([this](TransmissionType type) {
+ RetransmitDataPacket(5, type, ENCRYPTION_INITIAL);
+ })));
+ manager_.RetransmitInitialDataIfAny();
+ // Verify PTO does not change.
+ EXPECT_EQ(packet2_sent_time + expected_pto_delay,
+ manager_.GetRetransmissionTime());
+}
+
} // namespace
} // namespace test
} // namespace quic