Skip to content

Commit a07f2f2

Browse files
committed
Added runtime feature.
Signed-off-by: Biren Roy <birenroy@google.com>
1 parent c7cb710 commit a07f2f2

13 files changed

Lines changed: 176 additions & 25 deletions

File tree

source/common/http/conn_manager_impl.cc

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -525,16 +525,18 @@ Network::FilterStatus ConnectionManagerImpl::onData(Buffer::Instance& data, bool
525525
createCodec(data);
526526
}
527527

528-
if (should_send_go_away_and_close_on_dispatch_ != nullptr &&
529-
should_send_go_away_and_close_on_dispatch_->shouldShedLoad()) {
530-
sendGoAwayAndClose(/*graceful=*/false);
531-
handleCodecOverloadError(
532-
"Load shed point http2_server_go_away_and_close_on_dispatch triggered");
533-
return Network::FilterStatus::StopIteration;
534-
}
535-
if (should_send_go_away_on_dispatch_ != nullptr &&
536-
should_send_go_away_on_dispatch_->shouldShedLoad()) {
537-
sendGoAwayAndClose(/*graceful=*/true);
528+
if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.http2_fix_goaway_loadshed_point")) {
529+
if (should_send_go_away_and_close_on_dispatch_ != nullptr &&
530+
should_send_go_away_and_close_on_dispatch_->shouldShedLoad()) {
531+
sendGoAwayAndClose(/*graceful=*/false);
532+
handleCodecOverloadError(
533+
"Load shed point http2_server_go_away_and_close_on_dispatch triggered");
534+
return Network::FilterStatus::StopIteration;
535+
}
536+
if (should_send_go_away_on_dispatch_ != nullptr &&
537+
should_send_go_away_on_dispatch_->shouldShedLoad()) {
538+
sendGoAwayAndClose(/*graceful=*/true);
539+
}
538540
}
539541

540542
bool redispatch;

source/common/http/conn_manager_utility.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ ServerConnectionPtr ConnectionManagerUtility::autoCreateCodec(
9999
Http2::CodecStats& stats = Http2::CodecStats::atomicGet(http2_codec_stats, scope);
100100
return std::make_unique<Http2::ServerConnectionImpl>(
101101
connection, callbacks, stats, random, http2_options, max_request_headers_kb,
102-
max_request_headers_count, headers_with_underscores_action);
102+
max_request_headers_count, headers_with_underscores_action, overload_manager);
103103
} else {
104104
Http1::CodecStats& stats = Http1::CodecStats::atomicGet(http1_codec_stats, scope);
105105
return std::make_unique<Http1::ServerConnectionImpl>(

source/common/http/http2/codec_impl.cc

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2371,10 +2371,22 @@ ServerConnectionImpl::ServerConnectionImpl(
23712371
const envoy::config::core::v3::Http2ProtocolOptions& http2_options,
23722372
const uint32_t max_request_headers_kb, const uint32_t max_request_headers_count,
23732373
envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction
2374-
headers_with_underscores_action)
2374+
headers_with_underscores_action,
2375+
Server::OverloadManager& overload_manager)
23752376
: ConnectionImpl(connection, stats, random_generator, http2_options, max_request_headers_kb,
23762377
max_request_headers_count),
2377-
callbacks_(callbacks), headers_with_underscores_action_(headers_with_underscores_action) {
2378+
callbacks_(callbacks), headers_with_underscores_action_(headers_with_underscores_action),
2379+
should_send_go_away_on_dispatch_(overload_manager.getLoadShedPoint(
2380+
Server::LoadShedPointName::get().H2ServerGoAwayOnDispatch)),
2381+
should_send_go_away_and_close_on_dispatch_(overload_manager.getLoadShedPoint(
2382+
Server::LoadShedPointName::get().H2ServerGoAwayAndCloseOnDispatch)) {
2383+
ENVOY_LOG_ONCE_IF(trace, should_send_go_away_on_dispatch_ == nullptr,
2384+
"LoadShedPoint envoy.load_shed_points.http2_server_go_away_on_dispatch is not "
2385+
"found. Is it configured?");
2386+
ENVOY_LOG_ONCE_IF(
2387+
trace, should_send_go_away_and_close_on_dispatch_ == nullptr,
2388+
"LoadShedPoint envoy.load_shed_points.http2_server_go_away_and_close_on_dispatch is not "
2389+
"found. Is it configured?");
23782390
Http2Options h2_options(http2_options, max_request_headers_kb);
23792391

23802392
auto direct_visitor = std::make_unique<Http2Visitor>(this);
@@ -2440,6 +2452,21 @@ int ServerConnectionImpl::onHeader(int32_t stream_id, HeaderString&& name, Heade
24402452
Http::Status ServerConnectionImpl::dispatch(Buffer::Instance& data) {
24412453
// Make sure downstream outbound queue was not flooded by the upstream frames.
24422454
RETURN_IF_ERROR(protocol_constraints_.checkOutboundFrameLimits());
2455+
if (!Runtime::runtimeFeatureEnabled(
2456+
"envoy.reloadable_features.http2_fix_goaway_loadshed_point")) {
2457+
if (should_send_go_away_and_close_on_dispatch_ != nullptr &&
2458+
should_send_go_away_and_close_on_dispatch_->shouldShedLoad()) {
2459+
ConnectionImpl::goAway();
2460+
sent_go_away_on_dispatch_ = true;
2461+
return envoyOverloadError(
2462+
"Load shed point http2_server_go_away_and_close_on_dispatch triggered");
2463+
}
2464+
if (should_send_go_away_on_dispatch_ != nullptr && !sent_go_away_on_dispatch_ &&
2465+
should_send_go_away_on_dispatch_->shouldShedLoad()) {
2466+
ConnectionImpl::goAway();
2467+
sent_go_away_on_dispatch_ = true;
2468+
}
2469+
}
24432470
return ConnectionImpl::dispatch(data);
24442471
}
24452472

source/common/http/http2/codec_impl.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -858,7 +858,8 @@ class ServerConnectionImpl : public ServerConnection, public ConnectionImpl {
858858
const uint32_t max_request_headers_kb,
859859
const uint32_t max_request_headers_count,
860860
envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction
861-
headers_with_underscores_action);
861+
headers_with_underscores_action,
862+
Server::OverloadManager& overload_manager);
862863

863864
private:
864865
// ConnectionImpl
@@ -884,6 +885,10 @@ class ServerConnectionImpl : public ServerConnection, public ConnectionImpl {
884885
// The action to take when a request header name contains underscore characters.
885886
envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction
886887
headers_with_underscores_action_;
888+
// Remove when removing runtime feature `http2_fix_goaway_loadshed_point`.
889+
Server::LoadShedPoint* should_send_go_away_on_dispatch_{nullptr};
890+
Server::LoadShedPoint* should_send_go_away_and_close_on_dispatch_{nullptr};
891+
bool sent_go_away_on_dispatch_{false};
887892
};
888893

889894
} // namespace Http2

source/common/runtime/runtime_features.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ RUNTIME_GUARD(envoy_reloadable_features_health_check_after_cluster_warming);
6565
RUNTIME_GUARD(envoy_reloadable_features_hide_transport_failure_reason_in_response_body);
6666
RUNTIME_GUARD(envoy_reloadable_features_http1_close_connection_on_zombie_stream_complete);
6767
RUNTIME_GUARD(envoy_reloadable_features_http2_discard_host_header);
68+
RUNTIME_GUARD(envoy_reloadable_features_http2_fix_goaway_loadshed_point);
6869
RUNTIME_GUARD(envoy_reloadable_features_http_async_client_retry_respect_buffer_limits);
6970
// Delay deprecation and decommission until UHV is enabled.
7071
RUNTIME_GUARD(envoy_reloadable_features_http_reject_path_with_fragment);

source/extensions/filters/network/http_connection_manager/config.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -776,7 +776,8 @@ Http::ServerConnectionPtr HttpConnectionManagerConfig::createCodec(
776776
connection, callbacks,
777777
Http::Http2::CodecStats::atomicGet(http2_codec_stats_, context_.scope()),
778778
context_.serverFactoryContext().api().randomGenerator(), http2_options_,
779-
maxRequestHeadersKb(), maxRequestHeadersCount(), headersWithUnderscoresAction());
779+
maxRequestHeadersKb(), maxRequestHeadersCount(), headersWithUnderscoresAction(),
780+
overload_manager);
780781
case CodecType::HTTP3:
781782
#ifdef ENVOY_ENABLE_QUIC
782783
return Config::Utility::getAndCheckFactoryByName<QuicHttpServerConnectionFactory>(

test/common/http/codec_impl_fuzz_test.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,7 @@ void codecFuzz(const test::common::http::CodecImplFuzzTestCase& input, HttpVersi
622622
server = std::make_unique<Http2::ServerConnectionImpl>(
623623
server_connection, server_callbacks, Http2::CodecStats::atomicGet(http2_stats, scope),
624624
random, server_http2_options, max_request_headers_kb, max_request_headers_count,
625-
headers_with_underscores_action);
625+
headers_with_underscores_action, overload_manager_);
626626
} else {
627627
const Http1Settings server_http1settings{fromHttp1Settings(input.h1_settings().server())};
628628
server = std::make_unique<Http1::ServerConnectionImpl>(

test/common/http/conn_manager_impl_test_3.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -957,6 +957,10 @@ TEST_F(HttpConnectionManagerImplTest, DecodeHeaderLoadShedPointCanRejectNewStrea
957957
}
958958

959959
TEST_F(HttpConnectionManagerImplTest, GoAwayLoadShedPoint) {
960+
if (!Runtime::runtimeFeatureEnabled(
961+
"envoy.reloadable_features.http2_fix_goaway_loadshed_point")) {
962+
GTEST_SKIP();
963+
}
960964
Server::MockLoadShedPoint goaway_point;
961965
EXPECT_CALL(overload_manager_,
962966
getLoadShedPoint(Server::LoadShedPointName::get().HcmDecodeHeaders))
@@ -999,6 +1003,10 @@ TEST_F(HttpConnectionManagerImplTest, GoAwayLoadShedPoint) {
9991003
}
10001004

10011005
TEST_F(HttpConnectionManagerImplTest, GoAwayAndCloseLoadShedPoint) {
1006+
if (!Runtime::runtimeFeatureEnabled(
1007+
"envoy.reloadable_features.http2_fix_goaway_loadshed_point")) {
1008+
GTEST_SKIP();
1009+
}
10021010
Server::MockLoadShedPoint goaway_and_close_point;
10031011
EXPECT_CALL(overload_manager_,
10041012
getLoadShedPoint(Server::LoadShedPointName::get().HcmDecodeHeaders))

test/common/http/http2/codec_impl_test.cc

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4591,6 +4591,102 @@ TEST_P(Http2CodecImplTest, ChunkProcessingShouldNotScheduleIfReadDisabled) {
45914591
}
45924592
}
45934593

4594+
TEST_P(Http2CodecImplTest, ServerDispatchLoadShedPointCanCauseServerToSendGoAway) {
4595+
if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.http2_fix_goaway_loadshed_point")) {
4596+
GTEST_SKIP();
4597+
}
4598+
initialize();
4599+
ASSERT_EQ(0, server_stats_store_.counter("http2.goaway_sent").value());
4600+
4601+
TestRequestHeaderMapImpl request_headers;
4602+
HttpTestUtility::addDefaultHeaders(request_headers);
4603+
EXPECT_CALL(server_->server_go_away_on_dispatch, shouldShedLoad()).WillOnce(Return(true));
4604+
EXPECT_CALL(client_callbacks_, onGoAway(_));
4605+
4606+
if (http2_implementation_ == Http2Impl::Oghttp2) {
4607+
EXPECT_CALL(request_decoder_, decodeHeaders_(_, true));
4608+
EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok());
4609+
} else {
4610+
// nghttp2 does not raise the headers to the decoder.
4611+
EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok());
4612+
}
4613+
driveToCompletion();
4614+
4615+
EXPECT_EQ(1, server_stats_store_.counter("http2.goaway_sent").value());
4616+
}
4617+
4618+
TEST_P(Http2CodecImplTest, ServerDispatchLoadShedPointSendGoAwayAndClose) {
4619+
if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.http2_fix_goaway_loadshed_point")) {
4620+
GTEST_SKIP();
4621+
}
4622+
expect_buffered_data_on_teardown_ = true;
4623+
4624+
initialize();
4625+
ASSERT_EQ(0, server_stats_store_.counter("http2.goaway_sent").value());
4626+
4627+
TestRequestHeaderMapImpl request_headers;
4628+
HttpTestUtility::addDefaultHeaders(request_headers);
4629+
EXPECT_CALL(server_->server_go_away_and_close_on_dispatch, shouldShedLoad())
4630+
.WillOnce(Return(true));
4631+
EXPECT_CALL(client_callbacks_, onGoAway(_));
4632+
4633+
EXPECT_CALL(request_decoder_, decodeHeaders_(_, _)).Times(0);
4634+
EXPECT_TRUE(request_encoder_->encodeHeaders(request_headers, true).ok());
4635+
4636+
driveToCompletion();
4637+
4638+
EXPECT_EQ(1, server_stats_store_.counter("http2.goaway_sent").value());
4639+
}
4640+
4641+
TEST_P(Http2CodecImplTest, ServerDispatchLoadShedPointsAreOnlyConsultedOncePerDispatch) {
4642+
if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.http2_fix_goaway_loadshed_point")) {
4643+
GTEST_SKIP();
4644+
}
4645+
4646+
initialize();
4647+
4648+
int times_shed_load_goaway_invoked = 0;
4649+
EXPECT_CALL(server_->server_go_away_on_dispatch, shouldShedLoad())
4650+
.WillRepeatedly(Invoke([&times_shed_load_goaway_invoked]() {
4651+
++times_shed_load_goaway_invoked;
4652+
return false;
4653+
}));
4654+
int times_shed_load_goaway_and_close_invoked = 0;
4655+
EXPECT_CALL(server_->server_go_away_and_close_on_dispatch, shouldShedLoad())
4656+
.WillRepeatedly(Invoke([&times_shed_load_goaway_and_close_invoked]() {
4657+
++times_shed_load_goaway_and_close_invoked;
4658+
return false;
4659+
}));
4660+
4661+
// Drive new streams to be created within a single server dispatch.
4662+
const int num_streams_to_create = 20;
4663+
TestRequestHeaderMapImpl request_headers;
4664+
HttpTestUtility::addDefaultHeaders(request_headers);
4665+
4666+
std::vector<RequestEncoder*> request_encoders;
4667+
std::vector<ResponseEncoder*> response_encoders;
4668+
request_encoders.reserve(num_streams_to_create);
4669+
response_encoders.reserve(num_streams_to_create);
4670+
for (int i = 0; i < num_streams_to_create; ++i) {
4671+
request_encoders.push_back(&client_->newStream(response_decoder_));
4672+
EXPECT_CALL(server_callbacks_, newStream(_, _))
4673+
.WillRepeatedly(Invoke([&](ResponseEncoder& encoder, bool) -> RequestDecoder& {
4674+
response_encoders.push_back(&encoder);
4675+
encoder.getStream().addCallbacks(server_stream_callbacks_);
4676+
return request_decoder_;
4677+
}));
4678+
4679+
EXPECT_TRUE(request_encoders[i]->encodeHeaders(request_headers, true).ok());
4680+
}
4681+
4682+
// All the newly created streams are queued in the connection buffer.
4683+
EXPECT_CALL(request_decoder_, decodeHeaders_(_, true)).Times(num_streams_to_create);
4684+
driveToCompletion();
4685+
EXPECT_EQ(1, times_shed_load_goaway_invoked);
4686+
EXPECT_EQ(1, times_shed_load_goaway_and_close_invoked);
4687+
EXPECT_EQ(0, server_stats_store_.counter("http2.goaway_sent").value());
4688+
}
4689+
45944690
TEST_P(Http2CodecImplTest, CheckHeaderPaddedWhitespaceValidation) {
45954691
// Per https://datatracker.ietf.org/doc/html/rfc9113#section-8.2.1,
45964692
// leading & trailing whitespace characters in headers are not valid, but this is a new

test/common/http/http2/codec_impl_test_util.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ class TestServerConnectionImpl : public TestCodecStatsProvider,
9595
: TestCodecStatsProvider(scope),
9696
ServerConnectionImpl(connection, callbacks, http2CodecStats(), random, http2_options,
9797
max_request_headers_kb, max_request_headers_count,
98-
headers_with_underscores_action) {}
98+
headers_with_underscores_action, overload_manager_) {}
9999

100100
http2::adapter::Http2Adapter* adapter() { return adapter_.get(); }
101101
using ServerConnectionImpl::getStream;

0 commit comments

Comments
 (0)