Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,11 @@ message HttpConnectionManager {
// The maximum request headers size for incoming connections. The default max
// is 60K, based on default settings for http codecs. For HTTP1, the current
// limit set by http_parser is 80K. for HTTP2, the default allowed header
// block in nghttp2 is 64K. The max configurable setting is 63K in order to
// block in nghttp2 is 64K. The max configurable setting is 64K in order to
// stay under both codec limits.
// Requests that exceed this size will receive a 431 response.
google.protobuf.UInt32Value max_request_headers_kb = 29
[(validate.rules).uint32.gt = 0, (validate.rules).uint32.lte = 63];
[(validate.rules).uint32.gt = 0, (validate.rules).uint32.lte = 64];

// The idle timeout for connections managed by the connection manager. The
// idle timeout is defined as the period in which there are no active
Expand Down
2 changes: 1 addition & 1 deletion docs/root/configuration/http_conn_man/stats.rst
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ All http2 statistics are rooted at *http2.*
:header: Name, Type, Description
:widths: 1, 1, 2

header_overflow, Counter, Total number of connections reset due to the headers being larger than `Envoy::Http::Http2::ConnectionImpl::StreamImpl::MAX_HEADER_SIZE` (63k)
header_overflow, Counter, Total number of connections reset due to the headers being larger than the :ref:`configured value <envoy_api_field_config.filter.network.http_connection_manager.v2.HttpConnectionManager.max_request_headers_kb>`.
headers_cb_no_stream, Counter, Total number of errors where a header callback is called without an associated stream. This tracks an unexpected occurrence due to an as yet undiagnosed bug
rx_messaging_error, Counter, Total number of invalid received frames that violated `section 8 <https://tools.ietf.org/html/rfc7540#section-8>`_ of the HTTP/2 spec. This will result in a *tx_reset*
rx_reset, Counter, Total number of reset stream frames received by Envoy
Expand Down
2 changes: 1 addition & 1 deletion include/envoy/http/codec.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace Envoy {
namespace Http {

// Legacy default value of 60K is safely under both codec default limits.
static const uint32_t DEFAULT_MAX_REQUEST_HEADERS_SIZE_KB = 60;
static const uint32_t DEFAULT_MAX_REQUEST_HEADERS_KB = 60;

class Stream;

Expand Down
3 changes: 2 additions & 1 deletion source/common/http/codec_client.cc
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,8 @@ CodecClientProd::CodecClientProd(Type type, Network::ClientConnectionPtr&& conne
}
case Type::HTTP2: {
codec_ = std::make_unique<Http2::ClientConnectionImpl>(
*connection_, *this, host->cluster().statsScope(), host->cluster().http2Settings());
*connection_, *this, host->cluster().statsScope(), host->cluster().http2Settings(),
Http::DEFAULT_MAX_REQUEST_HEADERS_KB);
break;
}
}
Expand Down
2 changes: 1 addition & 1 deletion source/common/http/conn_manager_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ class ConnectionManagerConfig {
/**
* @return maximum request headers size the connection manager will accept.
*/
virtual uint32_t maxRequestHeadersSizeKb() const PURE;
virtual uint32_t maxRequestHeadersKb() const PURE;

/**
* @return per-stream idle timeout for incoming connection manager connections. Zero indicates a
Expand Down
5 changes: 2 additions & 3 deletions source/common/http/conn_manager_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -617,9 +617,8 @@ void ConnectionManagerImpl::ActiveStream::decodeHeaders(HeaderMapPtr&& headers,
}
}

ASSERT(connection_manager_.config_.maxRequestHeadersSizeKb() > 0);
if (request_headers_->byteSize() >
(connection_manager_.config_.maxRequestHeadersSizeKb() * 1024)) {
ASSERT(connection_manager_.config_.maxRequestHeadersKb() > 0);
if (request_headers_->byteSize() > (connection_manager_.config_.maxRequestHeadersKb() * 1024)) {
sendLocalReply(Grpc::Common::hasGrpcContentType(*request_headers_),
Code::RequestHeaderFieldsTooLarge, "", nullptr, is_head_request_, absl::nullopt);
return;
Expand Down
14 changes: 6 additions & 8 deletions source/common/http/conn_manager_utility.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,13 @@ std::string ConnectionManagerUtility::determineNextProtocol(Network::Connection&
return "";
}

ServerConnectionPtr ConnectionManagerUtility::autoCreateCodec(Network::Connection& connection,
const Buffer::Instance& data,
ServerConnectionCallbacks& callbacks,
Stats::Scope& scope,
const Http1Settings& http1_settings,
const Http2Settings& http2_settings) {
ServerConnectionPtr ConnectionManagerUtility::autoCreateCodec(
Network::Connection& connection, const Buffer::Instance& data,
ServerConnectionCallbacks& callbacks, Stats::Scope& scope, const Http1Settings& http1_settings,
const Http2Settings& http2_settings, const uint32_t max_request_headers_kb) {
if (determineNextProtocol(connection, data) == Http2::ALPN_STRING) {
return ServerConnectionPtr{
new Http2::ServerConnectionImpl(connection, callbacks, scope, http2_settings)};
return ServerConnectionPtr{new Http2::ServerConnectionImpl(
connection, callbacks, scope, http2_settings, max_request_headers_kb)};
} else {
return ServerConnectionPtr{
new Http1::ServerConnectionImpl(connection, callbacks, http1_settings)};
Expand Down
3 changes: 2 additions & 1 deletion source/common/http/conn_manager_utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ class ConnectionManagerUtility {
static ServerConnectionPtr
autoCreateCodec(Network::Connection& connection, const Buffer::Instance& data,
ServerConnectionCallbacks& callbacks, Stats::Scope& scope,
const Http1Settings& http1_settings, const Http2Settings& http2_settings);
const Http1Settings& http1_settings, const Http2Settings& http2_settings,
const uint32_t max_request_headers_kb);

/**
* Mutates request headers in various ways. This functionality is broken out because of its
Expand Down
14 changes: 9 additions & 5 deletions source/common/http/http2/codec_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,7 @@ int ConnectionImpl::saveHeader(const nghttp2_frame* frame, HeaderString&& name,
}

stream->saveHeader(std::move(name), std::move(value));
if (stream->headers_->byteSize() > StreamImpl::MAX_HEADER_SIZE) {
if (stream->headers_->byteSize() > max_request_headers_kb_ * 1024) {
// This will cause the library to reset/close the stream.
stats_.header_overflow_.inc();
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
Expand Down Expand Up @@ -877,8 +877,10 @@ ConnectionImpl::ClientHttp2Options::ClientHttp2Options(const Http2Settings& http

ClientConnectionImpl::ClientConnectionImpl(Network::Connection& connection,
Http::ConnectionCallbacks& callbacks,
Stats::Scope& stats, const Http2Settings& http2_settings)
: ConnectionImpl(connection, stats, http2_settings), callbacks_(callbacks) {
Stats::Scope& stats, const Http2Settings& http2_settings,
const uint32_t max_request_headers_kb)
: ConnectionImpl(connection, stats, http2_settings, max_request_headers_kb),
callbacks_(callbacks) {
ClientHttp2Options client_http2_options(http2_settings);
nghttp2_session_client_new2(&session_, http2_callbacks_.callbacks(), base(),
client_http2_options.options());
Expand Down Expand Up @@ -924,8 +926,10 @@ int ClientConnectionImpl::onHeader(const nghttp2_frame* frame, HeaderString&& na

ServerConnectionImpl::ServerConnectionImpl(Network::Connection& connection,
Http::ServerConnectionCallbacks& callbacks,
Stats::Scope& scope, const Http2Settings& http2_settings)
: ConnectionImpl(connection, scope, http2_settings), callbacks_(callbacks) {
Stats::Scope& scope, const Http2Settings& http2_settings,
const uint32_t max_request_headers_kb)
: ConnectionImpl(connection, scope, http2_settings, max_request_headers_kb),
callbacks_(callbacks) {
Http2Options http2_options(http2_settings);
nghttp2_session_server_new2(&session_, http2_callbacks_.callbacks(), base(),
http2_options.options());
Expand Down
16 changes: 7 additions & 9 deletions source/common/http/http2/codec_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,9 @@ class Utility {
class ConnectionImpl : public virtual Connection, protected Logger::Loggable<Logger::Id::http2> {
public:
ConnectionImpl(Network::Connection& connection, Stats::Scope& stats,
const Http2Settings& http2_settings)
const Http2Settings& http2_settings, const uint32_t max_request_headers_kb)
: stats_{ALL_HTTP2_CODEC_STATS(POOL_COUNTER_PREFIX(stats, "http2."))},
connection_(connection),
connection_(connection), max_request_headers_kb_(max_request_headers_kb),
per_stream_buffer_limit_(http2_settings.initial_stream_window_size_), dispatching_(false),
raised_goaway_(false), pending_deferred_reset_(false) {}

Expand Down Expand Up @@ -185,11 +185,6 @@ class ConnectionImpl : public virtual Connection, protected Logger::Loggable<Log
void pendingSendBufferHighWatermark();
void pendingSendBufferLowWatermark();

// Max header size of 63K. This is arbitrary but makes it easier to test since nghttp2 doesn't
// appear to transmit headers greater than approximately 64K (NGHTTP2_MAX_HEADERSLEN) for
// reasons I don't fully understand.
static const uint64_t MAX_HEADER_SIZE = 63 * 1024;

// Does any necessary WebSocket/Upgrade conversion, then passes the headers
// to the decoder_.
void decodeHeaders();
Expand Down Expand Up @@ -291,6 +286,7 @@ class ConnectionImpl : public virtual Connection, protected Logger::Loggable<Log
nghttp2_session* session_{};
CodecStats stats_;
Network::Connection& connection_;
const uint32_t max_request_headers_kb_;
uint32_t per_stream_buffer_limit_;
bool allow_metadata_;

Expand Down Expand Up @@ -319,7 +315,8 @@ class ConnectionImpl : public virtual Connection, protected Logger::Loggable<Log
class ClientConnectionImpl : public ClientConnection, public ConnectionImpl {
public:
ClientConnectionImpl(Network::Connection& connection, ConnectionCallbacks& callbacks,
Stats::Scope& stats, const Http2Settings& http2_settings);
Stats::Scope& stats, const Http2Settings& http2_settings,
const uint32_t max_request_headers_kb);

// Http::ClientConnection
Http::StreamEncoder& newStream(StreamDecoder& response_decoder) override;
Expand All @@ -339,7 +336,8 @@ class ClientConnectionImpl : public ClientConnection, public ConnectionImpl {
class ServerConnectionImpl : public ServerConnection, public ConnectionImpl {
public:
ServerConnectionImpl(Network::Connection& connection, ServerConnectionCallbacks& callbacks,
Stats::Scope& scope, const Http2Settings& http2_settings);
Stats::Scope& scope, const Http2Settings& http2_settings,
const uint32_t max_request_headers_kb);

private:
// ConnectionImpl
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,8 @@ HttpConnectionManagerConfig::HttpConnectionManagerConfig(
route_config_provider_manager_(route_config_provider_manager),
http2_settings_(Http::Utility::parseHttp2Settings(config.http2_protocol_options())),
http1_settings_(Http::Utility::parseHttp1Settings(config.http_protocol_options())),
max_request_headers_size_kb_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(
config, max_request_headers_kb, Http::DEFAULT_MAX_REQUEST_HEADERS_SIZE_KB)),
max_request_headers_kb_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(
config, max_request_headers_kb, Http::DEFAULT_MAX_REQUEST_HEADERS_KB)),
idle_timeout_(PROTOBUF_GET_OPTIONAL_MS(config, idle_timeout)),
stream_idle_timeout_(
PROTOBUF_GET_MS_OR_DEFAULT(config, stream_idle_timeout, StreamIdleTimeoutMs)),
Expand Down Expand Up @@ -326,10 +326,11 @@ HttpConnectionManagerConfig::createCodec(Network::Connection& connection,
new Http::Http1::ServerConnectionImpl(connection, callbacks, http1_settings_)};
case CodecType::HTTP2:
return Http::ServerConnectionPtr{new Http::Http2::ServerConnectionImpl(
connection, callbacks, context_.scope(), http2_settings_)};
connection, callbacks, context_.scope(), http2_settings_, maxRequestHeadersKb())};
case CodecType::AUTO:
return Http::ConnectionManagerUtility::autoCreateCodec(
connection, data, callbacks, context_.scope(), http1_settings_, http2_settings_);
return Http::ConnectionManagerUtility::autoCreateCodec(connection, data, callbacks,
context_.scope(), http1_settings_,
http2_settings_, maxRequestHeadersKb());
}

NOT_REACHED_GCOVR_EXCL_LINE;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ class HttpConnectionManagerConfig : Logger::Loggable<Logger::Id::config>,
FilterChainFactory& filterFactory() override { return *this; }
bool reverseEncodeOrder() override { return reverse_encode_order_; }
bool generateRequestId() override { return generate_request_id_; }
uint32_t maxRequestHeadersSizeKb() const override { return max_request_headers_size_kb_; }
uint32_t maxRequestHeadersKb() const override { return max_request_headers_kb_; }
absl::optional<std::chrono::milliseconds> idleTimeout() const override { return idle_timeout_; }
std::chrono::milliseconds streamIdleTimeout() const override { return stream_idle_timeout_; }
std::chrono::milliseconds requestTimeout() const override { return request_timeout_; }
Expand Down Expand Up @@ -158,7 +158,7 @@ class HttpConnectionManagerConfig : Logger::Loggable<Logger::Id::config>,
std::string server_name_;
Http::TracingConnectionManagerConfigPtr tracing_config_;
absl::optional<std::string> user_agent_;
uint32_t max_request_headers_size_kb_;
const uint32_t max_request_headers_kb_;
absl::optional<std::chrono::milliseconds> idle_timeout_;
std::chrono::milliseconds stream_idle_timeout_;
std::chrono::milliseconds request_timeout_;
Expand Down
3 changes: 2 additions & 1 deletion source/server/http/admin.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1106,7 +1106,8 @@ Http::ServerConnectionPtr AdminImpl::createCodec(Network::Connection& connection
const Buffer::Instance& data,
Http::ServerConnectionCallbacks& callbacks) {
return Http::ConnectionManagerUtility::autoCreateCodec(
connection, data, callbacks, server_.stats(), Http::Http1Settings(), Http::Http2Settings());
connection, data, callbacks, server_.stats(), Http::Http1Settings(), Http::Http2Settings(),
maxRequestHeadersKb());
}

bool AdminImpl::createNetworkFilterChain(Network::Connection& connection,
Expand Down
4 changes: 2 additions & 2 deletions source/server/http/admin.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ class AdminImpl : public Admin,
bool reverseEncodeOrder() override { return false; }
bool generateRequestId() override { return false; }
absl::optional<std::chrono::milliseconds> idleTimeout() const override { return idle_timeout_; }
uint32_t maxRequestHeadersSizeKb() const override { return max_request_headers_size_kb_; }
uint32_t maxRequestHeadersKb() const override { return max_request_headers_kb_; }
std::chrono::milliseconds streamIdleTimeout() const override { return {}; }
std::chrono::milliseconds requestTimeout() const override { return {}; }
std::chrono::milliseconds delayedCloseTimeout() const override { return {}; }
Expand Down Expand Up @@ -305,7 +305,7 @@ class AdminImpl : public Admin,
Http::ConnectionManagerTracingStats tracing_stats_;
NullRouteConfigProvider route_config_provider_;
std::list<UrlHandler> handlers_;
uint32_t max_request_headers_size_kb_{Http::DEFAULT_MAX_REQUEST_HEADERS_SIZE_KB};
const uint32_t max_request_headers_kb_{Http::DEFAULT_MAX_REQUEST_HEADERS_KB};
absl::optional<std::chrono::milliseconds> idle_timeout_;
absl::optional<std::string> user_agent_;
Http::SlowDateProviderImpl date_provider_;
Expand Down
4 changes: 2 additions & 2 deletions test/common/http/conn_manager_impl_fuzz_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ class FuzzConfig : public ConnectionManagerConfig {
FilterChainFactory& filterFactory() override { return filter_factory_; }
bool reverseEncodeOrder() override { return true; }
bool generateRequestId() override { return true; }
uint32_t maxRequestHeadersSizeKb() const override { return max_request_headers_size_kb_; }
uint32_t maxRequestHeadersKb() const override { return max_request_headers_kb_; }
absl::optional<std::chrono::milliseconds> idleTimeout() const override { return idle_timeout_; }
std::chrono::milliseconds streamIdleTimeout() const override { return stream_idle_timeout_; }
std::chrono::milliseconds requestTimeout() const override { return request_timeout_; }
Expand Down Expand Up @@ -132,7 +132,7 @@ class FuzzConfig : public ConnectionManagerConfig {
ConnectionManagerStats stats_;
ConnectionManagerTracingStats tracing_stats_;
ConnectionManagerListenerStats listener_stats_;
uint32_t max_request_headers_size_kb_{Http::DEFAULT_MAX_REQUEST_HEADERS_SIZE_KB};
uint32_t max_request_headers_kb_{Http::DEFAULT_MAX_REQUEST_HEADERS_KB};
absl::optional<std::chrono::milliseconds> idle_timeout_;
std::chrono::milliseconds stream_idle_timeout_{};
std::chrono::milliseconds request_timeout_{};
Expand Down
6 changes: 3 additions & 3 deletions test/common/http/conn_manager_impl_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ class HttpConnectionManagerImplTest : public TestBase, public ConnectionManagerC
FilterChainFactory& filterFactory() override { return filter_factory_; }
bool reverseEncodeOrder() override { return reverse_encode_order_; }
bool generateRequestId() override { return true; }
uint32_t maxRequestHeadersSizeKb() const override { return max_request_headers_size_kb_; }
uint32_t maxRequestHeadersKb() const override { return max_request_headers_kb_; }
absl::optional<std::chrono::milliseconds> idleTimeout() const override { return idle_timeout_; }
std::chrono::milliseconds streamIdleTimeout() const override { return stream_idle_timeout_; }
std::chrono::milliseconds requestTimeout() const override { return request_timeout_; }
Expand Down Expand Up @@ -281,7 +281,7 @@ class HttpConnectionManagerImplTest : public TestBase, public ConnectionManagerC
Http::ForwardClientCertType forward_client_cert_{Http::ForwardClientCertType::Sanitize};
std::vector<Http::ClientCertDetailsType> set_current_client_cert_details_;
absl::optional<std::string> user_agent_;
uint32_t max_request_headers_size_kb_{Http::DEFAULT_MAX_REQUEST_HEADERS_SIZE_KB};
uint32_t max_request_headers_kb_{Http::DEFAULT_MAX_REQUEST_HEADERS_KB};
absl::optional<std::chrono::milliseconds> idle_timeout_;
std::chrono::milliseconds stream_idle_timeout_{};
std::chrono::milliseconds request_timeout_{};
Expand Down Expand Up @@ -3624,7 +3624,7 @@ TEST_F(HttpConnectionManagerImplTest, OverlyLongHeadersRejected) {
}

TEST_F(HttpConnectionManagerImplTest, OverlyLongHeadersAcceptedIfConfigured) {
max_request_headers_size_kb_ = 62;
max_request_headers_kb_ = 62;
setup(false, "");

EXPECT_CALL(*codec_, dispatch(_)).Times(1).WillOnce(Invoke([&](Buffer::Instance&) -> void {
Expand Down
2 changes: 1 addition & 1 deletion test/common/http/conn_manager_utility_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class MockConnectionManagerConfig : public ConnectionManagerConfig {
MOCK_METHOD0(filterFactory, FilterChainFactory&());
MOCK_METHOD0(reverseEncodeOrder, bool());
MOCK_METHOD0(generateRequestId, bool());
MOCK_CONST_METHOD0(maxRequestHeadersSizeKb, uint32_t());
MOCK_CONST_METHOD0(maxRequestHeadersKb, uint32_t());
MOCK_CONST_METHOD0(idleTimeout, absl::optional<std::chrono::milliseconds>());
MOCK_CONST_METHOD0(streamIdleTimeout, std::chrono::milliseconds());
MOCK_CONST_METHOD0(requestTimeout, std::chrono::milliseconds());
Expand Down
11 changes: 11 additions & 0 deletions test/common/http/http2/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ load(
"//bazel:envoy_build_system.bzl",
"envoy_cc_fuzz_test",
"envoy_cc_test",
"envoy_cc_test_library",
"envoy_package",
"envoy_proto_library",
)
Expand All @@ -22,6 +23,7 @@ envoy_cc_fuzz_test(
corpus = "codec_impl_corpus",
deps = [
":codec_impl_fuzz_proto_cc",
":codec_impl_test_util",
"//source/common/http:header_map_lib",
"//source/common/http/http2:codec_lib",
"//test/fuzz:utility_lib",
Expand All @@ -35,6 +37,7 @@ envoy_cc_test(
srcs = ["codec_impl_test.cc"],
shard_count = 5,
deps = [
":codec_impl_test_util",
"//source/common/event:dispatcher_lib",
"//source/common/http:exception_lib",
"//source/common/http:header_map_lib",
Expand All @@ -48,6 +51,14 @@ envoy_cc_test(
],
)

envoy_cc_test_library(
name = "codec_impl_test_util",
hdrs = ["codec_impl_test_util.h"],
deps = [
"//source/common/http/http2:codec_lib",
],
)

envoy_cc_test(
name = "conn_pool_test",
srcs = ["conn_pool_test.cc"],
Expand Down
Loading