diff --git a/test/common/http/http2/http2_frame.cc b/test/common/http/http2/http2_frame.cc index 4d8a8c85cc15..34decb609a46 100644 --- a/test/common/http/http2/http2_frame.cc +++ b/test/common/http/http2/http2_frame.cc @@ -151,6 +151,42 @@ Http2Frame Http2Frame::makeEmptyHeadersFrame(uint32_t stream_index, HeadersFlags return frame; } +Http2Frame Http2Frame::makeHeadersFrameNoStatus(uint32_t stream_index) { + Http2Frame frame; + frame.buildHeader( + Type::Headers, 0, + static_cast(orFlags(HeadersFlags::EndStream, HeadersFlags::EndHeaders)), + makeNetworkOrderStreamId(stream_index)); + return frame; +} + +Http2Frame Http2Frame::makeHeadersFrameWithStatus(std::string status, uint32_t stream_index) { + Http2Frame frame; + frame.buildHeader( + Type::Headers, 0, + orFlags(HeadersFlags::EndStream, + HeadersFlags::EndHeaders), // TODO: Support not hardcoding these two flags + makeNetworkOrderStreamId(stream_index)); + if (status == "200") { + frame.appendStaticHeader(StaticHeaderIndex::Status200); + } else if (status == "204") { + frame.appendStaticHeader(StaticHeaderIndex::Status204); + } else if (status == "206") { + frame.appendStaticHeader(StaticHeaderIndex::Status206); + } else if (status == "304") { + frame.appendStaticHeader(StaticHeaderIndex::Status304); + } else if (status == "400") { + frame.appendStaticHeader(StaticHeaderIndex::Status400); + } else if (status == "500") { + frame.appendStaticHeader(StaticHeaderIndex::Status500); + } else { // Not a static header + Header statusHeader = Header(":status", status); + frame.appendHeaderWithoutIndexing(statusHeader); + } + frame.adjustPayloadSize(); + return frame; +} + Http2Frame Http2Frame::makeEmptyContinuationFrame(uint32_t stream_index, HeadersFlags flags) { Http2Frame frame; frame.buildHeader(Type::Continuation, 0, static_cast(flags), diff --git a/test/common/http/http2/http2_frame.h b/test/common/http/http2/http2_frame.h index d9a32fab64f2..b2592891ccee 100644 --- a/test/common/http/http2/http2_frame.h +++ b/test/common/http/http2/http2_frame.h @@ -69,7 +69,12 @@ class Http2Frame { MethodPost = 3, Path = 4, Status200 = 8, + Status204 = 9, + Status206 = 10, + Status304 = 11, + Status400 = 12, Status404 = 13, + Status500 = 14, SchemeHttps = 7, Host = 38, }; @@ -113,6 +118,11 @@ class Http2Frame { static Http2Frame makeEmptySettingsFrame(SettingsFlags flags = SettingsFlags::None); static Http2Frame makeEmptyHeadersFrame(uint32_t stream_index, HeadersFlags flags = HeadersFlags::None); + static Http2Frame makeHeadersFrameNoStatus(uint32_t stream_index); + static Http2Frame makeHeadersFrameWithStatus( + std::string status, + uint32_t stream_index); // Want to test overridden int here, so make it string + // TODO: MakeHeadersFrameWithStatusAndNonStaticHeaders static Http2Frame makeEmptyContinuationFrame(uint32_t stream_index, HeadersFlags flags = HeadersFlags::None); static Http2Frame makeEmptyDataFrame(uint32_t stream_index, DataFlags flags = DataFlags::None); diff --git a/test/integration/BUILD b/test/integration/BUILD index 010d85cc480b..a35bd29833dc 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -440,6 +440,7 @@ envoy_cc_test( "//source/common/http:header_map_lib", "//source/extensions/filters/http/buffer:config", "//source/extensions/filters/http/health_check:config", + "//test/common/http/http2:http2_frame", "//test/integration/filters:continue_headers_only_inject_body", "//test/integration/filters:encoder_decoder_buffer_filter_lib", "//test/integration/filters:local_reply_during_encoding_filter_lib", diff --git a/test/integration/protocol_integration_test.cc b/test/integration/protocol_integration_test.cc index 39e5b2738e87..cb72353e1ad2 100644 --- a/test/integration/protocol_integration_test.cc +++ b/test/integration/protocol_integration_test.cc @@ -24,6 +24,7 @@ #include "common/runtime/runtime_impl.h" #include "common/upstream/upstream_impl.h" +#include "test/common/http/http2/http2_frame.h" #include "test/common/upstream/utility.h" #include "test/integration/autonomous_upstream.h" #include "test/integration/http_integration.h" @@ -1093,6 +1094,73 @@ TEST_P(ProtocolIntegrationTest, 304WithBody) { } } +TEST_P(ProtocolIntegrationTest, OverflowingResponseCode) { + initialize(); + + FakeRawConnectionPtr fake_upstream_connection; + // HTTP1, uses a defined protocol which doesn't split up messages into raw byte frames + codec_client_ = makeHttpConnection(lookupPort("http")); + auto response = codec_client_->makeHeaderOnlyRequest(default_request_headers_); + + ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection)); + ASSERT(fake_upstream_connection != nullptr); + + if (upstreamProtocol() == FakeHttpConnection::Type::HTTP1) { + ASSERT_TRUE(fake_upstream_connection->write( + "HTTP/1.1 11111111111111111111111111111111111111111111111111111111111111111 OK\r\n", + false)); + ASSERT_TRUE(fake_upstream_connection->waitForDisconnect()); + } else { + Http::Http2::Http2Frame::SettingsFlags settings_flags = + static_cast(0); + Http2Frame setting_frame = Http::Http2::Http2Frame::makeEmptySettingsFrame(settings_flags); + // Ack settings + settings_flags = static_cast(1); // ack + Http2Frame ack_frame = Http::Http2::Http2Frame::makeEmptySettingsFrame(settings_flags); + ASSERT(fake_upstream_connection->write(std::string(setting_frame))); // empty settings + ASSERT(fake_upstream_connection->write(std::string(ack_frame))); // ack setting + Http::Http2::Http2Frame overflowed_status = Http::Http2::Http2Frame::makeHeadersFrameWithStatus( + "11111111111111111111111111111111111111111111111111111111111111111", 0); + ASSERT_TRUE(fake_upstream_connection->write(std::string(overflowed_status))); + } + + ASSERT_TRUE(fake_upstream_connection->waitForDisconnect()); + response->waitForEndStream(); + EXPECT_EQ("503", response->headers().getStatusValue()); +} + +TEST_P(ProtocolIntegrationTest, MissingStatus) { + initialize(); + + FakeRawConnectionPtr fake_upstream_connection; + // HTTP1, uses a defined protocol which doesn't split up messages into raw byte frames + codec_client_ = makeHttpConnection(lookupPort("http")); + auto response = codec_client_->makeHeaderOnlyRequest(default_request_headers_); + + ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection)); + ASSERT(fake_upstream_connection != nullptr); + + if (upstreamProtocol() == FakeHttpConnection::Type::HTTP1) { + ASSERT_TRUE(fake_upstream_connection->write("HTTP/1.1 OK\r\n", false)); + ASSERT_TRUE(fake_upstream_connection->waitForDisconnect()); + } else { + Http::Http2::Http2Frame::SettingsFlags settings_flags = + static_cast(0); + Http2Frame setting_frame = Http::Http2::Http2Frame::makeEmptySettingsFrame(settings_flags); + // Ack settings + settings_flags = static_cast(1); // ack + Http2Frame ack_frame = Http::Http2::Http2Frame::makeEmptySettingsFrame(settings_flags); + ASSERT(fake_upstream_connection->write(std::string(setting_frame))); // empty settings + ASSERT(fake_upstream_connection->write(std::string(ack_frame))); // ack setting + Http::Http2::Http2Frame missing_status = Http::Http2::Http2Frame::makeHeadersFrameNoStatus(0); + ASSERT_TRUE(fake_upstream_connection->write(std::string(missing_status))); + } + + ASSERT_TRUE(fake_upstream_connection->waitForDisconnect()); + response->waitForEndStream(); + EXPECT_EQ("503", response->headers().getStatusValue()); +} + // Validate that lots of tiny cookies doesn't cause a DoS (single cookie header). TEST_P(DownstreamProtocolIntegrationTest, LargeCookieParsingConcatenated) { initialize();