Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
23 changes: 4 additions & 19 deletions test/common/http/http2/http2_frame.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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 <typename First, typename... Rest> struct FirstArgType {
using type = First; // NOLINT(readability-identifier-naming)
};

template <typename Flag> constexpr uint8_t orFlags(Flag flag) { return static_cast<uint8_t>(flag); }

template <typename Flag, typename... Flags> constexpr uint8_t orFlags(Flag first, Flags... rest) {
static_assert(std::is_same<Flag, typename FirstArgType<Flags...>::type>::value,
"All flag types must be the same!");
return static_cast<uint8_t>(first) | orFlags(rest...);
}

} // namespace

namespace Envoy {
Expand Down Expand Up @@ -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<uint8_t>(flags),
makeNetworkOrderStreamId(stream_index));
if (status == "200") {
frame.appendStaticHeader(StaticHeaderIndex::Status200);
} else if (status == "204") {
Expand Down
18 changes: 16 additions & 2 deletions test/common/http/http2/http2_frame.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,19 @@ namespace Envoy {
namespace Http {
namespace Http2 {

template <typename Flag> constexpr uint8_t orFlags(Flag flag) { return static_cast<uint8_t>(flag); }

// All this templatized stuff is for the typesafe constexpr bitwise ORing of the "enum class" values
template <typename First, typename... Rest> struct FirstArgType {
using type = First; // NOLINT(readability-identifier-naming)
};

template <typename Flag, typename... Flags> constexpr uint8_t orFlags(Flag first, Flags... rest) {
static_assert(std::is_same<Flag, typename FirstArgType<Flags...>::type>::value,
"All flag types must be the same!");
return static_cast<uint8_t>(first) | orFlags(rest...);
}

// Rudimentary facility for building and parsing of HTTP2 frames for unit tests
class Http2Frame {
using DataContainer = std::vector<uint8_t>;
Expand Down Expand Up @@ -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<HeadersFlags>(orFlags(HeadersFlags::EndStream,
HeadersFlags::EndHeaders)));
// TODO: MakeHeadersFrameWithStatusAndNonStaticHeaders
static Http2Frame makeEmptyContinuationFrame(uint32_t stream_index,
HeadersFlags flags = HeadersFlags::None);
Expand Down
54 changes: 54 additions & 0 deletions test/integration/http2_flood_integration_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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