Skip to content

Commit

Permalink
quic: Add option to disable QUIC early data (0-RTT) (#38434)
Browse files Browse the repository at this point in the history
This will be useful for certain mobile clients to disable early data /
0-RTT in the QUICHE layer itself.

Signed-off-by: Ali Beyad <abeyad@google.com>
  • Loading branch information
abeyad authored Feb 13, 2025
1 parent 0bf518c commit 488e693
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 0 deletions.
4 changes: 4 additions & 0 deletions source/common/quic/quic_client_transport_socket_factory.cc
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ std::shared_ptr<quic::QuicCryptoClientConfig> QuicClientTransportSocketFactory::
std::make_unique<Quic::EnvoyQuicProofVerifier>(std::move(context), accept_untrusted),
std::make_unique<quic::QuicClientSessionCache>());

if (Runtime::runtimeFeatureEnabled(
"envoy.reloadable_features.quic_disable_client_early_data")) {
tls_config.crypto_config_->ssl_config().early_data_enabled = false;
}
CertCompression::registerSslContext(tls_config.crypto_config_->ssl_ctx());
}
// Return the latest crypto config.
Expand Down
3 changes: 3 additions & 0 deletions source/common/runtime/runtime_features.cc
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,9 @@ FALSE_RUNTIME_GUARD(envoy_reloadable_features_allow_multiplexed_upstream_half_cl
FALSE_RUNTIME_GUARD(envoy_reloadable_features_use_network_type_socket_option);
// TODO(fredyw): Remove after prod testing.
FALSE_RUNTIME_GUARD(envoy_reloadable_features_dns_nodata_noname_is_success);
// TODO(abeyad): Evaluate and either remove or make a config knob in
// https://github.com/envoyproxy/envoy/blob/main/api/envoy/extensions/transport_sockets/tls/v3/tls.proto#L29.
FALSE_RUNTIME_GUARD(envoy_reloadable_features_quic_disable_client_early_data);

// Block of non-boolean flags. Use of int flags is deprecated. Do not add more.
ABSL_FLAG(uint64_t, re2_max_program_size_error_level, 100, ""); // NOLINT
Expand Down
34 changes: 34 additions & 0 deletions test/integration/multiplexed_upstream_integration_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -791,6 +791,40 @@ TEST_P(MultiplexedUpstreamIntegrationTest, DisableUpstreamEarlyData) {
ASSERT_TRUE(response2->waitForEndStream());
}

TEST_P(MultiplexedUpstreamIntegrationTest, ClientDisableUpstreamEarlyData) {
#ifdef WIN32
// TODO: debug why waiting on the 2nd upstream connection times out on Windows.
GTEST_SKIP() << "Skipping on Windows";
#endif
config_helper_.addRuntimeOverride("envoy.reloadable_features.quic_disable_client_early_data",
"true");
initialize();
codec_client_ = makeHttpConnection(lookupPort("http"));

auto response = codec_client_->makeHeaderOnlyRequest(default_request_headers_);
waitForNextUpstreamRequest();

upstream_request_->encodeHeaders(default_response_headers_, true);
ASSERT_TRUE(response->waitForEndStream());
upstream_request_.reset();

ASSERT_TRUE(fake_upstream_connection_->close());
ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect());
fake_upstream_connection_.reset();
test_server_->waitForCounterEq("cluster.cluster_0.upstream_cx_destroy", 1);

EXPECT_EQ(0u, test_server_->counter("cluster.cluster_0.upstream_cx_connect_with_0_rtt")->value());

default_request_headers_.addCopy("second_request", "1");
auto response2 = codec_client_->makeHeaderOnlyRequest(default_request_headers_);
waitForNextUpstreamRequest();

EXPECT_EQ(0u, test_server_->counter("cluster.cluster_0.upstream_cx_connect_with_0_rtt")->value());
EXPECT_EQ(0u, test_server_->counter("cluster.cluster_0.upstream_rq_0rtt")->value());
upstream_request_->encodeHeaders(default_response_headers_, true);
ASSERT_TRUE(response2->waitForEndStream());
}

// Tests that Envoy will automatically retry a GET request sent over early data if the upstream
// rejects it with TooEarly response.
TEST_P(MultiplexedUpstreamIntegrationTest, UpstreamEarlyDataRejected) {
Expand Down
39 changes: 39 additions & 0 deletions test/integration/quic_http_integration_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,45 @@ TEST_P(QuicHttpIntegrationTest, EarlyDataDisabled) {
codec_client_->close();
}

// Envoy Mobile does not have listeners, so the above test is not applicable.
// This test ensures that a mobile client can connect when early data is disabled on the QUICHE
// layer.
TEST_P(QuicHttpIntegrationTest, ClientEarlyDataDisabled) {
config_helper_.addRuntimeOverride("envoy.reloadable_features.quic_disable_client_early_data",
"true");
// Make sure all connections use the same PersistentQuicInfoImpl.
concurrency_ = 1;
initialize();
// Start the first connection.
codec_client_ = makeHttpConnection(makeClientConnection((lookupPort("http"))));
EXPECT_EQ(transport_socket_factory_->clientContextConfig()->serverNameIndication(),
codec_client_->connection()->requestedServerName());
// Send a complete request on the first connection.
auto response1 = codec_client_->makeHeaderOnlyRequest(default_request_headers_);
waitForNextUpstreamRequest(0);
upstream_request_->encodeHeaders(default_response_headers_, true);
ASSERT_TRUE(response1->waitForEndStream());
// Close the first connection.
codec_client_->close();

// Start a second connection.
codec_client_ = makeHttpConnection(makeClientConnection((lookupPort("http"))));
// Send a complete request on the second connection.
auto response2 = codec_client_->makeHeaderOnlyRequest(default_request_headers_);
waitForNextUpstreamRequest(0);
upstream_request_->encodeHeaders(default_response_headers_, true);
ASSERT_TRUE(response2->waitForEndStream());
// Ensure the 2nd connection is using resumption ticket but doesn't accept early data.
EnvoyQuicClientSession* quic_session =
static_cast<EnvoyQuicClientSession*>(codec_client_->connection());
EXPECT_TRUE(quic_session->IsResumption());
EXPECT_FALSE(quic_session->EarlyDataAccepted());
EXPECT_TRUE(upstream_request_->headers().get(Http::Headers::get().EarlyData).empty());

// Close the second connection.
codec_client_->close();
}

// Not only test multiple quic connections, but disconnect and reconnect to
// trigger resumption.
TEST_P(QuicHttpIntegrationTest, MultipleUpstreamQuicConnections) {
Expand Down

0 comments on commit 488e693

Please sign in to comment.