diff --git a/api/envoy/config/listener/v3/quic_config.proto b/api/envoy/config/listener/v3/quic_config.proto index 9949da2e0d70..c024be95bace 100644 --- a/api/envoy/config/listener/v3/quic_config.proto +++ b/api/envoy/config/listener/v3/quic_config.proto @@ -2,6 +2,8 @@ syntax = "proto3"; package envoy.config.listener.v3; +import "envoy/config/core/v3/base.proto"; + import "google/protobuf/duration.proto"; import "google/protobuf/wrappers.proto"; @@ -16,7 +18,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: QUIC listener Config] // Configuration specific to the QUIC protocol. -// Next id: 4 +// Next id: 5 message QuicProtocolOptions { option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.listener.QuicProtocolOptions"; @@ -32,4 +34,8 @@ message QuicProtocolOptions { // Connection timeout in milliseconds before the crypto handshake is finished. // 20000ms if not specified. google.protobuf.Duration crypto_handshake_timeout = 3; + + // Runtime flag that controls whether the listener is enabled or not. If not specified, defaults + // to enabled. + core.v3.RuntimeFeatureFlag enabled = 4; } diff --git a/api/envoy/config/listener/v4alpha/quic_config.proto b/api/envoy/config/listener/v4alpha/quic_config.proto index 97866e4b6ed8..b2b1df1e374f 100644 --- a/api/envoy/config/listener/v4alpha/quic_config.proto +++ b/api/envoy/config/listener/v4alpha/quic_config.proto @@ -2,6 +2,8 @@ syntax = "proto3"; package envoy.config.listener.v4alpha; +import "envoy/config/core/v4alpha/base.proto"; + import "google/protobuf/duration.proto"; import "google/protobuf/wrappers.proto"; @@ -16,7 +18,7 @@ option (udpa.annotations.file_status).package_version_status = NEXT_MAJOR_VERSIO // [#protodoc-title: QUIC listener Config] // Configuration specific to the QUIC protocol. -// Next id: 4 +// Next id: 5 message QuicProtocolOptions { option (udpa.annotations.versioning).previous_message_type = "envoy.config.listener.v3.QuicProtocolOptions"; @@ -32,4 +34,8 @@ message QuicProtocolOptions { // Connection timeout in milliseconds before the crypto handshake is finished. // 20000ms if not specified. google.protobuf.Duration crypto_handshake_timeout = 3; + + // Runtime flag that controls whether the listener is enabled or not. If not specified, defaults + // to enabled. + core.v4alpha.RuntimeFeatureFlag enabled = 4; } diff --git a/generated_api_shadow/envoy/config/listener/v3/quic_config.proto b/generated_api_shadow/envoy/config/listener/v3/quic_config.proto index 9949da2e0d70..c024be95bace 100644 --- a/generated_api_shadow/envoy/config/listener/v3/quic_config.proto +++ b/generated_api_shadow/envoy/config/listener/v3/quic_config.proto @@ -2,6 +2,8 @@ syntax = "proto3"; package envoy.config.listener.v3; +import "envoy/config/core/v3/base.proto"; + import "google/protobuf/duration.proto"; import "google/protobuf/wrappers.proto"; @@ -16,7 +18,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: QUIC listener Config] // Configuration specific to the QUIC protocol. -// Next id: 4 +// Next id: 5 message QuicProtocolOptions { option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.listener.QuicProtocolOptions"; @@ -32,4 +34,8 @@ message QuicProtocolOptions { // Connection timeout in milliseconds before the crypto handshake is finished. // 20000ms if not specified. google.protobuf.Duration crypto_handshake_timeout = 3; + + // Runtime flag that controls whether the listener is enabled or not. If not specified, defaults + // to enabled. + core.v3.RuntimeFeatureFlag enabled = 4; } diff --git a/generated_api_shadow/envoy/config/listener/v4alpha/quic_config.proto b/generated_api_shadow/envoy/config/listener/v4alpha/quic_config.proto index 97866e4b6ed8..b2b1df1e374f 100644 --- a/generated_api_shadow/envoy/config/listener/v4alpha/quic_config.proto +++ b/generated_api_shadow/envoy/config/listener/v4alpha/quic_config.proto @@ -2,6 +2,8 @@ syntax = "proto3"; package envoy.config.listener.v4alpha; +import "envoy/config/core/v4alpha/base.proto"; + import "google/protobuf/duration.proto"; import "google/protobuf/wrappers.proto"; @@ -16,7 +18,7 @@ option (udpa.annotations.file_status).package_version_status = NEXT_MAJOR_VERSIO // [#protodoc-title: QUIC listener Config] // Configuration specific to the QUIC protocol. -// Next id: 4 +// Next id: 5 message QuicProtocolOptions { option (udpa.annotations.versioning).previous_message_type = "envoy.config.listener.v3.QuicProtocolOptions"; @@ -32,4 +34,8 @@ message QuicProtocolOptions { // Connection timeout in milliseconds before the crypto handshake is finished. // 20000ms if not specified. google.protobuf.Duration crypto_handshake_timeout = 3; + + // Runtime flag that controls whether the listener is enabled or not. If not specified, defaults + // to enabled. + core.v4alpha.RuntimeFeatureFlag enabled = 4; } diff --git a/source/extensions/quic_listeners/quiche/BUILD b/source/extensions/quic_listeners/quiche/BUILD index dde1927d81ab..aa0be8877546 100644 --- a/source/extensions/quic_listeners/quiche/BUILD +++ b/source/extensions/quic_listeners/quiche/BUILD @@ -274,6 +274,7 @@ envoy_cc_library( "//include/envoy/network:listener_interface", "//source/common/network:listener_lib", "//source/common/protobuf:utility_lib", + "//source/common/runtime:runtime_lib", "//source/server:connection_handler_lib", "@envoy_api//envoy/config/listener/v3:pkg_cc_proto", ], diff --git a/source/extensions/quic_listeners/quiche/active_quic_listener.cc b/source/extensions/quic_listeners/quiche/active_quic_listener.cc index 8ab780021d62..30c65d443e8a 100644 --- a/source/extensions/quic_listeners/quiche/active_quic_listener.cc +++ b/source/extensions/quic_listeners/quiche/active_quic_listener.cc @@ -20,20 +20,22 @@ ActiveQuicListener::ActiveQuicListener(Event::Dispatcher& dispatcher, Network::ConnectionHandler& parent, Network::ListenerConfig& listener_config, const quic::QuicConfig& quic_config, - Network::Socket::OptionsSharedPtr options) + Network::Socket::OptionsSharedPtr options, + const envoy::config::core::v3::RuntimeFeatureFlag& enabled) : ActiveQuicListener(dispatcher, parent, listener_config.listenSocketFactory().getListenSocket(), listener_config, - quic_config, std::move(options)) {} + quic_config, std::move(options), enabled) {} ActiveQuicListener::ActiveQuicListener(Event::Dispatcher& dispatcher, Network::ConnectionHandler& parent, Network::SocketSharedPtr listen_socket, Network::ListenerConfig& listener_config, const quic::QuicConfig& quic_config, - Network::Socket::OptionsSharedPtr options) + Network::Socket::OptionsSharedPtr options, + const envoy::config::core::v3::RuntimeFeatureFlag& enabled) : Server::ConnectionHandlerImpl::ActiveListenerImplBase(parent, &listener_config), dispatcher_(dispatcher), version_manager_(quic::CurrentSupportedVersions()), - listen_socket_(*listen_socket) { + listen_socket_(*listen_socket), enabled_(enabled, Runtime::LoaderSingleton::get()) { if (options != nullptr) { const bool ok = Network::Socket::applyOptions( options, listen_socket_, envoy::config::core::v3::SocketOption::STATE_BOUND); @@ -44,7 +46,6 @@ ActiveQuicListener::ActiveQuicListener(Event::Dispatcher& dispatcher, } listen_socket_.addOptions(options); } - udp_listener_ = dispatcher_.createUdpListener(std::move(listen_socket), *this); quic::QuicRandom* const random = quic::QuicRandom::GetInstance(); random->RandBytes(random_seed_, sizeof(random_seed_)); @@ -93,6 +94,10 @@ void ActiveQuicListener::onData(Network::UdpRecvData& data) { } void ActiveQuicListener::onReadReady() { + if (!enabled_.enabled()) { + ENVOY_LOG(trace, "Quic listener {}: runtime disabled", config_->name()); + return; + } quic_dispatcher_->ProcessBufferedChlos(kNumSessionsToCreatePerLoop); } @@ -112,7 +117,7 @@ void ActiveQuicListener::shutdownListener() { ActiveQuicListenerFactory::ActiveQuicListenerFactory( const envoy::config::listener::v3::QuicProtocolOptions& config, uint32_t concurrency) - : concurrency_(concurrency) { + : concurrency_(concurrency), enabled_(config.enabled()) { uint64_t idle_network_timeout_ms = config.has_idle_timeout() ? DurationUtil::durationToMilliseconds(config.idle_timeout()) : 300000; @@ -191,8 +196,9 @@ ActiveQuicListenerFactory::createActiveUdpListener(Network::ConnectionHandler& p #endif } #endif + return std::make_unique(disptacher, parent, config, quic_config_, - std::move(options)); + std::move(options), enabled_); } } // namespace Quic diff --git a/source/extensions/quic_listeners/quiche/active_quic_listener.h b/source/extensions/quic_listeners/quiche/active_quic_listener.h index 6536731c199f..8d0d5c9dd46e 100644 --- a/source/extensions/quic_listeners/quiche/active_quic_listener.h +++ b/source/extensions/quic_listeners/quiche/active_quic_listener.h @@ -3,9 +3,11 @@ #include "envoy/config/listener/v3/quic_config.pb.h" #include "envoy/network/connection_handler.h" #include "envoy/network/listener.h" +#include "envoy/runtime/runtime.h" #include "common/network/socket_option_impl.h" #include "common/protobuf/utility.h" +#include "common/runtime/runtime_protos.h" #include "server/connection_handler_impl.h" @@ -25,12 +27,14 @@ class ActiveQuicListener : public Network::UdpListenerCallbacks, ActiveQuicListener(Event::Dispatcher& dispatcher, Network::ConnectionHandler& parent, Network::ListenerConfig& listener_config, const quic::QuicConfig& quic_config, - Network::Socket::OptionsSharedPtr options); + Network::Socket::OptionsSharedPtr options, + const envoy::config::core::v3::RuntimeFeatureFlag& enabled); ActiveQuicListener(Event::Dispatcher& dispatcher, Network::ConnectionHandler& parent, Network::SocketSharedPtr listen_socket, Network::ListenerConfig& listener_config, const quic::QuicConfig& quic_config, - Network::Socket::OptionsSharedPtr options); + Network::Socket::OptionsSharedPtr options, + const envoy::config::core::v3::RuntimeFeatureFlag& enabled); ~ActiveQuicListener() override; @@ -60,6 +64,7 @@ class ActiveQuicListener : public Network::UdpListenerCallbacks, quic::QuicVersionManager version_manager_; std::unique_ptr quic_dispatcher_; Network::Socket& listen_socket_; + Runtime::FeatureFlag enabled_; }; using ActiveQuicListenerPtr = std::unique_ptr; @@ -83,6 +88,7 @@ class ActiveQuicListenerFactory : public Network::ActiveUdpListenerFactory, quic::QuicConfig quic_config_; const uint32_t concurrency_; absl::once_flag install_bpf_once_; + envoy::config::core::v3::RuntimeFeatureFlag enabled_; }; } // namespace Quic diff --git a/test/extensions/quic_listeners/quiche/BUILD b/test/extensions/quic_listeners/quiche/BUILD index 7e24810fe50b..43347b72524d 100644 --- a/test/extensions/quic_listeners/quiche/BUILD +++ b/test/extensions/quic_listeners/quiche/BUILD @@ -167,13 +167,16 @@ envoy_cc_test( ], deps = [ ":quic_test_utils_for_envoy_lib", + "//source/extensions/quic_listeners/quiche:active_quic_listener_config_lib", "//source/extensions/quic_listeners/quiche:active_quic_listener_lib", "//source/extensions/quic_listeners/quiche:envoy_quic_utils_lib", "//source/server:configuration_lib", "//test/mocks/network:network_mocks", + "//test/mocks/server:server_mocks", "//test/test_common:environment_lib", "//test/test_common:network_utility_lib", "//test/test_common:simulated_time_system_lib", + "@envoy_api//envoy/config/core/v3:pkg_cc_proto", ], ) diff --git a/test/extensions/quic_listeners/quiche/active_quic_listener_config_test.cc b/test/extensions/quic_listeners/quiche/active_quic_listener_config_test.cc index b93afa375ea3..d116f816b6ca 100644 --- a/test/extensions/quic_listeners/quiche/active_quic_listener_config_test.cc +++ b/test/extensions/quic_listeners/quiche/active_quic_listener_config_test.cc @@ -15,6 +15,10 @@ class ActiveQuicListenerFactoryPeer { static quic::QuicConfig& quicConfig(ActiveQuicListenerFactory& factory) { return factory.quic_config_; } + static envoy::config::core::v3::RuntimeFeatureFlag& + runtimeEnabled(ActiveQuicListenerFactory& factory) { + return factory.enabled_; + } }; TEST(ActiveQuicListenerConfigTest, CreateActiveQuicListenerFactory) { @@ -29,6 +33,9 @@ TEST(ActiveQuicListenerConfigTest, CreateActiveQuicListenerFactory) { idle_timeout: { seconds: 2 } + enabled: + default_value: true + runtime_key: foo_key )EOF"; TestUtility::loadFromYaml(yaml, *config); Network::ActiveUdpListenerFactoryPtr listener_factory = @@ -41,6 +48,38 @@ TEST(ActiveQuicListenerConfigTest, CreateActiveQuicListenerFactory) { EXPECT_EQ(2000u, quic_config.IdleNetworkTimeout().ToMilliseconds()); // Default value if not present in config. EXPECT_EQ(20000u, quic_config.max_time_before_crypto_handshake().ToMilliseconds()); + envoy::config::core::v3::RuntimeFeatureFlag& runtime_enabled = + ActiveQuicListenerFactoryPeer::runtimeEnabled( + dynamic_cast(*listener_factory)); + EXPECT_EQ(true, runtime_enabled.default_value().value()); + EXPECT_EQ("foo_key", runtime_enabled.runtime_key()); +} + +TEST(ActiveQuicListenerConfigTest, QuicListenerFlagNotConfigured) { + std::string listener_name = QuicListenerName; + auto& config_factory = + Config::Utility::getAndCheckFactoryByName( + listener_name); + ProtobufTypes::MessagePtr config = config_factory.createEmptyConfigProto(); + + std::string yaml = R"EOF( + max_concurrent_streams: 10 + idle_timeout: { + seconds: 2 + } + )EOF"; + TestUtility::loadFromYaml(yaml, *config); + Network::ActiveUdpListenerFactoryPtr listener_factory = + config_factory.createActiveUdpListenerFactory(*config, /*concurrency=*/1); + EXPECT_NE(nullptr, listener_factory); + envoy::config::core::v3::RuntimeFeatureFlag& runtime_enabled = + ActiveQuicListenerFactoryPeer::runtimeEnabled( + dynamic_cast(*listener_factory)); + auto& quic_config = + dynamic_cast(*config); + EXPECT_FALSE(runtime_enabled.has_default_value()); + EXPECT_FALSE(quic_config.has_enabled()); + EXPECT_EQ("", runtime_enabled.runtime_key()); } } // namespace Quic diff --git a/test/extensions/quic_listeners/quiche/active_quic_listener_test.cc b/test/extensions/quic_listeners/quiche/active_quic_listener_test.cc index a9850565fa0f..c3836ac38163 100644 --- a/test/extensions/quic_listeners/quiche/active_quic_listener_test.cc +++ b/test/extensions/quic_listeners/quiche/active_quic_listener_test.cc @@ -8,6 +8,11 @@ #include +#include "common/runtime/runtime_impl.h" + +#include "envoy/config/core/v3/base.pb.h" +#include "envoy/config/core/v3/base.pb.validate.h" + #include "quiche/quic/core/crypto/crypto_protocol.h" #include "quiche/quic/test_tools/crypto_test_utils.h" #include "quiche/quic/test_tools/quic_dispatcher_peer.h" @@ -23,11 +28,14 @@ #include "test/test_common/simulated_time_system.h" #include "test/test_common/environment.h" #include "test/mocks/network/mocks.h" +#include "test/mocks/server/mocks.h" +#include "test/mocks/runtime/mocks.h" #include "test/test_common/utility.h" #include "test/test_common/network_utility.h" #include "absl/time/time.h" #include "gtest/gtest.h" #include "gmock/gmock.h" +#include "extensions/quic_listeners/quiche/active_quic_listener_config.h" #include "extensions/quic_listeners/quiche/platform/envoy_quic_clock.h" #include "extensions/quic_listeners/quiche/envoy_quic_utils.h" @@ -39,13 +47,23 @@ namespace Quic { class ActiveQuicListenerPeer { public: - static EnvoyQuicDispatcher* quic_dispatcher(ActiveQuicListener& listener) { + static EnvoyQuicDispatcher* quicDispatcher(ActiveQuicListener& listener) { return listener.quic_dispatcher_.get(); } - static quic::QuicCryptoServerConfig& crypto_config(ActiveQuicListener& listener) { + static quic::QuicCryptoServerConfig& cryptoConfig(ActiveQuicListener& listener) { return *listener.crypto_config_; } + + static bool enabled(ActiveQuicListener& listener) { return listener.enabled_.enabled(); } +}; + +class ActiveQuicListenerFactoryPeer { +public: + static envoy::config::core::v3::RuntimeFeatureFlag& + runtimeEnabled(ActiveQuicListenerFactory* factory) { + return factory->enabled_; + } }; class ActiveQuicListenerTest : public testing::TestWithParam { @@ -59,17 +77,44 @@ class ActiveQuicListenerTest : public testing::TestWithParam + std::unique_ptr staticUniquePointerCast(std::unique_ptr&& source) { + return std::unique_ptr{static_cast(source.release())}; + } + void SetUp() override { + envoy::config::bootstrap::v3::LayeredRuntime config; + config.add_layers()->mutable_admin_layer(); + loader_ = std::make_unique( + Runtime::LoaderPtr{new Runtime::LoaderImpl(*dispatcher_, tls_, config, local_info_, store_, + generator_, validation_visitor_, *api_)}); + listen_socket_ = std::make_shared(local_address_, nullptr, /*bind*/ true); listen_socket_->addOptions(Network::SocketOptionFactory::buildIpPacketInfoOptions()); listen_socket_->addOptions(Network::SocketOptionFactory::buildRxQueueOverFlowOptions()); - quic_listener_ = std::make_unique( - *dispatcher_, connection_handler_, listen_socket_, listener_config_, quic_config_, nullptr); + ON_CALL(listener_config_, listenSocketFactory()).WillByDefault(ReturnRef(socket_factory_)); + ON_CALL(socket_factory_, getListenSocket()).WillByDefault(Return(listen_socket_)); + + listener_factory_ = createQuicListenerFactory(yamlForQuicConfig()); + quic_listener_ = + staticUniquePointerCast(listener_factory_->createActiveUdpListener( + connection_handler_, *dispatcher_, listener_config_)); + quic_dispatcher_ = ActiveQuicListenerPeer::quicDispatcher(*quic_listener_); simulated_time_system_.advanceTimeWait(std::chrono::milliseconds(100)); } + Network::ActiveUdpListenerFactoryPtr createQuicListenerFactory(const std::string& yaml) { + std::string listener_name = QuicListenerName; + auto& config_factory = + Config::Utility::getAndCheckFactoryByName( + listener_name); + ProtobufTypes::MessagePtr config_proto = config_factory.createEmptyConfigProto(); + TestUtility::loadFromYaml(yaml, *config_proto); + return config_factory.createActiveUdpListenerFactory(*config_proto, /*concurrency=*/1); + } + void configureMocks(int connection_count) { EXPECT_CALL(listener_config_, filterChainManager()) .Times(connection_count) @@ -116,11 +161,11 @@ class ActiveQuicListenerTest : public testing::TestWithParam(local_address_, nullptr, /*bind*/ false)); quic::CryptoHandshakeMessage chlo = quic::test::crypto_test_utils::GenerateDefaultInchoateCHLO( &clock_, quic::AllSupportedVersions()[0].transport_version, - &ActiveQuicListenerPeer::crypto_config(*quic_listener_)); + &ActiveQuicListenerPeer::cryptoConfig(*quic_listener_)); chlo.SetVector(quic::kCOPT, quic::QuicTagVector{quic::kREJ}); quic::CryptoHandshakeMessage full_chlo; quic::QuicReferenceCountedPointer signed_config( @@ -128,7 +173,7 @@ class ActiveQuicListenerTest : public testing::TestWithParamonListenerShutdown(); // Trigger alarm to fire before listener destruction. dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + Runtime::LoaderSingleton::clear(); + } + +protected: + virtual std::string yamlForQuicConfig() { + return R"EOF( + enabled: + default_value: true + runtime_key: quic.enabled +)EOF"; } Network::Address::IpVersion version_; @@ -201,6 +256,18 @@ class ActiveQuicListenerTest : public testing::TestWithParam quic_listener_; + Network::ActiveUdpListenerFactoryPtr listener_factory_; + NiceMock socket_factory_; + EnvoyQuicDispatcher* quic_dispatcher_; + std::unique_ptr loader_; + + NiceMock tls_; + Stats::TestUtil::TestStore store_; + Runtime::MockRandomGenerator generator_; + Runtime::MockRandomGenerator rand_; + NiceMock local_info_; + Init::MockManager init_manager_; + NiceMock validation_visitor_; std::list> client_sockets_; std::list> read_filters_; @@ -221,30 +288,34 @@ TEST_P(ActiveQuicListenerTest, FailSocketOptionUponCreation) { .WillOnce(Return(false)); auto options = std::make_shared>(); options->emplace_back(std::move(option)); - EXPECT_THROW_WITH_REGEX(std::make_unique(*dispatcher_, connection_handler_, - listen_socket_, listener_config_, - quic_config_, options), - EnvoyException, "Failed to apply socket options."); + EXPECT_THROW_WITH_REGEX( + std::make_unique( + *dispatcher_, connection_handler_, listen_socket_, listener_config_, quic_config_, + options, + ActiveQuicListenerFactoryPeer::runtimeEnabled( + static_cast(listener_factory_.get()))), + EnvoyException, "Failed to apply socket options."); } TEST_P(ActiveQuicListenerTest, ReceiveFullQuicCHLO) { + quic::QuicBufferedPacketStore* const buffered_packets = + quic::test::QuicDispatcherPeer::GetBufferedPackets(quic_dispatcher_); configureMocks(/* connection_count = */ 1); - SendFullCHLO(quic::test::TestConnectionId(1)); + sendFullCHLO(quic::test::TestConnectionId(1)); dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + EXPECT_FALSE(buffered_packets->HasChlosBuffered()); + EXPECT_FALSE(quic_dispatcher_->session_map().empty()); ReadFromClientSockets(); } TEST_P(ActiveQuicListenerTest, ProcessBufferedChlos) { - EnvoyQuicDispatcher* const envoy_quic_dispatcher = - ActiveQuicListenerPeer::quic_dispatcher(*quic_listener_); quic::QuicBufferedPacketStore* const buffered_packets = - quic::test::QuicDispatcherPeer::GetBufferedPackets(envoy_quic_dispatcher); - + quic::test::QuicDispatcherPeer::GetBufferedPackets(quic_dispatcher_); configureMocks(ActiveQuicListener::kNumSessionsToCreatePerLoop + 2); // Generate one more CHLO than can be processed immediately. for (size_t i = 1; i <= ActiveQuicListener::kNumSessionsToCreatePerLoop + 1; ++i) { - SendFullCHLO(quic::test::TestConnectionId(i)); + sendFullCHLO(quic::test::TestConnectionId(i)); } dispatcher_->run(Event::Dispatcher::RunType::NonBlock); @@ -256,9 +327,10 @@ TEST_P(ActiveQuicListenerTest, ProcessBufferedChlos) { EXPECT_TRUE(buffered_packets->HasBufferedPackets( quic::test::TestConnectionId(ActiveQuicListener::kNumSessionsToCreatePerLoop + 1))); EXPECT_TRUE(buffered_packets->HasChlosBuffered()); + EXPECT_FALSE(quic_dispatcher_->session_map().empty()); // Generate more data to trigger a socket read during the next event loop. - SendFullCHLO(quic::test::TestConnectionId(ActiveQuicListener::kNumSessionsToCreatePerLoop + 2)); + sendFullCHLO(quic::test::TestConnectionId(ActiveQuicListener::kNumSessionsToCreatePerLoop + 2)); dispatcher_->run(Event::Dispatcher::RunType::NonBlock); // The socket read results in processing all CHLOs. @@ -270,5 +342,47 @@ TEST_P(ActiveQuicListenerTest, ProcessBufferedChlos) { ReadFromClientSockets(); } +TEST_P(ActiveQuicListenerTest, QuicProcessingDisabledAndEnabled) { + EXPECT_TRUE(ActiveQuicListenerPeer::enabled(*quic_listener_)); + Runtime::LoaderSingleton::getExisting()->mergeValues({{"quic.enabled", " false"}}); + sendFullCHLO(quic::test::TestConnectionId(1)); + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + // If listener was enabled, there should have been session created for active connection. + EXPECT_TRUE(quic_dispatcher_->session_map().empty()); + EXPECT_FALSE(ActiveQuicListenerPeer::enabled(*quic_listener_)); + Runtime::LoaderSingleton::getExisting()->mergeValues({{"quic.enabled", " true"}}); + configureMocks(/* connection_count = */ 1); + sendFullCHLO(quic::test::TestConnectionId(1)); + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + EXPECT_FALSE(quic_dispatcher_->session_map().empty()); + EXPECT_TRUE(ActiveQuicListenerPeer::enabled(*quic_listener_)); +} + +class ActiveQuicListenerEmptyFlagConfigTest : public ActiveQuicListenerTest { +protected: + std::string yamlForQuicConfig() override { + return R"EOF( + max_concurrent_streams: 10 + )EOF"; + } +}; + +INSTANTIATE_TEST_SUITE_P(IpVersions, ActiveQuicListenerEmptyFlagConfigTest, + testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), + TestUtility::ipTestParamsToString); + +// Quic listener should be enabled by default, if not enabled explicitly in config. +TEST_P(ActiveQuicListenerEmptyFlagConfigTest, ReceiveFullQuicCHLO) { + quic::QuicBufferedPacketStore* const buffered_packets = + quic::test::QuicDispatcherPeer::GetBufferedPackets(quic_dispatcher_); + configureMocks(/* connection_count = */ 1); + sendFullCHLO(quic::test::TestConnectionId(1)); + dispatcher_->run(Event::Dispatcher::RunType::NonBlock); + EXPECT_FALSE(buffered_packets->HasChlosBuffered()); + EXPECT_FALSE(quic_dispatcher_->session_map().empty()); + EXPECT_TRUE(ActiveQuicListenerPeer::enabled(*quic_listener_)); + ReadFromClientSockets(); +} + } // namespace Quic } // namespace Envoy