diff --git a/source/common/http/http3/conn_pool.cc b/source/common/http/http3/conn_pool.cc index 3f1fbcbe5c1a1..40740a32e8ae8 100644 --- a/source/common/http/http3/conn_pool.cc +++ b/source/common/http/http3/conn_pool.cc @@ -12,50 +12,45 @@ #include "common/network/utility.h" #include "common/runtime/runtime_features.h" -#ifdef ENVOY_ENABLE_QUIC -#include "common/quic/client_connection_factory_impl.h" -#include "common/quic/envoy_quic_utils.h" -#include "common/quic/quic_transport_socket_factory.h" -#else -#error "http3 conn pool should not be built with QUIC disabled" -#endif - namespace Envoy { namespace Http { namespace Http3 { -// Http3 subclass of FixedHttpConnPoolImpl which exists to store quic data. -class Http3ConnPoolImpl : public FixedHttpConnPoolImpl { -public: - Http3ConnPoolImpl(Upstream::HostConstSharedPtr host, Upstream::ResourcePriority priority, - Event::Dispatcher& dispatcher, - const Network::ConnectionSocket::OptionsSharedPtr& options, - const Network::TransportSocketOptionsSharedPtr& transport_socket_options, - Random::RandomGenerator& random_generator, - Upstream::ClusterConnectivityState& state, CreateClientFn client_fn, - CreateCodecFn codec_fn, std::vector protocol, - TimeSource& time_source) - : FixedHttpConnPoolImpl(host, priority, dispatcher, options, transport_socket_options, - random_generator, state, client_fn, codec_fn, protocol) { - auto source_address = host_->cluster().sourceAddress(); - if (!source_address.get()) { - auto host_address = host->address(); - source_address = Network::Utility::getLocalAddress(host_address->ip()->version()); - } - Network::TransportSocketFactory& transport_socket_factory = host->transportSocketFactory(); - quic_info_ = std::make_unique( - dispatcher, transport_socket_factory, time_source, source_address); - Quic::configQuicInitialFlowControlWindow( - host_->cluster().http3Options().quic_protocol_options(), quic_info_->quic_config_); - } +void Http3ConnPoolImpl::setQuicConfigFromClusterConfig(const Upstream::ClusterInfo& cluster, + quic::QuicConfig& quic_config) { + quic::QuicTime::Delta crypto_timeout = + quic::QuicTime::Delta::FromMilliseconds(cluster.connectTimeout().count()); + quic_config.set_max_time_before_crypto_handshake(crypto_timeout); + int32_t max_streams = + cluster.http3Options().quic_protocol_options().max_concurrent_streams().value(); + quic_config.SetMaxBidirectionalStreamsToSend(max_streams); + quic_config.SetMaxUnidirectionalStreamsToSend(max_streams); + Quic::configQuicInitialFlowControlWindow(cluster.http3Options().quic_protocol_options(), + quic_config); +} - // Make sure all connections are torn down before quic_info_ is deleted. - ~Http3ConnPoolImpl() override { destructAllConnections(); } +Http3ConnPoolImpl::Http3ConnPoolImpl( + Upstream::HostConstSharedPtr host, Upstream::ResourcePriority priority, + Event::Dispatcher& dispatcher, const Network::ConnectionSocket::OptionsSharedPtr& options, + const Network::TransportSocketOptionsSharedPtr& transport_socket_options, + Random::RandomGenerator& random_generator, Upstream::ClusterConnectivityState& state, + CreateClientFn client_fn, CreateCodecFn codec_fn, std::vector protocol, + TimeSource& time_source) + : FixedHttpConnPoolImpl(host, priority, dispatcher, options, transport_socket_options, + random_generator, state, client_fn, codec_fn, protocol) { + auto source_address = host_->cluster().sourceAddress(); + if (!source_address.get()) { + auto host_address = host->address(); + source_address = Network::Utility::getLocalAddress(host_address->ip()->version()); + } + Network::TransportSocketFactory& transport_socket_factory = host->transportSocketFactory(); + quic_info_ = std::make_unique(dispatcher, transport_socket_factory, + time_source, source_address); + setQuicConfigFromClusterConfig(host_->cluster(), quic_info_->quic_config_); +} - // Store quic helpers which can be shared between connections and must live - // beyond the lifetime of individual connections. - std::unique_ptr quic_info_; -}; +// Make sure all connections are torn down before quic_info_ is deleted. +Http3ConnPoolImpl::~Http3ConnPoolImpl() { destructAllConnections(); } ConnectionPool::InstancePtr allocateConnPool(Event::Dispatcher& dispatcher, Random::RandomGenerator& random_generator, @@ -81,7 +76,7 @@ allocateConnPool(Event::Dispatcher& dispatcher, Random::RandomGenerator& random_ source_address = Network::Utility::getLocalAddress(host_address->ip()->version()); } data.connection_ = Quic::createQuicNetworkConnection( - *h3_pool->quic_info_, pool->dispatcher(), host_address, source_address); + h3_pool->quicInfo(), pool->dispatcher(), host_address, source_address); return std::make_unique(*pool, data); }, [](Upstream::Host::CreateConnectionData& data, HttpConnPoolImplBase* pool) { diff --git a/source/common/http/http3/conn_pool.h b/source/common/http/http3/conn_pool.h index 6f3196253048a..674063e3c9411 100644 --- a/source/common/http/http3/conn_pool.h +++ b/source/common/http/http3/conn_pool.h @@ -7,6 +7,14 @@ #include "common/http/codec_client.h" #include "common/http/conn_pool_base.h" +#ifdef ENVOY_ENABLE_QUIC +#include "common/quic/client_connection_factory_impl.h" +#include "common/quic/envoy_quic_utils.h" +#include "common/quic/quic_transport_socket_factory.h" +#else +#error "http3 conn pool should not be built with QUIC disabled" +#endif + namespace Envoy { namespace Http { namespace Http3 { @@ -26,6 +34,33 @@ class ActiveClient : public MultiplexedActiveClientBase { data) {} }; +// Http3 subclass of FixedHttpConnPoolImpl which exists to store quic data. +class Http3ConnPoolImpl : public FixedHttpConnPoolImpl { +public: + Http3ConnPoolImpl(Upstream::HostConstSharedPtr host, Upstream::ResourcePriority priority, + Event::Dispatcher& dispatcher, + const Network::ConnectionSocket::OptionsSharedPtr& options, + const Network::TransportSocketOptionsSharedPtr& transport_socket_options, + Random::RandomGenerator& random_generator, + Upstream::ClusterConnectivityState& state, CreateClientFn client_fn, + CreateCodecFn codec_fn, std::vector protocol, + TimeSource& time_source); + + ~Http3ConnPoolImpl() override; + + // Set relevant fields in quic_config based on the cluster configuration + // supplied in cluster. + static void setQuicConfigFromClusterConfig(const Upstream::ClusterInfo& cluster, + quic::QuicConfig& quic_config); + + Quic::PersistentQuicInfoImpl& quicInfo() { return *quic_info_; } + +private: + // Store quic helpers which can be shared between connections and must live + // beyond the lifetime of individual connections. + std::unique_ptr quic_info_; +}; + ConnectionPool::InstancePtr allocateConnPool(Event::Dispatcher& dispatcher, Random::RandomGenerator& random_generator, Upstream::HostConstSharedPtr host, Upstream::ResourcePriority priority, diff --git a/test/common/http/http3/BUILD b/test/common/http/http3/BUILD new file mode 100644 index 0000000000000..dcb9e6549a62d --- /dev/null +++ b/test/common/http/http3/BUILD @@ -0,0 +1,34 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_test", + "envoy_package", + "envoy_select_enable_http3", +) + +licenses(["notice"]) # Apache 2 + +envoy_package() + +envoy_cc_test( + name = "conn_pool_test", + srcs = envoy_select_enable_http3(["conn_pool_test.cc"]), + tags = ["nofips"], + deps = + envoy_select_enable_http3([ + "//source/common/event:dispatcher_lib", + "//source/common/http/http3:conn_pool_lib", + "//source/common/network:utility_lib", + "//source/common/upstream:upstream_includes", + "//source/common/upstream:upstream_lib", + "//test/common/http:common_lib", + "//test/common/upstream:utility_lib", + "//test/mocks/event:event_mocks", + "//test/mocks/http:http_mocks", + "//test/mocks/network:network_mocks", + "//test/mocks/runtime:runtime_mocks", + "//test/mocks/server:transport_socket_factory_context_mocks", + "//test/mocks/upstream:cluster_info_mocks", + "//test/mocks/upstream:transport_socket_match_mocks", + "//test/test_common:test_runtime_lib", + ]), +) diff --git a/test/common/http/http3/conn_pool_test.cc b/test/common/http/http3/conn_pool_test.cc new file mode 100644 index 0000000000000..5dccea1dacc41 --- /dev/null +++ b/test/common/http/http3/conn_pool_test.cc @@ -0,0 +1,83 @@ +#include "common/http/http3/conn_pool.h" +#include "common/quic/quic_transport_socket_factory.h" + +#include "test/common/upstream/utility.h" +#include "test/mocks/common.h" +#include "test/mocks/event/mocks.h" +#include "test/mocks/server/transport_socket_factory_context.h" +#include "test/mocks/ssl/mocks.h" +#include "test/mocks/upstream/cluster_info.h" +#include "test/mocks/upstream/host.h" +#include "test/test_common/simulated_time_system.h" + +using testing::NiceMock; +using testing::Return; + +namespace Envoy { +namespace Http { +namespace Http3 { + +TEST(Convert, Basic) { + NiceMock cluster_info; + quic::QuicConfig config; + + EXPECT_CALL(cluster_info, connectTimeout).WillOnce(Return(std::chrono::milliseconds(42))); + auto* protocol_options = cluster_info.http3_options_.mutable_quic_protocol_options(); + protocol_options->mutable_max_concurrent_streams()->set_value(43); + protocol_options->mutable_initial_stream_window_size()->set_value(65555); + + Http3ConnPoolImpl::setQuicConfigFromClusterConfig(cluster_info, config); + + EXPECT_EQ(config.max_time_before_crypto_handshake(), quic::QuicTime::Delta::FromMilliseconds(42)); + EXPECT_EQ(config.GetMaxBidirectionalStreamsToSend(), + protocol_options->max_concurrent_streams().value()); + EXPECT_EQ(config.GetMaxUnidirectionalStreamsToSend(), + protocol_options->max_concurrent_streams().value()); + EXPECT_EQ(config.GetInitialMaxStreamDataBytesIncomingBidirectionalToSend(), + protocol_options->initial_stream_window_size().value()); +} + +class Http3ConnPoolImplTest : public Event::TestUsingSimulatedTime, public testing::Test { +public: + void initialize() { + EXPECT_CALL(mockHost(), address()).WillRepeatedly(Return(test_address_)); + EXPECT_CALL(mockHost(), transportSocketFactory()).WillRepeatedly(testing::ReturnRef(factory_)); + new Event::MockSchedulableCallback(&dispatcher_); + Network::ConnectionSocket::OptionsSharedPtr options; + Network::TransportSocketOptionsSharedPtr transport_options; + pool_ = allocateConnPool(dispatcher_, random_, host_, Upstream::ResourcePriority::Default, + options, transport_options, state_, simTime()); + } + + Upstream::MockHost& mockHost() { return static_cast(*host_); } + + NiceMock dispatcher_; + Upstream::HostSharedPtr host_{new NiceMock}; + NiceMock random_; + Upstream::ClusterConnectivityState state_; + Network::Address::InstanceConstSharedPtr test_address_ = + Network::Utility::resolveUrl("tcp://127.0.0.1:3000"); + NiceMock context_; + Quic::QuicClientTransportSocketFactory factory_{ + std::unique_ptr(new NiceMock), + context_}; + ConnectionPool::InstancePtr pool_; +}; + +TEST_F(Http3ConnPoolImplTest, CreationWithConfig) { + // Set a couple of options from setQuicConfigFromClusterConfig to make sure they are applied. + auto* options = mockHost().cluster_.http3_options_.mutable_quic_protocol_options(); + options->mutable_max_concurrent_streams()->set_value(15); + options->mutable_initial_stream_window_size()->set_value(65555); + initialize(); + + Quic::PersistentQuicInfoImpl& info = static_cast(pool_.get())->quicInfo(); + EXPECT_EQ(info.quic_config_.GetMaxUnidirectionalStreamsToSend(), + options->max_concurrent_streams().value()); + EXPECT_EQ(info.quic_config_.GetInitialMaxStreamDataBytesIncomingBidirectionalToSend(), + options->initial_stream_window_size().value()); +} + +} // namespace Http3 +} // namespace Http +} // namespace Envoy