Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add http1-safe max_connection_duration for http connection manager #35209

Merged
merged 27 commits into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
a8a4879
add http1-safe max_connection_duration for http connection manager
antoniovleonti Jul 16, 2024
cd18bdb
fix doc link
antoniovleonti Jul 16, 2024
aae00ea
try fix doc
antoniovleonti Jul 16, 2024
012510c
revert back to what I think should be correct
antoniovleonti Jul 16, 2024
a69e483
try different prefix
antoniovleonti Jul 16, 2024
7f19c3b
fix mock
antoniovleonti Jul 16, 2024
75f5b33
fix formatting issue
antoniovleonti Jul 17, 2024
ef94656
implement http1SafeMaxConnectionDuration in AdminImpl
antoniovleonti Jul 17, 2024
3fc1ab5
fix fuzz test
antoniovleonti Jul 17, 2024
d19c475
fix other use of setup()
antoniovleonti Jul 17, 2024
1602ad5
fix initializations
antoniovleonti Jul 18, 2024
652ca27
add 2 more initializations
antoniovleonti Jul 18, 2024
de81f0d
Merge remote-tracking branch 'upstream/main' into soft-mcd
antoniovleonti Jul 18, 2024
342030b
clean up proto doc string wording
antoniovleonti Jul 19, 2024
ec18c84
inline false in admin.h
antoniovleonti Jul 19, 2024
536ae76
add guage for soft draining h1 cx
antoniovleonti Jul 19, 2024
592eb7e
formatting fixes
antoniovleonti Jul 19, 2024
f0da2d0
close connection after first stream after max_connection_duration ends
antoniovleonti Aug 2, 2024
ff2ace1
Merge branch 'main' into soft-mcd
antoniovleonti Aug 2, 2024
f4f6995
fix ext_proc test
antoniovleonti Aug 5, 2024
50b40b0
Merge branch 'soft-mcd' of https://github.com/antoniovleonti/envoy in…
antoniovleonti Aug 5, 2024
31f6f23
set drain_state_ in newStream instead of encodeHeaders
antoniovleonti Aug 5, 2024
ebc40f5
update proto doc comment
antoniovleonti Aug 6, 2024
fbe0104
document new stat
antoniovleonti Aug 6, 2024
85786f7
Merge branch 'main' into soft-mcd
antoniovleonti Aug 8, 2024
bba721c
fix bad merge
antoniovleonti Aug 8, 2024
0f0c6b0
replace log check with stat check
antoniovleonti Aug 8, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -447,16 +447,14 @@ message HttpConnectionManager {
config.core.v3.HttpProtocolOptions common_http_protocol_options = 35
[(udpa.annotations.security).configure_for_untrusted_downstream = true];

// If :ref:`common_http_protocol_options.max_connection_duration
// <envoy_v3_api_field_config.core.v3.HttpProtocolOptions.max_connection_duration>` is
// set and this is set to true, the ``max_connection_duration`` setting
// will not cause Envoy to ever close HTTP1 connections (other settings are unaffected). Instead,
// Envoy will start adding the Connection:close response header to all responses and let the
// client choose when to close the connection.
// If set to true, :ref:`common_http_protocol_options.max_connection_duration
// <envoy_v3_api_field_config.core.v3.HttpProtocolOptions.max_connection_duration>` will not
// trigger Envoy to close HTTP1 connections. Instead, Envoy will only add the Connection:close
// response header to all responses until the client chooses to close the connection.
antoniovleonti marked this conversation as resolved.
Show resolved Hide resolved
//
// If ``max_connection_duration`` is set and this is set to false, ``max_connection_duration``
// will cause Envoy to enter the normal drain sequence for HTTP1 with Envoy eventually closing the
// connection (once there are no active streams).
// If set to false, ``max_connection_duration`` will cause Envoy to enter the normal drain
// sequence for HTTP1 with Envoy eventually closing the connection (once there are no active
// streams).
//
// Has no effect if ``max_connection_duration`` is unset. Defaults to false.
bool http1_safe_max_connection_duration = 58;
antoniovleonti marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
2 changes: 1 addition & 1 deletion changelogs/current.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ behavior_changes:
HttpConnectionManager to close the connection itself. Defaults to off ("unsafe" -- check
\#34356) and is configurable via :ref:`http1_safe_max_connection_duration
<envoy_v3_api_field_extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.http1_safe_max_connection_duration>`.
- area: local_ratelimit
- area: stats scoped_rds
change: |
Added new tag extraction so that scoped rds stats have their :ref:'scope_route_config_name
<envoy_v3_api_msg_config/route/v3/scoped_route>' and stat prefix extracted.
Expand Down
1 change: 1 addition & 0 deletions source/common/http/conn_manager_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ namespace Http {
GAUGE(downstream_cx_ssl_active, Accumulate) \
GAUGE(downstream_cx_tx_bytes_buffered, Accumulate) \
GAUGE(downstream_cx_upgrades_active, Accumulate) \
GAUGE(downstream_cx_http1_soft_drain, Accumulate) \
antoniovleonti marked this conversation as resolved.
Show resolved Hide resolved
GAUGE(downstream_rq_active, Accumulate) \
HISTOGRAM(downstream_cx_length_ms, Milliseconds) \
HISTOGRAM(downstream_rq_time, Milliseconds)
Expand Down
14 changes: 12 additions & 2 deletions source/common/http/conn_manager_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,10 @@ ConnectionManagerImpl::~ConnectionManagerImpl() {
}
}

if (soft_drain_http1_) {
stats_.named_.downstream_cx_http1_soft_drain_.dec();
}

conn_length_->complete();
user_agent_.completeConnectionLength(*conn_length_);
}
Expand Down Expand Up @@ -413,6 +417,12 @@ RequestDecoder& ConnectionManagerImpl::newStream(ResponseEncoder& response_encod
stats_.named_.downstream_cx_max_requests_reached_.inc();
}

if (soft_drain_http1_) {
new_stream->filter_manager_.streamInfo().setShouldDrainConnectionUponCompletion(true);
// Prevent erroneous debug log of closing due to incoming connection close header.
drain_state_ = DrainState::Closing;
}

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 Expand Up @@ -728,6 +738,7 @@ void ConnectionManagerImpl::onConnectionDurationTimeout() {
ENVOY_CONN_LOG(debug,
"HTTP1-safe max connection duration is configured -- skipping drain sequence.",
read_callbacks_->connection());
stats_.named_.downstream_cx_http1_soft_drain_.inc();
soft_drain_http1_ = true;
antoniovleonti marked this conversation as resolved.
Show resolved Hide resolved
} else {
startDrainSequence();
Expand Down Expand Up @@ -1788,8 +1799,7 @@ void ConnectionManagerImpl::ActiveStream::encodeHeaders(ResponseHeaderMap& heade
// point, the cached route should never be updated or refreshed.
blockRouteCache();

if ((connection_manager_.drain_state_ != DrainState::NotDraining ||
connection_manager_.soft_drain_http1_) &&
if (connection_manager_.drain_state_ != DrainState::NotDraining &&
connection_manager_.codec_->protocol() < Protocol::Http2) {
// If the connection manager is draining send "Connection: Close" on HTTP/1.1 connections.
// Do not do this for H2 (which drains via GOAWAY) or Upgrade or CONNECT (as the
Expand Down
5 changes: 1 addition & 4 deletions source/server/admin/admin.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,7 @@ class AdminImpl : public Admin,
absl::optional<std::chrono::milliseconds> maxConnectionDuration() const override {
return max_connection_duration_;
}
bool http1SafeMaxConnectionDuration() const override {
return http1_safe_max_connection_duration_;
}
bool http1SafeMaxConnectionDuration() const override { return false; }
uint32_t maxRequestHeadersKb() const override { return max_request_headers_kb_; }
uint32_t maxRequestHeadersCount() const override { return max_request_headers_count_; }
std::chrono::milliseconds streamIdleTimeout() const override { return {}; }
Expand Down Expand Up @@ -481,7 +479,6 @@ class AdminImpl : public Admin,
const uint32_t max_request_headers_count_{Http::DEFAULT_MAX_HEADERS_COUNT};
absl::optional<std::chrono::milliseconds> idle_timeout_;
absl::optional<std::chrono::milliseconds> max_connection_duration_;
bool http1_safe_max_connection_duration_{false};
absl::optional<std::chrono::milliseconds> max_stream_duration_;
absl::optional<std::string> user_agent_;
Http::SlowDateProviderImpl date_provider_;
Expand Down
91 changes: 21 additions & 70 deletions test/common/http/conn_manager_impl_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@ namespace Envoy {
namespace Http {

TEST_F(HttpConnectionManagerImplTest, HeaderOnlyRequestAndResponse) {
SetupOpts setup_opts;
setup_opts.tracing = false;
setup(setup_opts);
setup(SetupOpts().setTracing(false));

// Store the basic request encoder during filter chain setup.
std::shared_ptr<MockStreamDecoderFilter> filter(new NiceMock<MockStreamDecoderFilter>());
Expand Down Expand Up @@ -96,9 +94,7 @@ TEST_F(HttpConnectionManagerImplTest, HeaderOnlyRequestAndResponse) {
// Similar to HeaderOnlyRequestAndResponse but uses newStreamHandle and has
// lifetime checks.
TEST_F(HttpConnectionManagerImplTest, HandleLifetime) {
SetupOpts setup_opts;
setup_opts.tracing = false;
setup(setup_opts);
setup(SetupOpts().setTracing(false));
Http::RequestDecoderHandlePtr decoder_handle;

// Store the basic request encoder during filter chain setup.
Expand Down Expand Up @@ -175,9 +171,7 @@ TEST_F(HttpConnectionManagerImplTest, HandleLifetime) {
}

TEST_F(HttpConnectionManagerImplTest, HeaderOnlyRequestAndResponseWithEarlyHeaderMutation) {
SetupOpts setup_opts;
setup_opts.tracing = false;
setup(setup_opts);
setup(SetupOpts().setTracing(false));

auto mock_early_header_mutation_1 = std::make_unique<NiceMock<Http::MockEarlyHeaderMutation>>();
auto mock_early_header_mutation_2 = std::make_unique<NiceMock<Http::MockEarlyHeaderMutation>>();
Expand Down Expand Up @@ -268,9 +262,7 @@ TEST_F(HttpConnectionManagerImplTest, HeaderOnlyRequestAndResponseWithEarlyHeade

TEST_F(HttpConnectionManagerImplTest, 1xxResponse) {
proxy_100_continue_ = true;
SetupOpts setup_opts;
setup_opts.tracing = false;
setup(setup_opts);
setup(SetupOpts().setTracing(false));

// Store the basic request encoder during filter chain setup.
std::shared_ptr<MockStreamDecoderFilter> filter(new NiceMock<MockStreamDecoderFilter>());
Expand Down Expand Up @@ -328,9 +320,7 @@ TEST_F(HttpConnectionManagerImplTest, 1xxResponse) {

TEST_F(HttpConnectionManagerImplTest, 1xxResponseWithEncoderFiltersProxyingDisabled) {
proxy_100_continue_ = false;
SetupOpts setup_opts;
setup_opts.tracing = false;
setup(setup_opts);
setup(SetupOpts().setTracing(false));
setUpEncoderAndDecoder(false, false);
sendRequestHeadersAndData();

Expand All @@ -357,9 +347,7 @@ TEST_F(HttpConnectionManagerImplTest, 1xxResponseWithEncoderFiltersProxyingDisab

TEST_F(HttpConnectionManagerImplTest, 1xxResponseWithEncoderFilters) {
proxy_100_continue_ = true;
SetupOpts setup_opts;
setup_opts.tracing = false;
setup(setup_opts);
setup(SetupOpts().setTracing(false));
setUpEncoderAndDecoder(false, false);
sendRequestHeadersAndData();

Expand All @@ -385,9 +373,7 @@ TEST_F(HttpConnectionManagerImplTest, 1xxResponseWithEncoderFilters) {

TEST_F(HttpConnectionManagerImplTest, PauseResume1xx) {
proxy_100_continue_ = true;
SetupOpts setup_opts;
setup_opts.tracing = false;
setup(setup_opts);
setup(SetupOpts().setTracing(false));
setUpEncoderAndDecoder(false, false);
sendRequestHeadersAndData();

Expand Down Expand Up @@ -421,9 +407,7 @@ TEST_F(HttpConnectionManagerImplTest, PauseResume1xx) {
// Regression test for https://github.com/envoyproxy/envoy/issues/10923.
TEST_F(HttpConnectionManagerImplTest, 1xxResponseWithDecoderPause) {
proxy_100_continue_ = true;
SetupOpts setup_opts;
setup_opts.tracing = false;
setup(setup_opts);
setup(SetupOpts().setTracing(false));

std::shared_ptr<MockStreamDecoderFilter> filter(new NiceMock<MockStreamDecoderFilter>());

Expand Down Expand Up @@ -490,10 +474,7 @@ TEST_F(HttpConnectionManagerImplTest, 1xxResponseWithDecoderPause) {

// When create new stream, the stream info will be populated from the connection.
TEST_F(HttpConnectionManagerImplTest, PopulateStreamInfo) {
SetupOpts setup_opts;
setup_opts.ssl = true;
setup_opts.tracing = false;
setup(setup_opts);
setup(SetupOpts().setSsl(true).setTracing(false));

// Set up the codec.
Buffer::OwnedImpl fake_input("input");
Expand All @@ -517,10 +498,7 @@ TEST_F(HttpConnectionManagerImplTest, PopulateStreamInfo) {

// By default, Envoy will set the server header to the server name, here "custom-value"
TEST_F(HttpConnectionManagerImplTest, ServerHeaderOverwritten) {
SetupOpts setup_opts;
setup_opts.server_name = "custom-value";
setup_opts.tracing = false;
setup(setup_opts);
setup(SetupOpts().setServerName("custom-value").setTracing(false));
setUpEncoderAndDecoder(false, false);

sendRequestHeadersAndData();
Expand All @@ -534,10 +512,7 @@ TEST_F(HttpConnectionManagerImplTest, ServerHeaderOverwritten) {
// When configured APPEND_IF_ABSENT if the server header is present it will be retained.
TEST_F(HttpConnectionManagerImplTest, ServerHeaderAppendPresent) {
server_transformation_ = HttpConnectionManagerProto::APPEND_IF_ABSENT;
SetupOpts setup_opts;
setup_opts.server_name = "custom-value";
setup_opts.tracing = false;
setup(setup_opts);
setup(SetupOpts().setServerName("custom-value").setTracing(false));
setUpEncoderAndDecoder(false, false);

sendRequestHeadersAndData();
Expand All @@ -551,10 +526,7 @@ TEST_F(HttpConnectionManagerImplTest, ServerHeaderAppendPresent) {
// When configured APPEND_IF_ABSENT if the server header is absent the server name will be set.
TEST_F(HttpConnectionManagerImplTest, ServerHeaderAppendAbsent) {
server_transformation_ = HttpConnectionManagerProto::APPEND_IF_ABSENT;
SetupOpts setup_opts;
setup_opts.server_name = "custom-value";
setup_opts.tracing = false;
setup(setup_opts);
setup(SetupOpts().setServerName("custom-value").setTracing(false));
setUpEncoderAndDecoder(false, false);

sendRequestHeadersAndData();
Expand All @@ -568,10 +540,7 @@ TEST_F(HttpConnectionManagerImplTest, ServerHeaderAppendAbsent) {
// When configured PASS_THROUGH, the server name will pass through.
TEST_F(HttpConnectionManagerImplTest, ServerHeaderPassthroughPresent) {
server_transformation_ = HttpConnectionManagerProto::PASS_THROUGH;
SetupOpts setup_opts;
setup_opts.server_name = "custom-value";
setup_opts.tracing = false;
setup(setup_opts);
setup(SetupOpts().setServerName("custom-value").setTracing(false));
setUpEncoderAndDecoder(false, false);

sendRequestHeadersAndData();
Expand All @@ -585,10 +554,7 @@ TEST_F(HttpConnectionManagerImplTest, ServerHeaderPassthroughPresent) {
// When configured PASS_THROUGH, the server header will not be added if absent.
TEST_F(HttpConnectionManagerImplTest, ServerHeaderPassthroughAbsent) {
server_transformation_ = HttpConnectionManagerProto::PASS_THROUGH;
SetupOpts setup_opts;
setup_opts.server_name = "custom-value";
setup_opts.tracing = false;
setup(setup_opts);
setup(SetupOpts().setServerName("custom-value").setTracing(false));
setUpEncoderAndDecoder(false, false);

sendRequestHeadersAndData();
Expand Down Expand Up @@ -2664,9 +2630,7 @@ TEST_F(HttpConnectionManagerImplTest, TestPeriodicAccessLogging) {
request_headers_timeout_ = std::chrono::milliseconds(0);
max_stream_duration_ = std::nullopt;

SetupOpts setup_opts;
setup_opts.server_name = "server-opts";
setup(setup_opts);
setup(SetupOpts().setServerName("server-opts"));

std::shared_ptr<MockStreamDecoderFilter> filter(new NiceMock<MockStreamDecoderFilter>());
std::shared_ptr<AccessLog::MockInstance> handler(new NiceMock<AccessLog::MockInstance>());
Expand Down Expand Up @@ -2825,9 +2789,7 @@ TEST_F(StreamErrorOnInvalidHttpMessageTest, ConnectionOpenIfCodecStreamErrorIsTr
}

TEST_F(HttpConnectionManagerImplTest, TestAccessLogSsl) {
SetupOpts setup_opts;
setup_opts.ssl = true;
setup(setup_opts);
setup(SetupOpts().setSsl(true));

std::shared_ptr<MockStreamDecoderFilter> filter(new NiceMock<MockStreamDecoderFilter>());
std::shared_ptr<AccessLog::MockInstance> handler(new NiceMock<AccessLog::MockInstance>());
Expand Down Expand Up @@ -4095,9 +4057,7 @@ TEST_F(HttpConnectionManagerImplTest, RejectWebSocketOnNonWebSocketRoute) {

// Make sure for upgrades, we do not append Connection: Close when draining.
TEST_F(HttpConnectionManagerImplTest, FooUpgradeDrainClose) {
SetupOpts setup_opts;
setup_opts.tracing = false;
setup(setup_opts);
setup(SetupOpts().setTracing(false));

// Store the basic request encoder during filter chain setup.
auto* filter = new MockStreamFilter();
Expand Down Expand Up @@ -4163,9 +4123,7 @@ TEST_F(HttpConnectionManagerImplTest, FooUpgradeDrainClose) {

// Make sure CONNECT requests hit the upgrade filter path.
TEST_F(HttpConnectionManagerImplTest, ConnectAsUpgrade) {
SetupOpts setup_opts;
setup_opts.tracing = false;
setup(setup_opts);
setup(SetupOpts().setTracing(false));

EXPECT_CALL(filter_factory_, createUpgradeFilterChain("CONNECT", _, _, _))
.WillRepeatedly(Return(true));
Expand All @@ -4189,9 +4147,7 @@ TEST_F(HttpConnectionManagerImplTest, ConnectAsUpgrade) {
}

TEST_F(HttpConnectionManagerImplTest, ConnectWithEmptyPath) {
SetupOpts setup_opts;
setup_opts.tracing = false;
setup(setup_opts);
setup(SetupOpts().setTracing(false));

EXPECT_CALL(filter_factory_, createUpgradeFilterChain("CONNECT", _, _, _))
.WillRepeatedly(Return(true));
Expand Down Expand Up @@ -4310,9 +4266,7 @@ TEST_F(HttpConnectionManagerImplTest,
}

TEST_F(HttpConnectionManagerImplTest, DrainClose) {
SetupOpts setup_opts;
setup_opts.ssl = true;
setup(setup_opts);
setup(SetupOpts().setSsl(true));

MockStreamDecoderFilter* filter = new NiceMock<MockStreamDecoderFilter>();
EXPECT_CALL(filter_factory_, createFilterChain(_))
Expand Down Expand Up @@ -4368,10 +4322,7 @@ TEST_F(HttpConnectionManagerImplTest, DrainClose) {
class ProxyStatusTest : public HttpConnectionManagerImplTest {
public:
void initialize() {
SetupOpts setup_opts;
setup_opts.server_name = servername_;
setup_opts.tracing = false;
setup(setup_opts);
setup(SetupOpts().setServerName(servername_).setTracing(false));
setUpEncoderAndDecoder(/*request_with_data_and_trailers=*/false,
/*decode_headers_stop_all=*/false);
sendRequestHeadersAndData();
Expand Down
Loading