From 1ba6c4838f9d4cdd78345a5b11b5eeb9b9bc6453 Mon Sep 17 00:00:00 2001 From: Yan Avlasov Date: Fri, 30 Jun 2023 00:53:49 +0000 Subject: [PATCH] Add client HTTP/2 fuzzer Signed-off-by: Yan Avlasov --- test/common/http/BUILD | 1 + .../http/client_codec_diff_fuzz_test.cc | 401 +++++++++++++----- test/common/http/http2/http2_frame.cc | 2 +- test/common/http/http2/http2_frame.h | 2 +- .../kitchen_sink_content_length_only | 59 +++ .../redirect_with_content_length | 36 ++ .../http/server_codec_diff_fuzz_test.cc | 2 +- 7 files changed, 397 insertions(+), 106 deletions(-) create mode 100644 test/common/http/server_codec_diff_fuzz_corpus/kitchen_sink_content_length_only create mode 100644 test/common/http/server_codec_diff_fuzz_corpus/redirect_with_content_length diff --git a/test/common/http/BUILD b/test/common/http/BUILD index 1b62b5022f83..030a76f7f4fe 100644 --- a/test/common/http/BUILD +++ b/test/common/http/BUILD @@ -644,6 +644,7 @@ envoy_cc_fuzz_test( "//source/common/stream_info:stream_info_lib", "//source/common/upstream:upstream_includes", "//source/common/upstream:upstream_lib", + "//test/common/http/http2:http2_frame", "//test/common/upstream:utility_lib", "//test/config:utility_lib", "//test/fuzz:utility_lib", diff --git a/test/common/http/client_codec_diff_fuzz_test.cc b/test/common/http/client_codec_diff_fuzz_test.cc index 6dda633eae0a..c79b7c6c7b54 100644 --- a/test/common/http/client_codec_diff_fuzz_test.cc +++ b/test/common/http/client_codec_diff_fuzz_test.cc @@ -12,6 +12,7 @@ #include "source/common/upstream/upstream_impl.h" #include "test/common/http/common.h" +#include "test/common/http/http2/http2_frame.h" #include "test/common/http/server_codec_diff_fuzz.pb.validate.h" #include "test/common/upstream/utility.h" #include "test/config/utility.h" @@ -38,6 +39,7 @@ namespace Envoy { namespace Http { +using Http2::Http2Frame; using testing::Return; using testing::ReturnPointee; using testing::ReturnRef; @@ -49,8 +51,7 @@ constexpr absl::string_view response_body = "0123456789aBcDeFgHiJkLmNoPqRsTuVwXy class CodecClientTestContext { public: - CodecClientTestContext(Http1ParserImpl codec_impl, - const test::common::http::ServerCodecDiffFuzzTestCase& input) + CodecClientTestContext(const test::common::http::ServerCodecDiffFuzzTestCase& input) : input_(input) { EXPECT_CALL(*connection_, connecting()).WillOnce(Return(true)); EXPECT_CALL(*connection_, detectEarlyCloseWhenReadDisabled(false)); @@ -107,7 +108,6 @@ class CodecClientTestContext { // EXPECT_CALL(dispatcher_, createTimer_(_)); cluster_->max_response_headers_count_ = 200; - envoy::config::core::v3::Http2ProtocolOptions http2_options; cluster_->http2_options_ = Envoy::Http2::Utility::initializeAndValidateOptions( envoy::config::core::v3::Http2ProtocolOptions()); cluster_->http2_options_.set_allow_connect(true); @@ -115,7 +115,6 @@ class CodecClientTestContext { cluster_->http3_options_ = ConfigHelper::http2ToHttp3ProtocolOptions(cluster_->http2_options_, 16 * 1024 * 1024); cluster_->http3_options_.set_allow_extended_connect(true); - cluster_->http1_settings_.use_balsa_parser_ = codec_impl == Http1ParserImpl::BalsaParser; cluster_->http1_settings_.allow_absolute_url_ = input_.configuration().http1_options().allow_absolute_url(); cluster_->http1_settings_.allow_chunked_length_ = @@ -127,11 +126,10 @@ class CodecClientTestContext { host_description_ = Upstream::makeTestHostDescription(cluster_, "tcp://127.0.0.1:80", time_system_); - codec_ = std::make_unique(CodecType::HTTP1, - Network::ClientConnectionPtr(connection_), - host_description_, dispatcher_, random_, nullptr); } + virtual ~CodecClientTestContext() = default; + void sendRequest() { Http::RequestEncoder& request_encoder = codec_->newStream(decoder_); request_encoder.getStream().addCallbacks(stream_callbacks_); @@ -151,57 +149,8 @@ class CodecClientTestContext { } } - void sendResponse() { - ASSERT(input_.has_response()); - std::string wire_bytes; - // TODO(yanavlasov): get version from request - wire_bytes = "HTTP/1.1 "; - if (input_.response().has_status()) { - absl::StrAppend(&wire_bytes, input_.response().status().value(), " ", - CodeUtility::toString( - static_cast(std::atoi(input_.response().status().value().c_str()))), - "\r\n"); - } - bool chunked_encoding = false; - for (const auto& header : input_.response().headers()) { - if (absl::EqualsIgnoreCase(header.key(), "transfer-encoding") && - absl::StrContainsIgnoreCase(header.value(), "chunked")) { - chunked_encoding = true; - } - std::string value(header.value()); - if (input_.send_response_body() && absl::EqualsIgnoreCase(header.key(), "content-length")) { - value = std::to_string(response_body.size()); - } - absl::StrAppend(&wire_bytes, header.key(), ": ", value, "\r\n"); - } - - absl::StrAppend(&wire_bytes, "\r\n"); - if (input_.send_response_body()) { - if (chunked_encoding) { - absl::StrAppend(&wire_bytes, absl::StrFormat("%X", response_body.size()), "\r\n", - response_body, "\r\n0\r\n"); - if (!input_.has_response_trailers()) { - absl::StrAppend(&wire_bytes, "\r\n"); - } - } else { - absl::StrAppend(&wire_bytes, response_body); - } - } - - if (input_.has_response_trailers() && chunked_encoding) { - if (!input_.send_response_body()) { - absl::StrAppend(&wire_bytes, "0\r\n"); - } - for (const auto& trailer : input_.response_trailers().trailers()) { - absl::StrAppend(&wire_bytes, trailer.key(), ": ", trailer.value(), "\r\n"); - } - absl::StrAppend(&wire_bytes, "\r\n"); - } - - std::cout << "~~~~~~~~~~~~~~~~~~~~~\n" << wire_bytes << std::endl; - Buffer::OwnedImpl buffer(wire_bytes); - filter_->onData(buffer, true); - } + virtual void compareOutputWireBytes(absl::string_view wire_bytes) = 0; + virtual void sendResponse() = 0; RequestHeaderMapPtr makeRequestHeaders() { ASSERT(input_.has_request()); @@ -278,70 +227,313 @@ class CodecClientTestContext { bool response_end_stream_{false}; }; +class Http1CodecClientTestContext : public CodecClientTestContext { +public: + Http1CodecClientTestContext(Http1ParserImpl codec_impl, + const test::common::http::ServerCodecDiffFuzzTestCase& input) + : CodecClientTestContext(input) { + cluster_->http1_settings_.use_balsa_parser_ = codec_impl == Http1ParserImpl::BalsaParser; + codec_ = std::make_unique(CodecType::HTTP1, + Network::ClientConnectionPtr(connection_), + host_description_, dispatcher_, random_, nullptr); + } + + void compareOutputWireBytes(absl::string_view wire_bytes) override { + FUZZ_ASSERT(written_wire_bytes_ == wire_bytes); + } + + void sendResponse() override { + ASSERT(input_.has_response()); + std::string wire_bytes; + // TODO(yanavlasov): get version from request + wire_bytes = "HTTP/1.1 "; + if (input_.response().has_status()) { + absl::StrAppend(&wire_bytes, input_.response().status().value(), " ", + CodeUtility::toString( + static_cast(std::atoi(input_.response().status().value().c_str()))), + "\r\n"); + } + bool chunked_encoding = false; + for (const auto& header : input_.response().headers()) { + if (absl::EqualsIgnoreCase(header.key(), "transfer-encoding") && + absl::StrContainsIgnoreCase(header.value(), "chunked")) { + chunked_encoding = true; + } + std::string value(header.value()); + if (input_.send_response_body() && absl::EqualsIgnoreCase(header.key(), "content-length")) { + value = std::to_string(response_body.size()); + } + absl::StrAppend(&wire_bytes, header.key(), ": ", value, "\r\n"); + } + + absl::StrAppend(&wire_bytes, "\r\n"); + if (input_.send_response_body()) { + if (chunked_encoding) { + absl::StrAppend(&wire_bytes, absl::StrFormat("%X", response_body.size()), "\r\n", + response_body, "\r\n0\r\n"); + if (!input_.has_response_trailers()) { + absl::StrAppend(&wire_bytes, "\r\n"); + } + } else { + absl::StrAppend(&wire_bytes, response_body); + } + } + + if (input_.has_response_trailers() && chunked_encoding) { + if (!input_.send_response_body()) { + absl::StrAppend(&wire_bytes, "0\r\n"); + } + for (const auto& trailer : input_.response_trailers().trailers()) { + absl::StrAppend(&wire_bytes, trailer.key(), ": ", trailer.value(), "\r\n"); + } + absl::StrAppend(&wire_bytes, "\r\n"); + } + + // std::cout << "~~~~~~~~~~~~~~~~~~~~~\n" << wire_bytes << std::endl; + Buffer::OwnedImpl buffer(wire_bytes); + filter_->onData(buffer, true); + } +}; + +class Http2CodecClientTestContext : public CodecClientTestContext { +public: + enum class Http2Impl { + Nghttp2, + Oghttp2, + }; + Http2CodecClientTestContext(Http2Impl codec_impl, + const test::common::http::ServerCodecDiffFuzzTestCase& input) + : CodecClientTestContext(input) { + cluster_->http2_options_.mutable_use_oghttp2_codec()->set_value(codec_impl == + Http2Impl::Oghttp2); + codec_ = std::make_unique(CodecType::HTTP2, + Network::ClientConnectionPtr(connection_), + host_description_, dispatcher_, random_, nullptr); + } + + void compareOutputWireBytes(absl::string_view wire_bytes) override { + // First just compare bytes one to one + if (written_wire_bytes_ == wire_bytes) { + return; + } + + absl::string_view wire_bytes_1(written_wire_bytes_); + // Snip H/2 preamble + if (absl::StartsWith(wire_bytes_1, Http2Frame::Preamble)) { + FUZZ_ASSERT(absl::StartsWith(wire_bytes, Http2Frame::Preamble)); + wire_bytes_1 = wire_bytes_1.substr(Http2Frame::Preamble.size()); + wire_bytes = wire_bytes.substr(Http2Frame::Preamble.size()); + } + + // If output bytes do not match, it does not indicate failure yet. + // Strip all control frames and make sure all other frames match in content and order + compareFrames(wire_bytes_1, wire_bytes); + } + + void compareFrames(absl::string_view wire_bytes_1, absl::string_view wire_bytes_2) { + std::vector frames_1(parseNonControlFrames(wire_bytes_1)); + std::vector frames_2(parseNonControlFrames(wire_bytes_2)); + + if (frames_1.empty() && frames_2.empty()) { + return; + } + + FUZZ_ASSERT(frames_1.size() == frames_2.size()); + spdy::HpackDecoderAdapter decoder_1; + spdy::HpackDecoderAdapter decoder_2; + for (auto frame_1 = frames_1.cbegin(), frame_2 = frames_2.cbegin(); frame_1 != frames_1.cend(); + ++frame_1, ++frame_2) { + FUZZ_ASSERT(frame_1->type() == frame_2->type()); + if (frame_1->type() == Http2Frame::Type::RstStream) { + // There should be nothing after RST_STREAM + FUZZ_ASSERT(++frame_1 == frames_1.cend()); + break; + } else if (frame_1->type() == Http2Frame::Type::Headers) { + std::vector headers_1 = frame_1->parseHeadersFrame(decoder_1); + std::vector headers_2 = frame_2->parseHeadersFrame(decoder_2); + // Headers should be the same + FUZZ_ASSERT(headers_1.size() == headers_2.size()); + FUZZ_ASSERT(std::equal(headers_1.begin(), headers_1.end(), headers_2.begin())); + } else if (frame_1->type() == Http2Frame::Type::Data) { + // Payload should be the same + FUZZ_ASSERT(frame_1->payloadSize() == frame_2->payloadSize()); + FUZZ_ASSERT(std::equal(frame_1->payloadBegin(), frame_1->end(), frame_2->payloadBegin())); + } else { + FUZZ_ASSERT(false); // should never get here + } + } + } + + std::vector parseNonControlFrames(absl::string_view wire_bytes) { + std::vector frames; + while (!wire_bytes.empty()) { + Http2Frame frame = Http2Frame::makeGenericFrame(wire_bytes); + const uint32_t frame_size = frame.frameSize(); + ASSERT(frame_size <= wire_bytes.size()); + if (frame.type() == Http2Frame::Type::Headers || + frame.type() == Http2Frame::Type::RstStream || frame.type() == Http2Frame::Type::Data) { + frames.push_back(std::move(frame)); + } + wire_bytes = wire_bytes.substr(frame_size); + } + return frames; + } + + void sendResponse() override { + ASSERT(input_.has_response()); + sendInitialFrames(); + + const uint32_t stream_index = Http2Frame::makeClientStreamId(0); + const bool end_stream = !input_.send_request_body() && !input_.has_request_trailers(); + auto headers = Http2Frame::makeEmptyHeadersFrame( + stream_index, end_stream ? Http2::orFlags(Http2Frame::HeadersFlags::EndStream, + Http2Frame::HeadersFlags::EndHeaders) + : Http2Frame::HeadersFlags::EndHeaders); + if (input_.response().has_status()) { + headers.appendHeaderWithoutIndexing( + Http2Frame::Header(":status", input_.response().status().value())); + } + + for (const auto& header : input_.response().headers()) { + std::string value = header.value(); + if (input_.send_response_body() && header.key() == "content-length") { + value = std::to_string(response_body.size()); + } + headers.appendHeaderWithoutIndexing(Http2Frame::Header(header.key(), value)); + } + headers.adjustPayloadSize(); + + Buffer::OwnedImpl wire_bytes(headers.getStringView()); + filter_->onData(wire_bytes, false); + + if (input_.send_response_body()) { + const bool end_stream = !input_.has_request_trailers(); + Http2Frame body = Http2Frame::makeDataFrame(stream_index, response_body, + end_stream ? Http2Frame::DataFlags::EndStream + : Http2Frame::DataFlags::None); + Buffer::OwnedImpl wire_input(body.getStringView()); + filter_->onData(wire_input, false); + } + + if (input_.has_request_trailers()) { + auto trailers = Http2Frame::makeEmptyHeadersFrame( + stream_index, Http2::orFlags(Http2Frame::HeadersFlags::EndStream, + Http2Frame::HeadersFlags::EndHeaders)); + + for (const auto& trailer : input_.request_trailers().trailers()) { + trailers.appendHeaderWithoutIndexing(Http2Frame::Header(trailer.key(), trailer.value())); + } + trailers.adjustPayloadSize(); + + Buffer::OwnedImpl wire_input(trailers.getStringView()); + filter_->onData(wire_input, false); + } + } + + void sendInitialFrames() { + // As a 'server' fuzzer sends its SETTINGS (empty), ACKs client's SETTINGS and when sends + // WINDOW_UPDATE + std::string wire_bytes = absl::StrCat( + Http2::Http2Frame::makeEmptySettingsFrame().getStringView(), + Http2Frame::makeEmptySettingsFrame(Http2Frame::SettingsFlags::Ack).getStringView(), + Http2Frame::makeWindowUpdateFrame(0, 0x1FFFFFFF).getStringView()); + + Buffer::OwnedImpl buffer(wire_bytes); + filter_->onData(buffer, false); + } +}; + +namespace { +[[maybe_unused]] void printBytes(absl::string_view bytes) { + for (const char byte : bytes) { + std::cout << absl::StrFormat("%02X ", + static_cast(static_cast(byte))); + } + std::cout << std::endl; +} +} // namespace + class CodecClientTest { public: CodecClientTest(Http1ParserImpl codec1, Http1ParserImpl codec2, const test::common::http::ServerCodecDiffFuzzTestCase& input) - : codec_under_test_1_(codec1, input), codec_under_test_2_(codec2, input) {} + : codec_under_test_1_(std::make_unique(codec1, input)), + codec_under_test_2_(std::make_unique(codec2, input)) {} + + CodecClientTest(Http2CodecClientTestContext::Http2Impl codec1, + Http2CodecClientTestContext::Http2Impl codec2, + const test::common::http::ServerCodecDiffFuzzTestCase& input) + : codec_under_test_1_(std::make_unique(codec1, input)), + codec_under_test_2_(std::make_unique(codec2, input)) {} void test() { - codec_under_test_1_.sendRequest(); - codec_under_test_2_.sendRequest(); - FUZZ_ASSERT(codec_under_test_1_.request_encoder_status_ == - codec_under_test_2_.request_encoder_status_); - FUZZ_ASSERT(codec_under_test_1_.written_wire_bytes_ == codec_under_test_2_.written_wire_bytes_); - - // std::cout << "--------------\n" << codec_under_test_1_.written_wire_bytes_ << std::endl; - - if (!codec_under_test_1_.written_wire_bytes_.empty() && - codec_under_test_1_.request_encoder_status_.ok() && - codec_under_test_1_.input_.has_response()) { - codec_under_test_1_.sendResponse(); - codec_under_test_2_.sendResponse(); - - FUZZ_ASSERT(codec_under_test_1_.stream_reset_ == codec_under_test_2_.stream_reset_); - FUZZ_ASSERT(codec_under_test_1_.response_end_stream_ == - codec_under_test_2_.response_end_stream_); - FUZZ_ASSERT((codec_under_test_1_.response_headers_ != nullptr && - codec_under_test_2_.response_headers_ != nullptr) || - (codec_under_test_1_.response_headers_ == nullptr && - codec_under_test_2_.response_headers_ == nullptr)); - - if (codec_under_test_1_.response_headers_ != nullptr) { + codec_under_test_1_->sendRequest(); + codec_under_test_2_->sendRequest(); + + // printBytes(codec_under_test_1_->written_wire_bytes_); + // printBytes(codec_under_test_2_->written_wire_bytes_); + // std::cout << "--------------\n" << codec_under_test_1_->written_wire_bytes_ << std::endl; + + FUZZ_ASSERT(codec_under_test_1_->request_encoder_status_ == + codec_under_test_2_->request_encoder_status_); + + codec_under_test_1_->compareOutputWireBytes(codec_under_test_2_->written_wire_bytes_); + + if (!codec_under_test_1_->written_wire_bytes_.empty() && + codec_under_test_1_->request_encoder_status_.ok() && + codec_under_test_1_->input_.has_response()) { + codec_under_test_1_->written_wire_bytes_.clear(); + codec_under_test_2_->written_wire_bytes_.clear(); + codec_under_test_1_->sendResponse(); + codec_under_test_2_->sendResponse(); + + // printBytes(codec_under_test_1_->written_wire_bytes_); + codec_under_test_1_->compareOutputWireBytes(codec_under_test_2_->written_wire_bytes_); + + FUZZ_ASSERT(codec_under_test_1_->stream_reset_ == codec_under_test_2_->stream_reset_); + FUZZ_ASSERT(codec_under_test_1_->response_end_stream_ == + codec_under_test_2_->response_end_stream_); + FUZZ_ASSERT((codec_under_test_1_->response_headers_ != nullptr && + codec_under_test_2_->response_headers_ != nullptr) || + (codec_under_test_1_->response_headers_ == nullptr && + codec_under_test_2_->response_headers_ == nullptr)); + + if (codec_under_test_1_->response_headers_ != nullptr) { // When both codecs produced response headers they must be the same - FUZZ_ASSERT(*codec_under_test_1_.response_headers_ == - *codec_under_test_2_.response_headers_); + FUZZ_ASSERT(*codec_under_test_1_->response_headers_ == + *codec_under_test_2_->response_headers_); } - FUZZ_ASSERT(codec_under_test_1_.response_body_ == codec_under_test_2_.response_body_); + FUZZ_ASSERT(codec_under_test_1_->response_body_ == codec_under_test_2_->response_body_); - FUZZ_ASSERT((codec_under_test_1_.response_trailers_ != nullptr && - codec_under_test_2_.response_trailers_ != nullptr) || - (codec_under_test_1_.response_trailers_ == nullptr && - codec_under_test_2_.response_trailers_ == nullptr)); + FUZZ_ASSERT((codec_under_test_1_->response_trailers_ != nullptr && + codec_under_test_2_->response_trailers_ != nullptr) || + (codec_under_test_1_->response_trailers_ == nullptr && + codec_under_test_2_->response_trailers_ == nullptr)); - if (codec_under_test_1_.response_trailers_ != nullptr) { + if (codec_under_test_1_->response_trailers_ != nullptr) { // When both codecs produced response trailers they must be the same - FUZZ_ASSERT(*codec_under_test_1_.response_trailers_ == - *codec_under_test_2_.response_trailers_); + FUZZ_ASSERT(*codec_under_test_1_->response_trailers_ == + *codec_under_test_2_->response_trailers_); } - FUZZ_ASSERT(codec_under_test_1_.connection_state_ == codec_under_test_2_.connection_state_); - FUZZ_ASSERT(codec_under_test_1_.stream_reset_ == codec_under_test_2_.stream_reset_); + FUZZ_ASSERT(codec_under_test_1_->connection_state_ == codec_under_test_2_->connection_state_); + FUZZ_ASSERT(codec_under_test_1_->stream_reset_ == codec_under_test_2_->stream_reset_); + /* - if (codec_under_test_1_.response_headers_) { - std::cout << "++++++++++++++\n" << *codec_under_test_1_.response_headers_ << std::endl << - codec_under_test_1_.response_body_ << "\n\n"; + if (codec_under_test_1_->response_headers_) { + std::cout << "++++++++++++++\n" << *codec_under_test_1_->response_headers_ << std::endl << + codec_under_test_1_->response_body_ << "\n\n"; } - if (codec_under_test_1_.response_trailers_) { - std::cout << *codec_under_test_1_.response_trailers_ << std::endl; + if (codec_under_test_1_->response_trailers_) { + std::cout << *codec_under_test_1_->response_trailers_ << std::endl; } */ } } - CodecClientTestContext codec_under_test_1_; - CodecClientTestContext codec_under_test_2_; + std::unique_ptr codec_under_test_1_; + std::unique_ptr codec_under_test_2_; }; DEFINE_PROTO_FUZZER(const test::common::http::ServerCodecDiffFuzzTestCase& input) { @@ -359,9 +551,12 @@ DEFINE_PROTO_FUZZER(const test::common::http::ServerCodecDiffFuzzTestCase& input return; } - CodecClientTest http1_test(Http1ParserImpl::HttpParser, Http1ParserImpl::BalsaParser, input); + // CodecClientTest http1_test(Http1ParserImpl::HttpParser, Http1ParserImpl::BalsaParser, input); + // http1_test.test(); - http1_test.test(); + CodecClientTest http2_test(Http2CodecClientTestContext::Http2Impl::Nghttp2, + Http2CodecClientTestContext::Http2Impl::Oghttp2, input); + http2_test.test(); } } // namespace Http diff --git a/test/common/http/http2/http2_frame.cc b/test/common/http/http2/http2_frame.cc index ecf779f5f77f..b57ca0a5aea7 100644 --- a/test/common/http/http2/http2_frame.cc +++ b/test/common/http/http2/http2_frame.cc @@ -19,7 +19,7 @@ namespace Envoy { namespace Http { namespace Http2 { -const char Http2Frame::Preamble[25] = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"; +const absl::string_view Http2Frame::Preamble("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"); void Http2Frame::setHeader(absl::string_view header) { ASSERT(header.size() >= HeaderSize); diff --git a/test/common/http/http2/http2_frame.h b/test/common/http/http2/http2_frame.h index a1889412029e..6dbac998591d 100644 --- a/test/common/http/http2/http2_frame.h +++ b/test/common/http/http2/http2_frame.h @@ -50,7 +50,7 @@ class Http2Frame { using ConstIterator = DataContainer::const_iterator; static constexpr size_t HeaderSize = 9; - static const char Preamble[25]; + static const absl::string_view Preamble; enum class Type : uint8_t { Data = 0, diff --git a/test/common/http/server_codec_diff_fuzz_corpus/kitchen_sink_content_length_only b/test/common/http/server_codec_diff_fuzz_corpus/kitchen_sink_content_length_only new file mode 100644 index 000000000000..2b56e91a2c75 --- /dev/null +++ b/test/common/http/server_codec_diff_fuzz_corpus/kitchen_sink_content_length_only @@ -0,0 +1,59 @@ +configuration { + normalize_path: true + http1_options { + enable_trailers: true + } +} + +request { + scheme { value: "https" } + method { value: "POST" } + path { value: "/database?key=value&key2=value2&etc&etc2" } + authority { value: "foo.bar.com" } + headers { + key: "content-length" + value: "10" + } + headers { + key: "baz" + value: "fuz" + } +} + +send_request_body: true + +request_trailers { + trailers { + key: "something" + value: "other" + } + trailers { + key: "key" + value: "valUe" + } +} + +response { + status { value: "200" } + headers { + key: "content-length" + value: "10" + } + headers { + key: "clutz" + value: "O___P1278NS" + } +} + +send_response_body: true + +response_trailers { + trailers { + key: "start" + value: "stop" + } + trailers { + key: "data" + value: "01234now" + } +} diff --git a/test/common/http/server_codec_diff_fuzz_corpus/redirect_with_content_length b/test/common/http/server_codec_diff_fuzz_corpus/redirect_with_content_length new file mode 100644 index 000000000000..51b37ca51203 --- /dev/null +++ b/test/common/http/server_codec_diff_fuzz_corpus/redirect_with_content_length @@ -0,0 +1,36 @@ +configuration { + normalize_path: true + merge_slashes: true + path_with_escaped_slashes_action: UNESCAPE_AND_FORWARD +} +request { + scheme { + value: "https" + } + method { + value: "GET" + } + path { + value: "/path%2Fwith%2fescaped%5Cslashes%5c" + } + authority { + value: "foo.bar.com" + } + headers { + key: "foo" + value: "bar" + } +} +response { + status { + value: "301" + } + headers { + key: "location" + value: "/somewhere_else/not/../here/./?with=query" + } + headers { + key: "content-length" + value: "10" + } +} diff --git a/test/common/http/server_codec_diff_fuzz_test.cc b/test/common/http/server_codec_diff_fuzz_test.cc index efe143e7da80..08a6e0fc11b5 100644 --- a/test/common/http/server_codec_diff_fuzz_test.cc +++ b/test/common/http/server_codec_diff_fuzz_test.cc @@ -735,7 +735,7 @@ class Http2HcmTest : public HcmTest { } for (const auto& header : input_.request().headers()) { - absl::string_view value = header.value(); + std::string value = header.value(); if (input_.send_request_body() && header.key() == "content-length") { value = std::to_string(request_body.size()); }