In IETF QUIC client: Delay retransmission of 0-RTT data to be after QuicSession config is set to the fresh server config.
This allows us to verify server config in QuicSession::OnConfigNegotiated() before attempting retransmission. Implementation will come as follow-up.
Also enable T050 in quic_spdy_client_session_test
Client side change only. not protected.
PiperOrigin-RevId: 315352910
Change-Id: I7357e217729c6e59187cce17a30b364517c2c632
diff --git a/quic/core/http/quic_server_session_base_test.cc b/quic/core/http/quic_server_session_base_test.cc
index 291884d..6af67a6 100644
--- a/quic/core/http/quic_server_session_base_test.cc
+++ b/quic/core/http/quic_server_session_base_test.cc
@@ -352,6 +352,7 @@
// more than the negotiated stream limit to deal with rare cases where a
// client FIN/RST is lost.
+ connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
session_->OnConfigNegotiated();
if (!VersionHasIetfQuicFrames(transport_version())) {
// The slightly increased stream limit is set during config negotiation. It
@@ -404,6 +405,7 @@
// streams available. The server accepts slightly more than the negotiated
// stream limit to deal with rare cases where a client FIN/RST is lost.
+ connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
session_->OnConfigNegotiated();
const size_t kAvailableStreamLimit =
session_->MaxAvailableBidirectionalStreams();
@@ -509,6 +511,7 @@
QuicTagVector copt;
copt.push_back(kBWRE);
QuicConfigPeer::SetReceivedConnectionOptions(session_->config(), copt);
+ connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
session_->OnConfigNegotiated();
EXPECT_TRUE(
QuicServerSessionBasePeer::IsBandwidthResumptionEnabled(session_.get()));
@@ -699,6 +702,7 @@
QuicTagVector copt;
copt.push_back(kBWMX);
QuicConfigPeer::SetReceivedConnectionOptions(session_->config(), copt);
+ connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
session_->OnConfigNegotiated();
EXPECT_TRUE(
QuicServerSessionBasePeer::IsBandwidthResumptionEnabled(session_.get()));
@@ -707,6 +711,7 @@
TEST_P(QuicServerSessionBaseTest, NoBandwidthResumptionByDefault) {
EXPECT_FALSE(
QuicServerSessionBasePeer::IsBandwidthResumptionEnabled(session_.get()));
+ connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
session_->OnConfigNegotiated();
EXPECT_FALSE(
QuicServerSessionBasePeer::IsBandwidthResumptionEnabled(session_.get()));
diff --git a/quic/core/http/quic_spdy_client_session.cc b/quic/core/http/quic_spdy_client_session.cc
index 6127e1b..7440edc 100644
--- a/quic/core/http/quic_spdy_client_session.cc
+++ b/quic/core/http/quic_spdy_client_session.cc
@@ -186,7 +186,7 @@
return std::make_unique<QuicCryptoClientStream>(
server_id_, this,
crypto_config_->proof_verifier()->CreateDefaultContext(), crypto_config_,
- this, /*has_application_state = */ true);
+ this, /*has_application_state = */ version().UsesHttp3());
}
bool QuicSpdyClientSession::IsAuthorized(const std::string& /*authority*/) {
diff --git a/quic/core/http/quic_spdy_client_session_test.cc b/quic/core/http/quic_spdy_client_session_test.cc
index 34d95c9..1a6ed6c 100644
--- a/quic/core/http/quic_spdy_client_session_test.cc
+++ b/quic/core/http/quic_spdy_client_session_test.cc
@@ -974,23 +974,31 @@
}
TEST_P(QuicSpdyClientSessionTest, IetfZeroRttSetup) {
- // This feature is HTTP/3 only
- if (!VersionUsesHttp3(session_->transport_version())) {
+ // This feature is TLS-only.
+ if (session_->version().UsesQuicCrypto()) {
return;
}
+
CompleteCryptoHandshake();
EXPECT_FALSE(session_->GetCryptoStream()->IsResumption());
- SettingsFrame settings;
- settings.values[SETTINGS_QPACK_MAX_TABLE_CAPACITY] = 2;
- settings.values[SETTINGS_MAX_HEADER_LIST_SIZE] = 5;
- settings.values[256] = 4; // unknown setting
- session_->OnSettingsFrame(settings);
+ if (session_->version().UsesHttp3()) {
+ SettingsFrame settings;
+ settings.values[SETTINGS_QPACK_MAX_TABLE_CAPACITY] = 2;
+ settings.values[SETTINGS_MAX_HEADER_LIST_SIZE] = 5;
+ settings.values[256] = 4; // unknown setting
+ session_->OnSettingsFrame(settings);
+ }
CreateConnection();
// Session configs should be in initial state.
- EXPECT_EQ(0u, session_->flow_controller()->send_window_offset());
- EXPECT_EQ(std::numeric_limits<size_t>::max(),
- session_->max_outbound_header_list_size());
+ if (session_->version().UsesHttp3()) {
+ EXPECT_EQ(0u, session_->flow_controller()->send_window_offset());
+ EXPECT_EQ(std::numeric_limits<size_t>::max(),
+ session_->max_outbound_header_list_size());
+ } else {
+ EXPECT_EQ(kMinimumFlowControlSendWindow,
+ session_->flow_controller()->send_window_offset());
+ }
session_->CryptoConnect();
EXPECT_TRUE(session_->IsEncryptionEstablished());
EXPECT_EQ(ENCRYPTION_ZERO_RTT, session_->connection()->encryption_level());
@@ -999,17 +1007,23 @@
// succeeds.
EXPECT_EQ(kInitialSessionFlowControlWindowForTest,
session_->flow_controller()->send_window_offset());
- auto* id_manager = QuicSessionPeer::v99_streamid_manager(session_.get());
- EXPECT_EQ(kDefaultMaxStreamsPerConnection,
- id_manager->max_outgoing_bidirectional_streams());
- EXPECT_EQ(
- kDefaultMaxStreamsPerConnection + kHttp3StaticUnidirectionalStreamCount,
- id_manager->max_outgoing_unidirectional_streams());
- auto* control_stream =
- QuicSpdySessionPeer::GetSendControlStream(session_.get());
- EXPECT_EQ(kInitialStreamFlowControlWindowForTest,
- control_stream->flow_controller()->send_window_offset());
- EXPECT_EQ(5u, session_->max_outbound_header_list_size());
+ if (session_->version().UsesHttp3()) {
+ auto* id_manager = QuicSessionPeer::v99_streamid_manager(session_.get());
+ EXPECT_EQ(kDefaultMaxStreamsPerConnection,
+ id_manager->max_outgoing_bidirectional_streams());
+ EXPECT_EQ(
+ kDefaultMaxStreamsPerConnection + kHttp3StaticUnidirectionalStreamCount,
+ id_manager->max_outgoing_unidirectional_streams());
+ auto* control_stream =
+ QuicSpdySessionPeer::GetSendControlStream(session_.get());
+ EXPECT_EQ(kInitialStreamFlowControlWindowForTest,
+ control_stream->flow_controller()->send_window_offset());
+ EXPECT_EQ(5u, session_->max_outbound_header_list_size());
+ } else {
+ auto* id_manager = QuicSessionPeer::GetStreamIdManager(session_.get());
+ EXPECT_EQ(kDefaultMaxStreamsPerConnection,
+ id_manager->max_open_outgoing_streams());
+ }
// Complete the handshake with a different config.
QuicConfig config = DefaultQuicConfig();
@@ -1026,28 +1040,39 @@
EXPECT_TRUE(session_->GetCryptoStream()->IsResumption());
EXPECT_EQ(kInitialSessionFlowControlWindowForTest + 1,
session_->flow_controller()->send_window_offset());
- EXPECT_EQ(kDefaultMaxStreamsPerConnection + 1,
- id_manager->max_outgoing_bidirectional_streams());
- EXPECT_EQ(kDefaultMaxStreamsPerConnection +
- kHttp3StaticUnidirectionalStreamCount + 1,
- id_manager->max_outgoing_unidirectional_streams());
- EXPECT_EQ(kInitialStreamFlowControlWindowForTest + 1,
- control_stream->flow_controller()->send_window_offset());
+ if (session_->version().UsesHttp3()) {
+ auto* id_manager = QuicSessionPeer::v99_streamid_manager(session_.get());
+ auto* control_stream =
+ QuicSpdySessionPeer::GetSendControlStream(session_.get());
+ EXPECT_EQ(kDefaultMaxStreamsPerConnection + 1,
+ id_manager->max_outgoing_bidirectional_streams());
+ EXPECT_EQ(kDefaultMaxStreamsPerConnection +
+ kHttp3StaticUnidirectionalStreamCount + 1,
+ id_manager->max_outgoing_unidirectional_streams());
+ EXPECT_EQ(kInitialStreamFlowControlWindowForTest + 1,
+ control_stream->flow_controller()->send_window_offset());
+ } else {
+ auto* id_manager = QuicSessionPeer::GetStreamIdManager(session_.get());
+ EXPECT_EQ(kDefaultMaxStreamsPerConnection + 1,
+ id_manager->max_open_outgoing_streams());
+ }
}
TEST_P(QuicSpdyClientSessionTest, RetransmitDataOnZeroRttReject) {
- // This feature is HTTP/3 only
- if (!VersionUsesHttp3(session_->transport_version())) {
+ // This feature is TLS-only.
+ if (session_->version().UsesQuicCrypto()) {
return;
}
CompleteCryptoHandshake();
EXPECT_FALSE(session_->GetCryptoStream()->IsResumption());
- SettingsFrame settings;
- settings.values[SETTINGS_QPACK_MAX_TABLE_CAPACITY] = 2;
- settings.values[SETTINGS_MAX_HEADER_LIST_SIZE] = 5;
- settings.values[256] = 4; // unknown setting
- session_->OnSettingsFrame(settings);
+ if (session_->version().UsesHttp3()) {
+ SettingsFrame settings;
+ settings.values[SETTINGS_QPACK_MAX_TABLE_CAPACITY] = 2;
+ settings.values[SETTINGS_MAX_HEADER_LIST_SIZE] = 5;
+ settings.values[256] = 4; // unknown setting
+ session_->OnSettingsFrame(settings);
+ }
// Create a second connection, but disable 0-RTT on the server.
CreateConnection();
@@ -1056,12 +1081,13 @@
config.SetMaxBidirectionalStreamsToSend(kDefaultMaxStreamsPerConnection);
SSL_CTX_set_early_data_enabled(server_crypto_config_->ssl_ctx(), false);
- // 3 packets will be written: CHLO, HTTP/3 SETTINGS, and request data.
+ // Packets will be written: CHLO, HTTP/3 SETTINGS (H/3 only), and request
+ // data.
EXPECT_CALL(*connection_,
OnPacketSent(ENCRYPTION_INITIAL, NOT_RETRANSMISSION));
EXPECT_CALL(*connection_,
OnPacketSent(ENCRYPTION_ZERO_RTT, NOT_RETRANSMISSION))
- .Times(2);
+ .Times(session_->version().UsesHttp3() ? 2 : 1);
session_->CryptoConnect();
EXPECT_TRUE(session_->IsEncryptionEstablished());
EXPECT_EQ(ENCRYPTION_ZERO_RTT, session_->connection()->encryption_level());
diff --git a/quic/core/http/quic_spdy_session_test.cc b/quic/core/http/quic_spdy_session_test.cc
index 7b4e0d7..b391c06 100644
--- a/quic/core/http/quic_spdy_session_test.cc
+++ b/quic/core/http/quic_spdy_session_test.cc
@@ -1615,6 +1615,7 @@
return;
}
QuicConfigPeer::SetReceivedMaxUnidirectionalStreams(session_.config(), 2u);
+ connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
EXPECT_CALL(
*connection_,
@@ -1629,6 +1630,7 @@
copt.push_back(kIFW7);
QuicConfigPeer::SetReceivedConnectionOptions(session_.config(), copt);
+ connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
session_.OnConfigNegotiated();
EXPECT_EQ(192 * 1024u, QuicFlowControllerPeer::ReceiveWindowSize(
session_.flow_controller()));
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc
index fe79962..912b0cb 100644
--- a/quic/core/quic_session.cc
+++ b/quic/core/quic_session.cc
@@ -999,6 +999,21 @@
}
void QuicSession::OnConfigNegotiated() {
+ // In versions with TLS, the configs will be set twice if 0-RTT is available.
+ // In the second config setting, 1-RTT keys are guaranteed to be available.
+ if (GetQuicReloadableFlag(quic_enable_zero_rtt_for_tls) &&
+ version().UsesTls() && is_configured_ &&
+ connection_->encryption_level() != ENCRYPTION_FORWARD_SECURE) {
+ QUIC_BUG
+ << ENDPOINT
+ << "1-RTT keys missing when config is negotiated for the second time.";
+ connection_->CloseConnection(
+ QUIC_INTERNAL_ERROR,
+ "1-RTT keys missing when config is negotiated for the second time.",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return;
+ }
+
QUIC_DVLOG(1) << ENDPOINT << "OnConfigNegotiated";
connection_->SetFromConfig(config_);
@@ -1150,11 +1165,16 @@
OnNewSessionFlowControlWindow(
config_.ReceivedInitialSessionFlowControlWindowBytes());
}
+
is_configured_ = true;
connection()->OnConfigNegotiated();
// Ask flow controllers to try again since the config could have unblocked us.
- if (connection_->version().AllowsLowFlowControlLimits()) {
+ // Or if this session is configured on TLS enabled QUIC versions,
+ // attempt to retransmit 0-RTT data if there's any.
+ if (connection_->version().AllowsLowFlowControlLimits() ||
+ (GetQuicReloadableFlag(quic_enable_zero_rtt_for_tls) &&
+ version().UsesTls())) {
OnCanWrite();
}
}
@@ -1382,11 +1402,6 @@
QUIC_DVLOG(1) << ENDPOINT << "Set default encryption level to " << level;
connection()->SetDefaultEncryptionLevel(level);
- if (perspective() == Perspective::IS_CLIENT &&
- level == ENCRYPTION_FORWARD_SECURE) {
- // 1-RTT write key is available. Retransmit 0-RTT data if there is any.
- OnCanWrite();
- }
}
void QuicSession::SetDefaultEncryptionLevel(EncryptionLevel level) {
@@ -1476,7 +1491,11 @@
// too many.
connection_->RetransmitZeroRttPackets();
if (connection_->encryption_level() == ENCRYPTION_FORWARD_SECURE) {
- OnCanWrite();
+ QUIC_BUG << "1-RTT keys already available when 0-RTT is rejected.";
+ connection_->CloseConnection(
+ QUIC_INTERNAL_ERROR,
+ "1-RTT keys already available when 0-RTT is rejected.",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
}
}
diff --git a/quic/core/quic_session_test.cc b/quic/core/quic_session_test.cc
index 5f0f9b7..70fda35 100644
--- a/quic/core/quic_session_test.cc
+++ b/quic/core/quic_session_test.cc
@@ -611,6 +611,7 @@
if (connection_->version().HasHandshakeDone()) {
EXPECT_CALL(*connection_, SendControlFrame(_));
}
+ connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
session_.GetMutableCryptoStream()->OnHandshakeMessage(message);
EXPECT_TRUE(session_.OneRttKeysAvailable());
}
@@ -1006,6 +1007,7 @@
QuicTagVector copt;
copt.push_back(kH2PR);
QuicConfigPeer::SetReceivedConnectionOptions(session_.config(), copt);
+ connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
session_.OnConfigNegotiated();
ASSERT_TRUE(session_.use_http2_priority_write_scheduler());
@@ -1088,6 +1090,7 @@
QuicTagVector copt;
copt.push_back(kRRWS);
QuicConfigPeer::SetReceivedConnectionOptions(session_.config(), copt);
+ connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
session_.OnConfigNegotiated();
session_.set_writev_consumes_all_data(true);
@@ -1133,6 +1136,7 @@
CryptoHandshakeMessage msg;
MockPacketWriter* writer = static_cast<MockPacketWriter*>(
QuicConnectionPeer::GetWriter(session_.connection()));
+ connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
session_.GetMutableCryptoStream()->OnHandshakeMessage(msg);
// Drive congestion control manually.
@@ -1511,6 +1515,7 @@
EXPECT_CALL(*connection_, SendControlFrame(_));
}
CryptoHandshakeMessage msg;
+ connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
session_.GetMutableCryptoStream()->OnHandshakeMessage(msg);
EXPECT_EQ(kMaximumIdleTimeoutSecs + 3,
QuicConnectionPeer::GetNetworkTimeout(connection_).ToSeconds());
@@ -1791,6 +1796,7 @@
} else {
EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0);
}
+ connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
session_.OnConfigNegotiated();
}
@@ -1800,6 +1806,7 @@
copt.push_back(kIFW7);
QuicConfigPeer::SetReceivedConnectionOptions(session_.config(), copt);
+ connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
session_.OnConfigNegotiated();
EXPECT_EQ(192 * 1024u, QuicFlowControllerPeer::ReceiveWindowSize(
session_.flow_controller()));
@@ -2065,6 +2072,7 @@
kInvalidWindow);
EXPECT_CALL(*connection_,
CloseConnection(QUIC_FLOW_CONTROL_INVALID_WINDOW, _, _));
+ connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
session_.OnConfigNegotiated();
}
@@ -2076,6 +2084,7 @@
QuicConfigPeer::SetReceivedMaxBidirectionalStreams(
session_.config(), kDefaultMaxStreamsPerConnection - 1);
EXPECT_CALL(*connection_, CloseConnection(QUIC_MAX_STREAMS_ERROR, _, _));
+ connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
session_.OnConfigNegotiated();
}
@@ -2087,6 +2096,7 @@
QuicConfigPeer::SetReceivedMaxUnidirectionalStreams(
session_.config(), kDefaultMaxStreamsPerConnection - 1);
EXPECT_CALL(*connection_, CloseConnection(QUIC_MAX_STREAMS_ERROR, _, _));
+ connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
session_.OnConfigNegotiated();
}
@@ -2104,6 +2114,8 @@
.WillOnce(
Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _));
+
+ connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
session_.OnConfigNegotiated();
}
@@ -2477,6 +2489,7 @@
EXPECT_CALL(*connection_, SendControlFrame(_));
}
CryptoHandshakeMessage handshake_message;
+ connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
session_.GetMutableCryptoStream()->OnHandshakeMessage(handshake_message);
EXPECT_TRUE(session_.OneRttKeysAvailable());