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(); }
diff --git a/quic/quartc/quartc_factory.cc b/quic/quartc/quartc_factory.cc index 0c11bd0..8a69ab5 100644 --- a/quic/quartc/quartc_factory.cc +++ b/quic/quartc/quartc_factory.cc
@@ -43,6 +43,13 @@ dummy_id, dummy_address, connection_helper, alarm_factory, writer.get(), Perspective::IS_CLIENT, supported_versions); + // Quartc sets its own ack delay; get that ack delay and copy it over + // to the QuicConfig so that it can be properly advertised to the peer + // via transport parameter negotiation. + quic_config.SetMaxAckDelayToSendMs(quic_connection->sent_packet_manager() + .local_max_ack_delay() + .ToMilliseconds()); + return QuicMakeUnique<QuartcClientSession>( std::move(quic_connection), quic_config, supported_versions, clock, std::move(writer),