Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion api/envoy/config/core/v3/protocol.proto
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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]
Expand Down
8 changes: 7 additions & 1 deletion api/envoy/config/core/v4alpha/protocol.proto

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions docs/root/configuration/http/http_conn_man/stats.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions docs/root/version_history/current.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 <config_http_conn_man_header_casing>` documentation for more information.
* http: added support for :ref:`max_requests_per_connection <envoy_v3_api_field_config.core.v3.HttpProtocolOptions.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 <arch_overview_load_balancing_slow_start>`, which allows to progresively increase traffic for new endpoints.
* upstream: extended :ref:`Round Robin load balancer configuration <envoy_v3_api_field_config.cluster.v3.Cluster.round_robin_lb_config>` with :ref:`slow start <envoy_v3_api_field_config.cluster.v3.Cluster.RoundRobinLbConfig.slow_start_config>` support.
Expand Down
8 changes: 7 additions & 1 deletion generated_api_shadow/envoy/config/core/v3/protocol.proto

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions source/common/http/conn_manager_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down Expand Up @@ -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
15 changes: 15 additions & 0 deletions source/common/http/conn_manager_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions source/common/http/conn_manager_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,8 @@ class ConnectionManagerImpl : Logger::Loggable<Logger::Id::http>,
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
Expand Down
11 changes: 9 additions & 2 deletions source/common/upstream/upstream_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -699,8 +699,9 @@ ClusterInfoImpl::ClusterInfoImpl(
extensionProtocolOptionsTyped<HttpProtocolOptionsConfigImpl>(
"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,
Expand Down Expand Up @@ -754,6 +755,12 @@ ClusterInfoImpl::ClusterInfoImpl(
: absl::nullopt),
factory_context_(
std::make_unique<FactoryContextImpl>(*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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ class HttpConnectionManagerConfig : Logger::Loggable<Logger::Id::config>,
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 };
Expand Down Expand Up @@ -269,6 +270,7 @@ class HttpConnectionManagerConfig : Logger::Loggable<Logger::Id::config>,
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_;
};

/**
Expand Down
1 change: 1 addition & 0 deletions source/server/admin/admin.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
/**
Expand Down
1 change: 1 addition & 0 deletions test/common/http/conn_manager_impl_fuzz_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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_;
Expand Down
1 change: 1 addition & 0 deletions test/common/http/conn_manager_impl_test_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<Router::MockRouteConfigProvider> route_config_provider_;
Expand Down
33 changes: 33 additions & 0 deletions test/common/upstream/upstream_impl_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
11 changes: 11 additions & 0 deletions test/config/utility.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions test/config/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 7 additions & 1 deletion test/integration/integration_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
Loading