For IETF QUIC client, Retransmit 0-RTT packets with 1-RTT key when 0-RTT is rejected. Client side change only. not protected. PiperOrigin-RevId: 314632009 Change-Id: Ib6fc91aa28a5ea292a8063e7902a6c2ef0d4876d
diff --git a/quic/core/http/quic_spdy_client_session_test.cc b/quic/core/http/quic_spdy_client_session_test.cc index e68199c..e7d4421 100644 --- a/quic/core/http/quic_spdy_client_session_test.cc +++ b/quic/core/http/quic_spdy_client_session_test.cc
@@ -1035,6 +1035,57 @@ control_stream->flow_controller()->send_window_offset()); } +TEST_P(QuicSpdyClientSessionTest, RetransmitDataOnZeroRttReject) { + // This feature is HTTP/3 only + if (!VersionUsesHttp3(session_->transport_version())) { + 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); + + // Create a second connection, but disable 0-RTT on the server. + CreateConnection(); + QuicCryptoClientStream* crypto_stream = + static_cast<QuicCryptoClientStream*>(session_->GetMutableCryptoStream()); + std::unique_ptr<QuicCryptoServerConfig> crypto_config = + crypto_test_utils::CryptoServerConfigForTesting(); + QuicConfig config = DefaultQuicConfig(); + config.SetMaxUnidirectionalStreamsToSend(kDefaultMaxStreamsPerConnection); + config.SetMaxBidirectionalStreamsToSend(kDefaultMaxStreamsPerConnection); + SSL_CTX_set_early_data_enabled(crypto_config->ssl_ctx(), false); + + // 3 packets will be written: CHLO, HTTP/3 SETTINGS, and request data. + EXPECT_CALL(*connection_, + OnPacketSent(ENCRYPTION_INITIAL, NOT_RETRANSMISSION)); + EXPECT_CALL(*connection_, + OnPacketSent(ENCRYPTION_ZERO_RTT, NOT_RETRANSMISSION)) + .Times(2); + session_->CryptoConnect(); + EXPECT_TRUE(session_->IsEncryptionEstablished()); + EXPECT_EQ(ENCRYPTION_ZERO_RTT, session_->connection()->encryption_level()); + QuicSpdyClientStream* stream = session_->CreateOutgoingBidirectionalStream(); + ASSERT_TRUE(stream); + stream->WriteOrBufferData("hello", true, nullptr); + + // When handshake is done, the client sends 2 packet: HANDSHAKE FINISHED, and + // coalesced retransmission of HTTP/3 SETTINGS and request data. + EXPECT_CALL(*connection_, + OnPacketSent(ENCRYPTION_HANDSHAKE, NOT_RETRANSMISSION)); + // TODO(b/158027651): change transmission type to ALL_ZERO_RTT_RETRANSMISSION. + EXPECT_CALL(*connection_, + OnPacketSent(ENCRYPTION_FORWARD_SECURE, LOSS_RETRANSMISSION)); + crypto_test_utils::HandshakeWithFakeServer( + &config, crypto_config.get(), &helper_, &alarm_factory_, connection_, + crypto_stream, AlpnForVersion(connection_->version())); + EXPECT_TRUE(session_->GetCryptoStream()->IsResumption()); +} + } // namespace } // namespace test } // namespace quic
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc index 1eb69e9..e88d109 100644 --- a/quic/core/quic_session.cc +++ b/quic/core/quic_session.cc
@@ -1386,6 +1386,11 @@ QUIC_DVLOG(1) << ENDPOINT << "Set default encryption level to " << level; QUIC_RELOADABLE_FLAG_COUNT(quic_change_default_encryption_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(); + } return; } @@ -1395,6 +1400,9 @@ // available. QUIC_DVLOG(1) << ENDPOINT << "Set default encryption level to " << level; connection()->SetDefaultEncryptionLevel(level); + if (perspective() == Perspective::IS_CLIENT) { + OnCanWrite(); + } } } @@ -1480,7 +1488,13 @@ } void QuicSession::OnZeroRttRejected() { - // TODO(b/153726130): Handle early data rejection. + // TODO(b/153726130): Read stream limit and flow control limit from server + // transport params, and close the connection proactively if client has used + // too many. + connection_->RetransmitZeroRttPackets(); + if (connection_->encryption_level() == ENCRYPTION_FORWARD_SECURE) { + OnCanWrite(); + } } bool QuicSession::FillTransportParameters(TransportParameters* params) {
diff --git a/quic/core/tls_client_handshaker.cc b/quic/core/tls_client_handshaker.cc index 6ab93e3..a6c713e 100644 --- a/quic/core/tls_client_handshaker.cc +++ b/quic/core/tls_client_handshaker.cc
@@ -12,6 +12,7 @@ #include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h" #include "net/third_party/quiche/src/quic/core/crypto/transport_parameters.h" #include "net/third_party/quiche/src/quic/core/quic_session.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" #include "net/third_party/quiche/src/quic/platform/api/quic_hostname_utils.h" #include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" @@ -388,6 +389,9 @@ if (level == ENCRYPTION_FORWARD_SECURE || level == ENCRYPTION_ZERO_RTT) { encryption_established_ = true; } + if (level == ENCRYPTION_FORWARD_SECURE) { + handshaker_delegate()->DiscardOldEncryptionKey(ENCRYPTION_ZERO_RTT); + } TlsHandshaker::SetWriteSecret(level, cipher, write_secret); }
diff --git a/quic/core/tls_client_handshaker_test.cc b/quic/core/tls_client_handshaker_test.cc index fff5135..a59e6b8 100644 --- a/quic/core/tls_client_handshaker_test.cc +++ b/quic/core/tls_client_handshaker_test.cc
@@ -10,11 +10,15 @@ #include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h" #include "net/third_party/quiche/src/quic/core/quic_packets.h" #include "net/third_party/quiche/src/quic/core/quic_server_id.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" #include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/core/quic_versions.h" #include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" #include "net/third_party/quiche/src/quic/platform/api/quic_test.h" #include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_connection_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_framer_peer.h" #include "net/third_party/quiche/src/quic/test_tools/quic_session_peer.h" #include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" #include "net/third_party/quiche/src/quic/test_tools/simple_session_cache.h" @@ -381,9 +385,34 @@ // Create a second connection, but disable 0-RTT on the server. SSL_CTX_set_early_data_enabled(server_crypto_config_->ssl_ctx(), false); CreateConnection(); - EXPECT_CALL(*session_, OnZeroRttRejected()); + + EXPECT_CALL(*session_, OnZeroRttRejected()) + .WillOnce(testing::Invoke( + session_.get(), &TestQuicSpdyClientSession::ReallyOnZeroRttRejected)); + + // 4 packets will be sent in this connection: initial handshake packet, 0-RTT + // packet containing SETTINGS, handshake packet upon 0-RTT rejection, 0-RTT + // packet retransmission. + EXPECT_CALL(*connection_, + OnPacketSent(ENCRYPTION_INITIAL, NOT_RETRANSMISSION)); + if (VersionUsesHttp3(session_->transport_version())) { + EXPECT_CALL(*connection_, + OnPacketSent(ENCRYPTION_ZERO_RTT, NOT_RETRANSMISSION)); + } + EXPECT_CALL(*connection_, + OnPacketSent(ENCRYPTION_HANDSHAKE, NOT_RETRANSMISSION)); + if (VersionUsesHttp3(session_->transport_version())) { + // TODO(b/158027651): change transmission type to + // ALL_ZERO_RTT_RETRANSMISSION. + EXPECT_CALL(*connection_, + OnPacketSent(ENCRYPTION_FORWARD_SECURE, LOSS_RETRANSMISSION)); + } + CompleteCryptoHandshake(); + QuicFramer* framer = QuicConnectionPeer::GetFramer(connection_); + EXPECT_EQ(nullptr, QuicFramerPeer::GetEncrypter(framer, ENCRYPTION_ZERO_RTT)); + EXPECT_EQ(PROTOCOL_TLS1_3, stream()->handshake_protocol()); EXPECT_TRUE(stream()->encryption_established()); EXPECT_TRUE(stream()->one_rtt_keys_available());