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
1 change: 1 addition & 0 deletions docs/root/version_history/current.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Minor Behavior Changes
`envoy.reloadable_features.http_strip_fragment_from_path_unsafe_if_disabled`. This runtime guard must only be set
to false when existing non-compliant traffic relies on #fragment in URI. When this option is enabled, Envoy request
authorization extensions may be bypassed. This override and its associated behavior will be decommissioned after the standard deprecation period.
* http: when envoy run out of ``max_requests_per_connection``, it will send an HTTP/2 "shutdown nofitication" (GOAWAY frame with max stream ID) and go to a default grace period of 5000 milliseconds (5 seconds) if ``drain_timeout`` is not specified. During this grace period, envoy will continue to accept new streams. After the grace period, a final GOAWAY is sent and envoy will start refusing new streams. However before bugfix, during the grace period, every time a new stream is received, old envoy will always send a "shutdown notification" and restart drain again which actually causes the grace period to be extended and is no longer equal to ``drain_timeout``.

Bug Fixes
---------
Expand Down
2 changes: 1 addition & 1 deletion source/common/http/conn_manager_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ RequestDecoder& ConnectionManagerImpl::newStream(ResponseEncoder& response_encod
new_stream->state_.saw_connection_close_ = true;
// Prevent erroneous debug log of closing due to incoming connection close header.
drain_state_ = DrainState::Closing;
} else {
} else if (drain_state_ == DrainState::NotDraining) {
startDrainSequence();
}
ENVOY_CONN_LOG(debug, "max requests per connection reached", read_callbacks_->connection());
Expand Down
29 changes: 29 additions & 0 deletions test/common/http/conn_manager_impl_test_2.cc
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,35 @@ TEST_F(HttpConnectionManagerImplTest, ConnectionDurationNoCodec) {
EXPECT_EQ(1U, stats_.named_.downstream_cx_max_duration_reached_.value());
}

// Regression test for https://github.com/envoyproxy/envoy/issues/19045
TEST_F(HttpConnectionManagerImplTest, MaxRequests) {
max_requests_per_connection_ = 1;
codec_->protocol_ = Protocol::Http2;
setup(false, "");

Event::MockTimer* drain_timer = setUpTimer();
EXPECT_CALL(*drain_timer, enableTimer(_, _));

EXPECT_CALL(*codec_, dispatch(_)).WillRepeatedly(Invoke([&](Buffer::Instance&) -> Http::Status {
conn_manager_->newStream(response_encoder_);
return Http::okStatus();
}));

EXPECT_CALL(*codec_, goAway());
EXPECT_CALL(*codec_, shutdownNotice());
EXPECT_CALL(*drain_timer, disableTimer());

// Kick off two requests.
Buffer::OwnedImpl fake_input("hello");
conn_manager_->onData(fake_input, false);
conn_manager_->onData(fake_input, false);
drain_timer->invokeCallback();

EXPECT_EQ(2U, stats_.named_.downstream_cx_max_requests_reached_.value());

conn_manager_->onEvent(Network::ConnectionEvent::RemoteClose);
}

TEST_F(HttpConnectionManagerImplTest, ConnectionDuration) {
max_connection_duration_ = (std::chrono::milliseconds(10));
Event::MockTimer* connection_duration_timer = setUpTimer();
Expand Down
5 changes: 3 additions & 2 deletions test/common/http/conn_manager_impl_test_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +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; }
uint64_t maxRequestsPerConnection() const override { return max_requests_per_connection_; }

Envoy::Event::SimulatedTimeSystem test_time_;
NiceMock<Router::MockRouteConfigProvider> route_config_provider_;
Expand Down Expand Up @@ -177,6 +177,7 @@ class HttpConnectionManagerImplTest : public testing::Test, public ConnectionMan
absl::optional<std::string> user_agent_;
uint32_t max_request_headers_kb_{Http::DEFAULT_MAX_REQUEST_HEADERS_KB};
uint32_t max_request_headers_count_{Http::DEFAULT_MAX_HEADERS_COUNT};
uint64_t max_requests_per_connection_{};
absl::optional<std::chrono::milliseconds> idle_timeout_;
absl::optional<std::chrono::milliseconds> max_connection_duration_;
std::chrono::milliseconds stream_idle_timeout_{};
Expand Down Expand Up @@ -224,7 +225,7 @@ class HttpConnectionManagerImplTest : public testing::Test, public ConnectionMan
PathWithEscapedSlashesAction path_with_escaped_slashes_action_{
envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager::
KEEP_UNCHANGED};
};
};

} // namespace Http
} // namespace Envoy