Wire in the ack exponent trransport config for IETF QUIC
gfe-relnote: N/A, for IETF QUIC Only, protected by V99 flag.
PiperOrigin-RevId: 260736433
Change-Id: I5468bc4f052d78d2bf6890cdf0734cece5b107c0
diff --git a/quic/core/crypto/crypto_protocol.h b/quic/core/crypto/crypto_protocol.h
index fdd4fd1..12afbd1 100644
--- a/quic/core/crypto/crypto_protocol.h
+++ b/quic/core/crypto/crypto_protocol.h
@@ -219,6 +219,8 @@
const QuicTag kSCLS = TAG('S', 'C', 'L', 'S'); // Silently close on timeout
const QuicTag kMIBS = TAG('M', 'I', 'D', 'S'); // Max incoming bidi streams
const QuicTag kMIUS = TAG('M', 'I', 'U', 'S'); // Max incoming unidi streams
+const QuicTag kADE = TAG('A', 'D', 'E', 0); // Ack Delay Exponent (IETF
+ // QUIC ACK Frame Only).
const QuicTag kIRTT = TAG('I', 'R', 'T', 'T'); // Estimated initial RTT in us.
const QuicTag kSNI = TAG('S', 'N', 'I', '\0'); // Server name
// indication
diff --git a/quic/core/http/end_to_end_test.cc b/quic/core/http/end_to_end_test.cc
index c78fab4..10455e8 100644
--- a/quic/core/http/end_to_end_test.cc
+++ b/quic/core/http/end_to_end_test.cc
@@ -634,6 +634,39 @@
}
}
+// Simple transaction, but set a non-default ack exponent at the client
+// and ensure it gets to the server.
+TEST_P(EndToEndTest, SimpleRequestResponseWithAckExponentChange) {
+ const uint32_t kClientAckDelayExponent = kDefaultAckDelayExponent + 100u;
+ // Force the ACK exponent to be something other than the default.
+ // Note that it is sent only if doing IETF QUIC.
+ client_config_.SetAckDelayExponentToSend(kClientAckDelayExponent);
+ 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 (VersionHasIetfQuicFrames(
+ GetParam().negotiated_version.transport_version)) {
+ // Should be only for IETF QUIC.
+ EXPECT_EQ(kClientAckDelayExponent,
+ GetServerConnection()->framer().peer_ack_delay_exponent());
+ } else {
+ // No change for Google QUIC.
+ EXPECT_EQ(kDefaultAckDelayExponent,
+ GetServerConnection()->framer().peer_ack_delay_exponent());
+ }
+ // No change, regardless of version.
+ EXPECT_EQ(kDefaultAckDelayExponent,
+ GetServerConnection()->framer().local_ack_delay_exponent());
+}
+
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 661b5f3..22aef80 100644
--- a/quic/core/quic_config.cc
+++ b/quic/core/quic_config.cc
@@ -414,7 +414,8 @@
support_max_header_list_size_(kSMHL, PRESENCE_OPTIONAL),
stateless_reset_token_(kSRST, PRESENCE_OPTIONAL),
max_incoming_unidirectional_streams_(kMIUS, PRESENCE_OPTIONAL),
- max_ack_delay_ms_(kMAD, PRESENCE_OPTIONAL) {
+ max_ack_delay_ms_(kMAD, PRESENCE_OPTIONAL),
+ ack_delay_exponent_(kADE, PRESENCE_OPTIONAL) {
SetDefaults();
}
@@ -557,6 +558,22 @@
return max_ack_delay_ms_.GetReceivedValue();
}
+void QuicConfig::SetAckDelayExponentToSend(uint32_t exponent) {
+ ack_delay_exponent_.SetSendValue(exponent);
+}
+
+uint32_t QuicConfig::GetAckDelayExponentToSend() {
+ return ack_delay_exponent_.GetSendValue();
+}
+
+bool QuicConfig::HasReceivedAckDelayExponent() const {
+ return ack_delay_exponent_.HasReceivedValue();
+}
+
+uint32_t QuicConfig::ReceivedAckDelayExponent() const {
+ return ack_delay_exponent_.GetReceivedValue();
+}
+
bool QuicConfig::HasSetBytesForConnectionIdToSend() const {
return bytes_for_connection_id_.HasSendValue();
}
@@ -711,6 +728,7 @@
SetInitialSessionFlowControlWindowToSend(kMinimumFlowControlSendWindow);
SetMaxAckDelayToSendMs(kDefaultDelayedAckTimeMs);
SetSupportMaxHeaderListSize();
+ SetAckDelayExponentToSend(kDefaultAckDelayExponent);
}
void QuicConfig::ToHandshakeMessage(
@@ -724,6 +742,7 @@
max_incoming_bidirectional_streams_.ToHandshakeMessage(out);
if (VersionHasIetfQuicFrames(transport_version)) {
max_incoming_unidirectional_streams_.ToHandshakeMessage(out);
+ ack_delay_exponent_.ToHandshakeMessage(out);
}
if (GetQuicReloadableFlag(quic_negotiate_ack_delay_time)) {
QUIC_RELOADABLE_FLAG_COUNT_N(quic_negotiate_ack_delay_time, 1, 4);
@@ -806,6 +825,10 @@
error = max_ack_delay_ms_.ProcessPeerHello(peer_hello, hello_type,
error_details);
}
+ if (error == QUIC_NO_ERROR) {
+ error = ack_delay_exponent_.ProcessPeerHello(peer_hello, hello_type,
+ error_details);
+ }
return error;
}
@@ -838,6 +861,7 @@
QUIC_RELOADABLE_FLAG_COUNT_N(quic_negotiate_ack_delay_time, 3, 4);
params->max_ack_delay.set_value(kDefaultDelayedAckTimeMs);
}
+ params->ack_delay_exponent.set_value(ack_delay_exponent_.GetSendValue());
params->disable_migration =
connection_migration_disabled_.HasSendValue() &&
connection_migration_disabled_.GetSendValue() != 0;
@@ -924,6 +948,9 @@
max_ack_delay_ms_.SetReceivedValue(std::min<uint32_t>(
params.max_ack_delay.value(), std::numeric_limits<uint32_t>::max()));
}
+ if (params.ack_delay_exponent.IsValid()) {
+ ack_delay_exponent_.SetReceivedValue(params.ack_delay_exponent.value());
+ }
connection_migration_disabled_.SetReceivedValue(
params.disable_migration ? 1u : 0u);
diff --git a/quic/core/quic_config.h b/quic/core/quic_config.h
index 3c82a68..b4a4ca9 100644
--- a/quic/core/quic_config.h
+++ b/quic/core/quic_config.h
@@ -424,6 +424,11 @@
bool HasReceivedMaxAckDelayMs() const;
uint32_t ReceivedMaxAckDelayMs() const;
+ void SetAckDelayExponentToSend(uint32_t exponent);
+ uint32_t GetAckDelayExponentToSend();
+ bool HasReceivedAckDelayExponent() const;
+ uint32_t ReceivedAckDelayExponent() const;
+
bool negotiated() const;
void SetCreateSessionTagIndicators(QuicTagVector tags);
@@ -516,6 +521,13 @@
// The received value is the value received from the peer and used by
// the peer.
QuicFixedUint32 max_ack_delay_ms_;
+
+ // ack_delay_exponent parameter negotiated in IETF QUIC transport
+ // parameter negotiation. The sent exponent is the exponent that this
+ // node uses when serializing an ACK frame (and the peer should use when
+ // deserializing the frame); the received exponent is the value the peer uses
+ // to serialize frames and this node uses to deserialize them.
+ QuicFixedUint32 ack_delay_exponent_;
};
} // namespace quic
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index a13c506..e65492f 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -425,6 +425,9 @@
stateless_reset_token_received_ = true;
received_stateless_reset_token_ = config.ReceivedStatelessResetToken();
}
+ if (config.HasReceivedAckDelayExponent()) {
+ framer_.set_peer_ack_delay_exponent(config.ReceivedAckDelayExponent());
+ }
if (GetQuicReloadableFlag(quic_send_timestamps) &&
config.HasClientSentConnectionOption(kSTMP, perspective_)) {
QUIC_RELOADABLE_FLAG_COUNT(quic_send_timestamps);
diff --git a/quic/core/quic_constants.h b/quic/core/quic_constants.h
index 1ba32d8..cdee0f9 100644
--- a/quic/core/quic_constants.h
+++ b/quic/core/quic_constants.h
@@ -113,6 +113,9 @@
// in low-bandwidth (< ~384 kbps), where an ack is sent per packet.
const int64_t kDefaultDelayedAckTimeMs = 25;
+// Default shift of the ACK delay in the IETF QUIC ACK frame.
+const uint32_t kDefaultAckDelayExponent = 3;
+
// Minimum tail loss probe time in ms.
static const int64_t kMinTailLossProbeTimeoutMs = 10;
diff --git a/quic/core/quic_framer.cc b/quic/core/quic_framer.cc
index e9d6fa9..59edc79 100644
--- a/quic/core/quic_framer.cc
+++ b/quic/core/quic_framer.cc
@@ -50,11 +50,6 @@
#define ENDPOINT \
(perspective_ == Perspective::IS_SERVER ? "Server: " : "Client: ")
-// How much to shift the timestamp in the IETF Ack frame.
-// TODO(fkastenholz) when we get real IETF QUIC, need to get
-// the currect shift from the transport parameters.
-const int kIetfAckTimestampShift = 3;
-
// Number of bits the packet number length bits are shifted from the right
// edge of the header.
const uint8_t kPublicHeaderSequenceNumberShift = 4;
@@ -482,7 +477,9 @@
expected_server_connection_id_length),
expected_client_connection_id_length_(0),
supports_multiple_packet_number_spaces_(false),
- last_written_packet_number_length_(0) {
+ last_written_packet_number_length_(0),
+ peer_ack_delay_exponent_(kDefaultAckDelayExponent),
+ local_ack_delay_exponent_(kDefaultAckDelayExponent) {
DCHECK(!supported_versions.empty());
version_ = supported_versions_[0];
decrypter_[ENCRYPTION_INITIAL] = QuicMakeUnique<NullDecrypter>(perspective);
@@ -3753,12 +3750,10 @@
return false;
}
- // TODO(fkastenholz) when we get real IETF QUIC, need to get
- // the currect shift from the transport parameters.
if (ack_delay_time_in_us == kVarInt62MaxValue) {
ack_frame->ack_delay_time = QuicTime::Delta::Infinite();
} else {
- ack_delay_time_in_us = (ack_delay_time_in_us << kIetfAckTimestampShift);
+ ack_delay_time_in_us = (ack_delay_time_in_us << peer_ack_delay_exponent_);
ack_frame->ack_delay_time =
QuicTime::Delta::FromMicroseconds(ack_delay_time_in_us);
}
@@ -4566,7 +4561,7 @@
ack_frame_size += QuicDataWriter::GetVarInt62Len(largest_acked.ToUint64());
uint64_t ack_delay_time_us;
ack_delay_time_us = frame.ack_delay_time.ToMicroseconds();
- ack_delay_time_us = ack_delay_time_us >> kIetfAckTimestampShift;
+ ack_delay_time_us = ack_delay_time_us >> local_ack_delay_exponent_;
ack_frame_size += QuicDataWriter::GetVarInt62Len(ack_delay_time_us);
// If |ecn_counters_populated| is true and any of the ecn counters is non-0
@@ -5423,8 +5418,7 @@
if (!frame.ack_delay_time.IsInfinite()) {
DCHECK_LE(0u, frame.ack_delay_time.ToMicroseconds());
ack_delay_time_us = frame.ack_delay_time.ToMicroseconds();
- // TODO(fkastenholz): Use the shift from TLS transport parameters.
- ack_delay_time_us = ack_delay_time_us >> kIetfAckTimestampShift;
+ ack_delay_time_us = ack_delay_time_us >> local_ack_delay_exponent_;
}
if (!writer->WriteVarInt62(ack_delay_time_us)) {
diff --git a/quic/core/quic_framer.h b/quic/core/quic_framer.h
index 2349a77..0143abe 100644
--- a/quic/core/quic_framer.h
+++ b/quic/core/quic_framer.h
@@ -611,6 +611,18 @@
uint8_t* source_connection_id_length_out,
std::string* detailed_error);
+ void set_local_ack_delay_exponent(uint32_t exponent) {
+ local_ack_delay_exponent_ = exponent;
+ }
+ uint32_t local_ack_delay_exponent() const {
+ return local_ack_delay_exponent_;
+ }
+
+ void set_peer_ack_delay_exponent(uint32_t exponent) {
+ peer_ack_delay_exponent_ = exponent;
+ }
+ uint32_t peer_ack_delay_exponent() const { return peer_ack_delay_exponent_; }
+
private:
friend class test::QuicFramerPeer;
@@ -1020,6 +1032,15 @@
// The length in bytes of the last packet number written to an IETF-framed
// packet.
size_t last_written_packet_number_length_;
+
+ // The amount to shift the ack timestamp in ACK frames. The default is 3.
+ // Local_ is the amount this node shifts timestamps in ACK frames it
+ // generates. it is sent to the peer in a transport parameter negotiation.
+ // Peer_ is the amount the peer shifts timestamps when it sends ACK frames to
+ // this node. This node "unshifts" by this amount. The value is received from
+ // the peer in the transport parameter negotiation. IETF QUIC only.
+ uint32_t peer_ack_delay_exponent_;
+ uint32_t local_ack_delay_exponent_;
};
} // namespace quic