diff --git a/api/envoy/extensions/upstreams/http/v3/http_protocol_options.proto b/api/envoy/extensions/upstreams/http/v3/http_protocol_options.proto index 00cac9d27336e..2ce22fe6c0a79 100644 --- a/api/envoy/extensions/upstreams/http/v3/http_protocol_options.proto +++ b/api/envoy/extensions/upstreams/http/v3/http_protocol_options.proto @@ -97,6 +97,13 @@ message HttpProtocolOptions { config.core.v3.Http1ProtocolOptions http_protocol_options = 1; config.core.v3.Http2ProtocolOptions http2_protocol_options = 2; + + // [#not-implemented-hide:] + // Unlike HTTP/1 and HTTP/2, HTTP/3 will not be configured unless it is + // present. If HTTP/3 is present, attempts to connect will first be made + // via HTTP/3, and if the HTTP/3 connection fails, TCP (HTTP/1 or HTTP/2 + // based on ALPN) will be used instead. + config.core.v3.Http3ProtocolOptions http3_protocol_options = 3; } // This contains options common across HTTP/1 and HTTP/2 diff --git a/api/envoy/extensions/upstreams/http/v4alpha/http_protocol_options.proto b/api/envoy/extensions/upstreams/http/v4alpha/http_protocol_options.proto index e3cf4476983a9..2011abc5a5a79 100644 --- a/api/envoy/extensions/upstreams/http/v4alpha/http_protocol_options.proto +++ b/api/envoy/extensions/upstreams/http/v4alpha/http_protocol_options.proto @@ -110,6 +110,13 @@ message HttpProtocolOptions { config.core.v4alpha.Http1ProtocolOptions http_protocol_options = 1; config.core.v4alpha.Http2ProtocolOptions http2_protocol_options = 2; + + // [#not-implemented-hide:] + // Unlike HTTP/1 and HTTP/2, HTTP/3 will not be configured unless it is + // present. If HTTP/3 is present, attempts to connect will first be made + // via HTTP/3, and if the HTTP/3 connection fails, TCP (HTTP/1 or HTTP/2 + // based on ALPN) will be used instead. + config.core.v4alpha.Http3ProtocolOptions http3_protocol_options = 3; } // This contains options common across HTTP/1 and HTTP/2 diff --git a/generated_api_shadow/envoy/extensions/upstreams/http/v3/http_protocol_options.proto b/generated_api_shadow/envoy/extensions/upstreams/http/v3/http_protocol_options.proto index 00cac9d27336e..2ce22fe6c0a79 100644 --- a/generated_api_shadow/envoy/extensions/upstreams/http/v3/http_protocol_options.proto +++ b/generated_api_shadow/envoy/extensions/upstreams/http/v3/http_protocol_options.proto @@ -97,6 +97,13 @@ message HttpProtocolOptions { config.core.v3.Http1ProtocolOptions http_protocol_options = 1; config.core.v3.Http2ProtocolOptions http2_protocol_options = 2; + + // [#not-implemented-hide:] + // Unlike HTTP/1 and HTTP/2, HTTP/3 will not be configured unless it is + // present. If HTTP/3 is present, attempts to connect will first be made + // via HTTP/3, and if the HTTP/3 connection fails, TCP (HTTP/1 or HTTP/2 + // based on ALPN) will be used instead. + config.core.v3.Http3ProtocolOptions http3_protocol_options = 3; } // This contains options common across HTTP/1 and HTTP/2 diff --git a/generated_api_shadow/envoy/extensions/upstreams/http/v4alpha/http_protocol_options.proto b/generated_api_shadow/envoy/extensions/upstreams/http/v4alpha/http_protocol_options.proto index e3cf4476983a9..2011abc5a5a79 100644 --- a/generated_api_shadow/envoy/extensions/upstreams/http/v4alpha/http_protocol_options.proto +++ b/generated_api_shadow/envoy/extensions/upstreams/http/v4alpha/http_protocol_options.proto @@ -110,6 +110,13 @@ message HttpProtocolOptions { config.core.v4alpha.Http1ProtocolOptions http_protocol_options = 1; config.core.v4alpha.Http2ProtocolOptions http2_protocol_options = 2; + + // [#not-implemented-hide:] + // Unlike HTTP/1 and HTTP/2, HTTP/3 will not be configured unless it is + // present. If HTTP/3 is present, attempts to connect will first be made + // via HTTP/3, and if the HTTP/3 connection fails, TCP (HTTP/1 or HTTP/2 + // based on ALPN) will be used instead. + config.core.v4alpha.Http3ProtocolOptions http3_protocol_options = 3; } // This contains options common across HTTP/1 and HTTP/2 diff --git a/source/common/http/conn_pool_grid.cc b/source/common/http/conn_pool_grid.cc index e41007825e01b..5a3fdef5b5036 100644 --- a/source/common/http/conn_pool_grid.cc +++ b/source/common/http/conn_pool_grid.cc @@ -6,18 +6,6 @@ namespace Envoy { namespace Http { -// Helper function to make sure each protocol in expected_protocols is present -// in protocols (only used for an ASSERT in debug builds) -bool contains(const std::vector& protocols, - const std::vector& expected_protocols) { - for (auto protocol : expected_protocols) { - if (std::find(protocols.begin(), protocols.end(), protocol) == protocols.end()) { - return false; - } - } - return true; -} - absl::string_view describePool(const ConnectionPool::Instance& pool) { return pool.protocolDescription(); } @@ -157,10 +145,10 @@ ConnectivityGrid::ConnectivityGrid( : dispatcher_(dispatcher), random_generator_(random_generator), host_(host), priority_(priority), options_(options), transport_socket_options_(transport_socket_options), state_(state), next_attempt_duration_(next_attempt_duration), time_source_(time_source) { + // ProdClusterManagerFactory::allocateConnPool verifies the protocols are HTTP/1, HTTP/2 and + // HTTP/3. // TODO(#15649) support v6/v4, WiFi/cellular. ASSERT(connectivity_options.protocols_.size() == 3); - ASSERT(contains(connectivity_options.protocols_, - {Http::Protocol::Http11, Http::Protocol::Http2, Http::Protocol::Http3})); } ConnectivityGrid::~ConnectivityGrid() { diff --git a/source/common/quic/BUILD b/source/common/quic/BUILD index 506ee80b4294f..d0f3b48491c6b 100644 --- a/source/common/quic/BUILD +++ b/source/common/quic/BUILD @@ -371,6 +371,7 @@ envoy_cc_library( "//source/common/common:assert_lib", "//source/extensions/transport_sockets:well_known_names", "//source/extensions/transport_sockets/tls:context_config_lib", + "//source/extensions/transport_sockets/tls:ssl_socket_lib", "@envoy_api//envoy/extensions/transport_sockets/quic/v3:pkg_cc_proto", ], ) diff --git a/source/common/quic/quic_transport_socket_factory.cc b/source/common/quic/quic_transport_socket_factory.cc index 4e87a90606721..8611477d4cf27 100644 --- a/source/common/quic/quic_transport_socket_factory.cc +++ b/source/common/quic/quic_transport_socket_factory.cc @@ -1,8 +1,7 @@ #include "common/quic/quic_transport_socket_factory.h" -// #include "envoy/extensions/transport_sockets/tls/v3/tls.pb.validate.h" -#include "envoy/extensions/transport_sockets/quic/v3/quic_transport.pb.h" #include "envoy/extensions/transport_sockets/quic/v3/quic_transport.pb.validate.h" + #include "extensions/transport_sockets/tls/context_config_impl.h" namespace Envoy { @@ -34,9 +33,15 @@ QuicClientTransportSocketConfigFactory::createTransportSocketFactory( config, context.messageValidationVisitor()); auto client_config = std::make_unique( quic_transport.upstream_tls_context(), context); - return std::make_unique(std::move(client_config)); + return std::make_unique(std::move(client_config), context); } +QuicClientTransportSocketFactory::QuicClientTransportSocketFactory( + Ssl::ClientContextConfigPtr config, + Server::Configuration::TransportSocketFactoryContext& factory_context) + : fallback_factory_(std::make_unique( + std::move(config), factory_context.sslContextManager(), factory_context.scope())) {} + ProtobufTypes::MessagePtr QuicClientTransportSocketConfigFactory::createEmptyConfigProto() { return std::make_unique(); } diff --git a/source/common/quic/quic_transport_socket_factory.h b/source/common/quic/quic_transport_socket_factory.h index fdcdd557c91bb..381d77f8acb15 100644 --- a/source/common/quic/quic_transport_socket_factory.h +++ b/source/common/quic/quic_transport_socket_factory.h @@ -1,11 +1,13 @@ #pragma once +#include "envoy/extensions/transport_sockets/quic/v3/quic_transport.pb.h" #include "envoy/network/transport_socket.h" #include "envoy/server/transport_socket_config.h" #include "envoy/ssl/context_config.h" #include "common/common/assert.h" +#include "extensions/transport_sockets/tls/ssl_socket.h" #include "extensions/transport_sockets/well_known_names.h" namespace Envoy { @@ -25,6 +27,7 @@ class QuicTransportSocketFactoryBase : public Network::TransportSocketFactory { } bool implementsSecureTransport() const override { return true; } bool usesProxyProtocolOptions() const override { return false; } + bool supportsAlpn() const override { return true; } }; // TODO(danzh): when implement ProofSource, examine of it's necessary to @@ -42,13 +45,29 @@ class QuicServerTransportSocketFactory : public QuicTransportSocketFactoryBase { class QuicClientTransportSocketFactory : public QuicTransportSocketFactoryBase { public: - QuicClientTransportSocketFactory(Envoy::Ssl::ClientContextConfigPtr config) - : config_(std::move(config)) {} + QuicClientTransportSocketFactory( + Ssl::ClientContextConfigPtr config, + Server::Configuration::TransportSocketFactoryContext& factory_context); + + // As documented above for QuicTransportSocketFactoryBase, the actual HTTP/3 + // code does not create transport sockets. + // QuicClientTransportSocketFactory::createTransportSocket is called by the + // connection grid when upstream HTTP/3 fails over to TCP, and a raw SSL socket + // is needed. In this case the QuicClientTransportSocketFactory falls over to + // using the fallback factory. + Network::TransportSocketPtr + createTransportSocket(Network::TransportSocketOptionsSharedPtr options) const override { + return fallback_factory_->createTransportSocket(options); + } - const Ssl::ClientContextConfig& clientContextConfig() const { return *config_; } + // TODO(14829) make sure that clientContextConfig() is safe when secrets are updated. + const Ssl::ClientContextConfig& clientContextConfig() const { + return fallback_factory_->config(); + } private: - std::unique_ptr config_; + // The QUIC client transport socket can create TLS sockets for fallback to TCP. + std::unique_ptr fallback_factory_; }; // Base class to create above QuicTransportSocketFactory for server and client diff --git a/source/common/upstream/BUILD b/source/common/upstream/BUILD index b970eacd34199..efcb951029fcc 100644 --- a/source/common/upstream/BUILD +++ b/source/common/upstream/BUILD @@ -92,6 +92,7 @@ envoy_cc_library( "@envoy_api//envoy/config/core/v3:pkg_cc_proto", ] + envoy_select_enable_http3([ "//source/common/http/http3:conn_pool_lib", + "//source/common/http:conn_pool_grid", ]), ) diff --git a/source/common/upstream/cluster_manager_impl.cc b/source/common/upstream/cluster_manager_impl.cc index 35a0aefeef5ae..63eaac2820e46 100644 --- a/source/common/upstream/cluster_manager_impl.cc +++ b/source/common/upstream/cluster_manager_impl.cc @@ -45,6 +45,7 @@ #include "common/upstream/subset_lb.h" #ifdef ENVOY_ENABLE_QUIC +#include "common/http/conn_pool_grid.h" #include "common/http/http3/conn_pool.h" #endif @@ -59,6 +60,18 @@ void addOptionsIfNotNull(Network::Socket::OptionsSharedPtr& options, } } +// Helper function to make sure each protocol in expected_protocols is present +// in protocols (only used for an ASSERT in debug builds) +bool contains(const std::vector& protocols, + const std::vector& expected_protocols) { + for (auto protocol : expected_protocols) { + if (std::find(protocols.begin(), protocols.end(), protocol) == protocols.end()) { + return false; + } + } + return true; +} + } // namespace void ClusterManagerInitHelper::addCluster(ClusterManagerCluster& cm_cluster) { @@ -1505,14 +1518,25 @@ Http::ConnectionPool::InstancePtr ProdClusterManagerFactory::allocateConnPool( const Network::ConnectionSocket::OptionsSharedPtr& options, const Network::TransportSocketOptionsSharedPtr& transport_socket_options, TimeSource& source, ClusterConnectivityState& state) { - if (protocols.size() == 2) { - ASSERT((protocols[0] == Http::Protocol::Http2 && protocols[1] == Http::Protocol::Http11) || - (protocols[1] == Http::Protocol::Http2 && protocols[0] == Http::Protocol::Http11)); + if (protocols.size() == 3 && runtime_.snapshot().featureEnabled("upstream.use_http3", 100)) { + ASSERT(contains(protocols, + {Http::Protocol::Http11, Http::Protocol::Http2, Http::Protocol::Http3})); +#ifdef ENVOY_ENABLE_QUIC + Envoy::Http::ConnectivityGrid::ConnectivityOptions coptions{protocols}; + return std::make_unique( + dispatcher, api_.randomGenerator(), host, priority, options, transport_socket_options, + state, source, std::chrono::milliseconds(300), coptions); +#else + // Should be blocked by configuration checking at an earlier point. + NOT_REACHED_GCOVR_EXCL_LINE; +#endif + } + if (protocols.size() >= 2) { + ASSERT(contains(protocols, {Http::Protocol::Http11, Http::Protocol::Http2})); return std::make_unique(dispatcher, api_.randomGenerator(), host, priority, options, transport_socket_options, state); } - if (protocols.size() == 1 && protocols[0] == Http::Protocol::Http2 && runtime_.snapshot().featureEnabled("upstream.use_http2", 100)) { return Http::Http2::allocateConnPool(dispatcher, api_.randomGenerator(), host, priority, diff --git a/source/common/upstream/upstream_impl.cc b/source/common/upstream/upstream_impl.cc index 5d70b2f90fabf..070063f768589 100644 --- a/source/common/upstream/upstream_impl.cc +++ b/source/common/upstream/upstream_impl.cc @@ -925,16 +925,21 @@ ClusterInfoImpl::upstreamHttpProtocol(absl::optional downstream_ if (downstream_protocol.has_value() && features_ & Upstream::ClusterInfo::Features::USE_DOWNSTREAM_PROTOCOL) { return {downstream_protocol.value()}; - } else if (features_ & Upstream::ClusterInfo::Features::USE_ALPN) { - ASSERT(!(features_ & Upstream::ClusterInfo::Features::HTTP3)); - return {Http::Protocol::Http2, Http::Protocol::Http11}; - } else { - if (features_ & Upstream::ClusterInfo::Features::HTTP3) { - return {Http::Protocol::Http3}; + } + + if (features_ & Upstream::ClusterInfo::Features::USE_ALPN) { + if (!(features_ & Upstream::ClusterInfo::Features::HTTP3)) { + return {Http::Protocol::Http2, Http::Protocol::Http11}; } - return {(features_ & Upstream::ClusterInfo::Features::HTTP2) ? Http::Protocol::Http2 - : Http::Protocol::Http11}; + return {Http::Protocol::Http3, Http::Protocol::Http2, Http::Protocol::Http11}; } + + if (features_ & Upstream::ClusterInfo::Features::HTTP3) { + return {Http::Protocol::Http3}; + } + + return {(features_ & Upstream::ClusterInfo::Features::HTTP2) ? Http::Protocol::Http2 + : Http::Protocol::Http11}; } ClusterImplBase::ClusterImplBase( diff --git a/source/extensions/transport_sockets/tls/ssl_socket.h b/source/extensions/transport_sockets/tls/ssl_socket.h index 534dcc29fc472..5b9239d266053 100644 --- a/source/extensions/transport_sockets/tls/ssl_socket.h +++ b/source/extensions/transport_sockets/tls/ssl_socket.h @@ -116,6 +116,8 @@ class ClientSslSocketFactory : public Network::TransportSocketFactory, // Secret::SecretCallbacks void onAddOrUpdateSecret() override; + const Ssl::ClientContextConfig& config() const { return *config_; } + private: Envoy::Ssl::ContextManager& manager_; Stats::Scope& stats_scope_; diff --git a/source/extensions/upstreams/http/config.cc b/source/extensions/upstreams/http/config.cc index 9822271413b30..5cb2acc4187f3 100644 --- a/source/extensions/upstreams/http/config.cc +++ b/source/extensions/upstreams/http/config.cc @@ -48,6 +48,9 @@ getHttp3Options(const envoy::extensions::upstreams::http::v3::HttpProtocolOption options.use_downstream_protocol_config().has_http3_protocol_options()) { return options.use_downstream_protocol_config().http3_protocol_options(); } + if (options.has_auto_config()) { + return options.auto_config().http3_protocol_options(); + } return options.explicit_http_config().http3_protocol_options(); } @@ -107,6 +110,7 @@ ProtocolOptionsConfigImpl::ProtocolOptionsConfigImpl( if (options.has_auto_config()) { use_http2_ = true; use_alpn_ = true; + use_http3_ = options.auto_config().has_http3_protocol_options(); } } diff --git a/test/common/upstream/BUILD b/test/common/upstream/BUILD index e4b4b0948812f..c4964177d10fb 100644 --- a/test/common/upstream/BUILD +++ b/test/common/upstream/BUILD @@ -608,6 +608,7 @@ envoy_cc_test( "//source/common/upstream:static_cluster_lib", "//source/common/upstream:strict_dns_cluster_lib", "//source/extensions/transport_sockets/raw_buffer:config", + "//source/extensions/transport_sockets/tls:config", "//source/extensions/upstreams/http:config", "//source/server:transport_socket_config_lib", "//test/common/stats:stat_test_utility_lib", diff --git a/test/common/upstream/upstream_impl_test.cc b/test/common/upstream/upstream_impl_test.cc index fdcf38fda1e7a..a0ce86ae68ac1 100644 --- a/test/common/upstream/upstream_impl_test.cc +++ b/test/common/upstream/upstream_impl_test.cc @@ -62,7 +62,7 @@ class UpstreamImplTestBase { UpstreamImplTestBase() : api_(Api::createApiForTest(stats_, random_)) {} NiceMock admin_; - Ssl::MockContextManager ssl_context_manager_; + NiceMock ssl_context_manager_; NiceMock cm_; NiceMock local_info_; NiceMock dispatcher_; @@ -2218,7 +2218,7 @@ class ClusterInfoImplTest : public testing::Test { }; Stats::TestUtil::TestStore stats_; - Ssl::MockContextManager ssl_context_manager_; + NiceMock ssl_context_manager_; std::shared_ptr dns_resolver_{new NiceMock()}; NiceMock dispatcher_; NiceMock runtime_; @@ -3165,7 +3165,6 @@ TEST_F(ClusterInfoImplTest, Http3) { - exact: 127.0.0.1 )EOF", Network::Address::IpVersion::v4); - auto cluster1 = makeCluster(yaml); ASSERT_TRUE(cluster1->info()->idleTimeout().has_value()); EXPECT_EQ(std::chrono::hours(1), cluster1->info()->idleTimeout().value()); @@ -3250,6 +3249,64 @@ TEST_F(ClusterInfoImplTest, Http3BadConfig) { EXPECT_THROW_WITH_REGEX(makeCluster(yaml), EnvoyException, "HTTP3 requires a QuicUpstreamTransport transport socket: name.*"); } + +TEST_F(ClusterInfoImplTest, Http3Auto) { + const std::string yaml = TestEnvironment::substitute(R"EOF( + name: name + connect_timeout: 0.25s + type: STRICT_DNS + lb_policy: MAGLEV + load_assignment: + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: foo.bar.com + port_value: 443 + transport_socket: + name: envoy.transport_sockets.quic + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.quic.v3.QuicUpstreamTransport + upstream_tls_context: + common_tls_context: + tls_certificates: + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_uri_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_uri_key.pem" + validation_context: + trusted_ca: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" + match_subject_alt_names: + - exact: localhost + - exact: 127.0.0.1 + )EOF", + Network::Address::IpVersion::v4); + + auto cluster1 = makeCluster(yaml); + ASSERT_TRUE(cluster1->info()->idleTimeout().has_value()); + EXPECT_EQ(std::chrono::hours(1), cluster1->info()->idleTimeout().value()); + + const std::string auto_http3 = R"EOF( + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + auto_config: + http3_protocol_options: + quic_protocol_options: + max_concurrent_streams: 2 + common_http_protocol_options: + idle_timeout: 1s + )EOF"; + + auto auto_h3 = makeCluster(yaml + auto_http3); + EXPECT_EQ(Http::Protocol::Http3, + auto_h3->info()->upstreamHttpProtocol({Http::Protocol::Http10})[0]); + EXPECT_EQ( + auto_h3->info()->http3Options().quic_protocol_options().max_concurrent_streams().value(), 2); +} + #else TEST_F(ClusterInfoImplTest, Http3BadConfig) { const std::string yaml = TestEnvironment::substitute(R"EOF( diff --git a/test/config/utility.cc b/test/config/utility.cc index 7dfbbceba7765..8da3ea9c1bffc 100644 --- a/test/config/utility.cc +++ b/test/config/utility.cc @@ -713,6 +713,10 @@ void ConfigHelper::configureUpstreamTls(bool use_alpn, bool http3) { new_protocol_options.mutable_auto_config()->mutable_http2_protocol_options()->MergeFrom( old_protocol_options.explicit_http_config().http2_protocol_options()); } + if (http3 || old_protocol_options.explicit_http_config().has_http3_protocol_options()) { + new_protocol_options.mutable_auto_config()->mutable_http3_protocol_options()->MergeFrom( + old_protocol_options.explicit_http_config().http3_protocol_options()); + } (*cluster->mutable_typed_extension_protocol_options()) ["envoy.extensions.upstreams.http.v3.HttpProtocolOptions"] .PackFrom(new_protocol_options); @@ -725,9 +729,9 @@ void ConfigHelper::configureUpstreamTls(bool use_alpn, bool http3) { // The test certs are for *.lyft.com, so make sure SNI matches. tls_context.set_sni("foo.lyft.com"); if (http3) { - cluster->mutable_transport_socket()->set_name("envoy.transport_sockets.quic"); envoy::extensions::transport_sockets::quic::v3::QuicUpstreamTransport quic_context; quic_context.mutable_upstream_tls_context()->CopyFrom(tls_context); + cluster->mutable_transport_socket()->set_name("envoy.transport_sockets.quic"); cluster->mutable_transport_socket()->mutable_typed_config()->PackFrom(quic_context); } else { cluster->mutable_transport_socket()->set_name("envoy.transport_sockets.tls"); diff --git a/test/integration/base_integration_test.h b/test/integration/base_integration_test.h index cc3008b249f27..1baab383b20e8 100644 --- a/test/integration/base_integration_test.h +++ b/test/integration/base_integration_test.h @@ -469,6 +469,8 @@ class BaseIntegrationTest : protected Logger::Loggable { bool v2_bootstrap_{false}; private: + friend class MixedUpstreamIntegrationTest; + // Configuration for the fake upstream. FakeUpstreamConfig upstream_config_{time_system_}; // True if initialized() has been called. diff --git a/test/integration/multiplexed_upstream_integration_test.cc b/test/integration/multiplexed_upstream_integration_test.cc index da0b16223b410..de2ac3cde8817 100644 --- a/test/integration/multiplexed_upstream_integration_test.cc +++ b/test/integration/multiplexed_upstream_integration_test.cc @@ -262,6 +262,7 @@ TEST_P(Http2UpstreamIntegrationTest, SimultaneousRequestAlpn) { } TEST_P(Http2UpstreamIntegrationTest, LargeSimultaneousRequestWithBufferLimitsAlpn) { + EXCLUDE_UPSTREAM_HTTP3; // No H3 support yet. use_alpn_ = true; config_helper_.setBufferLimits(1024, 1024); // Set buffer limits upstream and downstream. simultaneousRequest(1024 * 20, 1024 * 14 + 2, 1024 * 10 + 5, 1024 * 16); @@ -619,4 +620,45 @@ TEST_P(Http2UpstreamIntegrationTest, UpstreamGoaway) { cleanupUpstreamAndDownstream(); } +#ifdef ENVOY_ENABLE_QUIC + +class MixedUpstreamIntegrationTest : public Http2UpstreamIntegrationTest { +protected: + void initialize() override { + use_alpn_ = true; + Http2UpstreamIntegrationTest::initialize(); + } + void createUpstreams() override { + ASSERT_EQ(upstreamProtocol(), FakeHttpConnection::Type::HTTP3); + if (use_http2_) { + // Generally we always want to set these fields via accessors, which + // changes both the upstreams and Envoy's configuration at the same time. + // In this particular case, we want to change the upstreams without + // touching config, so edit the raw members directly. + upstream_config_.udp_fake_upstream_ = absl::nullopt; + upstream_config_.upstream_protocol_ = FakeHttpConnection::Type::HTTP2; + } + Http2UpstreamIntegrationTest::createUpstreams(); + upstream_config_.upstream_protocol_ = FakeHttpConnection::Type::HTTP3; + } + + bool use_http2_{false}; +}; + +TEST_P(MixedUpstreamIntegrationTest, SimultaneousRequestAutoWithHttp3) { + testRouterRequestAndResponseWithBody(0, 0, false); +} + +TEST_P(MixedUpstreamIntegrationTest, SimultaneousRequestAutoWithHttp2) { + use_http2_ = true; + testRouterRequestAndResponseWithBody(0, 0, false); +} + +INSTANTIATE_TEST_SUITE_P(Protocols, MixedUpstreamIntegrationTest, + testing::ValuesIn(HttpProtocolIntegrationTest::getProtocolTestParams( + {Http::CodecClient::Type::HTTP2}, {FakeHttpConnection::Type::HTTP3})), + HttpProtocolIntegrationTest::protocolTestParamsToString); + +#endif + } // namespace Envoy diff --git a/test/integration/multiplexed_upstream_integration_test.h b/test/integration/multiplexed_upstream_integration_test.h index 09d51ecf5fcf8..45e35a83b42e6 100644 --- a/test/integration/multiplexed_upstream_integration_test.h +++ b/test/integration/multiplexed_upstream_integration_test.h @@ -7,16 +7,13 @@ namespace Envoy { class Http2UpstreamIntegrationTest : public HttpProtocolIntegrationTest { public: - void SetUp() override { - HttpProtocolIntegrationTest::SetUp(); - + void initialize() override { upstream_tls_ = true; config_helper_.configureUpstreamTls(use_alpn_, upstreamProtocol() == FakeHttpConnection::Type::HTTP3); + HttpProtocolIntegrationTest::initialize(); } - void initialize() override { HttpIntegrationTest::initialize(); } - void bidirectionalStreaming(uint32_t bytes); void simultaneousRequest(uint32_t request1_bytes, uint32_t request2_bytes, uint32_t response1_bytes, uint32_t response2_bytes); diff --git a/test/mocks/server/BUILD b/test/mocks/server/BUILD index 1ab093a3b698d..7f4408de326d1 100644 --- a/test/mocks/server/BUILD +++ b/test/mocks/server/BUILD @@ -242,6 +242,7 @@ envoy_cc_mock( "//source/common/secret:secret_manager_impl_lib", "//test/mocks/api:api_mocks", "//test/mocks/server:config_tracker_mocks", + "//test/mocks/ssl:ssl_mocks", "//test/mocks/upstream:cluster_manager_mocks", ], ) diff --git a/test/mocks/server/transport_socket_factory_context.cc b/test/mocks/server/transport_socket_factory_context.cc index 0e4e50231a7d7..88ea41bd20fab 100644 --- a/test/mocks/server/transport_socket_factory_context.cc +++ b/test/mocks/server/transport_socket_factory_context.cc @@ -17,6 +17,8 @@ MockTransportSocketFactoryContext::MockTransportSocketFactoryContext() ON_CALL(*this, api()).WillByDefault(ReturnRef(api_)); ON_CALL(*this, messageValidationVisitor()) .WillByDefault(ReturnRef(ProtobufMessage::getStrictValidationVisitor())); + ON_CALL(*this, sslContextManager()).WillByDefault(ReturnRef(context_manager_)); + ON_CALL(*this, scope()).WillByDefault(ReturnRef(store_)); } MockTransportSocketFactoryContext::~MockTransportSocketFactoryContext() = default; diff --git a/test/mocks/server/transport_socket_factory_context.h b/test/mocks/server/transport_socket_factory_context.h index 2e346518cf935..f14bdd0ac9fe1 100644 --- a/test/mocks/server/transport_socket_factory_context.h +++ b/test/mocks/server/transport_socket_factory_context.h @@ -5,6 +5,8 @@ #include "common/secret/secret_manager_impl.h" #include "test/mocks/api/mocks.h" +#include "test/mocks/ssl/mocks.h" +#include "test/mocks/stats/mocks.h" #include "test/mocks/upstream/cluster_manager.h" #include "config_tracker.h" @@ -38,6 +40,8 @@ class MockTransportSocketFactoryContext : public TransportSocketFactoryContext { testing::NiceMock cluster_manager_; testing::NiceMock api_; testing::NiceMock config_tracker_; + testing::NiceMock context_manager_; + testing::NiceMock store_; std::unique_ptr secret_manager_; }; } // namespace Configuration