diff --git a/test/common/http/http2/http2_frame.cc b/test/common/http/http2/http2_frame.cc index f7eda300935dd..6717ec49b3b85 100644 --- a/test/common/http/http2/http2_frame.cc +++ b/test/common/http/http2/http2_frame.cc @@ -13,19 +13,6 @@ namespace { // Converts stream ID to the network byte order. Supports all values in the range [0, 2^30). uint32_t makeNetworkOrderStreamId(uint32_t stream_id) { return htonl(stream_id); } -// All this templatized stuff is for the typesafe constexpr bitwise ORing of the "enum class" values -template struct FirstArgType { - using type = First; // NOLINT(readability-identifier-naming) -}; - -template constexpr uint8_t orFlags(Flag flag) { return static_cast(flag); } - -template constexpr uint8_t orFlags(Flag first, Flags... rest) { - static_assert(std::is_same::type>::value, - "All flag types must be the same!"); - return static_cast(first) | orFlags(rest...); -} - } // namespace namespace Envoy { @@ -160,13 +147,11 @@ Http2Frame Http2Frame::makeHeadersFrameNoStatus(uint32_t stream_index) { return frame; } -Http2Frame Http2Frame::makeHeadersFrameWithStatus(std::string status, uint32_t stream_index) { +Http2Frame Http2Frame::makeHeadersFrameWithStatus(std::string status, uint32_t stream_index, + HeadersFlags flags) { Http2Frame frame; - frame.buildHeader( - Type::Headers, 0, - orFlags(HeadersFlags::EndStream, - HeadersFlags::EndHeaders), // TODO: Support not hardcoding these two flags - makeNetworkOrderStreamId(stream_index)); + frame.buildHeader(Type::Headers, 0, static_cast(flags), + makeNetworkOrderStreamId(stream_index)); if (status == "200") { frame.appendStaticHeader(StaticHeaderIndex::Status200); } else if (status == "204") { diff --git a/test/common/http/http2/http2_frame.h b/test/common/http/http2/http2_frame.h index a077e2601bf27..53465a6f9248a 100644 --- a/test/common/http/http2/http2_frame.h +++ b/test/common/http/http2/http2_frame.h @@ -14,6 +14,19 @@ namespace Envoy { namespace Http { namespace Http2 { +template constexpr uint8_t orFlags(Flag flag) { return static_cast(flag); } + +// All this templatized stuff is for the typesafe constexpr bitwise ORing of the "enum class" values +template struct FirstArgType { + using type = First; // NOLINT(readability-identifier-naming) +}; + +template constexpr uint8_t orFlags(Flag first, Flags... rest) { + static_assert(std::is_same::type>::value, + "All flag types must be the same!"); + return static_cast(first) | orFlags(rest...); +} + // Rudimentary facility for building and parsing of HTTP2 frames for unit tests class Http2Frame { using DataContainer = std::vector; @@ -120,8 +133,9 @@ class Http2Frame { 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 + std::string status, uint32_t stream_index, + HeadersFlags flags = static_cast(orFlags(HeadersFlags::EndStream, + HeadersFlags::EndHeaders))); // TODO: MakeHeadersFrameWithStatusAndNonStaticHeaders static Http2Frame makeEmptyContinuationFrame(uint32_t stream_index, HeadersFlags flags = HeadersFlags::None); diff --git a/test/integration/http2_flood_integration_test.cc b/test/integration/http2_flood_integration_test.cc index b7d8f95c755b1..01f7fe6753a50 100644 --- a/test/integration/http2_flood_integration_test.cc +++ b/test/integration/http2_flood_integration_test.cc @@ -1240,4 +1240,58 @@ TEST_P(Http2FloodMitigationTest, UpstreamZerolenHeaderAllowed) { EXPECT_THAT(waitForAccessLog(access_log_name_), HasSubstr("LR")); } +TEST_P(Http2FloodMitigationTest, UpstreamEmptyData) { + if (!initializeUpstreamFloodTest()) { + return; + } + + // Send client request which will send an upstream request. + codec_client_ = makeHttpConnection(lookupPort("http")); + auto response = codec_client_->makeHeaderOnlyRequest(default_request_headers_); + waitForNextUpstreamRequest(); + + // Start the response with a 200 status. + auto* upstream = fake_upstreams_.front().get(); + Http2Frame buf = Http2Frame::makeHeadersFrameWithStatus("200", Http2Frame::makeClientStreamId(0), + Http2Frame::HeadersFlags::EndHeaders); + ASSERT_TRUE(upstream->rawWriteConnection(0, std::string(buf.begin(), buf.end()))); + + // Send empty data frames. + for (int i = 0; i < 2; i++) { + buf = Http2Frame::makeEmptyDataFrame(Http2Frame::makeClientStreamId(0)); + ASSERT_TRUE(upstream->rawWriteConnection(0, std::string(buf.begin(), buf.end()))); + } + + ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); + response->waitForReset(); + EXPECT_EQ("200", response->headers().getStatusValue()); + EXPECT_EQ(1, + test_server_->counter("cluster.cluster_0.http2.inbound_empty_frames_flood")->value()); +} + +TEST_P(Http2FloodMitigationTest, UpstreamEmptyHeadersContinuation) { + if (!initializeUpstreamFloodTest()) { + return; + } + + codec_client_ = makeHttpConnection(lookupPort("http")); + auto response = codec_client_->makeHeaderOnlyRequest(default_request_headers_); + waitForNextUpstreamRequest(); + + auto* upstream = fake_upstreams_.front().get(); + Http2Frame buf = Http2Frame::makeEmptyHeadersFrame(Http2Frame::makeClientStreamId(0)); + ASSERT_TRUE(upstream->rawWriteConnection(0, std::string(buf.begin(), buf.end()))); + + for (int i = 0; i < 2; i++) { + buf = Http2Frame::makeEmptyContinuationFrame(Http2Frame::makeClientStreamId(0)); + ASSERT_TRUE(upstream->rawWriteConnection(0, std::string(buf.begin(), buf.end()))); + } + + ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); + response->waitForEndStream(); + EXPECT_EQ("503", response->headers().getStatusValue()); + EXPECT_EQ(1, + test_server_->counter("cluster.cluster_0.http2.inbound_empty_frames_flood")->value()); +} + } // namespace Envoy