Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
5 changes: 4 additions & 1 deletion api/envoy/api/v2/core/protocol.proto
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ message Http1ProtocolOptions {
bool enable_trailers = 5;
}

// [#next-free-field: 13]
// [#next-free-field: 14]
message Http2ProtocolOptions {
// `Maximum table size <https://httpwg.org/specs/rfc7541.html#rfc.section.4.2>`_
// (in octets) that the encoder is permitted to use for the dynamic HPACK table. Valid values
Expand Down Expand Up @@ -216,6 +216,9 @@ message Http2ProtocolOptions {
//
// See `RFC7540, sec. 8.1 <https://tools.ietf.org/html/rfc7540#section-8.1>`_ for details.
bool stream_error_on_invalid_http_messaging = 12;

// Specify duration to keep alive HTTP/2 stream. If not specified, this value is not set.
Comment thread
htuch marked this conversation as resolved.
Outdated
google.protobuf.Duration max_stream_duration = 13;
}

// [#not-implemented-hide:]
Expand Down
5 changes: 4 additions & 1 deletion api/envoy/config/core/v3/protocol.proto
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ message Http1ProtocolOptions {
bool enable_trailers = 5;
}

// [#next-free-field: 13]
// [#next-free-field: 14]
message Http2ProtocolOptions {
option (udpa.annotations.versioning).previous_message_type =
"envoy.api.v2.core.Http2ProtocolOptions";
Expand Down Expand Up @@ -235,6 +235,9 @@ message Http2ProtocolOptions {
//
// See `RFC7540, sec. 8.1 <https://tools.ietf.org/html/rfc7540#section-8.1>`_ for details.
bool stream_error_on_invalid_http_messaging = 12;

// Specify duration to keep alive HTTP/2 stream. If not specified, this value is not set.
google.protobuf.Duration max_stream_duration = 13;
}

// [#not-implemented-hide:]
Expand Down
5 changes: 4 additions & 1 deletion generated_api_shadow/envoy/api/v2/core/protocol.proto

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

5 changes: 4 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.

5 changes: 5 additions & 0 deletions source/common/http/conn_manager_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,11 @@ class ConnectionManagerConfig {
*/
virtual std::chrono::milliseconds delayedCloseTimeout() const PURE;

/**
* @return maximum duration time to keep alive stream
*/
virtual absl::optional<std::chrono::milliseconds> maxStreamDuration() const PURE;

/**
* @return Router::RouteConfigProvider* the configuration provider used to acquire a route
* config for each request flow. Pointer ownership is _not_ transferred to the caller of
Expand Down
14 changes: 14 additions & 0 deletions source/common/http/conn_manager_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,15 @@ ConnectionManagerImpl::ActiveStream::ActiveStream(ConnectionManagerImpl& connect
request_timer_->enableTimer(request_timeout_ms_, this);
}

if (connection_manager_.config_.maxStreamDuration().has_value()) {
std::chrono::milliseconds max_stream_duration_ms_ =
connection_manager_.config_.maxStreamDuration().value();
Comment thread
Shikugawa marked this conversation as resolved.
Outdated
max_stream_duration_timer_ =
connection_manager.read_callbacks_->connection().dispatcher().createTimer([this]() -> void {
onResetStream(StreamResetReason::ConnectionTermination, absl::string_view());
});
max_stream_duration_timer_->enableTimer(max_stream_duration_ms_, this);
}
stream_info_.setRequestedServerName(
connection_manager_.read_callbacks_->connection().requestedServerName());
}
Expand Down Expand Up @@ -1907,6 +1916,11 @@ bool ConnectionManagerImpl::ActiveStream::handleDataIfStopAll(ActiveStreamFilter
}

void ConnectionManagerImpl::ActiveStream::onResetStream(StreamResetReason, absl::string_view) {
if (max_stream_duration_timer_) {
max_stream_duration_timer_->disableTimer();
max_stream_duration_timer_.reset();
}

// NOTE: This function gets called in all of the following cases:
// 1) We TX an app level reset
// 2) The codec TX a codec level reset
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 @@ -701,6 +701,8 @@ class ConnectionManagerImpl : Logger::Loggable<Logger::Id::http>,
Event::TimerPtr stream_idle_timer_;
// Per-stream request timeout.
Event::TimerPtr request_timer_;
// Per-stream alive duration.
Event::TimerPtr max_stream_duration_timer_;
std::chrono::milliseconds idle_timeout_ms_{};
State state_;
StreamInfo::StreamInfoImpl stream_info_;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ HttpConnectionManagerConfig::HttpConnectionManagerConfig(
idle_timeout_(PROTOBUF_GET_OPTIONAL_MS(config.common_http_protocol_options(), idle_timeout)),
max_connection_duration_(
PROTOBUF_GET_OPTIONAL_MS(config.common_http_protocol_options(), max_connection_duration)),
max_stream_duration_(
PROTOBUF_GET_OPTIONAL_MS(config.http2_protocol_options(), max_stream_duration)),
stream_idle_timeout_(
PROTOBUF_GET_MS_OR_DEFAULT(config, stream_idle_timeout, StreamIdleTimeoutMs)),
request_timeout_(PROTOBUF_GET_MS_OR_DEFAULT(config, request_timeout, RequestTimeoutMs)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ class HttpConnectionManagerConfig : Logger::Loggable<Logger::Id::config>,
}
std::chrono::milliseconds streamIdleTimeout() const override { return stream_idle_timeout_; }
std::chrono::milliseconds requestTimeout() const override { return request_timeout_; }
absl::optional<std::chrono::milliseconds> maxStreamDuration() const override {
return max_stream_duration_;
}
Router::RouteConfigProvider* routeConfigProvider() override {
return route_config_provider_.get();
}
Expand Down Expand Up @@ -185,6 +188,7 @@ class HttpConnectionManagerConfig : Logger::Loggable<Logger::Id::config>,
const uint32_t max_request_headers_count_;
absl::optional<std::chrono::milliseconds> idle_timeout_;
absl::optional<std::chrono::milliseconds> max_connection_duration_;
absl::optional<std::chrono::milliseconds> max_stream_duration_;
std::chrono::milliseconds stream_idle_timeout_;
std::chrono::milliseconds request_timeout_;
Router::RouteConfigProviderSharedPtr route_config_provider_;
Expand Down
3 changes: 3 additions & 0 deletions source/server/http/admin.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ class AdminImpl : public Admin,
std::chrono::milliseconds streamIdleTimeout() const override { return {}; }
std::chrono::milliseconds requestTimeout() const override { return {}; }
std::chrono::milliseconds delayedCloseTimeout() const override { return {}; }
absl::optional<std::chrono::milliseconds> maxStreamDuration() const override {
return absl::make_optional<std::chrono::milliseconds>({});
}
Router::RouteConfigProvider* routeConfigProvider() override { return &route_config_provider_; }
Config::ConfigProvider* scopedRouteConfigProvider() override {
return &scoped_route_config_provider_;
Expand Down
22 changes: 22 additions & 0 deletions test/common/http/conn_manager_impl_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,9 @@ class HttpConnectionManagerImplTest : public testing::Test, public ConnectionMan
std::chrono::milliseconds streamIdleTimeout() const override { return stream_idle_timeout_; }
std::chrono::milliseconds requestTimeout() const override { return request_timeout_; }
std::chrono::milliseconds delayedCloseTimeout() const override { return delayed_close_timeout_; }
absl::optional<std::chrono::milliseconds> maxStreamDuration() const override {
return max_stream_duration_timer_;
}
bool use_srds_{};
Router::RouteConfigProvider* routeConfigProvider() override {
if (use_srds_) {
Expand Down Expand Up @@ -378,6 +381,7 @@ class HttpConnectionManagerImplTest : public testing::Test, public ConnectionMan
std::chrono::milliseconds stream_idle_timeout_{};
std::chrono::milliseconds request_timeout_{};
std::chrono::milliseconds delayed_close_timeout_{};
absl::optional<std::chrono::milliseconds> max_stream_duration_timer_{};
NiceMock<Runtime::MockRandomGenerator> random_;
NiceMock<LocalInfo::MockLocalInfo> local_info_;
NiceMock<Server::Configuration::MockFactoryContext> factory_context_;
Expand Down Expand Up @@ -2398,6 +2402,24 @@ TEST_F(HttpConnectionManagerImplTest, RequestTimeoutIsDisarmedOnConnectionTermin
EXPECT_EQ(0U, stats_.named_.downstream_rq_timeout_.value());
}

TEST_F(HttpConnectionManagerImplTest, StreamAliveDurationExpired) {
max_stream_duration_timer_ = absl::make_optional<std::chrono::milliseconds>(10);
setup(false, "");
Event::MockTimer* duration_timer = setUpTimer();

EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> void {
EXPECT_CALL(*duration_timer, enableTimer(max_stream_duration_timer_.value(), _)).Times(1);
conn_manager_->newStream(response_encoder_);
}));

Buffer::OwnedImpl fake_input("1234");
conn_manager_->onData(fake_input, false); // kick off request

EXPECT_CALL(*duration_timer, disableTimer()).Times(1);
Comment thread
Shikugawa marked this conversation as resolved.
Outdated
Comment thread
Shikugawa marked this conversation as resolved.
Outdated
duration_timer->invokeCallback();
EXPECT_EQ(1U, stats_.named_.downstream_rq_rx_reset_.value());
}

TEST_F(HttpConnectionManagerImplTest, RejectWebSocketOnNonWebSocketRoute) {
setup(false, "");
RequestDecoder* decoder = nullptr;
Expand Down