Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
fdd1442
Configure codec library max request header limits
Feb 4, 2019
932eaf0
Add hd to dictionary
Feb 6, 2019
999b247
Undo http1 codec changes
Feb 6, 2019
6e2e092
set http_parser limit in test
Feb 6, 2019
1ba5e19
Remove http2options for nghttp2 check
Feb 7, 2019
51e8fb7
Fix format
Feb 13, 2019
37d6900
add comment
Feb 14, 2019
8c1be4a
Clean up codec tests and bump limit to 96
Feb 14, 2019
d3e146d
Merge remote-tracking branch 'envoy/master' into maxer
Feb 14, 2019
d14f019
Add protocol tests
Feb 15, 2019
7fae643
Revert "Undo http1 codec changes"
Feb 15, 2019
6c6cd5c
Revert "Revert "Undo http1 codec changes""
Feb 20, 2019
87cd65b
set http parser in conn manager config
Feb 20, 2019
12cb34d
Finalize tests
Feb 20, 2019
2370471
Fix format and rmeove dead tests
Feb 20, 2019
7aba9ea
Move http_parser config to compile-time
Feb 22, 2019
dc0b47e
Remove request header rejected tests
Feb 22, 2019
e241e35
Clean comment
Feb 22, 2019
cff066e
bump up nghttp2 huge
Feb 22, 2019
358fd16
Clean test values for large headers
Feb 22, 2019
bbf0df2
matt comments
Feb 26, 2019
c8b006e
word
Feb 26, 2019
8bb762c
Revert "Remove request header rejected tests"
Feb 26, 2019
3bcfbdf
hard coded check w test
Feb 27, 2019
56644a1
Revert "Revert "Undo http1 codec changes""
Feb 27, 2019
7ece1c7
fix compile
Feb 27, 2019
5210dda
fix format
Feb 27, 2019
0f7911c
fix more compile
Feb 27, 2019
9bce125
fix compile more
Feb 27, 2019
7fb8701
fix bork test
Feb 28, 2019
19d10a2
fix format
Feb 28, 2019
b55c483
pr comments
Feb 28, 2019
6473a65
mattfix
Mar 1, 2019
e084dc7
Fix response side client connection
Mar 1, 2019
c7ec56b
fix format
Mar 1, 2019
f520e89
idk
Mar 1, 2019
4a5d64e
pr comments
Mar 4, 2019
9a20bb5
pr comments
Mar 4, 2019
bfbeed4
fix fuzz test
Mar 4, 2019
e28fcdb
update comments
Mar 4, 2019
be85a3f
Merge remote-tracking branch 'envoy/master' into maxer
Mar 4, 2019
b5ba8ef
pr change
Mar 5, 2019
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
2 changes: 2 additions & 0 deletions source/common/http/http2/codec_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,8 @@ ConnectionImpl::Http2Options::Http2Options(const Http2Settings& http2_settings)
// of kept alive HTTP/2 connections.
nghttp2_option_set_no_closed_streams(options_, 1);
nghttp2_option_set_no_auto_window_update(options_, 1);
nghttp2_option_set_max_send_header_block_length(options_,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this the setting for the size of an individual header frame? I think we want to change the total allowed headers, not the per-frame limits.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC this is not very well named, but I think this does actually set the header send limit, and the library will chunk it into frames using continuation frames if necessary. @auni53 to check me here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be. I'm not seeing any other size-related fields to tweak. I'd checked
https://nghttp2.org/documentation/nghttp2_option_set_max_send_header_block_length.html
"This option sets the maximum length of header block (a set of header fields per one HEADERS frame) to send. "
It was the "per one HEADERS frame" that got me thinking it might be the wrong field.
in code it's used in prep_frame which I thought was after the chunking but I could easily be misremembering. I'm happy to let Auni do the code sleuthing though :-)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh,

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeeee, I'm pretty certain this is the right call. Also because I did this TDD-style and the testing behaviour checks out.

NGHTTP2_MAX_SEND_HEADER_BLOCK_LENGTH * 1024);

if (http2_settings.hpack_table_size_ != NGHTTP2_DEFAULT_HEADER_TABLE_SIZE) {
nghttp2_option_set_max_deflate_dynamic_table_size(options_, http2_settings.hpack_table_size_);
Expand Down
1 change: 1 addition & 0 deletions source/common/http/http2/codec_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const std::string ALPN_STRING = "h2";
// This is not the full client magic, but it's the smallest size that should be able to
// differentiate between HTTP/1 and HTTP/2.
const std::string CLIENT_MAGIC_PREFIX = "PRI * HTTP/2";
static const uint32_t NGHTTP2_MAX_SEND_HEADER_BLOCK_LENGTH = 100;
Comment thread
aunu53 marked this conversation as resolved.
Outdated

/**
* All stats for the HTTP/2 codec. @see stats_macros.h
Expand Down
34 changes: 27 additions & 7 deletions test/common/http/http1/codec_impl_test.cc
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#include <http_parser.h>
Comment thread
aunu53 marked this conversation as resolved.
Outdated

#include <memory>
#include <string>

Expand Down Expand Up @@ -31,6 +33,7 @@ namespace Http1 {
class Http1ServerConnectionImplTest : public TestBase {
public:
void initialize() {
http_parser_set_max_header_size(max_request_headers_kb_ * 1024);
Comment thread
aunu53 marked this conversation as resolved.
Outdated
codec_ = std::make_unique<ServerConnectionImpl>(connection_, callbacks_, codec_settings_);
}

Expand All @@ -42,6 +45,9 @@ class Http1ServerConnectionImplTest : public TestBase {
void expectHeadersTest(Protocol p, bool allow_absolute_url, Buffer::OwnedImpl& buffer,
TestHeaderMapImpl& expected_headers);
void expect400(Protocol p, bool allow_absolute_url, Buffer::OwnedImpl& buffer);

protected:
uint32_t max_request_headers_kb_{Http::DEFAULT_MAX_REQUEST_HEADERS_KB};
};

void Http1ServerConnectionImplTest::expect400(Protocol p, bool allow_absolute_url,
Expand Down Expand Up @@ -1007,8 +1013,7 @@ TEST_F(Http1ClientConnectionImplTest, HighwatermarkMultipleResponses) {
->onUnderlyingConnectionBelowWriteBufferLowWatermark();
}

// For issue #1421 regression test that Envoy's HTTP parser applies header limits early.
TEST_F(Http1ServerConnectionImplTest, TestCodecHeaderLimits) {
TEST_F(Http1ServerConnectionImplTest, TestLargeHeadersRejected) {
initialize();

std::string exception_reason;
Expand All @@ -1022,16 +1027,31 @@ TEST_F(Http1ServerConnectionImplTest, TestCodecHeaderLimits) {

Buffer::OwnedImpl buffer("GET / HTTP/1.1\r\n");
codec_->dispatch(buffer);
std::string long_string = "foo: " + std::string(1024, 'q') + "\r\n";
for (int i = 0; i < 79; ++i) {
buffer = Buffer::OwnedImpl(long_string);
codec_->dispatch(buffer);
}
std::string long_string = "big: " + std::string(64 * 1024, 'q') + "\r\n";
buffer = Buffer::OwnedImpl(long_string);
EXPECT_THROW_WITH_MESSAGE(codec_->dispatch(buffer), EnvoyException,
"http/1.1 protocol error: HPE_HEADER_OVERFLOW");
}

TEST_F(Http1ServerConnectionImplTest, TestLargeHeadersAcceptedIfConfigured) {
max_request_headers_kb_ = 65;
initialize();

NiceMock<Http::MockStreamDecoder> decoder;
Http::StreamEncoder* response_encoder = nullptr;
EXPECT_CALL(callbacks_, newStream(_, _))
.WillOnce(Invoke([&](Http::StreamEncoder& encoder, bool) -> Http::StreamDecoder& {
response_encoder = &encoder;
return decoder;
}));

Buffer::OwnedImpl buffer("GET / HTTP/1.1\r\n");
codec_->dispatch(buffer);
std::string long_string = "big: " + std::string(64 * 1024, 'q') + "\r\n";
buffer = Buffer::OwnedImpl(long_string);
codec_->dispatch(buffer);
}

} // namespace Http1
} // namespace Http
} // namespace Envoy
48 changes: 48 additions & 0 deletions test/common/http/http2/codec_impl_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,54 @@ TEST_P(Http2CodecImplTest, TestLargeHeadersAtLimitAccepted) {
request_encoder_->encodeHeaders(request_headers, true);
}

TEST_P(Http2CodecImplTest, TestLargeHeadersOverDefaultCodecLimit) {
max_request_headers_kb_ = 100;
initialize();

TestHeaderMapImpl request_headers;
HttpTestUtility::addDefaultHeaders(request_headers);
std::string long_string = std::string(65 * 1024, 'q');
request_headers.addCopy("big", long_string);

EXPECT_CALL(request_decoder_, decodeHeaders_(_, _)).Times(1);
EXPECT_CALL(server_stream_callbacks_, onResetStream(_)).Times(0);
request_encoder_->encodeHeaders(request_headers, true);
}

TEST_P(Http2CodecImplTest, TestManyLargeHeadersWayOverDefaultCodecLimit) {
max_request_headers_kb_ = 100;
initialize();

TestHeaderMapImpl request_headers;
HttpTestUtility::addDefaultHeaders(request_headers);
std::string long_string = std::string(1024, 'q');
for (int i = 0; i < 80; i++) {
request_headers.addCopy(fmt::format("{}", i), long_string);
}

EXPECT_CALL(request_decoder_, decodeHeaders_(_, _)).Times(1);
EXPECT_CALL(server_stream_callbacks_, onResetStream(_)).Times(0);
request_encoder_->encodeHeaders(request_headers, true);
}

TEST_P(Http2CodecImplTest, TestSingleLargeHeadersWayOverDefaultCodecLimit) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If your goal is to test an unexpectedly large header frame, you could have several keys with fairly long strings.
It makes sense to both limit the size of overall headers as well as the size of any individual header, and I think rejecting a single header key or value of 65536 bytes is totally reasonable :-)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already had a test with several long strings! But I rearranged/reworded this stuff. Discussed offline, and I'm going to turn up this test to fail on single-large-header and document the nghttp2 limit in the max_request_headers_kb api.

// this fails unexpectedly due to the arbitrarily-set NGHTTP2_HD_MAX_NV in lib/nghttp2_hd.h
// TODO(auni53) turn up this test once that issue is resolved.
return;

max_request_headers_kb_ = 100;
initialize();

TestHeaderMapImpl request_headers;
HttpTestUtility::addDefaultHeaders(request_headers);
std::string long_string = std::string(80 * 1024, 'q');
request_headers.addCopy("big", long_string);

EXPECT_CALL(request_decoder_, decodeHeaders_(_, _)).Times(1);
EXPECT_CALL(server_stream_callbacks_, onResetStream(_)).Times(0);
request_encoder_->encodeHeaders(request_headers, true);
}

TEST_P(Http2CodecImplTest, TestCodecHeaderCompression) {
initialize();

Expand Down
1 change: 1 addition & 0 deletions tools/spelling_dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,7 @@ gregs
gzip
hacky
handshaker
hd
hdr
healths
healthz
Expand Down