Connect up IETF QUIC Max ACK Delay transport parameter
This CL sets the QuicSentPacketManager's ACK Delay from the
MAX ACK Delay transport parameter.
gfe-relnote: N/A done only if IETF QUIC enabled & negotiated.
PiperOrigin-RevId: 260502360
Change-Id: Idd8a9d0fe36e7ab0794aa7532d608489866248eb
diff --git a/quic/core/crypto/crypto_handshake_message.cc b/quic/core/crypto/crypto_handshake_message.cc
index bf1df2b..022a86b 100644
--- a/quic/core/crypto/crypto_handshake_message.cc
+++ b/quic/core/crypto/crypto_handshake_message.cc
@@ -278,6 +278,7 @@
case kMIBS:
case kSCLS:
case kTCID:
+ case kMAD:
// uint32_t value
if (it->second.size() == 4) {
uint32_t value;
diff --git a/quic/core/crypto/crypto_protocol.h b/quic/core/crypto/crypto_protocol.h
index f2033e1..fdd4fd1 100644
--- a/quic/core/crypto/crypto_protocol.h
+++ b/quic/core/crypto/crypto_protocol.h
@@ -238,6 +238,8 @@
const QuicTag kUAID = TAG('U', 'A', 'I', 'D'); // Client's User Agent ID.
const QuicTag kXLCT = TAG('X', 'L', 'C', 'T'); // Expected leaf certificate.
+const QuicTag kMAD = TAG('M', 'A', 'D', 0); // Max Ack Delay (IETF QUIC)
+
// Rejection tags
const QuicTag kRREJ = TAG('R', 'R', 'E', 'J'); // Reasons for server sending
diff --git a/quic/core/http/end_to_end_test.cc b/quic/core/http/end_to_end_test.cc
index cbfb84d..c78fab4 100644
--- a/quic/core/http/end_to_end_test.cc
+++ b/quic/core/http/end_to_end_test.cc
@@ -605,6 +605,35 @@
EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
}
+// Simple transaction, but set a non-default ack delay at the client
+// and ensure it gets to the server.
+TEST_P(EndToEndTest, SimpleRequestResponseWithAckDelayChange) {
+ // Force the ACK delay to be something other than the default.
+ // Note that it is sent only if doing IETF QUIC.
+ client_config_.SetMaxAckDelayToSendMs(kDefaultDelayedAckTimeMs + 100u);
+ ASSERT_TRUE(Initialize());
+
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ("200", client_->response_headers()->find(":status")->second);
+ int expected_num_client_hellos = 2;
+ if (ServerSendsVersionNegotiation()) {
+ ++expected_num_client_hellos;
+ }
+ EXPECT_EQ(expected_num_client_hellos,
+ client_->client()->GetNumSentClientHellos());
+ if (GetQuicReloadableFlag(quic_negotiate_ack_delay_time)) {
+ EXPECT_EQ(kDefaultDelayedAckTimeMs + 100u,
+ GetSentPacketManagerFromFirstServerSession()
+ ->peer_max_ack_delay()
+ .ToMilliseconds());
+ } else {
+ EXPECT_EQ(kDefaultDelayedAckTimeMs,
+ GetSentPacketManagerFromFirstServerSession()
+ ->peer_max_ack_delay()
+ .ToMilliseconds());
+ }
+}
+
TEST_P(EndToEndTest, SimpleRequestResponseForcedVersionNegotiation) {
client_supported_versions_.insert(client_supported_versions_.begin(),
QuicVersionReservedForNegotiation());
diff --git a/quic/core/quic_config.cc b/quic/core/quic_config.cc
index e24e84b..661b5f3 100644
--- a/quic/core/quic_config.cc
+++ b/quic/core/quic_config.cc
@@ -413,7 +413,8 @@
alternate_server_address_(kASAD, PRESENCE_OPTIONAL),
support_max_header_list_size_(kSMHL, PRESENCE_OPTIONAL),
stateless_reset_token_(kSRST, PRESENCE_OPTIONAL),
- max_incoming_unidirectional_streams_(kMIUS, PRESENCE_OPTIONAL) {
+ max_incoming_unidirectional_streams_(kMIUS, PRESENCE_OPTIONAL),
+ max_ack_delay_ms_(kMAD, PRESENCE_OPTIONAL) {
SetDefaults();
}
@@ -540,6 +541,22 @@
return max_incoming_unidirectional_streams_.GetReceivedValue();
}
+void QuicConfig::SetMaxAckDelayToSendMs(uint32_t max_ack_delay_ms) {
+ return max_ack_delay_ms_.SetSendValue(max_ack_delay_ms);
+}
+
+uint32_t QuicConfig::GetMaxAckDelayToToSendMs() const {
+ return max_ack_delay_ms_.GetSendValue();
+}
+
+bool QuicConfig::HasReceivedMaxAckDelayMs() const {
+ return max_ack_delay_ms_.HasReceivedValue();
+}
+
+uint32_t QuicConfig::ReceivedMaxAckDelayMs() const {
+ return max_ack_delay_ms_.GetReceivedValue();
+}
+
bool QuicConfig::HasSetBytesForConnectionIdToSend() const {
return bytes_for_connection_id_.HasSendValue();
}
@@ -692,6 +709,7 @@
SetInitialStreamFlowControlWindowToSend(kMinimumFlowControlSendWindow);
SetInitialSessionFlowControlWindowToSend(kMinimumFlowControlSendWindow);
+ SetMaxAckDelayToSendMs(kDefaultDelayedAckTimeMs);
SetSupportMaxHeaderListSize();
}
@@ -707,6 +725,10 @@
if (VersionHasIetfQuicFrames(transport_version)) {
max_incoming_unidirectional_streams_.ToHandshakeMessage(out);
}
+ if (GetQuicReloadableFlag(quic_negotiate_ack_delay_time)) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_negotiate_ack_delay_time, 1, 4);
+ max_ack_delay_ms_.ToHandshakeMessage(out);
+ }
bytes_for_connection_id_.ToHandshakeMessage(out);
initial_round_trip_time_us_.ToHandshakeMessage(out);
initial_stream_flow_control_window_bytes_.ToHandshakeMessage(out);
@@ -777,6 +799,13 @@
error = stateless_reset_token_.ProcessPeerHello(peer_hello, hello_type,
error_details);
}
+
+ if (GetQuicReloadableFlag(quic_negotiate_ack_delay_time) &&
+ error == QUIC_NO_ERROR) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_negotiate_ack_delay_time, 2, 4);
+ error = max_ack_delay_ms_.ProcessPeerHello(peer_hello, hello_type,
+ error_details);
+ }
return error;
}
@@ -805,7 +834,10 @@
max_incoming_bidirectional_streams_.GetSendValue());
params->initial_max_streams_uni.set_value(
max_incoming_unidirectional_streams_.GetSendValue());
- params->max_ack_delay.set_value(kDefaultDelayedAckTimeMs);
+ if (GetQuicReloadableFlag(quic_negotiate_ack_delay_time)) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_negotiate_ack_delay_time, 3, 4);
+ params->max_ack_delay.set_value(kDefaultDelayedAckTimeMs);
+ }
params->disable_migration =
connection_migration_disabled_.HasSendValue() &&
connection_migration_disabled_.GetSendValue() != 0;
@@ -887,7 +919,11 @@
initial_stream_flow_control_window_bytes_.SetReceivedValue(
std::min<uint64_t>(params.initial_max_stream_data_bidi_local.value(),
std::numeric_limits<uint32_t>::max()));
-
+ if (GetQuicReloadableFlag(quic_negotiate_ack_delay_time)) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_negotiate_ack_delay_time, 4, 4);
+ max_ack_delay_ms_.SetReceivedValue(std::min<uint32_t>(
+ params.max_ack_delay.value(), std::numeric_limits<uint32_t>::max()));
+ }
connection_migration_disabled_.SetReceivedValue(
params.disable_migration ? 1u : 0u);
diff --git a/quic/core/quic_config.h b/quic/core/quic_config.h
index ae15b6a..3c82a68 100644
--- a/quic/core/quic_config.h
+++ b/quic/core/quic_config.h
@@ -414,6 +414,16 @@
QuicUint128 ReceivedStatelessResetToken() const;
+ // Manage the IETF QUIC Max ACK Delay transport parameter.
+ // The sent value is the delay that this node uses
+ // (QuicSentPacketManager::local_max_ack_delay_).
+ // The received delay is the value received from
+ // the peer (QuicSentPacketManager::peer_max_ack_delay_).
+ void SetMaxAckDelayToSendMs(uint32_t max_ack_delay_ms);
+ uint32_t GetMaxAckDelayToToSendMs() const;
+ bool HasReceivedMaxAckDelayMs() const;
+ uint32_t ReceivedMaxAckDelayMs() const;
+
bool negotiated() const;
void SetCreateSessionTagIndicators(QuicTagVector tags);
@@ -501,6 +511,11 @@
// Maximum number of incoming unidirectional streams that the connection can
// support.
QuicFixedUint32 max_incoming_unidirectional_streams_;
+
+ // Maximum ack delay. The sent value is the value used on this node.
+ // The received value is the value received from the peer and used by
+ // the peer.
+ QuicFixedUint32 max_ack_delay_ms_;
};
} // namespace quic
diff --git a/quic/core/quic_config_test.cc b/quic/core/quic_config_test.cc
index 84e89c1..630e67a 100644
--- a/quic/core/quic_config_test.cc
+++ b/quic/core/quic_config_test.cc
@@ -56,6 +56,8 @@
}
TEST_P(QuicConfigTest, ProcessClientHello) {
+ const uint32_t kTestMaxAckDelayMs =
+ static_cast<uint32_t>(kDefaultDelayedAckTimeMs + 1);
QuicConfig client_config;
QuicTagVector cgst;
cgst.push_back(kQBIC);
@@ -70,6 +72,7 @@
QuicTagVector copt;
copt.push_back(kTBBR);
client_config.SetConnectionOptionsToSend(copt);
+ client_config.SetMaxAckDelayToSendMs(kTestMaxAckDelayMs);
CryptoHandshakeMessage msg;
client_config.ToHandshakeMessage(&msg, GetParam());
@@ -99,6 +102,12 @@
2 * kInitialStreamFlowControlWindowForTest);
EXPECT_EQ(config_.ReceivedInitialSessionFlowControlWindowBytes(),
2 * kInitialSessionFlowControlWindowForTest);
+ if (GetQuicReloadableFlag(quic_negotiate_ack_delay_time)) {
+ EXPECT_TRUE(config_.HasReceivedMaxAckDelayMs());
+ EXPECT_EQ(kTestMaxAckDelayMs, config_.ReceivedMaxAckDelayMs());
+ } else {
+ EXPECT_FALSE(config_.HasReceivedMaxAckDelayMs());
+ }
}
TEST_P(QuicConfigTest, ProcessServerHello) {
@@ -106,6 +115,8 @@
host.FromString("127.0.3.1");
const QuicSocketAddress kTestServerAddress = QuicSocketAddress(host, 1234);
const QuicUint128 kTestResetToken = MakeQuicUint128(0, 10111100001);
+ const uint32_t kTestMaxAckDelayMs =
+ static_cast<uint32_t>(kDefaultDelayedAckTimeMs + 1);
QuicConfig server_config;
QuicTagVector cgst;
cgst.push_back(kQBIC);
@@ -119,6 +130,7 @@
2 * kInitialSessionFlowControlWindowForTest);
server_config.SetAlternateServerAddressToSend(kTestServerAddress);
server_config.SetStatelessResetTokenToSend(kTestResetToken);
+ server_config.SetMaxAckDelayToSendMs(kTestMaxAckDelayMs);
CryptoHandshakeMessage msg;
server_config.ToHandshakeMessage(&msg, GetParam());
std::string error_details;
@@ -137,6 +149,12 @@
EXPECT_EQ(kTestServerAddress, config_.ReceivedAlternateServerAddress());
EXPECT_TRUE(config_.HasReceivedStatelessResetToken());
EXPECT_EQ(kTestResetToken, config_.ReceivedStatelessResetToken());
+ if (GetQuicReloadableFlag(quic_negotiate_ack_delay_time)) {
+ EXPECT_TRUE(config_.HasReceivedMaxAckDelayMs());
+ EXPECT_EQ(kTestMaxAckDelayMs, config_.ReceivedMaxAckDelayMs());
+ } else {
+ EXPECT_FALSE(config_.HasReceivedMaxAckDelayMs());
+ }
}
TEST_P(QuicConfigTest, MissingOptionalValuesInCHLO) {
diff --git a/quic/core/quic_sent_packet_manager.cc b/quic/core/quic_sent_packet_manager.cc
index 5762333..90771f8 100644
--- a/quic/core/quic_sent_packet_manager.cc
+++ b/quic/core/quic_sent_packet_manager.cc
@@ -243,6 +243,11 @@
}
send_algorithm_->SetFromConfig(config, perspective);
+ if (config.HasReceivedMaxAckDelayMs()) {
+ peer_max_ack_delay_ =
+ QuicTime::Delta::FromMilliseconds(config.ReceivedMaxAckDelayMs());
+ }
+
if (network_change_visitor_ != nullptr) {
network_change_visitor_->OnCongestionChange();
}