Skip to content

Commit d20dbc4

Browse files
authored
http: raise max_request_headers_kb limit to 8192 KiB (8MiB) from 96 KiB (#15921)
Commit Message: Raise max configurable max_request_headers_kb limit to 8192 KiB (8MiB) from 96 KiB in http connection manager protobuf. Additional Description: Added/Updated relevant unit tests and integration tests. This change will allow increasing max_request_headers_kb to 8MiB from http connection manager's configuration. The change is mainly updating the limit in a validation check in the protobuf. Also, the old (merged) PR #5859 is read, no nghttp2 library-related issues are observed on raising the max_request_headers_kb beyond 96 KiB. Risk Level: Low Testing: Unit, Integration, Manual Docs Changes: Inline in proto file. Signed-off-by: Anirudha Singh <[email protected]>
1 parent 25d9d30 commit d20dbc4

File tree

11 files changed

+84
-31
lines changed

11 files changed

+84
-31
lines changed

api/envoy/config/filter/network/http_connection_manager/v2/http_connection_manager.proto

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -299,10 +299,8 @@ message HttpConnectionManager {
299299
// The maximum request headers size for incoming connections.
300300
// If unconfigured, the default max request headers allowed is 60 KiB.
301301
// Requests that exceed this limit will receive a 431 response.
302-
// The max configurable limit is 96 KiB, based on current implementation
303-
// constraints.
304302
google.protobuf.UInt32Value max_request_headers_kb = 29
305-
[(validate.rules).uint32 = {lte: 96 gt: 0}];
303+
[(validate.rules).uint32 = {lte: 8192 gt: 0}];
306304

307305
// The idle timeout for connections managed by the connection manager. The
308306
// idle timeout is defined as the period in which there are no active

api/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -343,10 +343,8 @@ message HttpConnectionManager {
343343
// The maximum request headers size for incoming connections.
344344
// If unconfigured, the default max request headers allowed is 60 KiB.
345345
// Requests that exceed this limit will receive a 431 response.
346-
// The max configurable limit is 96 KiB, based on current implementation
347-
// constraints.
348346
google.protobuf.UInt32Value max_request_headers_kb = 29
349-
[(validate.rules).uint32 = {lte: 96 gt: 0}];
347+
[(validate.rules).uint32 = {lte: 8192 gt: 0}];
350348

351349
// The stream idle timeout for connections managed by the connection manager.
352350
// If not specified, this defaults to 5 minutes. The default value was selected

api/envoy/extensions/filters/network/http_connection_manager/v4alpha/http_connection_manager.proto

Lines changed: 1 addition & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/root/version_history/current.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Bug Fixes
1919
---------
2020
*Changes expected to improve the state of the world and are unlikely to have negative effects*
2121

22+
* http: raise max configurable max_request_headers_kb limit to 8192 KiB (8MiB) from 96 KiB in http connection manager.
2223
* validation: fix an issue that causes TAP sockets to panic during config validation mode.
2324
* xray: fix the default sampling 'rate' for AWS X-Ray tracer extension to be 5% as opposed to 50%.
2425
* zipkin: fix timestamp serializaiton in annotations. A prior bug fix exposed an issue with timestamps being serialized as strings.

generated_api_shadow/envoy/config/filter/network/http_connection_manager/v2/http_connection_manager.proto

Lines changed: 1 addition & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

generated_api_shadow/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto

Lines changed: 1 addition & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

generated_api_shadow/envoy/extensions/filters/network/http_connection_manager/v4alpha/http_connection_manager.proto

Lines changed: 1 addition & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/common/http/http1/codec_impl_test.cc

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,15 @@ std::string createHeaderFragment(int num_headers) {
4848
return headers;
4949
}
5050

51+
std::string createLargeHeaderFragment(int num_headers) {
52+
// Create a header field with num_headers headers with each header of size ~64 KiB.
53+
std::string headers;
54+
for (int i = 0; i < num_headers; i++) {
55+
headers += "header" + std::to_string(i) + ": " + std::string(64 * 1024, 'q') + "\r\n";
56+
}
57+
return headers;
58+
}
59+
5160
Buffer::OwnedImpl createBufferWithNByteSlices(absl::string_view input, size_t max_slice_size) {
5261
Buffer::OwnedImpl buffer;
5362
for (size_t offset = 0; offset < input.size(); offset += max_slice_size) {
@@ -2858,6 +2867,12 @@ TEST_F(Http1ServerConnectionImplTest, LargeRequestHeadersRejected) {
28582867
testRequestHeadersExceedLimit(long_string, "headers size exceeds limit", "");
28592868
}
28602869

2870+
TEST_F(Http1ServerConnectionImplTest, LargeRequestHeadersRejectedBeyondMaxConfigurable) {
2871+
max_request_headers_kb_ = 8192;
2872+
std::string long_string = "big: " + std::string(8193 * 1024, 'q') + "\r\n";
2873+
testRequestHeadersExceedLimit(long_string, "headers size exceeds limit", "");
2874+
}
2875+
28612876
// Tests that the default limit for the number of request headers is 100.
28622877
TEST_F(Http1ServerConnectionImplTest, ManyRequestHeadersRejected) {
28632878
// Send a request with 101 headers.
@@ -2894,6 +2909,36 @@ TEST_F(Http1ServerConnectionImplTest, LargeRequestHeadersSplitRejected) {
28942909
EXPECT_EQ("http1.headers_too_large", response_encoder->getStream().responseDetails());
28952910
}
28962911

2912+
TEST_F(Http1ServerConnectionImplTest, LargeRequestHeadersSplitRejectedMaxConfigurable) {
2913+
max_request_headers_kb_ = 8192;
2914+
max_request_headers_count_ = 150;
2915+
initialize();
2916+
2917+
std::string exception_reason;
2918+
NiceMock<MockRequestDecoder> decoder;
2919+
Http::ResponseEncoder* response_encoder = nullptr;
2920+
EXPECT_CALL(callbacks_, newStream(_, _))
2921+
.WillOnce(Invoke([&](ResponseEncoder& encoder, bool) -> RequestDecoder& {
2922+
response_encoder = &encoder;
2923+
return decoder;
2924+
}));
2925+
Buffer::OwnedImpl buffer("GET / HTTP/1.1\r\n");
2926+
auto status = codec_->dispatch(buffer);
2927+
2928+
std::string long_string = std::string(64 * 1024, 'q');
2929+
for (int i = 0; i < 127; i++) {
2930+
buffer = Buffer::OwnedImpl(fmt::format("big: {}\r\n", long_string));
2931+
status = codec_->dispatch(buffer);
2932+
}
2933+
// the 128th 64kb header should induce overflow
2934+
buffer = Buffer::OwnedImpl(fmt::format("big: {}\r\n", long_string));
2935+
EXPECT_CALL(decoder, sendLocalReply(_, _, _, _, _, _));
2936+
status = codec_->dispatch(buffer);
2937+
EXPECT_TRUE(isCodecProtocolError(status));
2938+
EXPECT_EQ(status.message(), "headers size exceeds limit");
2939+
EXPECT_EQ("http1.headers_too_large", response_encoder->getStream().responseDetails());
2940+
}
2941+
28972942
// Tests that the 101th request header causes overflow with the default max number of request
28982943
// headers.
28992944
TEST_F(Http1ServerConnectionImplTest, ManyRequestHeadersSplitRejected) {
@@ -2924,14 +2969,14 @@ TEST_F(Http1ServerConnectionImplTest, ManyRequestHeadersSplitRejected) {
29242969
}
29252970

29262971
TEST_F(Http1ServerConnectionImplTest, LargeRequestHeadersAccepted) {
2927-
max_request_headers_kb_ = 65;
2928-
std::string long_string = "big: " + std::string(64 * 1024, 'q') + "\r\n";
2972+
max_request_headers_kb_ = 4096;
2973+
std::string long_string = "big: " + std::string(1024 * 1024, 'q') + "\r\n";
29292974
testRequestHeadersAccepted(long_string);
29302975
}
29312976

29322977
TEST_F(Http1ServerConnectionImplTest, LargeRequestHeadersAcceptedMaxConfigurable) {
2933-
max_request_headers_kb_ = 96;
2934-
std::string long_string = "big: " + std::string(95 * 1024, 'q') + "\r\n";
2978+
max_request_headers_kb_ = 8192;
2979+
std::string long_string = "big: " + std::string(8191 * 1024, 'q') + "\r\n";
29352980
testRequestHeadersAccepted(long_string);
29362981
}
29372982

@@ -2942,6 +2987,12 @@ TEST_F(Http1ServerConnectionImplTest, ManyRequestHeadersAccepted) {
29422987
testRequestHeadersAccepted(createHeaderFragment(150));
29432988
}
29442989

2990+
TEST_F(Http1ServerConnectionImplTest, ManyLargeRequestHeadersAccepted) {
2991+
max_request_headers_kb_ = 8192;
2992+
// Create a request with 64 headers, each header of size ~64 KiB. Total size ~4MB.
2993+
testRequestHeadersAccepted(createLargeHeaderFragment(64));
2994+
}
2995+
29452996
// Tests that incomplete response headers of 80 kB header value fails.
29462997
TEST_F(Http1ClientConnectionImplTest, ResponseHeadersWithLargeValueRejected) {
29472998
initialize();

test/common/http/http2/codec_impl_test.cc

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2176,15 +2176,14 @@ TEST_P(Http2CodecImplTest, ManyLargeRequestHeadersUnderPerHeaderLimit) {
21762176
}
21772177

21782178
TEST_P(Http2CodecImplTest, LargeRequestHeadersAtMaxConfigurable) {
2179-
// Raising the limit past this triggers some unexpected nghttp2 error.
2180-
// Further debugging required to increase past ~96 KiB.
2181-
max_request_headers_kb_ = 96;
2179+
max_request_headers_kb_ = 8192;
2180+
max_request_headers_count_ = 150;
21822181
initialize();
21832182

21842183
TestRequestHeaderMapImpl request_headers;
21852184
HttpTestUtility::addDefaultHeaders(request_headers);
2186-
std::string long_string = std::string(1024, 'q');
2187-
for (int i = 0; i < 95; i++) {
2185+
std::string long_string = std::string(63 * 1024, 'q');
2186+
for (int i = 0; i < 129; i++) {
21882187
request_headers.addCopy(std::to_string(i), long_string);
21892188
}
21902189

test/extensions/filters/network/http_connection_manager/config_test.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -687,7 +687,7 @@ TEST_F(HttpConnectionManagerConfigTest, MaxRequestHeadersKbConfigured) {
687687
TEST_F(HttpConnectionManagerConfigTest, MaxRequestHeadersKbMaxConfigurable) {
688688
const std::string yaml_string = R"EOF(
689689
stat_prefix: ingress_http
690-
max_request_headers_kb: 96
690+
max_request_headers_kb: 8192
691691
route_config:
692692
name: local_route
693693
http_filters:
@@ -698,7 +698,7 @@ TEST_F(HttpConnectionManagerConfigTest, MaxRequestHeadersKbMaxConfigurable) {
698698
date_provider_, route_config_provider_manager_,
699699
scoped_routes_config_provider_manager_, http_tracer_manager_,
700700
filter_config_provider_manager_);
701-
EXPECT_EQ(96, config.maxRequestHeadersKb());
701+
EXPECT_EQ(8192, config.maxRequestHeadersKb());
702702
}
703703

704704
// Validated that an explicit zero stream idle timeout disables.

0 commit comments

Comments
 (0)