diff --git a/api/envoy/config/core/v3/protocol.proto b/api/envoy/config/core/v3/protocol.proto index 041d4eb4859cb..d01a591d865c2 100644 --- a/api/envoy/config/core/v3/protocol.proto +++ b/api/envoy/config/core/v3/protocol.proto @@ -41,7 +41,7 @@ message UpstreamHttpProtocolOptions { bool auto_san_validation = 2; } -// [#next-free-field: 6] +// [#next-free-field: 7] message HttpProtocolOptions { option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.core.HttpProtocolOptions"; @@ -105,6 +105,12 @@ message HttpProtocolOptions { // If this setting is not specified, the value defaults to ALLOW. // Note: upstream responses are not affected by this setting. HeadersWithUnderscoresAction headers_with_underscores_action = 5; + + // Optional maximum requests for both upstream and downstream connections. + // If not specified, there is no limit. + // Setting this parameter to 1 will effectively disable keep alive. + // For HTTP/2 and HTTP/3, due to concurrent stream processing, the limit is approximate. + google.protobuf.UInt32Value max_requests_per_connection = 6; } // [#next-free-field: 8] diff --git a/api/envoy/config/core/v4alpha/protocol.proto b/api/envoy/config/core/v4alpha/protocol.proto index f76f134ba0fd2..4f74968803e66 100644 --- a/api/envoy/config/core/v4alpha/protocol.proto +++ b/api/envoy/config/core/v4alpha/protocol.proto @@ -41,7 +41,7 @@ message UpstreamHttpProtocolOptions { bool auto_san_validation = 2; } -// [#next-free-field: 6] +// [#next-free-field: 7] message HttpProtocolOptions { option (udpa.annotations.versioning).previous_message_type = "envoy.config.core.v3.HttpProtocolOptions"; @@ -105,6 +105,12 @@ message HttpProtocolOptions { // If this setting is not specified, the value defaults to ALLOW. // Note: upstream responses are not affected by this setting. HeadersWithUnderscoresAction headers_with_underscores_action = 5; + + // Optional maximum requests for both upstream and downstream connections. + // If not specified, there is no limit. + // Setting this parameter to 1 will effectively disable keep alive. + // For HTTP/2 and HTTP/3, due to concurrent stream processing, the limit is approximate. + google.protobuf.UInt32Value max_requests_per_connection = 6; } // [#next-free-field: 8] diff --git a/docs/root/configuration/http/http_conn_man/stats.rst b/docs/root/configuration/http/http_conn_man/stats.rst index a2495b6b698c2..e1a4e9acefbf7 100644 --- a/docs/root/configuration/http/http_conn_man/stats.rst +++ b/docs/root/configuration/http/http_conn_man/stats.rst @@ -35,6 +35,7 @@ statistics: downstream_cx_drain_close, Counter, Total connections closed due to draining downstream_cx_idle_timeout, Counter, Total connections closed due to idle timeout downstream_cx_max_duration_reached, Counter, Total connections closed due to max connection duration + downstream_cx_max_requests_reached, Counter, Total connections closed due to max requests per connection downstream_cx_overload_disable_keepalive, Counter, Total connections for which HTTP 1.x keepalive has been disabled due to Envoy overload downstream_flow_control_paused_reading_total, Counter, Total number of times reads were disabled due to flow control downstream_flow_control_resumed_reading_total, Counter, Total number of times reads were enabled on the connection due to flow control diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index bc72fbaf7b030..75fe04ac22552 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -35,6 +35,7 @@ Removed Config or Runtime New Features ------------ * http: added the ability to preserve HTTP/1 header case across the proxy. See the :ref:`header casing ` documentation for more information. +* http: added support for :ref:`max_requests_per_connection ` for both upstream and downstream connections. * listener: added an option when balancing across active listeners and wildcard matching is used to return the listener that matches the IP family type associated with the listener's socket address. It is off by default, but is turned on by default in v1.19. To set change the runtime guard `envoy.reloadable_features.listener_wildcard_match_ip_family` to true. * upstream: added support for :ref:`slow start mode `, which allows to progresively increase traffic for new endpoints. * upstream: extended :ref:`Round Robin load balancer configuration ` with :ref:`slow start ` support. diff --git a/generated_api_shadow/envoy/config/core/v3/protocol.proto b/generated_api_shadow/envoy/config/core/v3/protocol.proto index 041d4eb4859cb..d01a591d865c2 100644 --- a/generated_api_shadow/envoy/config/core/v3/protocol.proto +++ b/generated_api_shadow/envoy/config/core/v3/protocol.proto @@ -41,7 +41,7 @@ message UpstreamHttpProtocolOptions { bool auto_san_validation = 2; } -// [#next-free-field: 6] +// [#next-free-field: 7] message HttpProtocolOptions { option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.core.HttpProtocolOptions"; @@ -105,6 +105,12 @@ message HttpProtocolOptions { // If this setting is not specified, the value defaults to ALLOW. // Note: upstream responses are not affected by this setting. HeadersWithUnderscoresAction headers_with_underscores_action = 5; + + // Optional maximum requests for both upstream and downstream connections. + // If not specified, there is no limit. + // Setting this parameter to 1 will effectively disable keep alive. + // For HTTP/2 and HTTP/3, due to concurrent stream processing, the limit is approximate. + google.protobuf.UInt32Value max_requests_per_connection = 6; } // [#next-free-field: 8] diff --git a/generated_api_shadow/envoy/config/core/v4alpha/protocol.proto b/generated_api_shadow/envoy/config/core/v4alpha/protocol.proto index f3d626e2ee740..a622a96887942 100644 --- a/generated_api_shadow/envoy/config/core/v4alpha/protocol.proto +++ b/generated_api_shadow/envoy/config/core/v4alpha/protocol.proto @@ -41,7 +41,7 @@ message UpstreamHttpProtocolOptions { bool auto_san_validation = 2; } -// [#next-free-field: 6] +// [#next-free-field: 7] message HttpProtocolOptions { option (udpa.annotations.versioning).previous_message_type = "envoy.config.core.v3.HttpProtocolOptions"; @@ -105,6 +105,12 @@ message HttpProtocolOptions { // If this setting is not specified, the value defaults to ALLOW. // Note: upstream responses are not affected by this setting. HeadersWithUnderscoresAction headers_with_underscores_action = 5; + + // Optional maximum requests for both upstream and downstream connections. + // If not specified, there is no limit. + // Setting this parameter to 1 will effectively disable keep alive. + // For HTTP/2 and HTTP/3, due to concurrent stream processing, the limit is approximate. + google.protobuf.UInt32Value max_requests_per_connection = 6; } // [#next-free-field: 8] diff --git a/source/common/http/conn_manager_config.h b/source/common/http/conn_manager_config.h index a72f07462e21c..ad5ce83562bed 100644 --- a/source/common/http/conn_manager_config.h +++ b/source/common/http/conn_manager_config.h @@ -34,6 +34,7 @@ namespace Http { COUNTER(downstream_cx_http3_total) \ COUNTER(downstream_cx_idle_timeout) \ COUNTER(downstream_cx_max_duration_reached) \ + COUNTER(downstream_cx_max_requests_reached) \ COUNTER(downstream_cx_overload_disable_keepalive) \ COUNTER(downstream_cx_protocol_error) \ COUNTER(downstream_cx_rx_bytes_total) \ @@ -476,6 +477,11 @@ class ConnectionManagerConfig { virtual envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager:: PathWithEscapedSlashesAction pathWithEscapedSlashesAction() const PURE; + + /** + * @return maximum requests for downstream. + */ + virtual uint64_t maxRequestsPerConnection() const PURE; }; } // namespace Http } // namespace Envoy diff --git a/source/common/http/conn_manager_impl.cc b/source/common/http/conn_manager_impl.cc index 9fea3af026ef4..cd5d04cd8497a 100644 --- a/source/common/http/conn_manager_impl.cc +++ b/source/common/http/conn_manager_impl.cc @@ -253,6 +253,21 @@ RequestDecoder& ConnectionManagerImpl::newStream(ResponseEncoder& response_encod ENVOY_CONN_LOG(debug, "new stream", read_callbacks_->connection()); ActiveStreamPtr new_stream(new ActiveStream(*this, response_encoder.getStream().bufferLimit())); + + accumulated_requests_++; + if (config_.maxRequestsPerConnection() > 0 && + accumulated_requests_ >= config_.maxRequestsPerConnection()) { + if (codec_->protocol() < Protocol::Http2) { + new_stream->state_.saw_connection_close_ = true; + // Prevent erroneous debug log of closing due to incoming connection close header. + drain_state_ = DrainState::Closing; + } else { + startDrainSequence(); + } + ENVOY_CONN_LOG(debug, "max requests per connection reached", read_callbacks_->connection()); + stats_.named_.downstream_cx_max_requests_reached_.inc(); + } + new_stream->state_.is_internally_created_ = is_internally_created; new_stream->response_encoder_ = &response_encoder; new_stream->response_encoder_->getStream().addCallbacks(*new_stream); diff --git a/source/common/http/conn_manager_impl.h b/source/common/http/conn_manager_impl.h index 4872cd1a75515..a74a5e9268fe0 100644 --- a/source/common/http/conn_manager_impl.h +++ b/source/common/http/conn_manager_impl.h @@ -442,6 +442,8 @@ class ConnectionManagerImpl : Logger::Loggable, const Server::OverloadActionState& overload_disable_keepalive_ref_; TimeSource& time_source_; bool remote_close_{}; + // The number of requests accumulated on the current connection. + uint64_t accumulated_requests_{}; }; } // namespace Http diff --git a/source/common/upstream/upstream_impl.cc b/source/common/upstream/upstream_impl.cc index 2d5233ae7426c..295a3711c1445 100644 --- a/source/common/upstream/upstream_impl.cc +++ b/source/common/upstream/upstream_impl.cc @@ -699,8 +699,9 @@ ClusterInfoImpl::ClusterInfoImpl( extensionProtocolOptionsTyped( "envoy.extensions.upstreams.http.v3.HttpProtocolOptions"), factory_context.messageValidationVisitor())), - max_requests_per_connection_( - PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, max_requests_per_connection, 0)), + max_requests_per_connection_(PROTOBUF_GET_WRAPPED_OR_DEFAULT( + http_protocol_options_->common_http_protocol_options_, max_requests_per_connection, + config.max_requests_per_connection().value())), max_response_headers_count_(PROTOBUF_GET_WRAPPED_OR_DEFAULT( http_protocol_options_->common_http_protocol_options_, max_headers_count, runtime_.snapshot().getInteger(Http::MaxResponseHeadersCountOverrideKey, @@ -754,6 +755,12 @@ ClusterInfoImpl::ClusterInfoImpl( : absl::nullopt), factory_context_( std::make_unique(*stats_scope_, runtime, factory_context)) { + if (config.has_max_requests_per_connection() && + http_protocol_options_->common_http_protocol_options_.has_max_requests_per_connection()) { + throw EnvoyException("Only one of max_requests_per_connection from Cluster or " + "HttpProtocolOptions can be specified"); + } + switch (config.lb_policy()) { case envoy::config::cluster::v3::Cluster::ROUND_ROBIN: lb_type_ = LoadBalancerType::RoundRobin; diff --git a/source/extensions/filters/network/http_connection_manager/config.cc b/source/extensions/filters/network/http_connection_manager/config.cc index 9a5dda4fcb3b1..df3f573634b1d 100644 --- a/source/extensions/filters/network/http_connection_manager/config.cc +++ b/source/extensions/filters/network/http_connection_manager/config.cc @@ -299,7 +299,9 @@ HttpConnectionManagerConfig::HttpConnectionManagerConfig( headers_with_underscores_action_( config.common_http_protocol_options().headers_with_underscores_action()), local_reply_(LocalReply::Factory::create(config.local_reply_config(), context)), - path_with_escaped_slashes_action_(getPathWithEscapedSlashesAction(config, context)) { + path_with_escaped_slashes_action_(getPathWithEscapedSlashesAction(config, context)), + max_requests_per_connection_(PROTOBUF_GET_WRAPPED_OR_DEFAULT( + config.common_http_protocol_options(), max_requests_per_connection, 0)) { // If idle_timeout_ was not configured in common_http_protocol_options, use value in deprecated // idle_timeout field. // TODO(asraa): Remove when idle_timeout is removed. diff --git a/source/extensions/filters/network/http_connection_manager/config.h b/source/extensions/filters/network/http_connection_manager/config.h index 344d3fdfd687c..5ef52604053d9 100644 --- a/source/extensions/filters/network/http_connection_manager/config.h +++ b/source/extensions/filters/network/http_connection_manager/config.h @@ -182,6 +182,7 @@ class HttpConnectionManagerConfig : Logger::Loggable, pathWithEscapedSlashesAction() const override { return path_with_escaped_slashes_action_; } + uint64_t maxRequestsPerConnection() const override { return max_requests_per_connection_; } private: enum class CodecType { HTTP1, HTTP2, HTTP3, AUTO }; @@ -269,6 +270,7 @@ class HttpConnectionManagerConfig : Logger::Loggable, static const uint64_t RequestHeaderTimeoutMs = 0; const envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager:: PathWithEscapedSlashesAction path_with_escaped_slashes_action_; + const uint64_t max_requests_per_connection_; }; /** diff --git a/source/server/admin/admin.h b/source/server/admin/admin.h index 8692cae1fa5f8..b648c1f5665aa 100644 --- a/source/server/admin/admin.h +++ b/source/server/admin/admin.h @@ -198,6 +198,7 @@ class AdminImpl : public Admin, return runCallback(path_and_query, response_headers, response, filter); }; } + uint64_t maxRequestsPerConnection() const override { return 0; } private: /** diff --git a/test/common/http/conn_manager_impl_fuzz_test.cc b/test/common/http/conn_manager_impl_fuzz_test.cc index 4a666ce2b8157..b90698b40b153 100644 --- a/test/common/http/conn_manager_impl_fuzz_test.cc +++ b/test/common/http/conn_manager_impl_fuzz_test.cc @@ -212,6 +212,7 @@ class FuzzConfig : public ConnectionManagerConfig { return envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager:: KEEP_UNCHANGED; } + uint64_t maxRequestsPerConnection() const override { return 0; } const envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager config_; diff --git a/test/common/http/conn_manager_impl_test_base.h b/test/common/http/conn_manager_impl_test_base.h index 6fb5496812189..aad4e49a64d21 100644 --- a/test/common/http/conn_manager_impl_test_base.h +++ b/test/common/http/conn_manager_impl_test_base.h @@ -147,6 +147,7 @@ class HttpConnectionManagerImplTest : public testing::Test, public ConnectionMan pathWithEscapedSlashesAction() const override { return path_with_escaped_slashes_action_; } + uint64_t maxRequestsPerConnection() const override { return 0; } Envoy::Event::SimulatedTimeSystem test_time_; NiceMock route_config_provider_; diff --git a/test/common/upstream/upstream_impl_test.cc b/test/common/upstream/upstream_impl_test.cc index 7d031bd37506e..92e29a3def978 100644 --- a/test/common/upstream/upstream_impl_test.cc +++ b/test/common/upstream/upstream_impl_test.cc @@ -3385,6 +3385,39 @@ TEST(HostPartitionTest, PartitionHosts) { EXPECT_EQ(1, update_hosts_params.excluded_hosts_per_locality->get()[1].size()); EXPECT_EQ(hosts[2], update_hosts_params.excluded_hosts_per_locality->get()[1][0]); } + +TEST_F(ClusterInfoImplTest, MaxRequestsPerConnectionValidation) { + const std::string yaml = R"EOF( + name: cluster1 + type: STRICT_DNS + lb_policy: ROUND_ROBIN + max_requests_per_connection: 3 + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + common_http_protocol_options: + max_requests_per_connection: 3 + use_downstream_protocol_config: {} +)EOF"; + + EXPECT_THROW_WITH_MESSAGE(makeCluster(yaml), EnvoyException, + "Only one of max_requests_per_connection from Cluster or " + "HttpProtocolOptions can be specified"); +} + +TEST_F(ClusterInfoImplTest, DeprecatedMaxRequestsPerConnection) { + const std::string yaml = R"EOF( + name: cluster1 + type: STRICT_DNS + lb_policy: ROUND_ROBIN + max_requests_per_connection: 3 +)EOF"; + + auto cluster = makeCluster(yaml); + + EXPECT_EQ(3U, cluster->info()->maxRequestsPerConnection()); +} + } // namespace } // namespace Upstream } // namespace Envoy diff --git a/test/config/utility.cc b/test/config/utility.cc index e0e6012e520c4..1a261ee99ee88 100644 --- a/test/config/utility.cc +++ b/test/config/utility.cc @@ -961,6 +961,17 @@ void ConfigHelper::setConnectTimeout(std::chrono::milliseconds timeout) { connect_timeout_set_ = true; } +void ConfigHelper::setDownstreamMaxRequestsPerConnection(uint64_t max_requests_per_connection) { + addConfigModifier( + [max_requests_per_connection]( + envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& + hcm) { + hcm.mutable_common_http_protocol_options() + ->mutable_max_requests_per_connection() + ->set_value(max_requests_per_connection); + }); +} + envoy::config::route::v3::VirtualHost ConfigHelper::createVirtualHost(const char* domain, const char* prefix, const char* cluster) { envoy::config::route::v3::VirtualHost virtual_host; diff --git a/test/config/utility.h b/test/config/utility.h index b51843eb3341d..510d68e70147a 100644 --- a/test/config/utility.h +++ b/test/config/utility.h @@ -185,6 +185,9 @@ class ConfigHelper { // Set the connect timeout on upstream connections. void setConnectTimeout(std::chrono::milliseconds timeout); + // Set the max_requests_per_connection for downstream through the HttpConnectionManager. + void setDownstreamMaxRequestsPerConnection(uint64_t max_requests_per_connection); + envoy::config::route::v3::VirtualHost createVirtualHost(const char* host, const char* route = "/", const char* cluster = "cluster_0"); diff --git a/test/extensions/filters/network/http_connection_manager/config_test.cc b/test/extensions/filters/network/http_connection_manager/config_test.cc index 452e0e1a240eb..6bd4f208fdcee 100644 --- a/test/extensions/filters/network/http_connection_manager/config_test.cc +++ b/test/extensions/filters/network/http_connection_manager/config_test.cc @@ -825,6 +825,42 @@ TEST_F(HttpConnectionManagerConfigTest, MaxRequestHeaderCountConfigurable) { EXPECT_EQ(200, config.maxRequestHeadersCount()); } +// Checking that default max_requests_per_connection is 0. +TEST_F(HttpConnectionManagerConfigTest, DefaultMaxRequestPerConnection) { + const std::string yaml_string = R"EOF( + stat_prefix: ingress_http + route_config: + name: local_route + http_filters: + - name: envoy.filters.http.router + )EOF"; + + HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, + date_provider_, route_config_provider_manager_, + scoped_routes_config_provider_manager_, http_tracer_manager_, + filter_config_provider_manager_); + EXPECT_EQ(0, config.maxRequestsPerConnection()); +} + +// Check that max_requests_per_connection is configured. +TEST_F(HttpConnectionManagerConfigTest, MaxRequestPerConnectionConfigurable) { + const std::string yaml_string = R"EOF( + stat_prefix: ingress_http + common_http_protocol_options: + max_requests_per_connection: 5 + route_config: + name: local_route + http_filters: + - name: envoy.filters.http.router + )EOF"; + + HttpConnectionManagerConfig config(parseHttpConnectionManagerFromYaml(yaml_string), context_, + date_provider_, route_config_provider_manager_, + scoped_routes_config_provider_manager_, http_tracer_manager_, + filter_config_provider_manager_); + EXPECT_EQ(5, config.maxRequestsPerConnection()); +} + TEST_F(HttpConnectionManagerConfigTest, ServerOverwrite) { const std::string yaml_string = R"EOF( stat_prefix: ingress_http diff --git a/test/integration/integration_test.cc b/test/integration/integration_test.cc index f662744274f94..5b91eef83511f 100644 --- a/test/integration/integration_test.cc +++ b/test/integration/integration_test.cc @@ -371,9 +371,15 @@ TEST_P(IntegrationTest, UpstreamDisconnectWithTwoRequests) { auto* static_resources = bootstrap.mutable_static_resources(); auto* cluster = static_resources->mutable_clusters(0); // Ensure we only have one connection upstream, one request active at a time. - cluster->mutable_max_requests_per_connection()->set_value(1); + ConfigHelper::HttpProtocolOptions protocol_options; + protocol_options.mutable_common_http_protocol_options() + ->mutable_max_requests_per_connection() + ->set_value(1); + protocol_options.mutable_use_downstream_protocol_config(); auto* circuit_breakers = cluster->mutable_circuit_breakers(); circuit_breakers->add_thresholds()->mutable_max_connections()->set_value(1); + ConfigHelper::setProtocolOptions(*bootstrap.mutable_static_resources()->mutable_clusters(0), + protocol_options); }); initialize(); diff --git a/test/integration/protocol_integration_test.cc b/test/integration/protocol_integration_test.cc index 0f0f137d2ff76..864017815079f 100644 --- a/test/integration/protocol_integration_test.cc +++ b/test/integration/protocol_integration_test.cc @@ -2055,6 +2055,48 @@ TEST_P(DownstreamProtocolIntegrationTest, BasicMaxStreamTimeoutLegacy) { EXPECT_THAT(waitForAccessLog(access_log_name_), HasSubstr("max_duration_timeout")); } +TEST_P(DownstreamProtocolIntegrationTest, MaxRequestsPerConnectionReached) { + config_helper_.setDownstreamMaxRequestsPerConnection(2); + initialize(); + codec_client_ = makeHttpConnection(lookupPort("http")); + + // Sending first request and waiting to complete the response. + auto encoder_decoder = codec_client_->startRequest(default_request_headers_); + request_encoder_ = &encoder_decoder.first; + auto response = std::move(encoder_decoder.second); + codec_client_->sendData(*request_encoder_, 1, true); + waitForNextUpstreamRequest(); + upstream_request_->encodeHeaders(default_response_headers_, true); + + ASSERT_TRUE(response->waitForEndStream()); + EXPECT_TRUE(upstream_request_->complete()); + EXPECT_TRUE(response->complete()); + EXPECT_EQ(test_server_->counter("http.config_test.downstream_cx_max_requests_reached")->value(), + 0); + + // Sending second request and waiting to complete the response. + auto encoder_decoder_2 = codec_client_->startRequest(default_request_headers_); + request_encoder_ = &encoder_decoder_2.first; + auto response_2 = std::move(encoder_decoder_2.second); + codec_client_->sendData(*request_encoder_, 1, true); + waitForNextUpstreamRequest(); + upstream_request_->encodeHeaders(default_response_headers_, true); + + ASSERT_TRUE(response_2->waitForEndStream()); + EXPECT_TRUE(upstream_request_->complete()); + EXPECT_TRUE(response_2->complete()); + EXPECT_EQ(test_server_->counter("http.config_test.downstream_cx_max_requests_reached")->value(), + 1); + + if (downstream_protocol_ == Http::CodecType::HTTP1) { + EXPECT_EQ(nullptr, response->headers().Connection()); + EXPECT_EQ("close", response_2->headers().getConnectionValue()); + } else { + EXPECT_TRUE(codec_client_->sawGoAway()); + } + ASSERT_TRUE(codec_client_->waitForDisconnect()); +} + // Make sure that invalid authority headers get blocked at or before the HCM. TEST_P(DownstreamProtocolIntegrationTest, InvalidAuthority) { initialize(); diff --git a/test/mocks/http/mocks.h b/test/mocks/http/mocks.h index 6d97f6b041395..36cf3d8aa9b00 100644 --- a/test/mocks/http/mocks.h +++ b/test/mocks/http/mocks.h @@ -567,6 +567,7 @@ class MockConnectionManagerConfig : public ConnectionManagerConfig { MOCK_METHOD(envoy::extensions::filters::network::http_connection_manager::v3:: HttpConnectionManager::PathWithEscapedSlashesAction, pathWithEscapedSlashesAction, (), (const)); + MOCK_METHOD(uint64_t, maxRequestsPerConnection, (), (const)); std::unique_ptr internal_address_config_ = std::make_unique();