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());
diff --git a/quic/test_tools/quic_test_utils.cc b/quic/test_tools/quic_test_utils.cc
index fec4cc7..ed668d7 100644
--- a/quic/test_tools/quic_test_utils.cc
+++ b/quic/test_tools/quic_test_utils.cc
@@ -566,6 +566,7 @@
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
// Transfer ownership of the packet to the SentPacketManager and the
// ack notifier to the AckNotifierManager.
+ OnPacketSent(packet.encryption_level, packet.transmission_type);
QuicConnectionPeer::GetSentPacketManager(this)->OnPacketSent(
&packet, clock_.ApproximateNow(), NOT_RETRANSMISSION,
HAS_RETRANSMITTABLE_DATA);
diff --git a/quic/test_tools/quic_test_utils.h b/quic/test_tools/quic_test_utils.h
index 51cc7bf..11bf3c6 100644
--- a/quic/test_tools/quic_test_utils.h
+++ b/quic/test_tools/quic_test_utils.h
@@ -742,6 +742,8 @@
void SendOrQueuePacket(SerializedPacket packet) override;
+ MOCK_METHOD(void, OnPacketSent, (EncryptionLevel, TransmissionType));
+
std::vector<std::unique_ptr<QuicEncryptedPacket>> encrypted_packets_;
MockClock clock_;
};
@@ -1143,6 +1145,8 @@
MOCK_METHOD(void, OnAlpnSelected, (quiche::QuicheStringPiece), (override));
MOCK_METHOD(void, OnZeroRttRejected, (), (override));
+ void ReallyOnZeroRttRejected() { QuicSession::OnZeroRttRejected(); }
+
QuicCryptoClientStream* GetMutableCryptoStream() override;
const QuicCryptoClientStream* GetCryptoStream() const override;