diff --git a/bazel/googletest.patch b/bazel/googletest.patch index 502ef58b3e482..421564b9db38b 100644 --- a/bazel/googletest.patch +++ b/bazel/googletest.patch @@ -1,13 +1,44 @@ +From b217f1131adb21e2c1193782d2659ae03840659a Mon Sep 17 00:00:00 2001 +From: Rohit Agrawal +Date: Sat, 3 May 2025 00:29:17 +0900 +Subject: [PATCH] Bazel Fixes + +--- + BUILD.bazel | 17 ----------------- + 1 file changed, 17 deletions(-) + diff --git a/BUILD.bazel b/BUILD.bazel -index 8099642a85..3598661079 100644 +index 53501454ce..fff6e491e7 100644 --- a/BUILD.bazel +++ b/BUILD.bazel -@@ -40,7 +40,7 @@ exports_files(["LICENSE"]) - +@@ -56,12 +56,6 @@ config_setting( + constraint_values = ["@platforms//os:openbsd"], + ) + +-# NOTE: Fuchsia is not an officially supported platform. +-config_setting( +- name = "fuchsia", +- constraint_values = ["@platforms//os:fuchsia"], +-) +- config_setting( - name = "windows", -- constraint_values = ["@bazel_tools//platforms:windows"], -+ constraint_values = ["@platforms//os:windows"], + name = "msvc_compiler", + flag_values = { +@@ -157,17 +151,6 @@ cc_library( + "@re2//:re2", + ], + "//conditions:default": [], +- }) + select({ +- # `gtest-death-test.cc` has `EXPECT_DEATH` that spawns a process, +- # expects it to crash and inspects its logs with the given matcher, +- # so that's why these libraries are needed. +- # Otherwise, builds targeting Fuchsia would fail to compile. +- ":fuchsia": [ +- "@fuchsia_sdk//pkg/fdio", +- "@fuchsia_sdk//pkg/syslog", +- "@fuchsia_sdk//pkg/zx", +- ], +- "//conditions:default": [], + }), ) - config_setting( diff --git a/bazel/repositories.bzl b/bazel/repositories.bzl index 98e43761ced10..1e19c0859f44e 100644 --- a/bazel/repositories.bzl +++ b/bazel/repositories.bzl @@ -574,6 +574,10 @@ def _com_google_googletest(): "com_google_googletest", patches = ["@envoy//bazel:googletest.patch"], patch_args = ["-p1"], + repo_mapping = { + "@abseil-cpp": "@com_google_absl", + "@re2": "@com_googlesource_code_re2", + }, ) # TODO(jmarantz): replace the use of bind and external_deps with just diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index c0b35f2a618fc..bd428a2f0cf01 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -912,11 +912,11 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_url = "https://github.com/google/googletest", # Pick up fix for MOCK_METHOD compilation with clang-cl for Windows (resolved after 1.10.0) # see https://github.com/google/googletest/issues/2490 - version = "a4ab0abb93620ce26efad9de9296b73b16e88588", - sha256 = "7897bfaa5ad39a479177cfb5c3ce010184dbaee22a7c3727b212282871918751", + version = "52eb8108c5bdec04579160ae17225d66034bd723", + sha256 = "745c55415660044610f7fcd3af7a6420d5de16a7dbb9ebfe2e131275676232be", strip_prefix = "googletest-{version}", urls = ["https://github.com/google/googletest/archive/{version}.tar.gz"], - release_date = "2020-09-10", + release_date = "2025-04-30", use_category = ["test_only"], cpe = "cpe:2.3:a:google:google_test:*", license = "BSD-3-Clause", diff --git a/test/common/http/BUILD b/test/common/http/BUILD index 0a4fd0d4c333a..66a9fe8892366 100644 --- a/test/common/http/BUILD +++ b/test/common/http/BUILD @@ -293,6 +293,7 @@ envoy_cc_test( # Split to avoid compiler OOM, especially on ASAN. "conn_manager_impl_test.cc", "conn_manager_impl_test_2.cc", + "conn_manager_impl_test_3.cc", ], rbe_pool = "2core", shard_count = 5, @@ -305,6 +306,20 @@ envoy_cc_test( ], ) +envoy_cc_test( + name = "conn_manager_misc_test", + srcs = ["conn_manager_misc_test.cc"], + rbe_pool = "2core", + shard_count = 5, + deps = [ + ":conn_manager_impl_test_base_lib", + ":custom_header_extension_lib", + "//envoy/network:proxy_protocol_options_lib", + "//test/extensions/filters/network/common/fuzz/utils:network_filter_fuzzer_fakes_lib", + "//test/server:utility_lib", + ], +) + envoy_cc_test( name = "conn_manager_utility_test", srcs = ["conn_manager_utility_test.cc"], diff --git a/test/common/http/codec_impl_fuzz_test.cc b/test/common/http/codec_impl_fuzz_test.cc index a8078357cb794..9f023872455b3 100644 --- a/test/common/http/codec_impl_fuzz_test.cc +++ b/test/common/http/codec_impl_fuzz_test.cc @@ -31,8 +31,11 @@ #include "quiche/common/platform/api/quiche_test.h" using testing::_; +using testing::AnyNumber; +using testing::Between; using testing::Invoke; using testing::InvokeWithoutArgs; +using testing::Return; namespace Envoy { namespace Http { @@ -427,7 +430,7 @@ class HttpStream : public LinkedObject { [&] { directionalAction(response_, stream_action.dispatching_action()); })); } else if (request_action == test::common::http::DirectionalAction::kData) { EXPECT_CALL(request_.request_decoder_, decodeData(_, _)) - .Times(testing::AtLeast(1)) + .Times(AnyNumber()) .WillRepeatedly(InvokeWithoutArgs([&] { // Only simulate response action if the stream action is active // otherwise the expectation could trigger in other moments diff --git a/test/common/http/conn_manager_impl_test.cc b/test/common/http/conn_manager_impl_test.cc index ed01f260adb5f..e12b10cc0ba92 100644 --- a/test/common/http/conn_manager_impl_test.cc +++ b/test/common/http/conn_manager_impl_test.cc @@ -7,8 +7,6 @@ #include "test/test_common/logging.h" #include "test/test_common/test_runtime.h" -#include "conn_manager_impl_test_base.h" - using testing::_; using testing::An; using testing::AnyNumber; @@ -2715,87 +2713,6 @@ TEST_F(HttpConnectionManagerImplTest, TestPeriodicAccessLogging) { filter->callbacks_->encodeHeaders(std::move(response_headers), true, "details"); } -class StreamErrorOnInvalidHttpMessageTest : public HttpConnectionManagerImplTest { -public: - void sendInvalidRequestAndVerifyConnectionState(bool stream_error_on_invalid_http_message, - bool send_complete_request = true) { - setup(); - - EXPECT_CALL(*codec_, dispatch(_)) - .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - decoder_ = &conn_manager_->newStream(response_encoder_); - - // These request headers are missing the necessary ":host" - RequestHeaderMapPtr headers{ - new TestRequestHeaderMapImpl{{":method", "GET"}, {":path", "/"}}}; - decoder_->decodeHeaders(std::move(headers), send_complete_request); - data.drain(0); - return Http::okStatus(); - })); - - auto* filter = new MockStreamFilter(); - EXPECT_CALL(filter_factory_, createFilterChain(_)) - .WillOnce(Invoke([&](FilterChainManager& manager) -> bool { - auto factory = createStreamFilterFactoryCb(StreamFilterSharedPtr{filter}); - manager.applyFilterFactoryCb({}, factory); - return true; - })); - EXPECT_CALL(*filter, setDecoderFilterCallbacks(_)); - EXPECT_CALL(*filter, setEncoderFilterCallbacks(_)); - - // codec stream error - EXPECT_CALL(response_encoder_, streamErrorOnInvalidHttpMessage()) - .WillOnce(Return(stream_error_on_invalid_http_message)); - EXPECT_CALL(*filter, encodeComplete()); - EXPECT_CALL(*filter, encodeHeaders(_, true)); - if (!stream_error_on_invalid_http_message) { - EXPECT_CALL(filter_callbacks_.connection_, close(_)).Times(AnyNumber()); - if (send_complete_request) { - // The request is complete, so we should not flush close. - EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::FlushWrite)) - .Times(AnyNumber()); - } else { - // If the request isn't complete, avoid a FIN/RST race with delay close. - EXPECT_CALL(filter_callbacks_.connection_, - close(Network::ConnectionCloseType::FlushWriteAndDelay)) - .Times(AnyNumber()); - } - } - EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) - .WillOnce(Invoke([&](const ResponseHeaderMap& headers, bool) -> void { - EXPECT_EQ("400", headers.getStatusValue()); - EXPECT_EQ("missing_host_header", - filter->decoder_callbacks_->streamInfo().responseCodeDetails().value()); - if (!stream_error_on_invalid_http_message) { - EXPECT_NE(nullptr, headers.Connection()); - EXPECT_EQ("close", headers.getConnectionValue()); - } else { - EXPECT_EQ(nullptr, headers.Connection()); - } - })); - - EXPECT_CALL(*filter, onStreamComplete()); - EXPECT_CALL(*filter, onDestroy()); - - Buffer::OwnedImpl fake_input; - conn_manager_->onData(fake_input, false); - } -}; - -TEST_F(StreamErrorOnInvalidHttpMessageTest, ConnectionTerminatedIfCodecStreamErrorIsFalse) { - sendInvalidRequestAndVerifyConnectionState(false); -} - -TEST_F(StreamErrorOnInvalidHttpMessageTest, - ConnectionTerminatedWithDelayIfCodecStreamErrorIsFalse) { - // Same as above, only with an incomplete request. - sendInvalidRequestAndVerifyConnectionState(false, false); -} - -TEST_F(StreamErrorOnInvalidHttpMessageTest, ConnectionOpenIfCodecStreamErrorIsTrue) { - sendInvalidRequestAndVerifyConnectionState(true); -} - TEST_F(HttpConnectionManagerImplTest, TestAccessLogSsl) { setup(SetupOpts().setSsl(true)); diff --git a/test/common/http/conn_manager_impl_test_2.cc b/test/common/http/conn_manager_impl_test_2.cc index 8b478653f9ba2..f1f82d7a8e11a 100644 --- a/test/common/http/conn_manager_impl_test_2.cc +++ b/test/common/http/conn_manager_impl_test_2.cc @@ -1,7 +1,6 @@ #include #include "test/common/http/conn_manager_impl_test_base.h" -#include "test/common/http/custom_header_extension.h" #include "test/extensions/filters/network/common/fuzz/utils/fakes.h" #include "test/server/utility.h" #include "test/test_common/logging.h" @@ -2074,2785 +2073,5 @@ TEST_F(HttpConnectionManagerImplTest, DownstreamRemoteResetRefused) { response_encoder_.stream_.resetStream(StreamResetReason::RemoteRefusedStreamReset); } -// Filter stops headers iteration without ending the stream, then injects a body later. -TEST_F(HttpConnectionManagerImplTest, FilterStopIterationInjectBody) { - setup(); - setupFilterChain(2, 2); - - // Decode filter 0 changes end_stream to false. - EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) - .WillOnce(Return(FilterHeadersStatus::StopIteration)); - EXPECT_CALL(*decoder_filters_[0], decodeComplete()); - EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - - // Kick off the incoming data. - startRequest(true); - - EXPECT_CALL(*decoder_filters_[1], decodeData(_, true)) - .WillOnce(Return(FilterDataStatus::Continue)); - EXPECT_CALL(*decoder_filters_[1], decodeComplete()); - - // Decode filter 0 injects request body later. - Buffer::OwnedImpl data("hello"); - decoder_filters_[0]->callbacks_->injectDecodedDataToFilterChain(data, true); - - // Encode filter 1 changes end_stream to false. - EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, true)) - .WillOnce(Return(FilterHeadersStatus::StopIteration)); - EXPECT_CALL(*encoder_filters_[1], encodeComplete()); - EXPECT_CALL(*encoder_filters_[0], encodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(response_encoder_, encodeHeaders(_, false)); - - decoder_filters_[1]->callbacks_->streamInfo().setResponseCodeDetails(""); - decoder_filters_[1]->callbacks_->encodeHeaders( - makeHeaderMap({{":status", "200"}}), true, "details"); - - EXPECT_CALL(*encoder_filters_[0], encodeData(_, true)) - .WillOnce(Return(FilterDataStatus::Continue)); - EXPECT_CALL(*encoder_filters_[0], encodeComplete()); - EXPECT_CALL(response_encoder_, encodeData(_, true)); - expectOnDestroy(); - - // Encode filter 1 injects request body later. - Buffer::OwnedImpl data2("hello"); - encoder_filters_[1]->callbacks_->injectEncodedDataToFilterChain(data2, true); -} - -// Filter continues headers iteration without ending the stream, then injects a body later. -TEST_F(HttpConnectionManagerImplTest, FilterContinueDontEndStreamInjectBody) { - setup(); - setupFilterChain(2, 2); - - // Decode filter 0 changes end_stream to false. - EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) - .WillOnce(Return(FilterHeadersStatus::ContinueAndDontEndStream)); - EXPECT_CALL(*decoder_filters_[0], decodeComplete()); - EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - - // Kick off the incoming data. - startRequest(true); - - EXPECT_CALL(*decoder_filters_[1], decodeData(_, true)) - .WillOnce(Return(FilterDataStatus::Continue)); - EXPECT_CALL(*decoder_filters_[1], decodeComplete()); - - // Decode filter 0 injects request body later. - Buffer::OwnedImpl data("hello"); - decoder_filters_[0]->callbacks_->injectDecodedDataToFilterChain(data, true); - - // Encode filter 1 changes end_stream to false. - EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, true)) - .WillOnce(Return(FilterHeadersStatus::ContinueAndDontEndStream)); - EXPECT_CALL(*encoder_filters_[1], encodeComplete()); - EXPECT_CALL(*encoder_filters_[0], encodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(response_encoder_, encodeHeaders(_, false)); - - decoder_filters_[1]->callbacks_->streamInfo().setResponseCodeDetails(""); - decoder_filters_[1]->callbacks_->encodeHeaders( - makeHeaderMap({{":status", "200"}}), true, "details"); - - EXPECT_CALL(*encoder_filters_[0], encodeData(_, true)) - .WillOnce(Return(FilterDataStatus::Continue)); - EXPECT_CALL(*encoder_filters_[0], encodeComplete()); - EXPECT_CALL(response_encoder_, encodeData(_, true)); - expectOnDestroy(); - - // Encode filter 1 injects request body later. - Buffer::OwnedImpl data2("hello"); - encoder_filters_[1]->callbacks_->injectEncodedDataToFilterChain(data2, true); -} - -TEST_F(HttpConnectionManagerImplTest, FilterAddBodyContinuation) { - setup(); - setupFilterChain(2, 2); - - EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) - .WillOnce(Return(FilterHeadersStatus::StopIteration)); - EXPECT_CALL(*decoder_filters_[0], decodeComplete()); - - // Kick off the incoming request. - startRequest(true); - - EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(*decoder_filters_[1], decodeData(_, true)) - .WillOnce(Return(FilterDataStatus::Continue)); - EXPECT_CALL(*decoder_filters_[1], decodeComplete()); - - Buffer::OwnedImpl data("hello"); - decoder_filters_[0]->callbacks_->addDecodedData(data, true); - decoder_filters_[0]->callbacks_->continueDecoding(); - - EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, true)) - .WillOnce(Return(FilterHeadersStatus::StopIteration)); - EXPECT_CALL(*encoder_filters_[1], encodeComplete()); - - decoder_filters_[1]->callbacks_->streamInfo().setResponseCodeDetails(""); - decoder_filters_[1]->callbacks_->encodeHeaders( - ResponseHeaderMapPtr{new TestResponseHeaderMapImpl{{":status", "200"}}}, true, "details"); - - EXPECT_CALL(*encoder_filters_[0], encodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(response_encoder_, encodeHeaders(_, false)); - EXPECT_CALL(*encoder_filters_[0], encodeData(_, true)) - .WillOnce(Return(FilterDataStatus::Continue)); - EXPECT_CALL(*encoder_filters_[0], encodeComplete()); - EXPECT_CALL(response_encoder_, encodeData(_, true)); - expectOnDestroy(); - - Buffer::OwnedImpl data2("hello"); - encoder_filters_[1]->callbacks_->addEncodedData(data2, true); - encoder_filters_[1]->callbacks_->continueEncoding(); -} - -// This test verifies proper sequences of decodeData() and encodeData() are called -// when all filers return "CONTINUE" in following case: -// -// 3 decode filters: -// -// filter0->decodeHeaders(_, true) -// return CONTINUE -// filter1->decodeHeaders(_, true) -// filter1->addDecodeData() -// return CONTINUE -// filter2->decodeHeaders(_, false) -// return CONTINUE -// filter2->decodeData(_, true) -// return CONTINUE -// -// filter0->decodeData(, true) is NOT called. -// filter1->decodeData(, true) is NOT called. -// -// 3 encode filters: -// -// filter2->encodeHeaders(_, true) -// return CONTINUE -// filter1->encodeHeaders(_, true) -// filter1->addEncodeData() -// return CONTINUE -// filter0->decodeHeaders(_, false) -// return CONTINUE -// filter0->decodeData(_, true) -// return CONTINUE -// -// filter2->encodeData(, true) is NOT called. -// filter1->encodeData(, true) is NOT called. -// -TEST_F(HttpConnectionManagerImplTest, AddDataWithAllContinue) { - setup(); - setupFilterChain(3, 3); - - EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(*decoder_filters_[0], decodeComplete()); - - EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, true)) - .WillOnce(InvokeWithoutArgs([&]() -> FilterHeadersStatus { - Buffer::OwnedImpl data2("hello"); - decoder_filters_[1]->callbacks_->addDecodedData(data2, true); - return FilterHeadersStatus::Continue; - })); - EXPECT_CALL(*decoder_filters_[1], decodeComplete()); - - EXPECT_CALL(*decoder_filters_[2], decodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(*decoder_filters_[2], decodeData(_, true)) - .WillOnce(Return(FilterDataStatus::Continue)); - EXPECT_CALL(*decoder_filters_[2], decodeComplete()); - - EXPECT_CALL(*decoder_filters_[0], decodeData(_, true)).Times(0); - EXPECT_CALL(*decoder_filters_[1], decodeData(_, true)).Times(0); - - // Kick off the incoming data. - startRequest(true); - - // For encode direction - EXPECT_CALL(*encoder_filters_[2], encodeHeaders(_, true)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(*encoder_filters_[2], encodeComplete()); - - EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, true)) - .WillOnce(InvokeWithoutArgs([&]() -> FilterHeadersStatus { - Buffer::OwnedImpl data2("goodbyte"); - encoder_filters_[1]->callbacks_->addEncodedData(data2, true); - return FilterHeadersStatus::Continue; - })); - EXPECT_CALL(*encoder_filters_[1], encodeComplete()); - - EXPECT_CALL(*encoder_filters_[0], encodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(response_encoder_, encodeHeaders(_, false)); - EXPECT_CALL(*encoder_filters_[0], encodeData(_, true)) - .WillOnce(Return(FilterDataStatus::Continue)); - EXPECT_CALL(*encoder_filters_[0], encodeComplete()); - EXPECT_CALL(response_encoder_, encodeData(_, true)); - expectOnDestroy(); - - EXPECT_CALL(*encoder_filters_[2], encodeData(_, true)).Times(0); - EXPECT_CALL(*encoder_filters_[1], encodeData(_, true)).Times(0); - - decoder_filters_[2]->callbacks_->streamInfo().setResponseCodeDetails(""); - decoder_filters_[2]->callbacks_->encodeHeaders( - ResponseHeaderMapPtr{new TestResponseHeaderMapImpl{{":status", "200"}}}, true, "details"); -} - -// This test verifies proper sequences of decodeData() and encodeData() are called -// when the first filter is "stopped" and "continue" in following case: -// -// 3 decode filters: -// -// filter0->decodeHeaders(_, true) -// return STOP -// filter0->continueDecoding() -// filter1->decodeHeaders(_, true) -// filter1->addDecodeData() -// return CONTINUE -// filter2->decodeHeaders(_, false) -// return CONTINUE -// filter2->decodeData(_, true) -// return CONTINUE -// -// filter0->decodeData(, true) is NOT called. -// filter1->decodeData(, true) is NOT called. -// -// 3 encode filters: -// -// filter2->encodeHeaders(_, true) -// return STOP -// filter2->continueEncoding() -// filter1->encodeHeaders(_, true) -// filter1->addEncodeData() -// return CONTINUE -// filter0->decodeHeaders(_, false) -// return CONTINUE -// filter0->decodeData(_, true) -// return CONTINUE -// -// filter2->encodeData(, true) is NOT called. -// filter1->encodeData(, true) is NOT called. -// -TEST_F(HttpConnectionManagerImplTest, AddDataWithStopAndContinue) { - setup(); - - setupFilterChain(3, 3); - - EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) - .WillOnce(Return(FilterHeadersStatus::StopIteration)); - EXPECT_CALL(*decoder_filters_[0], decodeComplete()); - - // Kick off the request. - startRequest(true); - - EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, true)) - .WillOnce(InvokeWithoutArgs([&]() -> FilterHeadersStatus { - Buffer::OwnedImpl data2("hello"); - decoder_filters_[1]->callbacks_->addDecodedData(data2, true); - return FilterHeadersStatus::Continue; - })); - EXPECT_CALL(*decoder_filters_[1], decodeComplete()); - - EXPECT_CALL(*decoder_filters_[2], decodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - // This fail, it is called twice. - EXPECT_CALL(*decoder_filters_[2], decodeData(_, true)) - .WillOnce(Return(FilterDataStatus::Continue)); - EXPECT_CALL(*decoder_filters_[2], decodeComplete()); - - EXPECT_CALL(*decoder_filters_[0], decodeData(_, true)).Times(0); - // This fail, it is called once - EXPECT_CALL(*decoder_filters_[1], decodeData(_, true)).Times(0); - - decoder_filters_[0]->callbacks_->continueDecoding(); - - // For encode direction - EXPECT_CALL(*encoder_filters_[2], encodeHeaders(_, true)) - .WillOnce(Return(FilterHeadersStatus::StopIteration)); - EXPECT_CALL(*encoder_filters_[2], encodeComplete()); - - decoder_filters_[2]->callbacks_->streamInfo().setResponseCodeDetails(""); - decoder_filters_[2]->callbacks_->encodeHeaders( - ResponseHeaderMapPtr{new TestResponseHeaderMapImpl{{":status", "200"}}}, true, "details"); - - EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, true)) - .WillOnce(InvokeWithoutArgs([&]() -> FilterHeadersStatus { - Buffer::OwnedImpl data2("goodbyte"); - encoder_filters_[1]->callbacks_->addEncodedData(data2, true); - return FilterHeadersStatus::Continue; - })); - EXPECT_CALL(*encoder_filters_[1], encodeComplete()); - - EXPECT_CALL(*encoder_filters_[0], encodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(response_encoder_, encodeHeaders(_, false)); - - EXPECT_CALL(*encoder_filters_[0], encodeData(_, true)) - .WillOnce(Return(FilterDataStatus::Continue)); - EXPECT_CALL(*encoder_filters_[0], encodeComplete()); - EXPECT_CALL(response_encoder_, encodeData(_, true)); - expectOnDestroy(); - - EXPECT_CALL(*encoder_filters_[2], encodeData(_, true)).Times(0); - EXPECT_CALL(*encoder_filters_[1], encodeData(_, true)).Times(0); - - encoder_filters_[2]->callbacks_->continueEncoding(); -} - -// This test verifies that when recreateStream is executed during the decodeHeader -// phase and returns StopIteration, executing continueDecoding does not proceed with the processing -// of subsequent filters. -TEST_F(HttpConnectionManagerImplTest, CannotContinueDecodingAfterRecreateStream) { - setup(); - decoder_filters_.push_back(new NiceMock()); - decoder_filters_.push_back(new NiceMock()); - - EXPECT_CALL(filter_factory_, createFilterChain(_)) - .WillOnce(Invoke([this](FilterChainManager& manager) -> bool { - bool applied_filters = false; - if (log_handler_.get()) { - auto factory = createLogHandlerFactoryCb(log_handler_); - manager.applyFilterFactoryCb({}, factory); - applied_filters = true; - } - for (int i = 0; i < 2; i++) { - auto factory = - createDecoderFilterFactoryCb(StreamDecoderFilterSharedPtr{decoder_filters_[i]}); - manager.applyFilterFactoryCb({}, factory); - applied_filters = true; - } - return applied_filters; - })) - .WillOnce(Return(true)); - - EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) - .WillOnce(InvokeWithoutArgs([&]() -> FilterHeadersStatus { - decoder_filters_[0]->callbacks_->recreateStream(nullptr); - return FilterHeadersStatus::StopIteration; - })); - - // Kick off the request. - startRequest(true); - - // Should not continue headers of filter 1. - EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, true)).Times(0); - decoder_filters_[0]->callbacks_->continueDecoding(); - filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); -} - -// This test verifies that when recreateStream is executed during the encodeHeader -// phase and returns StopIteration, executing continueEncoding does not proceed with the processing -// of subsequent filters. -TEST_F(HttpConnectionManagerImplTest, CannotContinueEncodingAfterRecreateStream) { - setup(); - decoder_filters_.push_back(new NiceMock()); - decoder_filters_.push_back(new NiceMock()); - encoder_filters_.push_back(new NiceMock()); - encoder_filters_.push_back(new NiceMock()); - - EXPECT_CALL(filter_factory_, createFilterChain(_)) - .WillOnce(Invoke([this](FilterChainManager& manager) -> bool { - bool applied_filters = false; - if (log_handler_.get()) { - auto factory = createLogHandlerFactoryCb(log_handler_); - manager.applyFilterFactoryCb({}, factory); - applied_filters = true; - } - for (int i = 0; i < 2; i++) { - auto factory = - createDecoderFilterFactoryCb(StreamDecoderFilterSharedPtr{decoder_filters_[i]}); - manager.applyFilterFactoryCb({}, factory); - applied_filters = true; - } - for (int i = 0; i < 2; i++) { - auto factory = - createEncoderFilterFactoryCb(StreamEncoderFilterSharedPtr{encoder_filters_[i]}); - manager.applyFilterFactoryCb({}, factory); - applied_filters = true; - } - return applied_filters; - })) - .WillOnce(Return(true)); - - EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, true)) - .WillOnce(Return(FilterHeadersStatus::StopIteration)); - - // Kick off the request. - startRequest(true); - - EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, true)) - .WillOnce(InvokeWithoutArgs([&]() -> FilterHeadersStatus { - decoder_filters_[1]->callbacks_->recreateStream(nullptr); - return FilterHeadersStatus::StopIteration; - })); - - decoder_filters_[1]->callbacks_->streamInfo().setResponseCodeDetails(""); - decoder_filters_[1]->callbacks_->encodeHeaders( - ResponseHeaderMapPtr{new TestResponseHeaderMapImpl{{":status", "200"}}}, true, "details"); - - // Should not continue headers of filter 0. - EXPECT_CALL(*encoder_filters_[0], encodeHeaders(_, true)).Times(0); - encoder_filters_[1]->callbacks_->continueEncoding(); - filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); -} - -// Use filter direct decode/encodeData() calls without trailers. -TEST_F(HttpConnectionManagerImplTest, FilterDirectDecodeEncodeDataNoTrailers) { - setup(); - EXPECT_CALL(*route_config_provider_.route_config_, route(_, _, _, _)); - setupFilterChain(2, 2); - - EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::StopIteration)); - - Buffer::OwnedImpl decode_buffer; - EXPECT_CALL(*decoder_filters_[0], decodeData(_, true)) - .WillOnce(Invoke([&](Buffer::Instance& data, bool) { - decode_buffer.move(data); - return FilterDataStatus::StopIterationNoBuffer; - })); - EXPECT_CALL(*decoder_filters_[0], decodeComplete()); - - // Kick off the request. - EXPECT_CALL(filter_callbacks_.connection_.stream_info_, protocol(Envoy::Http::Protocol::Http11)); - startRequest(true, "hello"); - - Buffer::OwnedImpl decoded_data_to_forward; - decoded_data_to_forward.move(decode_buffer, 2); - EXPECT_CALL(*decoder_filters_[1], decodeData(BufferStringEqual("he"), false)) - .WillOnce(Return(FilterDataStatus::StopIterationNoBuffer)); - decoder_filters_[0]->callbacks_->injectDecodedDataToFilterChain(decoded_data_to_forward, false); - - EXPECT_CALL(*decoder_filters_[1], decodeData(BufferStringEqual("llo"), true)) - .WillOnce(Return(FilterDataStatus::StopIterationNoBuffer)); - EXPECT_CALL(*decoder_filters_[1], decodeComplete()); - decoder_filters_[0]->callbacks_->injectDecodedDataToFilterChain(decode_buffer, true); - - // Response path. - EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(*encoder_filters_[0], encodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(response_encoder_, encodeHeaders(_, false)); - - Buffer::OwnedImpl encoder_buffer; - EXPECT_CALL(*encoder_filters_[1], encodeData(_, true)) - .WillOnce(Invoke([&](Buffer::Instance& data, bool) { - encoder_buffer.move(data); - return FilterDataStatus::StopIterationNoBuffer; - })); - EXPECT_CALL(*encoder_filters_[1], encodeComplete()); - - decoder_filters_[1]->callbacks_->streamInfo().setResponseCodeDetails(""); - decoder_filters_[1]->callbacks_->encodeHeaders( - ResponseHeaderMapPtr{new TestResponseHeaderMapImpl{{":status", "200"}}}, false, "details"); - Buffer::OwnedImpl response_body("response"); - decoder_filters_[1]->callbacks_->encodeData(response_body, true); - - Buffer::OwnedImpl encoded_data_to_forward; - encoded_data_to_forward.move(encoder_buffer, 3); - EXPECT_CALL(*encoder_filters_[0], encodeData(BufferStringEqual("res"), false)); - EXPECT_CALL(response_encoder_, encodeData(_, false)); - encoder_filters_[1]->callbacks_->injectEncodedDataToFilterChain(encoded_data_to_forward, false); - - EXPECT_CALL(*encoder_filters_[0], encodeData(BufferStringEqual("ponse"), true)); - EXPECT_CALL(*encoder_filters_[0], encodeComplete()); - EXPECT_CALL(response_encoder_, encodeData(_, true)); - expectOnDestroy(); - encoder_filters_[1]->callbacks_->injectEncodedDataToFilterChain(encoder_buffer, true); -} - -// Use filter direct decode/encodeData() calls with trailers. -TEST_F(HttpConnectionManagerImplTest, FilterDirectDecodeEncodeDataTrailers) { - InSequence s; - setup(); - - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - decoder_ = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{ - new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder_->decodeHeaders(std::move(headers), false); - - Buffer::OwnedImpl fake_data("hello"); - decoder_->decodeData(fake_data, false); - - RequestTrailerMapPtr trailers{new TestRequestTrailerMapImpl{{"foo", "bar"}}}; - decoder_->decodeTrailers(std::move(trailers)); - return Http::okStatus(); - })); - - EXPECT_CALL(*route_config_provider_.route_config_, route(_, _, _, _)); - setupFilterChain(2, 2); - - EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::StopIteration)); - - Buffer::OwnedImpl decode_buffer; - EXPECT_CALL(*decoder_filters_[0], decodeData(_, false)) - .WillOnce(Invoke([&](Buffer::Instance& data, bool) { - decode_buffer.move(data); - return FilterDataStatus::StopIterationNoBuffer; - })); - EXPECT_CALL(*decoder_filters_[0], decodeTrailers(_)) - .WillOnce(Return(FilterTrailersStatus::StopIteration)); - EXPECT_CALL(*decoder_filters_[0], decodeComplete()); - - // Kick off the incoming data. - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, false); - - Buffer::OwnedImpl decoded_data_to_forward; - decoded_data_to_forward.move(decode_buffer, 2); - EXPECT_CALL(*decoder_filters_[1], decodeData(BufferStringEqual("he"), false)) - .WillOnce(Return(FilterDataStatus::StopIterationNoBuffer)); - decoder_filters_[0]->callbacks_->injectDecodedDataToFilterChain(decoded_data_to_forward, false); - - EXPECT_CALL(*decoder_filters_[1], decodeData(BufferStringEqual("llo"), false)) - .WillOnce(Return(FilterDataStatus::StopIterationNoBuffer)); - decoder_filters_[0]->callbacks_->injectDecodedDataToFilterChain(decode_buffer, false); - - EXPECT_CALL(*decoder_filters_[1], decodeTrailers(_)); - EXPECT_CALL(*decoder_filters_[1], decodeComplete()); - decoder_filters_[0]->callbacks_->continueDecoding(); - - // Response path. - EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(*encoder_filters_[0], encodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(response_encoder_, encodeHeaders(_, false)); - - Buffer::OwnedImpl encoder_buffer; - EXPECT_CALL(*encoder_filters_[1], encodeData(_, false)) - .WillOnce(Invoke([&](Buffer::Instance& data, bool) { - encoder_buffer.move(data); - return FilterDataStatus::StopIterationNoBuffer; - })); - EXPECT_CALL(*encoder_filters_[1], encodeTrailers(_)) - .WillOnce(Return(FilterTrailersStatus::StopIteration)); - EXPECT_CALL(*encoder_filters_[1], encodeComplete()); - - decoder_filters_[1]->callbacks_->streamInfo().setResponseCodeDetails(""); - decoder_filters_[1]->callbacks_->encodeHeaders( - ResponseHeaderMapPtr{new TestResponseHeaderMapImpl{{":status", "200"}}}, false, "details"); - Buffer::OwnedImpl response_body("response"); - decoder_filters_[1]->callbacks_->encodeData(response_body, false); - decoder_filters_[1]->callbacks_->encodeTrailers( - ResponseTrailerMapPtr{new TestResponseTrailerMapImpl{{"some", "trailer"}}}); - - Buffer::OwnedImpl encoded_data_to_forward; - encoded_data_to_forward.move(encoder_buffer, 3); - EXPECT_CALL(*encoder_filters_[0], encodeData(BufferStringEqual("res"), false)); - EXPECT_CALL(response_encoder_, encodeData(_, false)); - encoder_filters_[1]->callbacks_->injectEncodedDataToFilterChain(encoded_data_to_forward, false); - - EXPECT_CALL(*encoder_filters_[0], encodeData(BufferStringEqual("ponse"), false)); - EXPECT_CALL(response_encoder_, encodeData(_, false)); - encoder_filters_[1]->callbacks_->injectEncodedDataToFilterChain(encoder_buffer, false); - - EXPECT_CALL(*encoder_filters_[0], encodeTrailers(_)); - EXPECT_CALL(*encoder_filters_[0], encodeComplete()); - EXPECT_CALL(response_encoder_, encodeTrailers(_)); - expectOnDestroy(); - encoder_filters_[1]->callbacks_->continueEncoding(); -} - -TEST_F(HttpConnectionManagerImplTest, MultipleFilters) { - InSequence s; - setup(); - - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - decoder_ = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{ - new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder_->decodeHeaders(std::move(headers), false); - - Buffer::OwnedImpl fake_data("hello"); - decoder_->decodeData(fake_data, false); - - Buffer::OwnedImpl fake_data2("world"); - decoder_->decodeData(fake_data2, true); - return Http::okStatus(); - })); - - EXPECT_CALL(*route_config_provider_.route_config_, route(_, _, _, _)); - setupFilterChain(3, 2); - - EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, false)) - .WillOnce(InvokeWithoutArgs([&]() -> FilterHeadersStatus { - EXPECT_EQ(route_config_provider_.route_config_->route_, - decoder_filters_[0]->callbacks_->route()); - EXPECT_EQ(ssl_connection_.get(), - decoder_filters_[0]->callbacks_->connection()->ssl().get()); - return FilterHeadersStatus::StopIteration; - })); - - EXPECT_CALL(*decoder_filters_[0], decodeData(_, false)) - .WillOnce(Return(FilterDataStatus::StopIterationAndBuffer)); - EXPECT_CALL(*decoder_filters_[0], decodeData(_, true)) - .WillOnce(Return(FilterDataStatus::StopIterationAndBuffer)); - EXPECT_CALL(*decoder_filters_[0], decodeComplete()); - - // Kick off the incoming data. - Buffer::OwnedImpl fake_input("1234"); - EXPECT_CALL(filter_callbacks_.connection_.stream_info_, protocol(Envoy::Http::Protocol::Http11)); - conn_manager_->onData(fake_input, false); - - // Mimic a decoder filter that trapped data and now sends it on, since the data was buffered - // by the first filter, we expect to get it in 1 decodeData() call. - EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, false)) - .WillOnce(InvokeWithoutArgs([&]() -> FilterHeadersStatus { - EXPECT_EQ(route_config_provider_.route_config_->route_, - decoder_filters_[1]->callbacks_->route()); - EXPECT_EQ(ssl_connection_.get(), - decoder_filters_[1]->callbacks_->connection()->ssl().get()); - return FilterHeadersStatus::StopIteration; - })); - EXPECT_CALL(*decoder_filters_[1], decodeData(_, true)) - .WillOnce(Return(FilterDataStatus::Continue)); - EXPECT_CALL(*decoder_filters_[1], decodeComplete()); - EXPECT_CALL(*decoder_filters_[2], decodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::StopIteration)); - EXPECT_CALL(*decoder_filters_[2], decodeData(_, true)) - .WillOnce(Return(FilterDataStatus::StopIterationNoBuffer)); - EXPECT_CALL(*decoder_filters_[2], decodeComplete()); - decoder_filters_[0]->callbacks_->continueDecoding(); - - // Now start encoding and mimic trapping in the encoding filter. - EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::StopIteration)); - EXPECT_CALL(*encoder_filters_[1], encodeData(_, false)) - .WillOnce(Return(FilterDataStatus::StopIterationAndBuffer)); - EXPECT_CALL(*encoder_filters_[1], encodeTrailers(_)) - .WillOnce(Return(FilterTrailersStatus::StopIteration)); - EXPECT_CALL(*encoder_filters_[1], encodeComplete()); - EXPECT_EQ(ssl_connection_.get(), encoder_filters_[1]->callbacks_->connection()->ssl().get()); - decoder_filters_[2]->callbacks_->streamInfo().setResponseCodeDetails(""); - decoder_filters_[2]->callbacks_->encodeHeaders( - ResponseHeaderMapPtr{new TestResponseHeaderMapImpl{{":status", "200"}}}, false, "details"); - Buffer::OwnedImpl response_body("response"); - decoder_filters_[2]->callbacks_->encodeData(response_body, false); - decoder_filters_[2]->callbacks_->encodeTrailers( - ResponseTrailerMapPtr{new TestResponseTrailerMapImpl{{"some", "trailer"}}}); - EXPECT_EQ(ssl_connection_.get(), decoder_filters_[2]->callbacks_->connection()->ssl().get()); - - // Now finish the encode. - EXPECT_CALL(*encoder_filters_[0], encodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(response_encoder_, encodeHeaders(_, false)); - EXPECT_CALL(*encoder_filters_[0], encodeData(_, false)) - .WillOnce(Return(FilterDataStatus::Continue)); - EXPECT_CALL(response_encoder_, encodeData(_, false)); - EXPECT_CALL(*encoder_filters_[0], encodeTrailers(_)) - .WillOnce(Return(FilterTrailersStatus::Continue)); - EXPECT_CALL(*encoder_filters_[0], encodeComplete()); - EXPECT_CALL(response_encoder_, encodeTrailers(_)); - expectOnDestroy(); - encoder_filters_[1]->callbacks_->continueEncoding(); - - EXPECT_EQ(ssl_connection_.get(), encoder_filters_[0]->callbacks_->connection()->ssl().get()); -} - -TEST(HttpConnectionManagerTracingStatsTest, verifyTracingStats) { - Stats::IsolatedStoreImpl stats; - ConnectionManagerTracingStats tracing_stats{CONN_MAN_TRACING_STATS(POOL_COUNTER(stats))}; - - ConnectionManagerImpl::chargeTracingStats(Tracing::Reason::ClientForced, tracing_stats); - EXPECT_EQ(1UL, tracing_stats.client_enabled_.value()); - - ConnectionManagerImpl::chargeTracingStats(Tracing::Reason::HealthCheck, tracing_stats); - ConnectionManagerImpl::chargeTracingStats(Tracing::Reason::NotTraceable, tracing_stats); - EXPECT_EQ(2UL, tracing_stats.not_traceable_.value()); - - ConnectionManagerImpl::chargeTracingStats(Tracing::Reason::Sampling, tracing_stats); - EXPECT_EQ(1UL, tracing_stats.random_sampling_.value()); -} - -TEST_F(HttpConnectionManagerImplTest, NoNewStreamWhenOverloaded) { - Server::OverloadActionState stop_accepting_requests(UnitFloat(0.8)); - ON_CALL(overload_manager_.overload_state_, - getState(Server::OverloadActionNames::get().StopAcceptingRequests)) - .WillByDefault(ReturnRef(stop_accepting_requests)); - - setup(); - - EXPECT_CALL(random_, random()) - .WillRepeatedly(Return(static_cast(Random::RandomGenerator::max()) * 0.5)); - - // 503 direct response when overloaded. - EXPECT_CALL(response_encoder_, encodeHeaders(_, false)) - .WillOnce(Invoke([](const ResponseHeaderMap& headers, bool) -> void { - EXPECT_EQ("503", headers.getStatusValue()); - })); - std::string response_body; - EXPECT_CALL(response_encoder_, encodeData(_, true)).WillOnce(AddBufferToString(&response_body)); - - startRequest(); - - EXPECT_EQ("envoy overloaded", response_body); - EXPECT_EQ(1U, stats_.named_.downstream_rq_overload_close_.value()); -} - -TEST_F(HttpConnectionManagerImplTest, DisableHttp1KeepAliveWhenOverloaded) { - Server::OverloadActionState disable_http_keep_alive(UnitFloat(0.8)); - ON_CALL(overload_manager_.overload_state_, - getState(Server::OverloadActionNames::get().DisableHttpKeepAlive)) - .WillByDefault(ReturnRef(disable_http_keep_alive)); - - codec_->protocol_ = Protocol::Http11; - setup(); - - EXPECT_CALL(random_, random()) - .WillRepeatedly(Return(static_cast(Random::RandomGenerator::max()) * 0.5)); - - std::shared_ptr filter(new NiceMock()); - EXPECT_CALL(filter_factory_, createFilterChain(_)) - .WillOnce(Invoke([&](FilterChainManager& manager) -> bool { - auto factory = createDecoderFilterFactoryCb(StreamDecoderFilterSharedPtr{filter}); - manager.applyFilterFactoryCb({}, factory); - return true; - })); - - EXPECT_CALL(*codec_, dispatch(_)) - .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - decoder_ = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{{":authority", "host"}, - {":path", "/"}, - {":method", "GET"}, - {"connection", "keep-alive"}}}; - decoder_->decodeHeaders(std::move(headers), true); - - ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; - filter->callbacks_->streamInfo().setResponseCodeDetails(""); - filter->callbacks_->encodeHeaders(std::move(response_headers), true, "details"); - - data.drain(4); - return Http::okStatus(); - })); - - EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) - .WillOnce(Invoke([](const ResponseHeaderMap& headers, bool) -> void { - EXPECT_EQ("close", headers.getConnectionValue()); - })); - - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, false); - EXPECT_EQ(1U, stats_.named_.downstream_cx_overload_disable_keepalive_.value()); -} - -// Verify that HTTP2 connections will receive a GOAWAY message when the overload action is -// triggered. -TEST_F(HttpConnectionManagerImplTest, DisableHttp2KeepAliveWhenOverloaded) { - Server::OverloadActionState disable_http_keep_alive = Server::OverloadActionState::saturated(); - ON_CALL(overload_manager_.overload_state_, - getState(Server::OverloadActionNames::get().DisableHttpKeepAlive)) - .WillByDefault(ReturnRef(disable_http_keep_alive)); - - codec_->protocol_ = Protocol::Http2; - setup(); - EXPECT_CALL(*codec_, shutdownNotice); - - std::shared_ptr filter(new NiceMock()); - EXPECT_CALL(filter_factory_, createFilterChain(_)) - .WillOnce(Invoke([&](FilterChainManager& manager) -> bool { - auto factory = createDecoderFilterFactoryCb(StreamDecoderFilterSharedPtr{filter}); - manager.applyFilterFactoryCb({}, factory); - return true; - })); - - EXPECT_CALL(*codec_, dispatch(_)) - .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - decoder_ = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{{":authority", "host"}, - {":path", "/"}, - {":method", "GET"}, - {"connection", "keep-alive"}}}; - decoder_->decodeHeaders(std::move(headers), true); - - ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; - filter->callbacks_->streamInfo().setResponseCodeDetails(""); - filter->callbacks_->encodeHeaders(std::move(response_headers), true, "details"); - - data.drain(4); - return Http::okStatus(); - })); - - EXPECT_CALL(response_encoder_, encodeHeaders(_, true)); - - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, false); - Mock::VerifyAndClearExpectations(codec_); - EXPECT_EQ(1, stats_.named_.downstream_cx_overload_disable_keepalive_.value()); -} - -TEST_F(HttpConnectionManagerImplTest, CodecCreationLoadShedPointCanCloseConnection) { - Server::MockLoadShedPoint close_connection_creating_codec_point; - EXPECT_CALL(overload_manager_, - getLoadShedPoint(Server::LoadShedPointName::get().HcmCodecCreation)) - .WillOnce(Return(&close_connection_creating_codec_point)); - EXPECT_CALL(overload_manager_, - getLoadShedPoint(Server::LoadShedPointName::get().HcmDecodeHeaders)) - .WillOnce(Return(nullptr)); - - setup(); - - EXPECT_CALL(close_connection_creating_codec_point, shouldShedLoad()).WillOnce(Return(true)); - EXPECT_CALL(filter_callbacks_.connection_, close(_, _)); - - Buffer::OwnedImpl fake_input("hello"); - conn_manager_->onData(fake_input, false); - - delete codec_; - EXPECT_EQ(1U, stats_.named_.downstream_rq_overload_close_.value()); - EXPECT_TRUE(filter_callbacks_.connection().streamInfo().hasResponseFlag( - StreamInfo::CoreResponseFlag::OverloadManager)); -} - -TEST_F(HttpConnectionManagerImplTest, CodecCreationLoadShedPointBypasscheck) { - Server::MockLoadShedPoint close_connection_creating_codec_point; - EXPECT_CALL(overload_manager_, - getLoadShedPoint(Server::LoadShedPointName::get().HcmCodecCreation)) - .WillOnce(Return(&close_connection_creating_codec_point)); - EXPECT_CALL(overload_manager_, - getLoadShedPoint(Server::LoadShedPointName::get().HcmDecodeHeaders)) - .WillOnce(Return(nullptr)); - EXPECT_CALL(overload_manager_, - getLoadShedPoint(Server::LoadShedPointName::get().HttpDownstreamFilterCheck)) - .WillOnce(Return(nullptr)); - - setup(); - - EXPECT_CALL(close_connection_creating_codec_point, shouldShedLoad()).WillOnce(Return(false)); - - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { - conn_manager_->newStream(response_encoder_); - data.drain(2); - return Http::okStatus(); - })); - - Buffer::OwnedImpl fake_input("12"); - conn_manager_->onData(fake_input, false); - conn_manager_->onEvent(Network::ConnectionEvent::RemoteClose); - EXPECT_EQ(0U, stats_.named_.downstream_rq_overload_close_.value()); - EXPECT_FALSE(filter_callbacks_.connection().streamInfo().hasResponseFlag( - StreamInfo::CoreResponseFlag::OverloadManager)); -} - -TEST_F(HttpConnectionManagerImplTest, DecodeHeaderLoadShedPointCanRejectNewStreams) { - Server::MockLoadShedPoint accept_new_stream_point; - EXPECT_CALL(overload_manager_, - getLoadShedPoint(Server::LoadShedPointName::get().HcmDecodeHeaders)) - .WillOnce(Return(&accept_new_stream_point)); - EXPECT_CALL(overload_manager_, - getLoadShedPoint(Server::LoadShedPointName::get().HcmCodecCreation)) - .WillOnce(Return(nullptr)); - EXPECT_CALL(overload_manager_, - getLoadShedPoint(Server::LoadShedPointName::get().HttpDownstreamFilterCheck)) - .WillRepeatedly(Return(nullptr)); - - setup(); - setupFilterChain(1, 0); - - EXPECT_CALL(accept_new_stream_point, shouldShedLoad()).WillOnce(Return(true)); - - // 503 direct response when overloaded. - EXPECT_CALL(response_encoder_, encodeHeaders(_, false)) - .WillOnce(Invoke([](const ResponseHeaderMap& headers, bool) -> void { - EXPECT_EQ("503", headers.getStatusValue()); - })); - std::string response_body; - EXPECT_CALL(response_encoder_, encodeData(_, true)).WillOnce(AddBufferToString(&response_body)); - - startRequest(); - - EXPECT_EQ("envoy overloaded", response_body); - EXPECT_EQ(1U, stats_.named_.downstream_rq_overload_close_.value()); - - // Let the load shed point allow a new stream. - EXPECT_CALL(accept_new_stream_point, shouldShedLoad()).WillOnce(Return(false)); - EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)); - EXPECT_CALL(*decoder_filters_[0], decodeComplete()); - startRequest(true); - - // Clean up. - EXPECT_CALL(response_encoder_, encodeHeaders(_, true)); - expectOnDestroy(); - - decoder_filters_[0]->callbacks_->streamInfo().setResponseCodeDetails(""); - decoder_filters_[0]->callbacks_->encodeHeaders( - ResponseHeaderMapPtr{new TestResponseHeaderMapImpl{{":status", "200"}}}, true, "details"); -} - -TEST_F(HttpConnectionManagerImplTest, TestStopAllIterationAndBufferOnDecodingPathFirstFilter) { - setup(SetupOpts().setTracing(false)); - setUpEncoderAndDecoder(true, true); - - // Kick off the incoming data. - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, false); - - // Verify that once the decoder_filters_[0]'s continueDecoding() is called, decoder_filters_[1]'s - // decodeHeaders() is called, and both filters receive data and trailers consequently. - EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, _)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(*decoder_filters_[0], decodeData(_, _)).WillOnce(Return(FilterDataStatus::Continue)); - EXPECT_CALL(*decoder_filters_[1], decodeData(_, _)).WillOnce(Return(FilterDataStatus::Continue)); - EXPECT_CALL(*decoder_filters_[0], decodeTrailers(_)) - .WillOnce(Return(FilterTrailersStatus::Continue)); - EXPECT_CALL(*decoder_filters_[1], decodeTrailers(_)) - .WillOnce(Return(FilterTrailersStatus::Continue)); - EXPECT_CALL(*decoder_filters_[1], decodeComplete()); - decoder_filters_[0]->callbacks_->continueDecoding(); - - doRemoteClose(); -} - -TEST_F(HttpConnectionManagerImplTest, TestStopAllIterationAndBufferOnDecodingPathSecondFilter) { - setup(SetupOpts().setTracing(false)); - setUpEncoderAndDecoder(true, false); - - // Verify headers go through both filters, and data and trailers go through the first filter only. - EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, _)) - .WillOnce(Return(FilterHeadersStatus::StopAllIterationAndBuffer)); - EXPECT_CALL(*decoder_filters_[0], decodeData(_, _)).WillOnce(Return(FilterDataStatus::Continue)); - EXPECT_CALL(*decoder_filters_[0], decodeTrailers(_)) - .WillOnce(Return(FilterTrailersStatus::Continue)); - // Kick off the incoming data. - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, false); - - // Verify that once the decoder_filters_[1]'s continueDecoding() is called, both data and trailers - // go through the second filter. - EXPECT_CALL(*decoder_filters_[1], decodeData(_, _)).WillOnce(Return(FilterDataStatus::Continue)); - EXPECT_CALL(*decoder_filters_[1], decodeTrailers(_)) - .WillOnce(Return(FilterTrailersStatus::Continue)); - EXPECT_CALL(*decoder_filters_[1], decodeComplete()); - decoder_filters_[1]->callbacks_->continueDecoding(); - - doRemoteClose(); -} - -TEST_F(HttpConnectionManagerImplTest, TestStopAllIterationAndBufferOnEncodingPath) { - setup(SetupOpts().setTracing(false)); - setUpEncoderAndDecoder(false, false); - sendRequestHeadersAndData(); - - // encoder_filters_[1] is the first filter in the chain. - EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, false)) - .WillOnce(Invoke([&](HeaderMap&, bool) -> FilterHeadersStatus { - return FilterHeadersStatus::StopAllIterationAndBuffer; - })); - ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; - decoder_filters_[0]->callbacks_->streamInfo().setResponseCodeDetails(""); - decoder_filters_[0]->callbacks_->encodeHeaders(std::move(response_headers), false, "details"); - - // Invoke encodeData while all iteration is stopped and make sure the filters do not have - // encodeData called. - EXPECT_CALL(*encoder_filters_[0], encodeData(_, _)).Times(0); - EXPECT_CALL(*encoder_filters_[1], encodeData(_, _)).Times(0); - Buffer::OwnedImpl response_body("response"); - decoder_filters_[0]->callbacks_->encodeData(response_body, false); - decoder_filters_[0]->callbacks_->encodeTrailers( - ResponseTrailerMapPtr{new TestResponseTrailerMapImpl{{"some", "trailer"}}}); - - // Verify that once encoder_filters_[1]'s continueEncoding() is called, encoder_filters_[0]'s - // encodeHeaders() is called, and both filters receive data and trailers consequently. - EXPECT_CALL(*encoder_filters_[0], encodeHeaders(_, _)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(response_encoder_, encodeHeaders(_, false)); - EXPECT_CALL(*encoder_filters_[1], encodeData(_, _)).WillOnce(Return(FilterDataStatus::Continue)); - EXPECT_CALL(*encoder_filters_[0], encodeData(_, _)).WillOnce(Return(FilterDataStatus::Continue)); - EXPECT_CALL(response_encoder_, encodeData(_, _)); - EXPECT_CALL(*encoder_filters_[1], encodeTrailers(_)) - .WillOnce(Return(FilterTrailersStatus::Continue)); - EXPECT_CALL(*encoder_filters_[0], encodeTrailers(_)) - .WillOnce(Return(FilterTrailersStatus::Continue)); - EXPECT_CALL(response_encoder_, encodeTrailers(_)); - EXPECT_CALL(*encoder_filters_[0], encodeComplete()); - EXPECT_CALL(*encoder_filters_[1], encodeComplete()); - expectOnDestroy(); - encoder_filters_[1]->callbacks_->continueEncoding(); -} - -TEST_F(HttpConnectionManagerImplTest, InboundOnlyDrainNoConnectionCloseForOutbound) { - std::string yaml = R"EOF( -address: - socket_address: { address: 127.0.0.1, port_value: 1234 } -metadata: { filter_metadata: { com.bar.foo: { baz: test_value } } } -traffic_direction: OUTBOUND - )EOF"; - auto cfg = Server::parseListenerFromV3Yaml(yaml); - const Network::ListenerInfo& listener_info = Server::Configuration::FakeListenerInfo(cfg); - EXPECT_CALL(factory_context_, listenerInfo()).WillOnce(ReturnRef(listener_info)); - setup(); - - // In this scenario, we have an inbound only drain, but the conn manager for an outbound listener - // is checking to see if it should drain. We set the expectation here that the answer is no, - // so we SHOULDN'T see a connection close header. - EXPECT_CALL(drain_close_, drainClose(Network::DrainDirection::All)).WillOnce(Return(false)); - - std::shared_ptr filter(new NiceMock()); - EXPECT_CALL(filter_factory_, createFilterChain(_)) - .WillOnce(Invoke([&](FilterChainManager& manager) -> bool { - auto factory = createDecoderFilterFactoryCb(StreamDecoderFilterSharedPtr{filter}); - manager.applyFilterFactoryCb({}, factory); - return true; - })); - - EXPECT_CALL(*codec_, dispatch(_)) - .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - decoder_ = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{{":authority", "host"}, - {":path", "/"}, - {":method", "GET"}, - {"connection", "keep-alive"}}}; - decoder_->decodeHeaders(std::move(headers), true); - - ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; - filter->callbacks_->streamInfo().setResponseCodeDetails(""); - filter->callbacks_->encodeHeaders(std::move(response_headers), true, "details"); - - data.drain(4); - return Http::okStatus(); - })); - - EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) - .WillOnce(Invoke([](const ResponseHeaderMap& headers, bool) -> void { - EXPECT_NE("close", headers.getConnectionValue()); - })); - - Buffer::OwnedImpl fake_input; - conn_manager_->onData(fake_input, false); -} - -TEST_F(HttpConnectionManagerImplTest, InboundOnlyDrainConnectionCloseForInbound) { - std::string yaml = R"EOF( -address: - socket_address: { address: 127.0.0.1, port_value: 1234 } -metadata: { filter_metadata: { com.bar.foo: { baz: test_value } } } -traffic_direction: INBOUND - )EOF"; - auto cfg = Server::parseListenerFromV3Yaml(yaml); - const Network::ListenerInfo& listener_info = Server::Configuration::FakeListenerInfo(cfg); - EXPECT_CALL(factory_context_, listenerInfo()).WillOnce(ReturnRef(listener_info)); - setup(); - - // In this scenario, we have an inbound only drain, and the conn manager for an inbound listener - // is checking to see if it should drain. We set the expectation here that the answer is yes, - // so we SHOULD see a connection close header. - EXPECT_CALL(drain_close_, drainClose(Network::DrainDirection::InboundOnly)) - .WillOnce(Return(true)); - - std::shared_ptr filter(new NiceMock()); - EXPECT_CALL(filter_factory_, createFilterChain(_)) - .WillOnce(Invoke([&](FilterChainManager& manager) -> bool { - auto factory = createDecoderFilterFactoryCb(StreamDecoderFilterSharedPtr{filter}); - manager.applyFilterFactoryCb({}, factory); - return true; - })); - - EXPECT_CALL(*codec_, dispatch(_)) - .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - decoder_ = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{{":authority", "host"}, - {":path", "/"}, - {":method", "GET"}, - {"connection", "keep-alive"}}}; - decoder_->decodeHeaders(std::move(headers), true); - - ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; - filter->callbacks_->streamInfo().setResponseCodeDetails(""); - filter->callbacks_->encodeHeaders(std::move(response_headers), true, "details"); - - data.drain(4); - return Http::okStatus(); - })); - - EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) - .WillOnce(Invoke([](const ResponseHeaderMap& headers, bool) -> void { - EXPECT_EQ("close", headers.getConnectionValue()); - })); - - Buffer::OwnedImpl fake_input; - conn_manager_->onData(fake_input, false); -} - -TEST_F(HttpConnectionManagerImplTest, DisableKeepAliveWhenDraining) { - setup(); - - EXPECT_CALL(drain_close_, drainClose(Network::DrainDirection::All)).WillOnce(Return(true)); - - std::shared_ptr filter(new NiceMock()); - EXPECT_CALL(filter_factory_, createFilterChain(_)) - .WillOnce(Invoke([&](FilterChainManager& manager) -> bool { - auto factory = createDecoderFilterFactoryCb(StreamDecoderFilterSharedPtr{filter}); - manager.applyFilterFactoryCb({}, factory); - return true; - })); - - EXPECT_CALL(*codec_, dispatch(_)) - .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - decoder_ = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{{":authority", "host"}, - {":path", "/"}, - {":method", "GET"}, - {"connection", "keep-alive"}}}; - decoder_->decodeHeaders(std::move(headers), true); - - ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; - filter->callbacks_->streamInfo().setResponseCodeDetails(""); - filter->callbacks_->encodeHeaders(std::move(response_headers), true, "details"); - - data.drain(4); - return Http::okStatus(); - })); - - EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) - .WillOnce(Invoke([](const ResponseHeaderMap& headers, bool) -> void { - EXPECT_EQ("close", headers.getConnectionValue()); - })); - - Buffer::OwnedImpl fake_input; - conn_manager_->onData(fake_input, false); -} - -TEST_F(HttpConnectionManagerImplTest, TestSessionTrace) { - setup(); - - // Set up the codec. - EXPECT_CALL(*codec_, dispatch(_)) - .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - data.drain(4); - return Http::okStatus(); - })); - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, false); - - setupFilterChain(1, 1); - - // Create a new stream - decoder_ = &conn_manager_->newStream(response_encoder_); - - // Send headers to that stream, and verify we both set and clear the tracked object. - { - RequestHeaderMapPtr headers{ - new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "POST"}}}; - - EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, pushTrackedObject(_)) - .Times(1) - .WillOnce(Invoke([](const ScopeTrackedObject* object) -> void { - ASSERT(object != nullptr); // On the first call, this should be the active stream. - std::stringstream out; - object->dumpState(out); - std::string state = out.str(); - EXPECT_THAT(state, - testing::HasSubstr("filter_manager_callbacks_.requestHeaders(): null")); - EXPECT_THAT(state, testing::HasSubstr("protocol_: 1")); - })); - EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, popTrackedObject(_)); - EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, false)) - .WillOnce(Invoke([](HeaderMap&, bool) -> FilterHeadersStatus { - return FilterHeadersStatus::StopIteration; - })); - decoder_->decodeHeaders(std::move(headers), false); - } - - // Send trailers to that stream, and verify by this point headers are in logged state. - { - RequestTrailerMapPtr trailers{new TestRequestTrailerMapImpl{{"foo", "bar"}}}; - EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, pushTrackedObject(_)) - .Times(1) - .WillOnce(Invoke([](const ScopeTrackedObject* object) -> void { - ASSERT(object != nullptr); // On the first call, this should be the active stream. - std::stringstream out; - object->dumpState(out); - std::string state = out.str(); - EXPECT_THAT(state, testing::HasSubstr("filter_manager_callbacks_.requestHeaders(): \n")); - EXPECT_THAT(state, testing::HasSubstr("':authority', 'host'\n")); - EXPECT_THAT(state, testing::HasSubstr("protocol_: 1")); - })); - EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, popTrackedObject(_)); - EXPECT_CALL(*decoder_filters_[0], decodeComplete()); - EXPECT_CALL(*decoder_filters_[0], decodeTrailers(_)) - .WillOnce(Return(FilterTrailersStatus::StopIteration)); - decoder_->decodeTrailers(std::move(trailers)); - } - - expectOnDestroy(); - filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); -} - -// SRDS no scope found. -TEST_F(HttpConnectionManagerImplTest, TestSrdsRouteNotFound) { - setup(SetupOpts().setUseSrds(true)); - setupFilterChain(1, 0); // Recreate the chain for second stream. - - EXPECT_CALL(*static_cast(scopeKeyBuilder().ptr()), - computeScopeKey(_)) - .Times(2); - EXPECT_CALL(*static_cast( - scopedRouteConfigProvider()->config().get()), - getRouteConfig(_)) - .Times(2) - .WillRepeatedly(Return(nullptr)); - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { - decoder_ = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ - {":authority", "host"}, {":method", "GET"}, {":path", "/foo"}}}; - decoder_->decodeHeaders(std::move(headers), true); - data.drain(4); - return Http::okStatus(); - })); - - EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) - .WillOnce(InvokeWithoutArgs([&]() -> FilterHeadersStatus { - EXPECT_EQ(nullptr, decoder_filters_[0]->callbacks_->route()); - return FilterHeadersStatus::StopIteration; - })); - EXPECT_CALL(*decoder_filters_[0], decodeComplete()); // end_stream=true. - - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, false); - - expectOnDestroy(); - filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); -} - -// SRDS updating scopes affects routing. -TEST_F(HttpConnectionManagerImplTest, TestSrdsUpdate) { - setup(SetupOpts().setUseSrds(true)); - - EXPECT_CALL(*static_cast(scopeKeyBuilder().ptr()), - computeScopeKey(_)) - .Times(3); - EXPECT_CALL(*static_cast( - scopedRouteConfigProvider()->config().get()), - getRouteConfig(_)) - .Times(3) - .WillOnce(Return(nullptr)) - .WillOnce(Return(nullptr)) // refreshCachedRoute first time. - .WillOnce(Return(route_config_)); // triggered by callbacks_->route(), SRDS now updated. - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { - decoder_ = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ - {":authority", "host"}, {":method", "GET"}, {":path", "/foo"}}}; - decoder_->decodeHeaders(std::move(headers), true); - data.drain(4); - return Http::okStatus(); - })); - const std::string fake_cluster1_name = "fake_cluster1"; - std::shared_ptr route1 = std::make_shared>(); - EXPECT_CALL(route1->route_entry_, clusterName()).WillRepeatedly(ReturnRef(fake_cluster1_name)); - std::shared_ptr fake_cluster1 = - std::make_shared>(); - EXPECT_CALL(cluster_manager_, getThreadLocalCluster(_)).WillOnce(Return(fake_cluster1.get())); - EXPECT_CALL(*route_config_, route(_, _, _, _)).WillOnce(Return(route1)); - // First no-scope-found request will be handled by decoder_filters_[0]. - setupFilterChain(1, 0); - EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) - .WillOnce(InvokeWithoutArgs([&]() -> FilterHeadersStatus { - EXPECT_EQ(nullptr, decoder_filters_[0]->callbacks_->route()); - - // Clear route and next call on callbacks_->route() will trigger a re-snapping of the - // snapped_route_config_. - decoder_filters_[0]->callbacks_->downstreamCallbacks()->clearRouteCache(); - - // Now route config provider returns something. - EXPECT_EQ(route1, decoder_filters_[0]->callbacks_->route()); - EXPECT_EQ(route1, decoder_filters_[0]->callbacks_->streamInfo().route()); - EXPECT_EQ(fake_cluster1->info(), decoder_filters_[0]->callbacks_->clusterInfo()); - return FilterHeadersStatus::StopIteration; - - return FilterHeadersStatus::StopIteration; - })); - EXPECT_CALL(*decoder_filters_[0], decodeComplete()); // end_stream=true. - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, false); - - expectOnDestroy(); - filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); -} - -// SRDS Scope header update cause cross-scope reroute. -TEST_F(HttpConnectionManagerImplTest, TestSrdsCrossScopeReroute) { - setup(SetupOpts().setUseSrds(true)); - - std::shared_ptr route_config1 = - std::make_shared>(); - std::shared_ptr route_config2 = - std::make_shared>(); - std::shared_ptr route1 = std::make_shared>(); - std::shared_ptr route2 = std::make_shared>(); - EXPECT_CALL(*route_config1, route(_, _, _, _)).WillRepeatedly(Return(route1)); - EXPECT_CALL(*route_config2, route(_, _, _, _)).WillRepeatedly(Return(route2)); - EXPECT_CALL(*static_cast(scopeKeyBuilder().ptr()), - computeScopeKey(_)) - .Times(3) - .WillRepeatedly(Invoke([&](const HeaderMap& headers) -> Router::ScopeKeyPtr { - auto& test_headers = dynamic_cast(headers); - if (test_headers.get_("scope_key") == "foo") { - Router::ScopeKey key; - return std::make_unique(std::move(key)); - } - return nullptr; - })); - EXPECT_CALL(*static_cast( - scopedRouteConfigProvider()->config().get()), - getRouteConfig(_)) - // 1. Snap scoped route config; - // 2. refreshCachedRoute (both in decodeHeaders(headers,end_stream); - // 3. then refreshCachedRoute triggered by decoder_filters_[1]->callbacks_->route(). - .Times(3) - .WillRepeatedly( - Invoke([&](const Router::ScopeKeyPtr& scope_key) -> Router::ConfigConstSharedPtr { - if (scope_key != nullptr) { - return route_config1; - } - return route_config2; - })); - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { - decoder_ = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ - {":authority", "host"}, {":method", "GET"}, {"scope_key", "foo"}, {":path", "/foo"}}}; - decoder_->decodeHeaders(std::move(headers), false); - data.drain(4); - return Http::okStatus(); - })); - setupFilterChain(2, 0); - EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, false)) - .WillOnce(Invoke([&](Http::HeaderMap& headers, bool) -> FilterHeadersStatus { - EXPECT_EQ(route1, decoder_filters_[0]->callbacks_->route()); - auto& test_headers = dynamic_cast(headers); - // Clear cached route and change scope key to "bar". - decoder_filters_[0]->callbacks_->downstreamCallbacks()->clearRouteCache(); - test_headers.remove("scope_key"); - test_headers.addCopy("scope_key", "bar"); - return FilterHeadersStatus::Continue; - })); - EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, false)) - .WillOnce(Invoke([&](Http::HeaderMap& headers, bool) -> FilterHeadersStatus { - auto& test_headers = dynamic_cast(headers); - EXPECT_EQ(test_headers.get_("scope_key"), "bar"); - // Route now switched to route2 as header "scope_key" has changed. - EXPECT_EQ(route2, decoder_filters_[1]->callbacks_->route()); - EXPECT_EQ(route2, decoder_filters_[1]->callbacks_->streamInfo().route()); - return FilterHeadersStatus::StopIteration; - })); - - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, false); - - expectOnDestroy(); - filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); -} - -// SRDS scoped RouteConfiguration found and route found. -TEST_F(HttpConnectionManagerImplTest, TestSrdsRouteFound) { - setup(SetupOpts().setUseSrds(true)); - setupFilterChain(1, 0); - - const std::string fake_cluster1_name = "fake_cluster1"; - std::shared_ptr route1 = std::make_shared>(); - EXPECT_CALL(route1->route_entry_, clusterName()).WillRepeatedly(ReturnRef(fake_cluster1_name)); - std::shared_ptr fake_cluster1 = - std::make_shared>(); - EXPECT_CALL(cluster_manager_, getThreadLocalCluster(_)).WillOnce(Return(fake_cluster1.get())); - EXPECT_CALL(*static_cast(scopeKeyBuilder().ptr()), - computeScopeKey(_)) - .Times(2); - EXPECT_CALL(*scopedRouteConfigProvider()->config(), getRouteConfig(_)) - // 1. decodeHeaders() snapping route config. - // 2. refreshCachedRoute() later in the same decodeHeaders(). - .Times(2); - EXPECT_CALL( - *static_cast( - scopedRouteConfigProvider()->config()->route_config_.get()), - route(_, _, _, _)) - .WillOnce(Return(route1)); - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { - decoder_ = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ - {":authority", "host"}, {":method", "GET"}, {":path", "/foo"}}}; - decoder_->decodeHeaders(std::move(headers), true); - data.drain(4); - return Http::okStatus(); - })); - EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) - .WillOnce(InvokeWithoutArgs([&]() -> FilterHeadersStatus { - EXPECT_EQ(route1, decoder_filters_[0]->callbacks_->route()); - EXPECT_EQ(route1, decoder_filters_[0]->callbacks_->streamInfo().route()); - EXPECT_EQ(fake_cluster1->info(), decoder_filters_[0]->callbacks_->clusterInfo()); - return FilterHeadersStatus::StopIteration; - })); - EXPECT_CALL(*decoder_filters_[0], decodeComplete()); - - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, false); - - expectOnDestroy(); - filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); -} - -TEST_F(HttpConnectionManagerImplTest, NewConnection) { - setup(SetupOpts().setUseSrds(true)); - - filter_callbacks_.connection_.stream_info_.protocol_ = absl::nullopt; - EXPECT_CALL(filter_callbacks_.connection_.stream_info_, protocol()); - EXPECT_EQ(Network::FilterStatus::Continue, conn_manager_->onNewConnection()); - EXPECT_EQ(0U, stats_.named_.downstream_cx_http3_total_.value()); - EXPECT_EQ(0U, stats_.named_.downstream_cx_http3_active_.value()); - - filter_callbacks_.connection_.stream_info_.protocol_ = Envoy::Http::Protocol::Http3; - codec_->protocol_ = Http::Protocol::Http3; - EXPECT_CALL(filter_callbacks_.connection_.stream_info_, protocol()); - EXPECT_CALL(*codec_, protocol()).Times(AtLeast(1)); - EXPECT_EQ(Network::FilterStatus::StopIteration, conn_manager_->onNewConnection()); - EXPECT_EQ(1U, stats_.named_.downstream_cx_http3_total_.value()); - EXPECT_EQ(1U, stats_.named_.downstream_cx_http3_active_.value()); -} - -TEST_F(HttpConnectionManagerImplTest, HeaderOnlyRequestAndResponseUsingHttp3) { - setup(SetupOpts().setTracing(false)); - - filter_callbacks_.connection_.stream_info_.protocol_ = Envoy::Http::Protocol::Http3; - codec_->protocol_ = Http::Protocol::Http3; - EXPECT_EQ(Network::FilterStatus::StopIteration, conn_manager_->onNewConnection()); - - // Store the basic request encoder during filter chain setup. - std::shared_ptr filter(new NiceMock()); - - EXPECT_CALL(*filter, decodeHeaders(_, true)) - .WillOnce(Invoke([&](RequestHeaderMap& headers, bool) -> FilterHeadersStatus { - EXPECT_NE(nullptr, headers.ForwardedFor()); - EXPECT_EQ("http", headers.getForwardedProtoValue()); - return FilterHeadersStatus::StopIteration; - })); - - EXPECT_CALL(*filter, setDecoderFilterCallbacks(_)); - - EXPECT_CALL(filter_factory_, createFilterChain(_)) - .WillOnce(Invoke([&](FilterChainManager& manager) -> bool { - auto factory = createDecoderFilterFactoryCb(StreamDecoderFilterSharedPtr{filter}); - manager.applyFilterFactoryCb({}, factory); - return true; - })); - - EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)); - - // Pretend to get a new stream and then fire a headers only request into it. Then we respond into - // the filter. - RequestDecoder& decoder = conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{ - new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder.decodeHeaders(std::move(headers), true); - - ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; - filter->callbacks_->streamInfo().setResponseCodeDetails(""); - filter->callbacks_->encodeHeaders(std::move(response_headers), true, "details"); - - EXPECT_EQ(1U, stats_.named_.downstream_rq_2xx_.value()); - EXPECT_EQ(1U, listener_stats_.downstream_rq_2xx_.value()); - EXPECT_EQ(1U, stats_.named_.downstream_rq_completed_.value()); - EXPECT_EQ(1U, listener_stats_.downstream_rq_completed_.value()); - EXPECT_EQ(1U, stats_.named_.downstream_cx_http3_total_.value()); - filter_callbacks_.connection_.dispatcher_.clearDeferredDeleteList(); - response_encoder_.stream_.codec_callbacks_->onCodecEncodeComplete(); - response_encoder_.stream_.codec_callbacks_ = nullptr; - conn_manager_.reset(); - EXPECT_EQ(0U, stats_.named_.downstream_cx_http3_active_.value()); -} - -namespace { - -class SimpleType : public StreamInfo::FilterState::Object { -public: - SimpleType(int value) : value_(value) {} - int access() const { return value_; } - -private: - int value_; -}; - -} // namespace - -TEST_F(HttpConnectionManagerImplTest, ConnectionFilterState) { - filter_callbacks_.connection_.stream_info_.filter_state_->setData( - "connection_provided_data", std::make_shared(555), - StreamInfo::FilterState::StateType::ReadOnly); - - setup(SetupOpts().setTracing(false)); - setupFilterChain(1, 0, /* num_requests = */ 3); - - EXPECT_CALL(*codec_, dispatch(_)) - .Times(2) - .WillRepeatedly(Invoke([&](Buffer::Instance&) -> Http::Status { - decoder_ = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ - {":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder_->decodeHeaders(std::move(headers), true); - return Http::okStatus(); - })); - { - InSequence s; - EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) - .WillOnce(Invoke([this](HeaderMap&, bool) -> FilterHeadersStatus { - decoder_filters_[0]->callbacks_->streamInfo().filterState()->setData( - "per_filter_chain", std::make_unique(1), - StreamInfo::FilterState::StateType::ReadOnly, - StreamInfo::FilterState::LifeSpan::FilterChain); - decoder_filters_[0]->callbacks_->streamInfo().filterState()->setData( - "per_downstream_request", std::make_unique(2), - StreamInfo::FilterState::StateType::ReadOnly, - StreamInfo::FilterState::LifeSpan::Request); - decoder_filters_[0]->callbacks_->streamInfo().filterState()->setData( - "per_downstream_connection", std::make_unique(3), - StreamInfo::FilterState::StateType::ReadOnly, - StreamInfo::FilterState::LifeSpan::Connection); - return FilterHeadersStatus::StopIteration; - })); - EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, true)) - .WillOnce(Invoke([this](HeaderMap&, bool) -> FilterHeadersStatus { - EXPECT_FALSE( - decoder_filters_[1]->callbacks_->streamInfo().filterState()->hasData( - "per_filter_chain")); - EXPECT_TRUE( - decoder_filters_[1]->callbacks_->streamInfo().filterState()->hasData( - "per_downstream_request")); - EXPECT_TRUE( - decoder_filters_[1]->callbacks_->streamInfo().filterState()->hasData( - "per_downstream_connection")); - EXPECT_TRUE( - decoder_filters_[1]->callbacks_->streamInfo().filterState()->hasData( - "connection_provided_data")); - return FilterHeadersStatus::StopIteration; - })); - EXPECT_CALL(*decoder_filters_[2], decodeHeaders(_, true)) - .WillOnce(Invoke([this](HeaderMap&, bool) -> FilterHeadersStatus { - EXPECT_FALSE( - decoder_filters_[2]->callbacks_->streamInfo().filterState()->hasData( - "per_filter_chain")); - EXPECT_FALSE( - decoder_filters_[2]->callbacks_->streamInfo().filterState()->hasData( - "per_downstream_request")); - EXPECT_TRUE( - decoder_filters_[2]->callbacks_->streamInfo().filterState()->hasData( - "per_downstream_connection")); - EXPECT_TRUE( - decoder_filters_[1]->callbacks_->streamInfo().filterState()->hasData( - "connection_provided_data")); - return FilterHeadersStatus::StopIteration; - })); - } - - EXPECT_CALL(*decoder_filters_[0], decodeComplete()); - EXPECT_CALL(*decoder_filters_[0], onStreamComplete()); - EXPECT_CALL(*decoder_filters_[0], onDestroy()); - EXPECT_CALL(*decoder_filters_[1], decodeComplete()); - EXPECT_CALL(*decoder_filters_[2], decodeComplete()); - - Buffer::OwnedImpl fake_input; - conn_manager_->onData(fake_input, false); - decoder_filters_[0]->callbacks_->recreateStream(nullptr); - conn_manager_->onData(fake_input, false); - - // The connection life time data should have been written to the connection filter state. - EXPECT_TRUE(filter_callbacks_.connection_.stream_info_.filter_state_->hasData( - "per_downstream_connection")); - EXPECT_CALL(*decoder_filters_[1], onStreamComplete()); - EXPECT_CALL(*decoder_filters_[1], onDestroy()); - EXPECT_CALL(*decoder_filters_[2], onStreamComplete()); - EXPECT_CALL(*decoder_filters_[2], onDestroy()); - filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); -} - -class HttpConnectionManagerImplDeathTest : public HttpConnectionManagerImplTest { -public: - Router::RouteConfigProvider* routeConfigProvider() override { - return route_config_provider2_.get(); - } - Config::ConfigProvider* scopedRouteConfigProvider() override { - return scoped_route_config_provider2_.get(); - } - OptRef scopeKeyBuilder() override { - return scope_key_builder2_ ? *scope_key_builder2_ : OptRef{}; - } - - std::shared_ptr route_config_provider2_; - std::shared_ptr scoped_route_config_provider2_; - std::unique_ptr scope_key_builder2_; -}; - -// HCM config can only have either RouteConfigProvider or ScopedRoutesConfigProvider. -TEST_F(HttpConnectionManagerImplDeathTest, InvalidConnectionManagerConfig) { - setup(); - - Buffer::OwnedImpl fake_input("1234"); - EXPECT_CALL(*codec_, dispatch(_)).WillRepeatedly(Invoke([&](Buffer::Instance&) -> Http::Status { - conn_manager_->newStream(response_encoder_); - return Http::okStatus(); - })); - // Either RDS or SRDS should be set. - EXPECT_DEBUG_DEATH(conn_manager_->onData(fake_input, false), - "Either routeConfigProvider or \\(scopedRouteConfigProvider and " - "scopeKeyBuilder\\) should be set in ConnectionManagerImpl."); - - route_config_provider2_ = std::make_shared>(); - - // Only route config provider valid. - EXPECT_NO_THROW(conn_manager_->onData(fake_input, false)); - - scoped_route_config_provider2_ = - std::make_shared>(); - scope_key_builder2_ = std::make_unique>(); - // Can't have RDS and SRDS provider in the same time. - EXPECT_DEBUG_DEATH(conn_manager_->onData(fake_input, false), - "Either routeConfigProvider or \\(scopedRouteConfigProvider and " - "scopeKeyBuilder\\) should be set in ConnectionManagerImpl."); - - route_config_provider2_.reset(); - // Only scoped route config provider valid. - EXPECT_NO_THROW(conn_manager_->onData(fake_input, false)); - filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); -} - -TEST_F(HttpConnectionManagerImplTest, RequestRejectedViaIPDetection) { - OriginalIPRejectRequestOptions reject_options = {Http::Code::Forbidden, "ip detection failed"}; - auto extension = getCustomHeaderExtension("x-ip", reject_options); - ip_detection_extensions_.push_back(extension); - - use_remote_address_ = false; - - setup(); - - // 403 direct response when IP detection fails. - EXPECT_CALL(response_encoder_, encodeHeaders(_, false)) - .WillOnce(Invoke([](const ResponseHeaderMap& headers, bool) -> void { - EXPECT_EQ("403", headers.getStatusValue()); - })); - std::string response_body; - EXPECT_CALL(response_encoder_, encodeData(_, true)).WillOnce(AddBufferToString(&response_body)); - - startRequest(); - - EXPECT_EQ("ip detection failed", response_body); - EXPECT_EQ(1U, stats_.named_.downstream_rq_rejected_via_ip_detection_.value()); -} - -TEST_F(HttpConnectionManagerImplTest, DisconnectDuringEncodeHeader) { - setup(); - setupFilterChain(1, 0); - - EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) - .WillOnce(Return(FilterHeadersStatus::StopIteration)); - startRequest(/*end_stream=*/true); - - EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) - .WillOnce(Invoke([&](const ResponseHeaderMap& headers, bool) -> void { - EXPECT_NE(nullptr, headers.Server()); - EXPECT_EQ("envoy-server-test", headers.getServerValue()); - conn_manager_->onEvent(Network::ConnectionEvent::LocalClose); - })); - EXPECT_CALL(*decoder_filters_[0], onStreamComplete()); - EXPECT_CALL(*decoder_filters_[0], onDestroy()); - - ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; - decoder_filters_[0]->callbacks_->streamInfo().setResponseCodeDetails(""); - decoder_filters_[0]->callbacks_->encodeHeaders(std::move(response_headers), true, "details"); -} - -TEST_F(HttpConnectionManagerImplTest, DisconnectDuringEncodeBody) { - setup(); - setupFilterChain(1, 0); - - EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) - .WillOnce(Return(FilterHeadersStatus::StopIteration)); - startRequest(/*end_stream=*/true); - - EXPECT_CALL(response_encoder_, encodeHeaders(_, false)) - .WillOnce(Invoke([&](const ResponseHeaderMap& headers, bool) -> void { - EXPECT_NE(nullptr, headers.Server()); - EXPECT_EQ("envoy-server-test", headers.getServerValue()); - })); - EXPECT_CALL(response_encoder_, encodeData(_, true)) - .WillOnce(Invoke([&](Buffer::Instance&, bool) -> void { - conn_manager_->onEvent(Network::ConnectionEvent::LocalClose); - })); - EXPECT_CALL(*decoder_filters_[0], onStreamComplete()); - EXPECT_CALL(*decoder_filters_[0], onDestroy()); - - ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; - decoder_filters_[0]->callbacks_->streamInfo().setResponseCodeDetails(""); - decoder_filters_[0]->callbacks_->encodeHeaders(std::move(response_headers), false, "details"); - Buffer::OwnedImpl response_body("response"); - decoder_filters_[0]->callbacks_->encodeData(response_body, true); -} - -// Verify that trailers added during a data encoding continuation are not double continued. -TEST_F(HttpConnectionManagerImplTest, AddTrailersDuringdDecodingContinue) { - InSequence s; - setup(); - - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - decoder_ = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{ - new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder_->decodeHeaders(std::move(headers), false); - - Buffer::OwnedImpl request_body("request"); - decoder_->decodeData(request_body, true); - - return Http::okStatus(); - })); - - setupFilterChain(3, 0); - - EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::StopIteration)); - EXPECT_CALL(*decoder_filters_[0], decodeData(_, true)) - .WillOnce(Return(FilterDataStatus::Continue)); - EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(*decoder_filters_[2], decodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(*decoder_filters_[1], decodeData(_, true)) - .WillOnce(Invoke([&](Buffer::Instance&, bool) -> FilterDataStatus { - decoder_filters_[1]->callbacks_->addDecodedTrailers().addCopy(LowerCaseString("hello"), 1); - return FilterDataStatus::Continue; - })); - EXPECT_CALL(*decoder_filters_[2], decodeData(_, false)) - .WillOnce(Return(FilterDataStatus::Continue)); - EXPECT_CALL(*decoder_filters_[2], decodeTrailers(_)) - .WillOnce(Return(FilterTrailersStatus::Continue)); - - Buffer::OwnedImpl fake_input; - conn_manager_->onData(fake_input, false); - - ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; - decoder_filters_[0]->callbacks_->encodeHeaders(std::move(response_headers), true, "details"); -} - -// Verify that trailers added during a data decoding continuation are not double continued. -TEST_F(HttpConnectionManagerImplTest, AddTrailersDuringEncodingContinue) { - InSequence s; - setup(); - - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - decoder_ = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{ - new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder_->decodeHeaders(std::move(headers), true); - return Http::okStatus(); - })); - - setupFilterChain(1, 2); - - EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) - .WillOnce(Return(FilterHeadersStatus::StopIteration)); - - Buffer::OwnedImpl fake_input; - conn_manager_->onData(fake_input, false); - - EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::StopIteration)); - EXPECT_CALL(*encoder_filters_[1], encodeData(_, true)) - .WillOnce(Return(FilterDataStatus::Continue)); - EXPECT_CALL(*encoder_filters_[0], encodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(response_encoder_, encodeHeaders(_, false)); - EXPECT_CALL(*encoder_filters_[0], encodeData(_, true)) - .WillOnce(Invoke([&](Buffer::Instance&, bool) -> FilterDataStatus { - encoder_filters_[0]->callbacks_->addEncodedTrailers().addCopy(LowerCaseString("hello"), 1); - return FilterDataStatus::Continue; - })); - EXPECT_CALL(response_encoder_, encodeData(_, false)); - EXPECT_CALL(response_encoder_, encodeTrailers(_)); - - ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; - decoder_filters_[0]->callbacks_->encodeHeaders(std::move(response_headers), false, "details"); - Buffer::OwnedImpl response_body("response"); - decoder_filters_[0]->callbacks_->encodeData(response_body, true); -} - -TEST_F(HttpConnectionManagerImplTest, DisconnectDuringEncodeTrailer) { - setup(); - setupFilterChain(1, 0); - - EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) - .WillOnce(Return(FilterHeadersStatus::StopIteration)); - startRequest(/*end_stream=*/true); - - EXPECT_CALL(response_encoder_, encodeHeaders(_, false)) - .WillOnce(Invoke([&](const ResponseHeaderMap& headers, bool) -> void { - EXPECT_NE(nullptr, headers.Server()); - EXPECT_EQ("envoy-server-test", headers.getServerValue()); - })); - EXPECT_CALL(response_encoder_, encodeData(_, false)); - EXPECT_CALL(response_encoder_, encodeTrailers(_)) - .WillOnce(Invoke([&](const Http::ResponseTrailerMap&) -> void { - conn_manager_->onEvent(Network::ConnectionEvent::LocalClose); - })); - EXPECT_CALL(*decoder_filters_[0], onStreamComplete()); - EXPECT_CALL(*decoder_filters_[0], onDestroy()); - - ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; - decoder_filters_[0]->callbacks_->streamInfo().setResponseCodeDetails(""); - decoder_filters_[0]->callbacks_->encodeHeaders(std::move(response_headers), false, "details"); - Buffer::OwnedImpl response_body("response"); - decoder_filters_[0]->callbacks_->encodeData(response_body, false); - decoder_filters_[0]->callbacks_->encodeTrailers( - ResponseTrailerMapPtr{new TestResponseTrailerMapImpl{{"some", "trailer"}}}); -} - -TEST_F(HttpConnectionManagerImplTest, DirectLocalReplyCausesDisconnect) { - initial_buffer_limit_ = 10; - setup(); - setUpEncoderAndDecoder(false, false); - sendRequestHeadersAndData(); - - // Start the response without processing the request headers through all - // filters. - ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; - EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::StopIteration)); - decoder_filters_[0]->callbacks_->streamInfo().setResponseCodeDetails(""); - decoder_filters_[0]->callbacks_->encodeHeaders(std::move(response_headers), false, "details"); - - // Now overload the buffer with response data. The filter returns - // StopIterationAndBuffer, which will trigger an early response. - - expectOnDestroy(); - Buffer::OwnedImpl fake_response("A long enough string to go over watermarks"); - // Fake response starts doing through the filter. - EXPECT_CALL(*encoder_filters_[1], encodeData(_, false)) - .WillOnce(Return(FilterDataStatus::StopIterationAndBuffer)); - std::string response_body; - // The 500 goes directly to the encoder. - EXPECT_CALL(response_encoder_, encodeHeaders(_, false)) - .WillOnce(Invoke([&](const ResponseHeaderMap& headers, bool) -> FilterHeadersStatus { - // Make sure this is a 500 - EXPECT_EQ("500", headers.getStatusValue()); - // Make sure Envoy standard sanitization has been applied. - EXPECT_TRUE(headers.Date() != nullptr); - EXPECT_EQ("response_payload_too_large", - decoder_filters_[0]->callbacks_->streamInfo().responseCodeDetails().value()); - return FilterHeadersStatus::Continue; - })); - EXPECT_CALL(response_encoder_, encodeData(_, true)) - .WillOnce(Invoke([&](Buffer::Instance&, bool) -> void { - conn_manager_->onEvent(Network::ConnectionEvent::LocalClose); - })); - decoder_filters_[0]->callbacks_->encodeData(fake_response, false); - - EXPECT_EQ(1U, stats_.named_.rs_too_large_.value()); -} - -// Header validator rejects header map for HTTP/1.x protocols -TEST_F(HttpConnectionManagerImplTest, HeaderValidatorRejectHttp1) { - setup(); - expectUhvHeaderCheck(HeaderValidator::ValidationResult( - HeaderValidator::ValidationResult::Action::Reject, "bad_header_map"), - ServerHeaderValidator::RequestHeadersTransformationResult::success()); - - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { - decoder_ = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ - {":authority", "host"}, {":path", "/something"}, {":method", "GET"}}}; - decoder_->decodeHeaders(std::move(headers), true); - data.drain(4); - return Http::okStatus(); - })); - EXPECT_CALL(response_encoder_, streamErrorOnInvalidHttpMessage()).WillRepeatedly(Return(false)); - - // This test also verifies that decoder/encoder filters have onDestroy() called only once. - auto* filter = new MockStreamFilter(); - EXPECT_CALL(filter_factory_, createFilterChain(_)) - .WillOnce(Invoke([&](FilterChainManager& manager) -> bool { - auto factory = createStreamFilterFactoryCb(StreamFilterSharedPtr{filter}); - manager.applyFilterFactoryCb({}, factory); - return true; - })); - EXPECT_CALL(*filter, setDecoderFilterCallbacks(_)); - EXPECT_CALL(*filter, setEncoderFilterCallbacks(_)); - EXPECT_CALL(*filter, encodeHeaders(_, true)); - EXPECT_CALL(*filter, encodeComplete()); - EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) - .WillOnce(Invoke([&](const ResponseHeaderMap& headers, bool) -> void { - EXPECT_EQ("400", headers.getStatusValue()); - // By default mock codec indicates HTTP/1.1 protocol which should result in closed - // connection on error - EXPECT_EQ("close", headers.getConnectionValue()); - EXPECT_EQ("bad_header_map", - filter->decoder_callbacks_->streamInfo().responseCodeDetails().value()); - })); - EXPECT_CALL(*filter, onStreamComplete()); - EXPECT_CALL(*filter, onDestroy()); - - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, false); -} - -// Header validator rejects header map for HTTP/2 protocols -TEST_F(HttpConnectionManagerImplTest, HeaderValidatorRejectHttp2) { - codec_->protocol_ = Protocol::Http2; - setup(); - expectUhvHeaderCheck(HeaderValidator::ValidationResult( - HeaderValidator::ValidationResult::Action::Reject, "bad_header_map"), - ServerHeaderValidator::RequestHeadersTransformationResult::success()); - - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { - decoder_ = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ - {":authority", "host"}, {":path", "/something"}, {":method", "GET"}}}; - decoder_->decodeHeaders(std::move(headers), true); - data.drain(4); - return Http::okStatus(); - })); - - EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) - .WillOnce(Invoke([&](const ResponseHeaderMap& headers, bool) -> void { - EXPECT_EQ("400", headers.getStatusValue()); - // For HTTP/2 protocols connection should not be closed - EXPECT_TRUE(headers.Connection() == nullptr); - EXPECT_EQ("bad_header_map", decoder_->streamInfo().responseCodeDetails().value()); - })); - - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, false); -} - -// Header validator rejects gRPC request -TEST_F(HttpConnectionManagerImplTest, HeaderValidatorRejectGrpcRequest) { - codec_->protocol_ = Protocol::Http2; - setup(); - expectUhvHeaderCheck(HeaderValidator::ValidationResult( - HeaderValidator::ValidationResult::Action::Reject, "bad_header_map"), - ServerHeaderValidator::RequestHeadersTransformationResult::success()); - - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { - decoder_ = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ - {"content-type", "application/grpc"}, // Make Envoy interpret this request as gRPC call - {":authority", "host"}, - {":path", "/something"}, - {":method", "GET"}}}; - decoder_->decodeHeaders(std::move(headers), true); - data.drain(4); - return Http::okStatus(); - })); - - EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) - .WillOnce(Invoke([&](const ResponseHeaderMap& headers, bool) -> void { - EXPECT_EQ("200", headers.getStatusValue()); - EXPECT_EQ("13", headers.getGrpcStatusValue()); - EXPECT_EQ("bad_header_map", decoder_->streamInfo().responseCodeDetails().value()); - })); - - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, false); -} - -// Header validator redirects -TEST_F(HttpConnectionManagerImplTest, HeaderValidatorRedirect) { - setup(); - expectUhvHeaderCheck( - HeaderValidator::ValidationResult::success(), - ServerHeaderValidator::RequestHeadersTransformationResult( - ServerHeaderValidator::RequestHeadersTransformationResult::Action::Redirect, - "bad_header_map")); - - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { - decoder_ = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ - {":authority", "host"}, {":path", "/something"}, {":method", "GET"}}}; - decoder_->decodeHeaders(std::move(headers), true); - data.drain(4); - return Http::okStatus(); - })); - - EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) - .WillOnce(Invoke([&](const ResponseHeaderMap& headers, bool) -> void { - EXPECT_EQ("307", headers.getStatusValue()); - EXPECT_EQ("/some/new/path", headers.getLocationValue()); - EXPECT_EQ("bad_header_map", decoder_->streamInfo().responseCodeDetails().value()); - })); - - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, false); -} - -// Header validator redirects gRPC request -TEST_F(HttpConnectionManagerImplTest, HeaderValidatorRedirectGrpcRequest) { - codec_->protocol_ = Protocol::Http2; - setup(); - expectUhvHeaderCheck( - HeaderValidator::ValidationResult::success(), - ServerHeaderValidator::RequestHeadersTransformationResult( - ServerHeaderValidator::RequestHeadersTransformationResult::Action::Redirect, - "bad_header_map")); - - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { - decoder_ = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ - {"content-type", "application/grpc"}, // Make Envoy interpret this request as gRPC call - {":authority", "host"}, - {":path", "/something"}, - {":method", "GET"}}}; - decoder_->decodeHeaders(std::move(headers), true); - data.drain(4); - return Http::okStatus(); - })); - - EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) - .WillOnce(Invoke([&](const ResponseHeaderMap& headers, bool) -> void { - EXPECT_EQ("200", headers.getStatusValue()); - EXPECT_EQ("13", headers.getGrpcStatusValue()); - EXPECT_EQ("bad_header_map", decoder_->streamInfo().responseCodeDetails().value()); - })); - - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, false); -} - -// Header validator rejects trailer map before response has started -TEST_F(HttpConnectionManagerImplTest, HeaderValidatorRejectTrailersBeforeResponseHttp1) { - codec_->protocol_ = Protocol::Http11; - setup(); - expectUhvTrailerCheck(HeaderValidator::ValidationResult( - HeaderValidator::ValidationResult::Action::Reject, "bad_trailer_map"), - HeaderValidator::TransformationResult::success()); - - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { - decoder_ = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ - {":authority", "host"}, {":path", "/something"}, {":method", "GET"}}}; - decoder_->decodeHeaders(std::move(headers), false); - RequestTrailerMapPtr trailers{ - new TestRequestTrailerMapImpl{{"trailer1", "value1"}, {"trailer2", "value2"}}}; - decoder_->decodeTrailers(std::move(trailers)); - data.drain(4); - return Http::okStatus(); - })); - - EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) - .WillOnce(Invoke([&](const ResponseHeaderMap& headers, bool) -> void { - EXPECT_EQ("400", headers.getStatusValue()); - EXPECT_EQ("bad_trailer_map", decoder_->streamInfo().responseCodeDetails().value()); - })); - - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, false); -} - -TEST_F(HttpConnectionManagerImplTest, HeaderValidatorRejectTrailersBeforeResponseHttp2) { - codec_->protocol_ = Protocol::Http2; - setup(); - expectUhvTrailerCheck(HeaderValidator::ValidationResult( - HeaderValidator::ValidationResult::Action::Reject, "bad_trailer_map"), - HeaderValidator::TransformationResult::success(), false); - - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { - decoder_ = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ - {":authority", "host"}, {":path", "/something"}, {":method", "GET"}}}; - decoder_->decodeHeaders(std::move(headers), false); - RequestTrailerMapPtr trailers{ - new TestRequestTrailerMapImpl{{"trailer1", "value1"}, {"trailer2", "value2"}}}; - decoder_->decodeTrailers(std::move(trailers)); - data.drain(4); - return Http::okStatus(); - })); - - EXPECT_CALL(response_encoder_.stream_, resetStream(_)); - - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, false); -} - -TEST_F(HttpConnectionManagerImplTest, HeaderValidatorFailTrailersTransformationBeforeResponse) { - codec_->protocol_ = Protocol::Http11; - setup(); - expectUhvTrailerCheck( - HeaderValidator::ValidationResult::success(), - HeaderValidator::TransformationResult(HeaderValidator::TransformationResult::Action::Reject, - "bad_trailer_map")); - - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { - decoder_ = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ - {":authority", "host"}, {":path", "/something"}, {":method", "GET"}}}; - decoder_->decodeHeaders(std::move(headers), false); - RequestTrailerMapPtr trailers{ - new TestRequestTrailerMapImpl{{"trailer1", "value1"}, {"trailer2", "value2"}}}; - decoder_->decodeTrailers(std::move(trailers)); - data.drain(4); - return Http::okStatus(); - })); - - EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) - .WillOnce(Invoke([&](const ResponseHeaderMap& headers, bool) -> void { - EXPECT_EQ("400", headers.getStatusValue()); - EXPECT_EQ("bad_trailer_map", decoder_->streamInfo().responseCodeDetails().value()); - })); - - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, false); -} - -// Header validator rejects trailer map after response has started -TEST_F(HttpConnectionManagerImplTest, HeaderValidatorRejectTrailersAfterResponse) { - codec_->protocol_ = Protocol::Http2; - setup(); - setupFilterChain(1, 0, 1); - expectUhvTrailerCheck(HeaderValidator::ValidationResult( - HeaderValidator::ValidationResult::Action::Reject, "bad_trailer_map"), - HeaderValidator::TransformationResult::success()); - EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, false)) - .WillRepeatedly(Invoke([&](RequestHeaderMap&, bool) -> FilterHeadersStatus { - return FilterHeadersStatus::StopIteration; - })); - - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { - decoder_ = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ - {":authority", "host"}, {":path", "/something"}, {":method", "GET"}}}; - decoder_->decodeHeaders(std::move(headers), false); - - ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; - decoder_filters_[0]->callbacks_->encodeHeaders(std::move(response_headers), false, ""); - - RequestTrailerMapPtr trailers{ - new TestRequestTrailerMapImpl{{"trailer1", "value1"}, {"trailer2", "value2"}}}; - decoder_->decodeTrailers(std::move(trailers)); - data.drain(4); - return Http::okStatus(); - })); - - EXPECT_CALL(response_encoder_, encodeHeaders(_, false)) - .WillOnce(Invoke([&](const ResponseHeaderMap& headers, bool) -> void { - EXPECT_EQ("200", headers.getStatusValue()); - })); - - EXPECT_CALL(response_encoder_.stream_, resetStream(_)); - - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, false); - EXPECT_EQ("bad_trailer_map", decoder_->streamInfo().responseCodeDetails().value()); -} - -// Request completes normally if header validator accepts it -TEST_F(HttpConnectionManagerImplTest, HeaderValidatorAccept) { - setup(); - expectUhvHeaderCheck(HeaderValidator::ValidationResult::success(), - ServerHeaderValidator::RequestHeadersTransformationResult::success()); - - // Store the basic request encoder during filter chain setup. - std::shared_ptr filter(new NiceMock()); - - EXPECT_CALL(*filter, decodeHeaders(_, true)) - .WillRepeatedly(Invoke([&](RequestHeaderMap&, bool) -> FilterHeadersStatus { - return FilterHeadersStatus::StopIteration; - })); - - EXPECT_CALL(*filter, setDecoderFilterCallbacks(_)); - - EXPECT_CALL(filter_factory_, createFilterChain(_)) - .WillRepeatedly(Invoke([&](FilterChainManager& manager) -> bool { - auto factory = createDecoderFilterFactoryCb(filter); - manager.applyFilterFactoryCb({}, factory); - return true; - })); - - EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)); - - // When dispatch is called on the codec, we pretend to get a new stream and then fire a headers - // only request into it. Then we respond into the filter. - EXPECT_CALL(*codec_, dispatch(_)) - .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - decoder_ = &conn_manager_->newStream(response_encoder_); - - RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ - {":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder_->decodeHeaders(std::move(headers), true); - - ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; - filter->callbacks_->streamInfo().setResponseCodeDetails(""); - filter->callbacks_->encodeHeaders(std::move(response_headers), true, "details"); - - data.drain(4); - return Http::okStatus(); - })); - - // Kick off the incoming data. - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, false); - - EXPECT_EQ(1U, stats_.named_.downstream_rq_2xx_.value()); - EXPECT_EQ(1U, listener_stats_.downstream_rq_2xx_.value()); - EXPECT_EQ(1U, stats_.named_.downstream_rq_completed_.value()); - EXPECT_EQ(1U, listener_stats_.downstream_rq_completed_.value()); -} - -TEST_F(HttpConnectionManagerImplTest, NoProxyProtocolAdded) { - add_proxy_protocol_connection_state_ = false; - setup(); - Buffer::OwnedImpl fake_input("input"); - conn_manager_->createCodec(fake_input); - - startRequest(false); - - EXPECT_FALSE(decoder_->streamInfo().filterState()->hasDataWithName( - Network::ProxyProtocolFilterState::key())); - // Clean up. - filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); -} - -// Validate that deferred streams are processed with a variety of -// headers, data, metadata, and trailers arriving in the same I/O cycle -TEST_F(HttpConnectionManagerImplTest, LimitWorkPerIOCycle) { - const int kRequestsSentPerIOCycle = 100; - EXPECT_CALL(runtime_.snapshot_, getInteger(_, _)).WillRepeatedly(ReturnArg<1>()); - // Process 1 request per I/O cycle - auto* deferred_request_callback = enableStreamsPerIoLimit(1); - setup(); - - // Store the basic request encoder during filter chain setup. - std::vector> decoder_filters; - int decode_headers_call_count = 0; - for (int i = 0; i < kRequestsSentPerIOCycle; ++i) { - int mod5 = i % 5; - std::shared_ptr filter(new NiceMock()); - - // Each 0th request is headers only - EXPECT_CALL(*filter, decodeHeaders(_, mod5 == 0 ? true : false)) - .WillRepeatedly(Invoke([&](RequestHeaderMap&, bool) -> FilterHeadersStatus { - ++decode_headers_call_count; - return FilterHeadersStatus::StopIteration; - })); - - // Each 1st request is headers and data only - // Each 2nd request is headers, data and trailers - if (mod5 == 1 || mod5 == 2) { - EXPECT_CALL(*filter, decodeData(_, mod5 == 1 ? true : false)) - .WillOnce(Return(FilterDataStatus::StopIterationNoBuffer)); - } - - // Each 3rd request is headers and trailers (no data) - if (mod5 == 2 || mod5 == 3) { - EXPECT_CALL(*filter, decodeTrailers(_)).WillOnce(Return(FilterTrailersStatus::StopIteration)); - } - - // Each 4th request is headers, metadata, and data. - if (mod5 == 4) { - EXPECT_CALL(*filter, decodeMetadata(_)).WillOnce(Return(FilterMetadataStatus::Continue)); - EXPECT_CALL(*filter, decodeData(_, true)) - .WillOnce(Return(FilterDataStatus::StopIterationNoBuffer)); - } - EXPECT_CALL(*filter, setDecoderFilterCallbacks(_)); - decoder_filters.push_back(std::move(filter)); - } - - uint64_t random_value = 0; - EXPECT_CALL(random_, random()).WillRepeatedly(Invoke([&random_value]() { - return random_value++; - })); - - EXPECT_CALL(filter_factory_, createFilterChain(_)) - .Times(kRequestsSentPerIOCycle) - .WillRepeatedly(Invoke([&decoder_filters](FilterChainManager& manager) -> bool { - static int index = 0; - int i = index++; - FilterFactoryCb factory([&decoder_filters, i](FilterChainFactoryCallbacks& callbacks) { - callbacks.addStreamDecoderFilter(decoder_filters[i]); - }); - manager.applyFilterFactoryCb({}, factory); - return true; - })); - - EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)) - .Times(kRequestsSentPerIOCycle); - - std::vector> response_encoders(kRequestsSentPerIOCycle); - for (auto& encoder : response_encoders) { - EXPECT_CALL(encoder, getStream()).WillRepeatedly(ReturnRef(encoder.stream_)); - } - - EXPECT_CALL(*codec_, dispatch(_)) - .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - for (int i = 0; i < kRequestsSentPerIOCycle; ++i) { - decoder_ = &conn_manager_->newStream(response_encoders[i]); - - RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ - {":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - - MetadataMapPtr metadata = std::make_unique(); - (*metadata)["key1"] = "value1"; - - RequestTrailerMapPtr trailers{ - new TestRequestTrailerMapImpl{{"key1", "value1"}, {"key2", "value2"}}}; - - Buffer::OwnedImpl data("data"); - - switch (i % 5) { - case 0: - decoder_->decodeHeaders(std::move(headers), true); - break; - case 1: - decoder_->decodeHeaders(std::move(headers), false); - decoder_->decodeData(data, true); - break; - case 2: - decoder_->decodeHeaders(std::move(headers), false); - decoder_->decodeData(data, false); - decoder_->decodeTrailers(std::move(trailers)); - break; - case 3: - decoder_->decodeHeaders(std::move(headers), false); - decoder_->decodeTrailers(std::move(trailers)); - break; - case 4: - decoder_->decodeHeaders(std::move(headers), false); - decoder_->decodeMetadata(std::move(metadata)); - decoder_->decodeData(data, true); - break; - } - } - - data.drain(4); - return Http::okStatus(); - })); - - // Kick off the incoming data. - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, false); - - EXPECT_TRUE(deferred_request_callback->enabled_); - // Only one request should go through the filter chain - ASSERT_EQ(decode_headers_call_count, 1); - - // Let other requests to go through the filter chain. Call expectations will fail - // if this is not the case. - int deferred_request_count = 0; - while (deferred_request_callback->enabled_) { - deferred_request_callback->invokeCallback(); - ++deferred_request_count; - } - - ASSERT_EQ(deferred_request_count, kRequestsSentPerIOCycle); - - for (auto& filter : decoder_filters) { - ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; - filter->callbacks_->streamInfo().setResponseCodeDetails(""); - filter->callbacks_->encodeHeaders(std::move(response_headers), true, "details"); - } - - EXPECT_EQ(kRequestsSentPerIOCycle, stats_.named_.downstream_rq_2xx_.value()); - EXPECT_EQ(kRequestsSentPerIOCycle, listener_stats_.downstream_rq_2xx_.value()); - EXPECT_EQ(kRequestsSentPerIOCycle, stats_.named_.downstream_rq_completed_.value()); - EXPECT_EQ(kRequestsSentPerIOCycle, listener_stats_.downstream_rq_completed_.value()); -} - -TEST_F(HttpConnectionManagerImplTest, StreamDeferralPreservesOrder) { - EXPECT_CALL(runtime_.snapshot_, getInteger(_, _)).WillRepeatedly(ReturnArg<1>()); - // Process 1 request per I/O cycle - auto* deferred_request_callback = enableStreamsPerIoLimit(1); - setup(); - - std::vector> encoder_filters; - int expected_request_id = 0; - const Http::LowerCaseString request_id_header(absl::string_view("request-id")); - // Two requests are processed in 2 I/O reads - const int TotalRequests = 2 * 2; - for (int i = 0; i < TotalRequests; ++i) { - std::shared_ptr filter(new NiceMock()); - - EXPECT_CALL(*filter, decodeHeaders(_, true)) - .WillRepeatedly(Invoke([&](RequestHeaderMap& headers, bool) -> FilterHeadersStatus { - // Check that requests are decoded in expected order - int request_id = 0; - ASSERT(absl::SimpleAtoi(headers.get(request_id_header)[0]->value().getStringView(), - &request_id)); - ASSERT(request_id == expected_request_id); - ++expected_request_id; - return FilterHeadersStatus::StopIteration; - })); - - EXPECT_CALL(*filter, setDecoderFilterCallbacks(_)); - encoder_filters.push_back(std::move(filter)); - } - - uint64_t random_value = 0; - EXPECT_CALL(random_, random()).WillRepeatedly(Invoke([&random_value]() { - return random_value++; - })); - - EXPECT_CALL(filter_factory_, createFilterChain(_)) - .Times(TotalRequests) - .WillRepeatedly(Invoke([&encoder_filters](FilterChainManager& manager) -> bool { - static int index = 0; - int i = index++; - FilterFactoryCb factory([&encoder_filters, i](FilterChainFactoryCallbacks& callbacks) { - callbacks.addStreamDecoderFilter(encoder_filters[i]); - }); - manager.applyFilterFactoryCb({}, factory); - return true; - })); - - EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)).Times(TotalRequests); - - std::vector> response_encoders(TotalRequests); - for (auto& encoder : response_encoders) { - EXPECT_CALL(encoder, getStream()).WillRepeatedly(ReturnRef(encoder.stream_)); - } - auto response_encoders_iter = response_encoders.begin(); - - int request_id = 0; - EXPECT_CALL(*codec_, dispatch(_)) - .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { - // The second request should be deferred - for (int i = 0; i < 2; ++i) { - decoder_ = &conn_manager_->newStream(*response_encoders_iter); - ++response_encoders_iter; - - RequestHeaderMapPtr headers{ - new TestRequestHeaderMapImpl{{":authority", "host"}, - {":path", "/"}, - {":method", "GET"}, - {"request-id", absl::StrCat(request_id)}}}; - - ++request_id; - decoder_->decodeHeaders(std::move(headers), true); - } - - data.drain(4); - return Http::okStatus(); - })); - - // Kick off the incoming data. - Buffer::OwnedImpl fake_input("1234"); - conn_manager_->onData(fake_input, false); - - EXPECT_TRUE(deferred_request_callback->enabled_); - // Only one request should go through the filter chain - ASSERT_EQ(expected_request_id, 1); - - // Test arrival of another request. New request is read from the socket before deferred callbacks. - Buffer::OwnedImpl fake_input2("1234"); - conn_manager_->onData(fake_input2, false); - - // No requests from the second read should go through as there are deferred stream present - ASSERT_EQ(expected_request_id, 1); - - // Let other requests to go through the filter chain. Call expectations will fail - // if this is not the case. - int deferred_request_count = 0; - while (deferred_request_callback->enabled_) { - deferred_request_callback->invokeCallback(); - ++deferred_request_count; - } - - ASSERT_EQ(deferred_request_count, TotalRequests); - - for (auto& filter : encoder_filters) { - ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; - filter->callbacks_->streamInfo().setResponseCodeDetails(""); - filter->callbacks_->encodeHeaders(std::move(response_headers), true, "details"); - } - - EXPECT_EQ(TotalRequests, stats_.named_.downstream_rq_2xx_.value()); - EXPECT_EQ(TotalRequests, listener_stats_.downstream_rq_2xx_.value()); - EXPECT_EQ(TotalRequests, stats_.named_.downstream_rq_completed_.value()); - EXPECT_EQ(TotalRequests, listener_stats_.downstream_rq_completed_.value()); -} - -TEST_F(HttpConnectionManagerImplTest, DownstreamTimingsRecordWhenRequestHeaderProcessingIsDone) { - setup(SetupOpts().setSsl(true).setTracing(false)); - - // Set up the codec. - Buffer::OwnedImpl fake_input("input"); - conn_manager_->createCodec(fake_input); - - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - // Set time to 5ms before creating the stream - test_time_.timeSystem().setMonotonicTime(MonotonicTime(std::chrono::milliseconds(5))); - decoder_ = &conn_manager_->newStream(response_encoder_); - RequestHeaderMapPtr headers{ - new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - // Advanced time to 20ms before decoding headers. - test_time_.timeSystem().setMonotonicTime(MonotonicTime(std::chrono::milliseconds(20))); - decoder_->decodeHeaders(std::move(headers), /*end_stream=*/false); - return Http::okStatus(); - })); - - conn_manager_->onData(fake_input, /*end_stream=*/false); - - auto request_headers_done = - decoder_->streamInfo().downstreamTiming().lastDownstreamHeaderRxByteReceived(); - auto request_headers_done_millis = std::chrono::duration_cast( - request_headers_done.value() - decoder_->streamInfo().startTimeMonotonic()); - // Expect time to be 20ms-5ms = 15ms. - EXPECT_EQ(request_headers_done_millis, std::chrono::milliseconds(15)); - - // Clean up. - filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); -} - -TEST_F(HttpConnectionManagerImplTest, PassMatchUpstreamSchemeHintToStreamInfo) { - setup(SetupOpts().setTracing(false)); - scheme_match_upstream_ = true; - - // Store the basic request encoder during filter chain setup. - std::shared_ptr filter(new NiceMock()); - - EXPECT_CALL(*filter, decodeHeaders(_, true)) - .WillOnce(Invoke([&](RequestHeaderMap&, bool) -> FilterHeadersStatus { - EXPECT_TRUE(filter->callbacks_->streamInfo().shouldSchemeMatchUpstream()); - return FilterHeadersStatus::StopIteration; - })); - - EXPECT_CALL(*filter, setDecoderFilterCallbacks(_)); - - EXPECT_CALL(filter_factory_, createFilterChain(_)) - .WillOnce(Invoke([&](FilterChainManager& manager) -> bool { - auto factory = createDecoderFilterFactoryCb(filter); - manager.applyFilterFactoryCb({}, factory); - return true; - })); - - EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)); - - EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { - decoder_ = &conn_manager_->newStream(response_encoder_); - - // Send request to be passed on through filter chain to check that - // filters have the correct hint passed through the callbacks - RequestHeaderMapPtr headers{ - new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; - decoder_->decodeHeaders(std::move(headers), true); - return Http::okStatus(); - })); - - // Kick off the incoming data. - Buffer::OwnedImpl fake_input{}; - conn_manager_->onData(fake_input, false); - - // Clean up. - filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); -} - -// Validate that incomplete request is terminated when a non terminal filter -// initiates encoding of the response (i.e. the cache filter). -// This only works when independent half-close mode is DISABLED. -TEST_F(HttpConnectionManagerImplTest, EncodingByNonTerminalFilter) { - TestScopedRuntime scoped_runtime; - scoped_runtime.mergeValues( - {{"envoy.reloadable_features.allow_multiplexed_upstream_half_close", "false"}}); - setup(); - constexpr int total_filters = 3; - constexpr int ecoder_filter_index = 1; - setupFilterChain(total_filters, total_filters); - - EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - - EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::StopIteration)); - - // Kick off the incomplete request (end_stream == false). - startRequest(false); - - // For encode direction - EXPECT_CALL(*encoder_filters_[2], encodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(*encoder_filters_[0], encodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - - // Second decoder filter (there are 3 in total) initiates encoding - ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; - decoder_filters_[ecoder_filter_index]->callbacks_->encodeHeaders(std::move(response_headers), - false, "details"); - - EXPECT_CALL(*encoder_filters_[2], encodeData(_, true)) - .WillOnce(Return(FilterDataStatus::Continue)); - EXPECT_CALL(*encoder_filters_[2], encodeComplete()); - EXPECT_CALL(*encoder_filters_[1], encodeData(_, true)) - .WillOnce(Return(FilterDataStatus::Continue)); - EXPECT_CALL(*encoder_filters_[1], encodeComplete()); - EXPECT_CALL(*encoder_filters_[0], encodeData(_, true)) - .WillOnce(Return(FilterDataStatus::Continue)); - EXPECT_CALL(*encoder_filters_[0], encodeComplete()); - // verify that after the end_stream is observed by the last encoder filter the request is - // completed. - expectOnDestroy(); - - // Second decoder filter then completes encoding with data - Buffer::OwnedImpl fake_response("world"); - decoder_filters_[ecoder_filter_index]->callbacks_->encodeData(fake_response, true); -} - -// Validate that when independent half-close is enabled, encoding end_stream by a -// non-final filter ends the request iff the filter that initiated encoding of the end_stream has -// already observed the request end_stream. -TEST_F(HttpConnectionManagerImplTest, EncodingByNonTerminalFilterWithIndependentHalfClose) { - TestScopedRuntime scoped_runtime; - scoped_runtime.mergeValues( - {{"envoy.reloadable_features.allow_multiplexed_upstream_half_close", "true"}}); - setup(); - constexpr int total_filters = 3; - constexpr int ecoder_filter_index = 1; - setupFilterChain(total_filters, total_filters); - - EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(*decoder_filters_[0], decodeComplete()); - EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, true)) - .WillOnce(Return(FilterHeadersStatus::StopIteration)); - EXPECT_CALL(*decoder_filters_[1], decodeComplete()); - - // Send complete request. - startRequest(true); - - // For encode direction - EXPECT_CALL(*encoder_filters_[2], encodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(*encoder_filters_[0], encodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - - // Second decoder filter (there are 3 in total) initiates encoding - ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; - decoder_filters_[ecoder_filter_index]->callbacks_->encodeHeaders(std::move(response_headers), - false, "details"); - - EXPECT_CALL(*encoder_filters_[2], encodeData(_, true)) - .WillOnce(Return(FilterDataStatus::Continue)); - EXPECT_CALL(*encoder_filters_[2], encodeComplete()); - EXPECT_CALL(*encoder_filters_[1], encodeData(_, true)) - .WillOnce(Return(FilterDataStatus::Continue)); - EXPECT_CALL(*encoder_filters_[1], encodeComplete()); - EXPECT_CALL(*encoder_filters_[0], encodeData(_, true)) - .WillOnce(Return(FilterDataStatus::Continue)); - EXPECT_CALL(*encoder_filters_[0], encodeComplete()); - // Verify that after the end_stream is observed by the last encoder filter the request is - // completed. even though the request end_stream never reached the terminal filter, but was - // observed by the filter that has initiated encoding. - expectOnDestroy(); - - // Second decoder filter then completes encoding with data - Buffer::OwnedImpl fake_response("world"); - decoder_filters_[ecoder_filter_index]->callbacks_->encodeData(fake_response, true); -} - -// Validate that when independent half-close is enabled, encoding end_stream by a -// non-final filter with incomplete request causes the request to be reset. -// Only the terminal filter (router) is allowed to half-close upstream response before the -// downstream request. -TEST_F(HttpConnectionManagerImplTest, DecodingByNonTerminalEncoderFilterWithIndependentHalfClose) { - TestScopedRuntime scoped_runtime; - scoped_runtime.mergeValues( - {{"envoy.reloadable_features.allow_multiplexed_upstream_half_close", "true"}}); - setup(); - constexpr int total_filters = 3; - constexpr int ecoder_filter_index = 1; - setupFilterChain(total_filters, total_filters); - - EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::StopIteration)); - - // Send incomplete request. - startRequest(false); - - // For encode direction - EXPECT_CALL(*encoder_filters_[2], encodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(*encoder_filters_[0], encodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - - // Second decoder filter (there are 3 in total) initiates encoding - ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; - decoder_filters_[ecoder_filter_index]->callbacks_->encodeHeaders(std::move(response_headers), - false, "details"); - - EXPECT_CALL(*encoder_filters_[2], encodeData(_, true)) - .WillOnce(Return(FilterDataStatus::Continue)); - EXPECT_CALL(*encoder_filters_[2], encodeComplete()); - EXPECT_CALL(*encoder_filters_[1], encodeData(_, true)) - .WillOnce(Return(FilterDataStatus::Continue)); - EXPECT_CALL(*encoder_filters_[1], encodeComplete()); - EXPECT_CALL(*encoder_filters_[0], encodeData(_, true)) - .WillOnce(Return(FilterDataStatus::Continue)); - EXPECT_CALL(*encoder_filters_[0], encodeComplete()); - - expectOnDestroy(); - - // Second decoder filter then completes encoding with data - Buffer::OwnedImpl fake_response("world"); - decoder_filters_[ecoder_filter_index]->callbacks_->encodeData(fake_response, true); -} - -// Validate that when independent half-close is enabled, encoding end_stream by a -// non-final filter with incomplete request makes the encoding filter the terminal filter. -// In this case decoding end_stream from the client only reaches the filter that encoded the -// end_stream after which the request is completed. -TEST_F(HttpConnectionManagerImplTest, DecodingWithAddedTrailersByNonTerminalEncoderFilter) { - TestScopedRuntime scoped_runtime; - scoped_runtime.mergeValues( - {{"envoy.reloadable_features.allow_multiplexed_upstream_half_close", "true"}}); - setup(); - constexpr int total_filters = 3; - constexpr int ecoder_filter_index = 1; - setupFilterChain(total_filters, total_filters); - - EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::StopIteration)); - - // Send incomplete request. - startRequest(false); - - // For encode direction - EXPECT_CALL(*encoder_filters_[2], encodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - EXPECT_CALL(*encoder_filters_[0], encodeHeaders(_, false)) - .WillOnce(Return(FilterHeadersStatus::Continue)); - - // Second decoder filter (there are 3 in total) initiates encoding - ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; - decoder_filters_[ecoder_filter_index]->callbacks_->encodeHeaders(std::move(response_headers), - false, "details"); - - EXPECT_CALL(*encoder_filters_[2], encodeData(_, true)) - .WillOnce(Return(FilterDataStatus::Continue)); - EXPECT_CALL(*encoder_filters_[2], encodeComplete()); - EXPECT_CALL(*encoder_filters_[1], encodeData(_, true)) - .WillOnce(Return(FilterDataStatus::Continue)); - EXPECT_CALL(*encoder_filters_[1], encodeComplete()); - EXPECT_CALL(*encoder_filters_[0], encodeData(_, true)) - .WillOnce(Return(FilterDataStatus::Continue)); - EXPECT_CALL(*encoder_filters_[0], encodeComplete()); - expectOnDestroy(); - - // Second decoder filter then completes encoding with data - Buffer::OwnedImpl fake_response("world"); - decoder_filters_[ecoder_filter_index]->callbacks_->encodeData(fake_response, true); -} - } // namespace Http } // namespace Envoy diff --git a/test/common/http/conn_manager_impl_test_3.cc b/test/common/http/conn_manager_impl_test_3.cc new file mode 100644 index 0000000000000..710abf06054fa --- /dev/null +++ b/test/common/http/conn_manager_impl_test_3.cc @@ -0,0 +1,2751 @@ +#include + +#include "test/common/http/conn_manager_impl_test_base.h" +#include "test/common/http/custom_header_extension.h" +#include "test/extensions/filters/network/common/fuzz/utils/fakes.h" +#include "test/server/utility.h" +#include "test/test_common/test_runtime.h" + +namespace Envoy { +namespace Http { + +using testing::AtLeast; +using testing::InSequence; +using testing::InvokeWithoutArgs; +using ::testing::Mock; +using testing::Return; +using testing::ReturnArg; +using testing::ReturnRef; + +// Filter stops headers iteration without ending the stream, then injects a body later. +TEST_F(HttpConnectionManagerImplTest, FilterStopIterationInjectBody) { + setup(); + setupFilterChain(2, 2); + + // Decode filter 0 changes end_stream to false. + EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) + .WillOnce(Return(FilterHeadersStatus::StopIteration)); + EXPECT_CALL(*decoder_filters_[0], decodeComplete()); + EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + + // Kick off the incoming data. + startRequest(true); + + EXPECT_CALL(*decoder_filters_[1], decodeData(_, true)) + .WillOnce(Return(FilterDataStatus::Continue)); + EXPECT_CALL(*decoder_filters_[1], decodeComplete()); + + // Decode filter 0 injects request body later. + Buffer::OwnedImpl data("hello"); + decoder_filters_[0]->callbacks_->injectDecodedDataToFilterChain(data, true); + + // Encode filter 1 changes end_stream to false. + EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, true)) + .WillOnce(Return(FilterHeadersStatus::StopIteration)); + EXPECT_CALL(*encoder_filters_[1], encodeComplete()); + EXPECT_CALL(*encoder_filters_[0], encodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + EXPECT_CALL(response_encoder_, encodeHeaders(_, false)); + + decoder_filters_[1]->callbacks_->streamInfo().setResponseCodeDetails(""); + decoder_filters_[1]->callbacks_->encodeHeaders( + makeHeaderMap({{":status", "200"}}), true, "details"); + + EXPECT_CALL(*encoder_filters_[0], encodeData(_, true)) + .WillOnce(Return(FilterDataStatus::Continue)); + EXPECT_CALL(*encoder_filters_[0], encodeComplete()); + EXPECT_CALL(response_encoder_, encodeData(_, true)); + expectOnDestroy(); + + // Encode filter 1 injects request body later. + Buffer::OwnedImpl data2("hello"); + encoder_filters_[1]->callbacks_->injectEncodedDataToFilterChain(data2, true); +} + +// Filter continues headers iteration without ending the stream, then injects a body later. +TEST_F(HttpConnectionManagerImplTest, FilterContinueDontEndStreamInjectBody) { + setup(); + setupFilterChain(2, 2); + + // Decode filter 0 changes end_stream to false. + EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) + .WillOnce(Return(FilterHeadersStatus::ContinueAndDontEndStream)); + EXPECT_CALL(*decoder_filters_[0], decodeComplete()); + EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + + // Kick off the incoming data. + startRequest(true); + + EXPECT_CALL(*decoder_filters_[1], decodeData(_, true)) + .WillOnce(Return(FilterDataStatus::Continue)); + EXPECT_CALL(*decoder_filters_[1], decodeComplete()); + + // Decode filter 0 injects request body later. + Buffer::OwnedImpl data("hello"); + decoder_filters_[0]->callbacks_->injectDecodedDataToFilterChain(data, true); + + // Encode filter 1 changes end_stream to false. + EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, true)) + .WillOnce(Return(FilterHeadersStatus::ContinueAndDontEndStream)); + EXPECT_CALL(*encoder_filters_[1], encodeComplete()); + EXPECT_CALL(*encoder_filters_[0], encodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + EXPECT_CALL(response_encoder_, encodeHeaders(_, false)); + + decoder_filters_[1]->callbacks_->streamInfo().setResponseCodeDetails(""); + decoder_filters_[1]->callbacks_->encodeHeaders( + makeHeaderMap({{":status", "200"}}), true, "details"); + + EXPECT_CALL(*encoder_filters_[0], encodeData(_, true)) + .WillOnce(Return(FilterDataStatus::Continue)); + EXPECT_CALL(*encoder_filters_[0], encodeComplete()); + EXPECT_CALL(response_encoder_, encodeData(_, true)); + expectOnDestroy(); + + // Encode filter 1 injects request body later. + Buffer::OwnedImpl data2("hello"); + encoder_filters_[1]->callbacks_->injectEncodedDataToFilterChain(data2, true); +} + +TEST_F(HttpConnectionManagerImplTest, FilterAddBodyContinuation) { + setup(); + setupFilterChain(2, 2); + + EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) + .WillOnce(Return(FilterHeadersStatus::StopIteration)); + EXPECT_CALL(*decoder_filters_[0], decodeComplete()); + + // Kick off the incoming request. + startRequest(true); + + EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + EXPECT_CALL(*decoder_filters_[1], decodeData(_, true)) + .WillOnce(Return(FilterDataStatus::Continue)); + EXPECT_CALL(*decoder_filters_[1], decodeComplete()); + + Buffer::OwnedImpl data("hello"); + decoder_filters_[0]->callbacks_->addDecodedData(data, true); + decoder_filters_[0]->callbacks_->continueDecoding(); + + EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, true)) + .WillOnce(Return(FilterHeadersStatus::StopIteration)); + EXPECT_CALL(*encoder_filters_[1], encodeComplete()); + + decoder_filters_[1]->callbacks_->streamInfo().setResponseCodeDetails(""); + decoder_filters_[1]->callbacks_->encodeHeaders( + ResponseHeaderMapPtr{new TestResponseHeaderMapImpl{{":status", "200"}}}, true, "details"); + + EXPECT_CALL(*encoder_filters_[0], encodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + EXPECT_CALL(response_encoder_, encodeHeaders(_, false)); + EXPECT_CALL(*encoder_filters_[0], encodeData(_, true)) + .WillOnce(Return(FilterDataStatus::Continue)); + EXPECT_CALL(*encoder_filters_[0], encodeComplete()); + EXPECT_CALL(response_encoder_, encodeData(_, true)); + expectOnDestroy(); + + Buffer::OwnedImpl data2("hello"); + encoder_filters_[1]->callbacks_->addEncodedData(data2, true); + encoder_filters_[1]->callbacks_->continueEncoding(); +} + +// This test verifies proper sequences of decodeData() and encodeData() are called +// when all filers return "CONTINUE" in following case: +// +// 3 decode filters: +// +// filter0->decodeHeaders(_, true) +// return CONTINUE +// filter1->decodeHeaders(_, true) +// filter1->addDecodeData() +// return CONTINUE +// filter2->decodeHeaders(_, false) +// return CONTINUE +// filter2->decodeData(_, true) +// return CONTINUE +// +// filter0->decodeData(, true) is NOT called. +// filter1->decodeData(, true) is NOT called. +// +// 3 encode filters: +// +// filter2->encodeHeaders(_, true) +// return CONTINUE +// filter1->encodeHeaders(_, true) +// filter1->addEncodeData() +// return CONTINUE +// filter0->decodeHeaders(_, false) +// return CONTINUE +// filter0->decodeData(_, true) +// return CONTINUE +// +// filter2->encodeData(, true) is NOT called. +// filter1->encodeData(, true) is NOT called. +// +TEST_F(HttpConnectionManagerImplTest, AddDataWithAllContinue) { + setup(); + setupFilterChain(3, 3); + + EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + EXPECT_CALL(*decoder_filters_[0], decodeComplete()); + + EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, true)) + .WillOnce(InvokeWithoutArgs([&]() -> FilterHeadersStatus { + Buffer::OwnedImpl data2("hello"); + decoder_filters_[1]->callbacks_->addDecodedData(data2, true); + return FilterHeadersStatus::Continue; + })); + EXPECT_CALL(*decoder_filters_[1], decodeComplete()); + + EXPECT_CALL(*decoder_filters_[2], decodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + EXPECT_CALL(*decoder_filters_[2], decodeData(_, true)) + .WillOnce(Return(FilterDataStatus::Continue)); + EXPECT_CALL(*decoder_filters_[2], decodeComplete()); + + EXPECT_CALL(*decoder_filters_[0], decodeData(_, true)).Times(0); + EXPECT_CALL(*decoder_filters_[1], decodeData(_, true)).Times(0); + + // Kick off the incoming data. + startRequest(true); + + // For encode direction + EXPECT_CALL(*encoder_filters_[2], encodeHeaders(_, true)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + EXPECT_CALL(*encoder_filters_[2], encodeComplete()); + + EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, true)) + .WillOnce(InvokeWithoutArgs([&]() -> FilterHeadersStatus { + Buffer::OwnedImpl data2("goodbyte"); + encoder_filters_[1]->callbacks_->addEncodedData(data2, true); + return FilterHeadersStatus::Continue; + })); + EXPECT_CALL(*encoder_filters_[1], encodeComplete()); + + EXPECT_CALL(*encoder_filters_[0], encodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + EXPECT_CALL(response_encoder_, encodeHeaders(_, false)); + EXPECT_CALL(*encoder_filters_[0], encodeData(_, true)) + .WillOnce(Return(FilterDataStatus::Continue)); + EXPECT_CALL(*encoder_filters_[0], encodeComplete()); + EXPECT_CALL(response_encoder_, encodeData(_, true)); + expectOnDestroy(); + + EXPECT_CALL(*encoder_filters_[2], encodeData(_, true)).Times(0); + EXPECT_CALL(*encoder_filters_[1], encodeData(_, true)).Times(0); + + decoder_filters_[2]->callbacks_->streamInfo().setResponseCodeDetails(""); + decoder_filters_[2]->callbacks_->encodeHeaders( + ResponseHeaderMapPtr{new TestResponseHeaderMapImpl{{":status", "200"}}}, true, "details"); +} + +// This test verifies proper sequences of decodeData() and encodeData() are called +// when the first filter is "stopped" and "continue" in following case: +// +// 3 decode filters: +// +// filter0->decodeHeaders(_, true) +// return STOP +// filter0->continueDecoding() +// filter1->decodeHeaders(_, true) +// filter1->addDecodeData() +// return CONTINUE +// filter2->decodeHeaders(_, false) +// return CONTINUE +// filter2->decodeData(_, true) +// return CONTINUE +// +// filter0->decodeData(, true) is NOT called. +// filter1->decodeData(, true) is NOT called. +// +// 3 encode filters: +// +// filter2->encodeHeaders(_, true) +// return STOP +// filter2->continueEncoding() +// filter1->encodeHeaders(_, true) +// filter1->addEncodeData() +// return CONTINUE +// filter0->decodeHeaders(_, false) +// return CONTINUE +// filter0->decodeData(_, true) +// return CONTINUE +// +// filter2->encodeData(, true) is NOT called. +// filter1->encodeData(, true) is NOT called. +// +TEST_F(HttpConnectionManagerImplTest, AddDataWithStopAndContinue) { + setup(); + + setupFilterChain(3, 3); + + EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) + .WillOnce(Return(FilterHeadersStatus::StopIteration)); + EXPECT_CALL(*decoder_filters_[0], decodeComplete()); + + // Kick off the request. + startRequest(true); + + EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, true)) + .WillOnce(InvokeWithoutArgs([&]() -> FilterHeadersStatus { + Buffer::OwnedImpl data2("hello"); + decoder_filters_[1]->callbacks_->addDecodedData(data2, true); + return FilterHeadersStatus::Continue; + })); + EXPECT_CALL(*decoder_filters_[1], decodeComplete()); + + EXPECT_CALL(*decoder_filters_[2], decodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + // This fail, it is called twice. + EXPECT_CALL(*decoder_filters_[2], decodeData(_, true)) + .WillOnce(Return(FilterDataStatus::Continue)); + EXPECT_CALL(*decoder_filters_[2], decodeComplete()); + + EXPECT_CALL(*decoder_filters_[0], decodeData(_, true)).Times(0); + // This fail, it is called once + EXPECT_CALL(*decoder_filters_[1], decodeData(_, true)).Times(0); + + decoder_filters_[0]->callbacks_->continueDecoding(); + + // For encode direction + EXPECT_CALL(*encoder_filters_[2], encodeHeaders(_, true)) + .WillOnce(Return(FilterHeadersStatus::StopIteration)); + EXPECT_CALL(*encoder_filters_[2], encodeComplete()); + + decoder_filters_[2]->callbacks_->streamInfo().setResponseCodeDetails(""); + decoder_filters_[2]->callbacks_->encodeHeaders( + ResponseHeaderMapPtr{new TestResponseHeaderMapImpl{{":status", "200"}}}, true, "details"); + + EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, true)) + .WillOnce(InvokeWithoutArgs([&]() -> FilterHeadersStatus { + Buffer::OwnedImpl data2("goodbyte"); + encoder_filters_[1]->callbacks_->addEncodedData(data2, true); + return FilterHeadersStatus::Continue; + })); + EXPECT_CALL(*encoder_filters_[1], encodeComplete()); + + EXPECT_CALL(*encoder_filters_[0], encodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + EXPECT_CALL(response_encoder_, encodeHeaders(_, false)); + + EXPECT_CALL(*encoder_filters_[0], encodeData(_, true)) + .WillOnce(Return(FilterDataStatus::Continue)); + EXPECT_CALL(*encoder_filters_[0], encodeComplete()); + EXPECT_CALL(response_encoder_, encodeData(_, true)); + expectOnDestroy(); + + EXPECT_CALL(*encoder_filters_[2], encodeData(_, true)).Times(0); + EXPECT_CALL(*encoder_filters_[1], encodeData(_, true)).Times(0); + + encoder_filters_[2]->callbacks_->continueEncoding(); +} + +// This test verifies that when recreateStream is executed during the decodeHeader +// phase and returns StopIteration, executing continueDecoding does not proceed with the processing +// of subsequent filters. +TEST_F(HttpConnectionManagerImplTest, CannotContinueDecodingAfterRecreateStream) { + setup(); + decoder_filters_.push_back(new NiceMock()); + decoder_filters_.push_back(new NiceMock()); + + EXPECT_CALL(filter_factory_, createFilterChain(_)) + .WillOnce(Invoke([this](FilterChainManager& manager) -> bool { + bool applied_filters = false; + if (log_handler_.get()) { + auto factory = createLogHandlerFactoryCb(log_handler_); + manager.applyFilterFactoryCb({}, factory); + applied_filters = true; + } + for (int i = 0; i < 2; i++) { + auto factory = + createDecoderFilterFactoryCb(StreamDecoderFilterSharedPtr{decoder_filters_[i]}); + manager.applyFilterFactoryCb({}, factory); + applied_filters = true; + } + return applied_filters; + })) + .WillOnce(Return(true)); + + EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) + .WillOnce(InvokeWithoutArgs([&]() -> FilterHeadersStatus { + decoder_filters_[0]->callbacks_->recreateStream(nullptr); + return FilterHeadersStatus::StopIteration; + })); + + // Kick off the request. + startRequest(true); + + // Should not continue headers of filter 1. + EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, true)).Times(0); + decoder_filters_[0]->callbacks_->continueDecoding(); + filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); +} + +// This test verifies that when recreateStream is executed during the encodeHeader +// phase and returns StopIteration, executing continueEncoding does not proceed with the processing +// of subsequent filters. +TEST_F(HttpConnectionManagerImplTest, CannotContinueEncodingAfterRecreateStream) { + setup(); + decoder_filters_.push_back(new NiceMock()); + decoder_filters_.push_back(new NiceMock()); + encoder_filters_.push_back(new NiceMock()); + encoder_filters_.push_back(new NiceMock()); + + EXPECT_CALL(filter_factory_, createFilterChain(_)) + .WillOnce(Invoke([this](FilterChainManager& manager) -> bool { + bool applied_filters = false; + if (log_handler_.get()) { + auto factory = createLogHandlerFactoryCb(log_handler_); + manager.applyFilterFactoryCb({}, factory); + applied_filters = true; + } + for (int i = 0; i < 2; i++) { + auto factory = + createDecoderFilterFactoryCb(StreamDecoderFilterSharedPtr{decoder_filters_[i]}); + manager.applyFilterFactoryCb({}, factory); + applied_filters = true; + } + for (int i = 0; i < 2; i++) { + auto factory = + createEncoderFilterFactoryCb(StreamEncoderFilterSharedPtr{encoder_filters_[i]}); + manager.applyFilterFactoryCb({}, factory); + applied_filters = true; + } + return applied_filters; + })) + .WillOnce(Return(true)); + + EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, true)) + .WillOnce(Return(FilterHeadersStatus::StopIteration)); + + // Kick off the request. + startRequest(true); + + EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, true)) + .WillOnce(InvokeWithoutArgs([&]() -> FilterHeadersStatus { + decoder_filters_[1]->callbacks_->recreateStream(nullptr); + return FilterHeadersStatus::StopIteration; + })); + + decoder_filters_[1]->callbacks_->streamInfo().setResponseCodeDetails(""); + decoder_filters_[1]->callbacks_->encodeHeaders( + ResponseHeaderMapPtr{new TestResponseHeaderMapImpl{{":status", "200"}}}, true, "details"); + + // Should not continue headers of filter 0. + EXPECT_CALL(*encoder_filters_[0], encodeHeaders(_, true)).Times(0); + encoder_filters_[1]->callbacks_->continueEncoding(); + filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); +} + +// Use filter direct decode/encodeData() calls without trailers. +TEST_F(HttpConnectionManagerImplTest, FilterDirectDecodeEncodeDataNoTrailers) { + setup(); + EXPECT_CALL(*route_config_provider_.route_config_, route(_, _, _, _)); + setupFilterChain(2, 2); + + EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::StopIteration)); + + Buffer::OwnedImpl decode_buffer; + EXPECT_CALL(*decoder_filters_[0], decodeData(_, true)) + .WillOnce(Invoke([&](Buffer::Instance& data, bool) { + decode_buffer.move(data); + return FilterDataStatus::StopIterationNoBuffer; + })); + EXPECT_CALL(*decoder_filters_[0], decodeComplete()); + + // Kick off the request. + EXPECT_CALL(filter_callbacks_.connection_.stream_info_, protocol(Envoy::Http::Protocol::Http11)); + startRequest(true, "hello"); + + Buffer::OwnedImpl decoded_data_to_forward; + decoded_data_to_forward.move(decode_buffer, 2); + EXPECT_CALL(*decoder_filters_[1], decodeData(BufferStringEqual("he"), false)) + .WillOnce(Return(FilterDataStatus::StopIterationNoBuffer)); + decoder_filters_[0]->callbacks_->injectDecodedDataToFilterChain(decoded_data_to_forward, false); + + EXPECT_CALL(*decoder_filters_[1], decodeData(BufferStringEqual("llo"), true)) + .WillOnce(Return(FilterDataStatus::StopIterationNoBuffer)); + EXPECT_CALL(*decoder_filters_[1], decodeComplete()); + decoder_filters_[0]->callbacks_->injectDecodedDataToFilterChain(decode_buffer, true); + + // Response path. + EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + EXPECT_CALL(*encoder_filters_[0], encodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + EXPECT_CALL(response_encoder_, encodeHeaders(_, false)); + + Buffer::OwnedImpl encoder_buffer; + EXPECT_CALL(*encoder_filters_[1], encodeData(_, true)) + .WillOnce(Invoke([&](Buffer::Instance& data, bool) { + encoder_buffer.move(data); + return FilterDataStatus::StopIterationNoBuffer; + })); + EXPECT_CALL(*encoder_filters_[1], encodeComplete()); + + decoder_filters_[1]->callbacks_->streamInfo().setResponseCodeDetails(""); + decoder_filters_[1]->callbacks_->encodeHeaders( + ResponseHeaderMapPtr{new TestResponseHeaderMapImpl{{":status", "200"}}}, false, "details"); + Buffer::OwnedImpl response_body("response"); + decoder_filters_[1]->callbacks_->encodeData(response_body, true); + + Buffer::OwnedImpl encoded_data_to_forward; + encoded_data_to_forward.move(encoder_buffer, 3); + EXPECT_CALL(*encoder_filters_[0], encodeData(BufferStringEqual("res"), false)); + EXPECT_CALL(response_encoder_, encodeData(_, false)); + encoder_filters_[1]->callbacks_->injectEncodedDataToFilterChain(encoded_data_to_forward, false); + + EXPECT_CALL(*encoder_filters_[0], encodeData(BufferStringEqual("ponse"), true)); + EXPECT_CALL(*encoder_filters_[0], encodeComplete()); + EXPECT_CALL(response_encoder_, encodeData(_, true)); + expectOnDestroy(); + encoder_filters_[1]->callbacks_->injectEncodedDataToFilterChain(encoder_buffer, true); +} + +// Use filter direct decode/encodeData() calls with trailers. +TEST_F(HttpConnectionManagerImplTest, FilterDirectDecodeEncodeDataTrailers) { + InSequence s; + setup(); + + EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { + decoder_ = &conn_manager_->newStream(response_encoder_); + RequestHeaderMapPtr headers{ + new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; + decoder_->decodeHeaders(std::move(headers), false); + + Buffer::OwnedImpl fake_data("hello"); + decoder_->decodeData(fake_data, false); + + RequestTrailerMapPtr trailers{new TestRequestTrailerMapImpl{{"foo", "bar"}}}; + decoder_->decodeTrailers(std::move(trailers)); + return Http::okStatus(); + })); + + EXPECT_CALL(*route_config_provider_.route_config_, route(_, _, _, _)); + setupFilterChain(2, 2); + + EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::StopIteration)); + + Buffer::OwnedImpl decode_buffer; + EXPECT_CALL(*decoder_filters_[0], decodeData(_, false)) + .WillOnce(Invoke([&](Buffer::Instance& data, bool) { + decode_buffer.move(data); + return FilterDataStatus::StopIterationNoBuffer; + })); + EXPECT_CALL(*decoder_filters_[0], decodeTrailers(_)) + .WillOnce(Return(FilterTrailersStatus::StopIteration)); + EXPECT_CALL(*decoder_filters_[0], decodeComplete()); + + // Kick off the incoming data. + Buffer::OwnedImpl fake_input("1234"); + conn_manager_->onData(fake_input, false); + + Buffer::OwnedImpl decoded_data_to_forward; + decoded_data_to_forward.move(decode_buffer, 2); + EXPECT_CALL(*decoder_filters_[1], decodeData(BufferStringEqual("he"), false)) + .WillOnce(Return(FilterDataStatus::StopIterationNoBuffer)); + decoder_filters_[0]->callbacks_->injectDecodedDataToFilterChain(decoded_data_to_forward, false); + + EXPECT_CALL(*decoder_filters_[1], decodeData(BufferStringEqual("llo"), false)) + .WillOnce(Return(FilterDataStatus::StopIterationNoBuffer)); + decoder_filters_[0]->callbacks_->injectDecodedDataToFilterChain(decode_buffer, false); + + EXPECT_CALL(*decoder_filters_[1], decodeTrailers(_)); + EXPECT_CALL(*decoder_filters_[1], decodeComplete()); + decoder_filters_[0]->callbacks_->continueDecoding(); + + // Response path. + EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + EXPECT_CALL(*encoder_filters_[0], encodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + EXPECT_CALL(response_encoder_, encodeHeaders(_, false)); + + Buffer::OwnedImpl encoder_buffer; + EXPECT_CALL(*encoder_filters_[1], encodeData(_, false)) + .WillOnce(Invoke([&](Buffer::Instance& data, bool) { + encoder_buffer.move(data); + return FilterDataStatus::StopIterationNoBuffer; + })); + EXPECT_CALL(*encoder_filters_[1], encodeTrailers(_)) + .WillOnce(Return(FilterTrailersStatus::StopIteration)); + EXPECT_CALL(*encoder_filters_[1], encodeComplete()); + + decoder_filters_[1]->callbacks_->streamInfo().setResponseCodeDetails(""); + decoder_filters_[1]->callbacks_->encodeHeaders( + ResponseHeaderMapPtr{new TestResponseHeaderMapImpl{{":status", "200"}}}, false, "details"); + Buffer::OwnedImpl response_body("response"); + decoder_filters_[1]->callbacks_->encodeData(response_body, false); + decoder_filters_[1]->callbacks_->encodeTrailers( + ResponseTrailerMapPtr{new TestResponseTrailerMapImpl{{"some", "trailer"}}}); + + Buffer::OwnedImpl encoded_data_to_forward; + encoded_data_to_forward.move(encoder_buffer, 3); + EXPECT_CALL(*encoder_filters_[0], encodeData(BufferStringEqual("res"), false)); + EXPECT_CALL(response_encoder_, encodeData(_, false)); + encoder_filters_[1]->callbacks_->injectEncodedDataToFilterChain(encoded_data_to_forward, false); + + EXPECT_CALL(*encoder_filters_[0], encodeData(BufferStringEqual("ponse"), false)); + EXPECT_CALL(response_encoder_, encodeData(_, false)); + encoder_filters_[1]->callbacks_->injectEncodedDataToFilterChain(encoder_buffer, false); + + EXPECT_CALL(*encoder_filters_[0], encodeTrailers(_)); + EXPECT_CALL(*encoder_filters_[0], encodeComplete()); + EXPECT_CALL(response_encoder_, encodeTrailers(_)); + expectOnDestroy(); + encoder_filters_[1]->callbacks_->continueEncoding(); +} + +TEST_F(HttpConnectionManagerImplTest, MultipleFilters) { + InSequence s; + setup(); + + EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { + decoder_ = &conn_manager_->newStream(response_encoder_); + RequestHeaderMapPtr headers{ + new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; + decoder_->decodeHeaders(std::move(headers), false); + + Buffer::OwnedImpl fake_data("hello"); + decoder_->decodeData(fake_data, false); + + Buffer::OwnedImpl fake_data2("world"); + decoder_->decodeData(fake_data2, true); + return Http::okStatus(); + })); + + EXPECT_CALL(*route_config_provider_.route_config_, route(_, _, _, _)); + setupFilterChain(3, 2); + + EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, false)) + .WillOnce(InvokeWithoutArgs([&]() -> FilterHeadersStatus { + EXPECT_EQ(route_config_provider_.route_config_->route_, + decoder_filters_[0]->callbacks_->route()); + EXPECT_EQ(ssl_connection_.get(), + decoder_filters_[0]->callbacks_->connection()->ssl().get()); + return FilterHeadersStatus::StopIteration; + })); + + EXPECT_CALL(*decoder_filters_[0], decodeData(_, false)) + .WillOnce(Return(FilterDataStatus::StopIterationAndBuffer)); + EXPECT_CALL(*decoder_filters_[0], decodeData(_, true)) + .WillOnce(Return(FilterDataStatus::StopIterationAndBuffer)); + EXPECT_CALL(*decoder_filters_[0], decodeComplete()); + + // Kick off the incoming data. + Buffer::OwnedImpl fake_input("1234"); + EXPECT_CALL(filter_callbacks_.connection_.stream_info_, protocol(Envoy::Http::Protocol::Http11)); + conn_manager_->onData(fake_input, false); + + // Mimic a decoder filter that trapped data and now sends it on, since the data was buffered + // by the first filter, we expect to get it in 1 decodeData() call. + EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, false)) + .WillOnce(InvokeWithoutArgs([&]() -> FilterHeadersStatus { + EXPECT_EQ(route_config_provider_.route_config_->route_, + decoder_filters_[1]->callbacks_->route()); + EXPECT_EQ(ssl_connection_.get(), + decoder_filters_[1]->callbacks_->connection()->ssl().get()); + return FilterHeadersStatus::StopIteration; + })); + EXPECT_CALL(*decoder_filters_[1], decodeData(_, true)) + .WillOnce(Return(FilterDataStatus::Continue)); + EXPECT_CALL(*decoder_filters_[1], decodeComplete()); + EXPECT_CALL(*decoder_filters_[2], decodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::StopIteration)); + EXPECT_CALL(*decoder_filters_[2], decodeData(_, true)) + .WillOnce(Return(FilterDataStatus::StopIterationNoBuffer)); + EXPECT_CALL(*decoder_filters_[2], decodeComplete()); + decoder_filters_[0]->callbacks_->continueDecoding(); + + // Now start encoding and mimic trapping in the encoding filter. + EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::StopIteration)); + EXPECT_CALL(*encoder_filters_[1], encodeData(_, false)) + .WillOnce(Return(FilterDataStatus::StopIterationAndBuffer)); + EXPECT_CALL(*encoder_filters_[1], encodeTrailers(_)) + .WillOnce(Return(FilterTrailersStatus::StopIteration)); + EXPECT_CALL(*encoder_filters_[1], encodeComplete()); + EXPECT_EQ(ssl_connection_.get(), encoder_filters_[1]->callbacks_->connection()->ssl().get()); + decoder_filters_[2]->callbacks_->streamInfo().setResponseCodeDetails(""); + decoder_filters_[2]->callbacks_->encodeHeaders( + ResponseHeaderMapPtr{new TestResponseHeaderMapImpl{{":status", "200"}}}, false, "details"); + Buffer::OwnedImpl response_body("response"); + decoder_filters_[2]->callbacks_->encodeData(response_body, false); + decoder_filters_[2]->callbacks_->encodeTrailers( + ResponseTrailerMapPtr{new TestResponseTrailerMapImpl{{"some", "trailer"}}}); + EXPECT_EQ(ssl_connection_.get(), decoder_filters_[2]->callbacks_->connection()->ssl().get()); + + // Now finish the encode. + EXPECT_CALL(*encoder_filters_[0], encodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + EXPECT_CALL(response_encoder_, encodeHeaders(_, false)); + EXPECT_CALL(*encoder_filters_[0], encodeData(_, false)) + .WillOnce(Return(FilterDataStatus::Continue)); + EXPECT_CALL(response_encoder_, encodeData(_, false)); + EXPECT_CALL(*encoder_filters_[0], encodeTrailers(_)) + .WillOnce(Return(FilterTrailersStatus::Continue)); + EXPECT_CALL(*encoder_filters_[0], encodeComplete()); + EXPECT_CALL(response_encoder_, encodeTrailers(_)); + expectOnDestroy(); + encoder_filters_[1]->callbacks_->continueEncoding(); + + EXPECT_EQ(ssl_connection_.get(), encoder_filters_[0]->callbacks_->connection()->ssl().get()); +} + +TEST(HttpConnectionManagerTracingStatsTest, verifyTracingStats) { + Stats::IsolatedStoreImpl stats; + ConnectionManagerTracingStats tracing_stats{CONN_MAN_TRACING_STATS(POOL_COUNTER(stats))}; + + ConnectionManagerImpl::chargeTracingStats(Tracing::Reason::ClientForced, tracing_stats); + EXPECT_EQ(1UL, tracing_stats.client_enabled_.value()); + + ConnectionManagerImpl::chargeTracingStats(Tracing::Reason::HealthCheck, tracing_stats); + ConnectionManagerImpl::chargeTracingStats(Tracing::Reason::NotTraceable, tracing_stats); + EXPECT_EQ(2UL, tracing_stats.not_traceable_.value()); + + ConnectionManagerImpl::chargeTracingStats(Tracing::Reason::Sampling, tracing_stats); + EXPECT_EQ(1UL, tracing_stats.random_sampling_.value()); +} + +TEST_F(HttpConnectionManagerImplTest, NoNewStreamWhenOverloaded) { + Server::OverloadActionState stop_accepting_requests(UnitFloat(0.8)); + ON_CALL(overload_manager_.overload_state_, + getState(Server::OverloadActionNames::get().StopAcceptingRequests)) + .WillByDefault(ReturnRef(stop_accepting_requests)); + + setup(); + + EXPECT_CALL(random_, random()) + .WillRepeatedly(Return(static_cast(Random::RandomGenerator::max()) * 0.5)); + + // 503 direct response when overloaded. + EXPECT_CALL(response_encoder_, encodeHeaders(_, false)) + .WillOnce(Invoke([](const ResponseHeaderMap& headers, bool) -> void { + EXPECT_EQ("503", headers.getStatusValue()); + })); + std::string response_body; + EXPECT_CALL(response_encoder_, encodeData(_, true)).WillOnce(AddBufferToString(&response_body)); + + startRequest(); + + EXPECT_EQ("envoy overloaded", response_body); + EXPECT_EQ(1U, stats_.named_.downstream_rq_overload_close_.value()); +} + +TEST_F(HttpConnectionManagerImplTest, DisableHttp1KeepAliveWhenOverloaded) { + Server::OverloadActionState disable_http_keep_alive(UnitFloat(0.8)); + ON_CALL(overload_manager_.overload_state_, + getState(Server::OverloadActionNames::get().DisableHttpKeepAlive)) + .WillByDefault(ReturnRef(disable_http_keep_alive)); + + codec_->protocol_ = Protocol::Http11; + setup(); + + EXPECT_CALL(random_, random()) + .WillRepeatedly(Return(static_cast(Random::RandomGenerator::max()) * 0.5)); + + std::shared_ptr filter(new NiceMock()); + EXPECT_CALL(filter_factory_, createFilterChain(_)) + .WillOnce(Invoke([&](FilterChainManager& manager) -> bool { + auto factory = createDecoderFilterFactoryCb(StreamDecoderFilterSharedPtr{filter}); + manager.applyFilterFactoryCb({}, factory); + return true; + })); + + EXPECT_CALL(*codec_, dispatch(_)) + .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { + decoder_ = &conn_manager_->newStream(response_encoder_); + RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{{":authority", "host"}, + {":path", "/"}, + {":method", "GET"}, + {"connection", "keep-alive"}}}; + decoder_->decodeHeaders(std::move(headers), true); + + ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; + filter->callbacks_->streamInfo().setResponseCodeDetails(""); + filter->callbacks_->encodeHeaders(std::move(response_headers), true, "details"); + + data.drain(4); + return Http::okStatus(); + })); + + EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) + .WillOnce(Invoke([](const ResponseHeaderMap& headers, bool) -> void { + EXPECT_EQ("close", headers.getConnectionValue()); + })); + + Buffer::OwnedImpl fake_input("1234"); + conn_manager_->onData(fake_input, false); + EXPECT_EQ(1U, stats_.named_.downstream_cx_overload_disable_keepalive_.value()); +} + +// Verify that HTTP2 connections will receive a GOAWAY message when the overload action is +// triggered. +TEST_F(HttpConnectionManagerImplTest, DisableHttp2KeepAliveWhenOverloaded) { + Server::OverloadActionState disable_http_keep_alive = Server::OverloadActionState::saturated(); + ON_CALL(overload_manager_.overload_state_, + getState(Server::OverloadActionNames::get().DisableHttpKeepAlive)) + .WillByDefault(ReturnRef(disable_http_keep_alive)); + + codec_->protocol_ = Protocol::Http2; + setup(); + EXPECT_CALL(*codec_, shutdownNotice); + + std::shared_ptr filter(new NiceMock()); + EXPECT_CALL(filter_factory_, createFilterChain(_)) + .WillOnce(Invoke([&](FilterChainManager& manager) -> bool { + auto factory = createDecoderFilterFactoryCb(StreamDecoderFilterSharedPtr{filter}); + manager.applyFilterFactoryCb({}, factory); + return true; + })); + + EXPECT_CALL(*codec_, dispatch(_)) + .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { + decoder_ = &conn_manager_->newStream(response_encoder_); + RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{{":authority", "host"}, + {":path", "/"}, + {":method", "GET"}, + {"connection", "keep-alive"}}}; + decoder_->decodeHeaders(std::move(headers), true); + + ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; + filter->callbacks_->streamInfo().setResponseCodeDetails(""); + filter->callbacks_->encodeHeaders(std::move(response_headers), true, "details"); + + data.drain(4); + return Http::okStatus(); + })); + + EXPECT_CALL(response_encoder_, encodeHeaders(_, true)); + + Buffer::OwnedImpl fake_input("1234"); + conn_manager_->onData(fake_input, false); + Mock::VerifyAndClearExpectations(codec_); + EXPECT_EQ(1, stats_.named_.downstream_cx_overload_disable_keepalive_.value()); +} + +TEST_F(HttpConnectionManagerImplTest, CodecCreationLoadShedPointCanCloseConnection) { + Server::MockLoadShedPoint close_connection_creating_codec_point; + EXPECT_CALL(overload_manager_, + getLoadShedPoint(Server::LoadShedPointName::get().HcmCodecCreation)) + .WillOnce(Return(&close_connection_creating_codec_point)); + EXPECT_CALL(overload_manager_, + getLoadShedPoint(Server::LoadShedPointName::get().HcmDecodeHeaders)) + .WillOnce(Return(nullptr)); + + setup(); + + EXPECT_CALL(close_connection_creating_codec_point, shouldShedLoad()).WillOnce(Return(true)); + EXPECT_CALL(filter_callbacks_.connection_, close(_, _)); + + Buffer::OwnedImpl fake_input("hello"); + conn_manager_->onData(fake_input, false); + + delete codec_; + EXPECT_EQ(1U, stats_.named_.downstream_rq_overload_close_.value()); + EXPECT_TRUE(filter_callbacks_.connection().streamInfo().hasResponseFlag( + StreamInfo::CoreResponseFlag::OverloadManager)); +} + +TEST_F(HttpConnectionManagerImplTest, CodecCreationLoadShedPointBypasscheck) { + Server::MockLoadShedPoint close_connection_creating_codec_point; + EXPECT_CALL(overload_manager_, + getLoadShedPoint(Server::LoadShedPointName::get().HcmCodecCreation)) + .WillOnce(Return(&close_connection_creating_codec_point)); + EXPECT_CALL(overload_manager_, + getLoadShedPoint(Server::LoadShedPointName::get().HcmDecodeHeaders)) + .WillOnce(Return(nullptr)); + EXPECT_CALL(overload_manager_, + getLoadShedPoint(Server::LoadShedPointName::get().HttpDownstreamFilterCheck)) + .WillOnce(Return(nullptr)); + + setup(); + + EXPECT_CALL(close_connection_creating_codec_point, shouldShedLoad()).WillOnce(Return(false)); + + EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { + conn_manager_->newStream(response_encoder_); + data.drain(2); + return Http::okStatus(); + })); + + Buffer::OwnedImpl fake_input("12"); + conn_manager_->onData(fake_input, false); + conn_manager_->onEvent(Network::ConnectionEvent::RemoteClose); + EXPECT_EQ(0U, stats_.named_.downstream_rq_overload_close_.value()); + EXPECT_FALSE(filter_callbacks_.connection().streamInfo().hasResponseFlag( + StreamInfo::CoreResponseFlag::OverloadManager)); +} + +TEST_F(HttpConnectionManagerImplTest, DecodeHeaderLoadShedPointCanRejectNewStreams) { + Server::MockLoadShedPoint accept_new_stream_point; + EXPECT_CALL(overload_manager_, + getLoadShedPoint(Server::LoadShedPointName::get().HcmDecodeHeaders)) + .WillOnce(Return(&accept_new_stream_point)); + EXPECT_CALL(overload_manager_, + getLoadShedPoint(Server::LoadShedPointName::get().HcmCodecCreation)) + .WillOnce(Return(nullptr)); + EXPECT_CALL(overload_manager_, + getLoadShedPoint(Server::LoadShedPointName::get().HttpDownstreamFilterCheck)) + .WillRepeatedly(Return(nullptr)); + + setup(); + setupFilterChain(1, 0); + + EXPECT_CALL(accept_new_stream_point, shouldShedLoad()).WillOnce(Return(true)); + + // 503 direct response when overloaded. + EXPECT_CALL(response_encoder_, encodeHeaders(_, false)) + .WillOnce(Invoke([](const ResponseHeaderMap& headers, bool) -> void { + EXPECT_EQ("503", headers.getStatusValue()); + })); + std::string response_body; + EXPECT_CALL(response_encoder_, encodeData(_, true)).WillOnce(AddBufferToString(&response_body)); + + startRequest(); + + EXPECT_EQ("envoy overloaded", response_body); + EXPECT_EQ(1U, stats_.named_.downstream_rq_overload_close_.value()); + + // Let the load shed point allow a new stream. + EXPECT_CALL(accept_new_stream_point, shouldShedLoad()).WillOnce(Return(false)); + EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)); + EXPECT_CALL(*decoder_filters_[0], decodeComplete()); + startRequest(true); + + // Clean up. + EXPECT_CALL(response_encoder_, encodeHeaders(_, true)); + expectOnDestroy(); + + decoder_filters_[0]->callbacks_->streamInfo().setResponseCodeDetails(""); + decoder_filters_[0]->callbacks_->encodeHeaders( + ResponseHeaderMapPtr{new TestResponseHeaderMapImpl{{":status", "200"}}}, true, "details"); +} + +TEST_F(HttpConnectionManagerImplTest, TestStopAllIterationAndBufferOnDecodingPathFirstFilter) { + setup(SetupOpts().setTracing(false)); + setUpEncoderAndDecoder(true, true); + + // Kick off the incoming data. + Buffer::OwnedImpl fake_input("1234"); + conn_manager_->onData(fake_input, false); + + // Verify that once the decoder_filters_[0]'s continueDecoding() is called, decoder_filters_[1]'s + // decodeHeaders() is called, and both filters receive data and trailers consequently. + EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, _)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + EXPECT_CALL(*decoder_filters_[0], decodeData(_, _)).WillOnce(Return(FilterDataStatus::Continue)); + EXPECT_CALL(*decoder_filters_[1], decodeData(_, _)).WillOnce(Return(FilterDataStatus::Continue)); + EXPECT_CALL(*decoder_filters_[0], decodeTrailers(_)) + .WillOnce(Return(FilterTrailersStatus::Continue)); + EXPECT_CALL(*decoder_filters_[1], decodeTrailers(_)) + .WillOnce(Return(FilterTrailersStatus::Continue)); + EXPECT_CALL(*decoder_filters_[1], decodeComplete()); + decoder_filters_[0]->callbacks_->continueDecoding(); + + doRemoteClose(); +} + +TEST_F(HttpConnectionManagerImplTest, TestStopAllIterationAndBufferOnDecodingPathSecondFilter) { + setup(SetupOpts().setTracing(false)); + setUpEncoderAndDecoder(true, false); + + // Verify headers go through both filters, and data and trailers go through the first filter only. + EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, _)) + .WillOnce(Return(FilterHeadersStatus::StopAllIterationAndBuffer)); + EXPECT_CALL(*decoder_filters_[0], decodeData(_, _)).WillOnce(Return(FilterDataStatus::Continue)); + EXPECT_CALL(*decoder_filters_[0], decodeTrailers(_)) + .WillOnce(Return(FilterTrailersStatus::Continue)); + // Kick off the incoming data. + Buffer::OwnedImpl fake_input("1234"); + conn_manager_->onData(fake_input, false); + + // Verify that once the decoder_filters_[1]'s continueDecoding() is called, both data and trailers + // go through the second filter. + EXPECT_CALL(*decoder_filters_[1], decodeData(_, _)).WillOnce(Return(FilterDataStatus::Continue)); + EXPECT_CALL(*decoder_filters_[1], decodeTrailers(_)) + .WillOnce(Return(FilterTrailersStatus::Continue)); + EXPECT_CALL(*decoder_filters_[1], decodeComplete()); + decoder_filters_[1]->callbacks_->continueDecoding(); + + doRemoteClose(); +} + +TEST_F(HttpConnectionManagerImplTest, TestStopAllIterationAndBufferOnEncodingPath) { + setup(SetupOpts().setTracing(false)); + setUpEncoderAndDecoder(false, false); + sendRequestHeadersAndData(); + + // encoder_filters_[1] is the first filter in the chain. + EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, false)) + .WillOnce(Invoke([&](HeaderMap&, bool) -> FilterHeadersStatus { + return FilterHeadersStatus::StopAllIterationAndBuffer; + })); + ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; + decoder_filters_[0]->callbacks_->streamInfo().setResponseCodeDetails(""); + decoder_filters_[0]->callbacks_->encodeHeaders(std::move(response_headers), false, "details"); + + // Invoke encodeData while all iteration is stopped and make sure the filters do not have + // encodeData called. + EXPECT_CALL(*encoder_filters_[0], encodeData(_, _)).Times(0); + EXPECT_CALL(*encoder_filters_[1], encodeData(_, _)).Times(0); + Buffer::OwnedImpl response_body("response"); + decoder_filters_[0]->callbacks_->encodeData(response_body, false); + decoder_filters_[0]->callbacks_->encodeTrailers( + ResponseTrailerMapPtr{new TestResponseTrailerMapImpl{{"some", "trailer"}}}); + + // Verify that once encoder_filters_[1]'s continueEncoding() is called, encoder_filters_[0]'s + // encodeHeaders() is called, and both filters receive data and trailers consequently. + EXPECT_CALL(*encoder_filters_[0], encodeHeaders(_, _)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + EXPECT_CALL(response_encoder_, encodeHeaders(_, false)); + EXPECT_CALL(*encoder_filters_[1], encodeData(_, _)).WillOnce(Return(FilterDataStatus::Continue)); + EXPECT_CALL(*encoder_filters_[0], encodeData(_, _)).WillOnce(Return(FilterDataStatus::Continue)); + EXPECT_CALL(response_encoder_, encodeData(_, _)); + EXPECT_CALL(*encoder_filters_[1], encodeTrailers(_)) + .WillOnce(Return(FilterTrailersStatus::Continue)); + EXPECT_CALL(*encoder_filters_[0], encodeTrailers(_)) + .WillOnce(Return(FilterTrailersStatus::Continue)); + EXPECT_CALL(response_encoder_, encodeTrailers(_)); + EXPECT_CALL(*encoder_filters_[0], encodeComplete()); + EXPECT_CALL(*encoder_filters_[1], encodeComplete()); + expectOnDestroy(); + encoder_filters_[1]->callbacks_->continueEncoding(); +} + +TEST_F(HttpConnectionManagerImplTest, InboundOnlyDrainNoConnectionCloseForOutbound) { + std::string yaml = R"EOF( +address: + socket_address: { address: 127.0.0.1, port_value: 1234 } +metadata: { filter_metadata: { com.bar.foo: { baz: test_value } } } +traffic_direction: OUTBOUND + )EOF"; + auto cfg = Server::parseListenerFromV3Yaml(yaml); + const Network::ListenerInfo& listener_info = Server::Configuration::FakeListenerInfo(cfg); + EXPECT_CALL(factory_context_, listenerInfo()).WillOnce(ReturnRef(listener_info)); + setup(); + + // In this scenario, we have an inbound only drain, but the conn manager for an outbound listener + // is checking to see if it should drain. We set the expectation here that the answer is no, + // so we SHOULDN'T see a connection close header. + EXPECT_CALL(drain_close_, drainClose(Network::DrainDirection::All)).WillOnce(Return(false)); + + std::shared_ptr filter(new NiceMock()); + EXPECT_CALL(filter_factory_, createFilterChain(_)) + .WillOnce(Invoke([&](FilterChainManager& manager) -> bool { + auto factory = createDecoderFilterFactoryCb(StreamDecoderFilterSharedPtr{filter}); + manager.applyFilterFactoryCb({}, factory); + return true; + })); + + EXPECT_CALL(*codec_, dispatch(_)) + .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { + decoder_ = &conn_manager_->newStream(response_encoder_); + RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{{":authority", "host"}, + {":path", "/"}, + {":method", "GET"}, + {"connection", "keep-alive"}}}; + decoder_->decodeHeaders(std::move(headers), true); + + ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; + filter->callbacks_->streamInfo().setResponseCodeDetails(""); + filter->callbacks_->encodeHeaders(std::move(response_headers), true, "details"); + + data.drain(4); + return Http::okStatus(); + })); + + EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) + .WillOnce(Invoke([](const ResponseHeaderMap& headers, bool) -> void { + EXPECT_NE("close", headers.getConnectionValue()); + })); + + Buffer::OwnedImpl fake_input; + conn_manager_->onData(fake_input, false); +} + +TEST_F(HttpConnectionManagerImplTest, InboundOnlyDrainConnectionCloseForInbound) { + std::string yaml = R"EOF( +address: + socket_address: { address: 127.0.0.1, port_value: 1234 } +metadata: { filter_metadata: { com.bar.foo: { baz: test_value } } } +traffic_direction: INBOUND + )EOF"; + auto cfg = Server::parseListenerFromV3Yaml(yaml); + const Network::ListenerInfo& listener_info = Server::Configuration::FakeListenerInfo(cfg); + EXPECT_CALL(factory_context_, listenerInfo()).WillOnce(ReturnRef(listener_info)); + setup(); + + // In this scenario, we have an inbound only drain, and the conn manager for an inbound listener + // is checking to see if it should drain. We set the expectation here that the answer is yes, + // so we SHOULD see a connection close header. + EXPECT_CALL(drain_close_, drainClose(Network::DrainDirection::InboundOnly)) + .WillOnce(Return(true)); + + std::shared_ptr filter(new NiceMock()); + EXPECT_CALL(filter_factory_, createFilterChain(_)) + .WillOnce(Invoke([&](FilterChainManager& manager) -> bool { + auto factory = createDecoderFilterFactoryCb(StreamDecoderFilterSharedPtr{filter}); + manager.applyFilterFactoryCb({}, factory); + return true; + })); + + EXPECT_CALL(*codec_, dispatch(_)) + .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { + decoder_ = &conn_manager_->newStream(response_encoder_); + RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{{":authority", "host"}, + {":path", "/"}, + {":method", "GET"}, + {"connection", "keep-alive"}}}; + decoder_->decodeHeaders(std::move(headers), true); + + ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; + filter->callbacks_->streamInfo().setResponseCodeDetails(""); + filter->callbacks_->encodeHeaders(std::move(response_headers), true, "details"); + + data.drain(4); + return Http::okStatus(); + })); + + EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) + .WillOnce(Invoke([](const ResponseHeaderMap& headers, bool) -> void { + EXPECT_EQ("close", headers.getConnectionValue()); + })); + + Buffer::OwnedImpl fake_input; + conn_manager_->onData(fake_input, false); +} + +TEST_F(HttpConnectionManagerImplTest, DisableKeepAliveWhenDraining) { + setup(); + + EXPECT_CALL(drain_close_, drainClose(Network::DrainDirection::All)).WillOnce(Return(true)); + + std::shared_ptr filter(new NiceMock()); + EXPECT_CALL(filter_factory_, createFilterChain(_)) + .WillOnce(Invoke([&](FilterChainManager& manager) -> bool { + auto factory = createDecoderFilterFactoryCb(StreamDecoderFilterSharedPtr{filter}); + manager.applyFilterFactoryCb({}, factory); + return true; + })); + + EXPECT_CALL(*codec_, dispatch(_)) + .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { + decoder_ = &conn_manager_->newStream(response_encoder_); + RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{{":authority", "host"}, + {":path", "/"}, + {":method", "GET"}, + {"connection", "keep-alive"}}}; + decoder_->decodeHeaders(std::move(headers), true); + + ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; + filter->callbacks_->streamInfo().setResponseCodeDetails(""); + filter->callbacks_->encodeHeaders(std::move(response_headers), true, "details"); + + data.drain(4); + return Http::okStatus(); + })); + + EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) + .WillOnce(Invoke([](const ResponseHeaderMap& headers, bool) -> void { + EXPECT_EQ("close", headers.getConnectionValue()); + })); + + Buffer::OwnedImpl fake_input; + conn_manager_->onData(fake_input, false); +} + +TEST_F(HttpConnectionManagerImplTest, TestSessionTrace) { + setup(); + + // Set up the codec. + EXPECT_CALL(*codec_, dispatch(_)) + .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { + data.drain(4); + return Http::okStatus(); + })); + Buffer::OwnedImpl fake_input("1234"); + conn_manager_->onData(fake_input, false); + + setupFilterChain(1, 1); + + // Create a new stream + decoder_ = &conn_manager_->newStream(response_encoder_); + + // Send headers to that stream, and verify we both set and clear the tracked object. + { + RequestHeaderMapPtr headers{ + new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "POST"}}}; + + EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, pushTrackedObject(_)) + .Times(1) + .WillOnce(Invoke([](const ScopeTrackedObject* object) -> void { + ASSERT(object != nullptr); // On the first call, this should be the active stream. + std::stringstream out; + object->dumpState(out); + std::string state = out.str(); + EXPECT_THAT(state, + testing::HasSubstr("filter_manager_callbacks_.requestHeaders(): null")); + EXPECT_THAT(state, testing::HasSubstr("protocol_: 1")); + })); + EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, popTrackedObject(_)); + EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, false)) + .WillOnce(Invoke([](HeaderMap&, bool) -> FilterHeadersStatus { + return FilterHeadersStatus::StopIteration; + })); + decoder_->decodeHeaders(std::move(headers), false); + } + + // Send trailers to that stream, and verify by this point headers are in logged state. + { + RequestTrailerMapPtr trailers{new TestRequestTrailerMapImpl{{"foo", "bar"}}}; + EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, pushTrackedObject(_)) + .Times(1) + .WillOnce(Invoke([](const ScopeTrackedObject* object) -> void { + ASSERT(object != nullptr); // On the first call, this should be the active stream. + std::stringstream out; + object->dumpState(out); + std::string state = out.str(); + EXPECT_THAT(state, testing::HasSubstr("filter_manager_callbacks_.requestHeaders(): \n")); + EXPECT_THAT(state, testing::HasSubstr("':authority', 'host'\n")); + EXPECT_THAT(state, testing::HasSubstr("protocol_: 1")); + })); + EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, popTrackedObject(_)); + EXPECT_CALL(*decoder_filters_[0], decodeComplete()); + EXPECT_CALL(*decoder_filters_[0], decodeTrailers(_)) + .WillOnce(Return(FilterTrailersStatus::StopIteration)); + decoder_->decodeTrailers(std::move(trailers)); + } + + expectOnDestroy(); + filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); +} + +// SRDS no scope found. +TEST_F(HttpConnectionManagerImplTest, TestSrdsRouteNotFound) { + setup(SetupOpts().setUseSrds(true)); + setupFilterChain(1, 0); // Recreate the chain for second stream. + + EXPECT_CALL(*static_cast(scopeKeyBuilder().ptr()), + computeScopeKey(_)) + .Times(2); + EXPECT_CALL(*static_cast( + scopedRouteConfigProvider()->config().get()), + getRouteConfig(_)) + .Times(2) + .WillRepeatedly(Return(nullptr)); + EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { + decoder_ = &conn_manager_->newStream(response_encoder_); + RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ + {":authority", "host"}, {":method", "GET"}, {":path", "/foo"}}}; + decoder_->decodeHeaders(std::move(headers), true); + data.drain(4); + return Http::okStatus(); + })); + + EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) + .WillOnce(InvokeWithoutArgs([&]() -> FilterHeadersStatus { + EXPECT_EQ(nullptr, decoder_filters_[0]->callbacks_->route()); + return FilterHeadersStatus::StopIteration; + })); + EXPECT_CALL(*decoder_filters_[0], decodeComplete()); // end_stream=true. + + Buffer::OwnedImpl fake_input("1234"); + conn_manager_->onData(fake_input, false); + + expectOnDestroy(); + filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); +} + +// SRDS updating scopes affects routing. +TEST_F(HttpConnectionManagerImplTest, TestSrdsUpdate) { + setup(SetupOpts().setUseSrds(true)); + + EXPECT_CALL(*static_cast(scopeKeyBuilder().ptr()), + computeScopeKey(_)) + .Times(3); + EXPECT_CALL(*static_cast( + scopedRouteConfigProvider()->config().get()), + getRouteConfig(_)) + .Times(3) + .WillOnce(Return(nullptr)) + .WillOnce(Return(nullptr)) // refreshCachedRoute first time. + .WillOnce(Return(route_config_)); // triggered by callbacks_->route(), SRDS now updated. + EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { + decoder_ = &conn_manager_->newStream(response_encoder_); + RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ + {":authority", "host"}, {":method", "GET"}, {":path", "/foo"}}}; + decoder_->decodeHeaders(std::move(headers), true); + data.drain(4); + return Http::okStatus(); + })); + const std::string fake_cluster1_name = "fake_cluster1"; + std::shared_ptr route1 = std::make_shared>(); + EXPECT_CALL(route1->route_entry_, clusterName()).WillRepeatedly(ReturnRef(fake_cluster1_name)); + std::shared_ptr fake_cluster1 = + std::make_shared>(); + EXPECT_CALL(cluster_manager_, getThreadLocalCluster(_)).WillOnce(Return(fake_cluster1.get())); + EXPECT_CALL(*route_config_, route(_, _, _, _)).WillOnce(Return(route1)); + // First no-scope-found request will be handled by decoder_filters_[0]. + setupFilterChain(1, 0); + EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) + .WillOnce(InvokeWithoutArgs([&]() -> FilterHeadersStatus { + EXPECT_EQ(nullptr, decoder_filters_[0]->callbacks_->route()); + + // Clear route and next call on callbacks_->route() will trigger a re-snapping of the + // snapped_route_config_. + decoder_filters_[0]->callbacks_->downstreamCallbacks()->clearRouteCache(); + + // Now route config provider returns something. + EXPECT_EQ(route1, decoder_filters_[0]->callbacks_->route()); + EXPECT_EQ(route1, decoder_filters_[0]->callbacks_->streamInfo().route()); + EXPECT_EQ(fake_cluster1->info(), decoder_filters_[0]->callbacks_->clusterInfo()); + return FilterHeadersStatus::StopIteration; + + return FilterHeadersStatus::StopIteration; + })); + EXPECT_CALL(*decoder_filters_[0], decodeComplete()); // end_stream=true. + Buffer::OwnedImpl fake_input("1234"); + conn_manager_->onData(fake_input, false); + + expectOnDestroy(); + filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); +} + +// SRDS Scope header update cause cross-scope reroute. +TEST_F(HttpConnectionManagerImplTest, TestSrdsCrossScopeReroute) { + setup(SetupOpts().setUseSrds(true)); + + std::shared_ptr route_config1 = + std::make_shared>(); + std::shared_ptr route_config2 = + std::make_shared>(); + std::shared_ptr route1 = std::make_shared>(); + std::shared_ptr route2 = std::make_shared>(); + EXPECT_CALL(*route_config1, route(_, _, _, _)).WillRepeatedly(Return(route1)); + EXPECT_CALL(*route_config2, route(_, _, _, _)).WillRepeatedly(Return(route2)); + EXPECT_CALL(*static_cast(scopeKeyBuilder().ptr()), + computeScopeKey(_)) + .Times(3) + .WillRepeatedly(Invoke([&](const HeaderMap& headers) -> Router::ScopeKeyPtr { + auto& test_headers = dynamic_cast(headers); + if (test_headers.get_("scope_key") == "foo") { + Router::ScopeKey key; + return std::make_unique(std::move(key)); + } + return nullptr; + })); + EXPECT_CALL(*static_cast( + scopedRouteConfigProvider()->config().get()), + getRouteConfig(_)) + // 1. Snap scoped route config; + // 2. refreshCachedRoute (both in decodeHeaders(headers,end_stream); + // 3. then refreshCachedRoute triggered by decoder_filters_[1]->callbacks_->route(). + .Times(3) + .WillRepeatedly( + Invoke([&](const Router::ScopeKeyPtr& scope_key) -> Router::ConfigConstSharedPtr { + if (scope_key != nullptr) { + return route_config1; + } + return route_config2; + })); + EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { + decoder_ = &conn_manager_->newStream(response_encoder_); + RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ + {":authority", "host"}, {":method", "GET"}, {"scope_key", "foo"}, {":path", "/foo"}}}; + decoder_->decodeHeaders(std::move(headers), false); + data.drain(4); + return Http::okStatus(); + })); + setupFilterChain(2, 0); + EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, false)) + .WillOnce(Invoke([&](Http::HeaderMap& headers, bool) -> FilterHeadersStatus { + EXPECT_EQ(route1, decoder_filters_[0]->callbacks_->route()); + auto& test_headers = dynamic_cast(headers); + // Clear cached route and change scope key to "bar". + decoder_filters_[0]->callbacks_->downstreamCallbacks()->clearRouteCache(); + test_headers.remove("scope_key"); + test_headers.addCopy("scope_key", "bar"); + return FilterHeadersStatus::Continue; + })); + EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, false)) + .WillOnce(Invoke([&](Http::HeaderMap& headers, bool) -> FilterHeadersStatus { + auto& test_headers = dynamic_cast(headers); + EXPECT_EQ(test_headers.get_("scope_key"), "bar"); + // Route now switched to route2 as header "scope_key" has changed. + EXPECT_EQ(route2, decoder_filters_[1]->callbacks_->route()); + EXPECT_EQ(route2, decoder_filters_[1]->callbacks_->streamInfo().route()); + return FilterHeadersStatus::StopIteration; + })); + + Buffer::OwnedImpl fake_input("1234"); + conn_manager_->onData(fake_input, false); + + expectOnDestroy(); + filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); +} + +// SRDS scoped RouteConfiguration found and route found. +TEST_F(HttpConnectionManagerImplTest, TestSrdsRouteFound) { + setup(SetupOpts().setUseSrds(true)); + setupFilterChain(1, 0); + + const std::string fake_cluster1_name = "fake_cluster1"; + std::shared_ptr route1 = std::make_shared>(); + EXPECT_CALL(route1->route_entry_, clusterName()).WillRepeatedly(ReturnRef(fake_cluster1_name)); + std::shared_ptr fake_cluster1 = + std::make_shared>(); + EXPECT_CALL(cluster_manager_, getThreadLocalCluster(_)).WillOnce(Return(fake_cluster1.get())); + EXPECT_CALL(*static_cast(scopeKeyBuilder().ptr()), + computeScopeKey(_)) + .Times(2); + EXPECT_CALL(*scopedRouteConfigProvider()->config(), getRouteConfig(_)) + // 1. decodeHeaders() snapping route config. + // 2. refreshCachedRoute() later in the same decodeHeaders(). + .Times(2); + EXPECT_CALL( + *static_cast( + scopedRouteConfigProvider()->config()->route_config_.get()), + route(_, _, _, _)) + .WillOnce(Return(route1)); + EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { + decoder_ = &conn_manager_->newStream(response_encoder_); + RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ + {":authority", "host"}, {":method", "GET"}, {":path", "/foo"}}}; + decoder_->decodeHeaders(std::move(headers), true); + data.drain(4); + return Http::okStatus(); + })); + EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) + .WillOnce(InvokeWithoutArgs([&]() -> FilterHeadersStatus { + EXPECT_EQ(route1, decoder_filters_[0]->callbacks_->route()); + EXPECT_EQ(route1, decoder_filters_[0]->callbacks_->streamInfo().route()); + EXPECT_EQ(fake_cluster1->info(), decoder_filters_[0]->callbacks_->clusterInfo()); + return FilterHeadersStatus::StopIteration; + })); + EXPECT_CALL(*decoder_filters_[0], decodeComplete()); + + Buffer::OwnedImpl fake_input("1234"); + conn_manager_->onData(fake_input, false); + + expectOnDestroy(); + filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); +} + +TEST_F(HttpConnectionManagerImplTest, NewConnection) { + setup(SetupOpts().setUseSrds(true)); + + filter_callbacks_.connection_.stream_info_.protocol_ = absl::nullopt; + EXPECT_CALL(filter_callbacks_.connection_.stream_info_, protocol()); + EXPECT_EQ(Network::FilterStatus::Continue, conn_manager_->onNewConnection()); + EXPECT_EQ(0U, stats_.named_.downstream_cx_http3_total_.value()); + EXPECT_EQ(0U, stats_.named_.downstream_cx_http3_active_.value()); + + filter_callbacks_.connection_.stream_info_.protocol_ = Envoy::Http::Protocol::Http3; + codec_->protocol_ = Http::Protocol::Http3; + EXPECT_CALL(filter_callbacks_.connection_.stream_info_, protocol()); + EXPECT_CALL(*codec_, protocol()).Times(AtLeast(1)); + EXPECT_EQ(Network::FilterStatus::StopIteration, conn_manager_->onNewConnection()); + EXPECT_EQ(1U, stats_.named_.downstream_cx_http3_total_.value()); + EXPECT_EQ(1U, stats_.named_.downstream_cx_http3_active_.value()); +} + +TEST_F(HttpConnectionManagerImplTest, HeaderOnlyRequestAndResponseUsingHttp3) { + setup(SetupOpts().setTracing(false)); + + filter_callbacks_.connection_.stream_info_.protocol_ = Envoy::Http::Protocol::Http3; + codec_->protocol_ = Http::Protocol::Http3; + EXPECT_EQ(Network::FilterStatus::StopIteration, conn_manager_->onNewConnection()); + + // Store the basic request encoder during filter chain setup. + std::shared_ptr filter(new NiceMock()); + + EXPECT_CALL(*filter, decodeHeaders(_, true)) + .WillOnce(Invoke([&](RequestHeaderMap& headers, bool) -> FilterHeadersStatus { + EXPECT_NE(nullptr, headers.ForwardedFor()); + EXPECT_EQ("http", headers.getForwardedProtoValue()); + return FilterHeadersStatus::StopIteration; + })); + + EXPECT_CALL(*filter, setDecoderFilterCallbacks(_)); + + EXPECT_CALL(filter_factory_, createFilterChain(_)) + .WillOnce(Invoke([&](FilterChainManager& manager) -> bool { + auto factory = createDecoderFilterFactoryCb(StreamDecoderFilterSharedPtr{filter}); + manager.applyFilterFactoryCb({}, factory); + return true; + })); + + EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)); + + // Pretend to get a new stream and then fire a headers only request into it. Then we respond into + // the filter. + RequestDecoder& decoder = conn_manager_->newStream(response_encoder_); + RequestHeaderMapPtr headers{ + new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; + decoder.decodeHeaders(std::move(headers), true); + + ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; + filter->callbacks_->streamInfo().setResponseCodeDetails(""); + filter->callbacks_->encodeHeaders(std::move(response_headers), true, "details"); + + EXPECT_EQ(1U, stats_.named_.downstream_rq_2xx_.value()); + EXPECT_EQ(1U, listener_stats_.downstream_rq_2xx_.value()); + EXPECT_EQ(1U, stats_.named_.downstream_rq_completed_.value()); + EXPECT_EQ(1U, listener_stats_.downstream_rq_completed_.value()); + EXPECT_EQ(1U, stats_.named_.downstream_cx_http3_total_.value()); + filter_callbacks_.connection_.dispatcher_.clearDeferredDeleteList(); + response_encoder_.stream_.codec_callbacks_->onCodecEncodeComplete(); + response_encoder_.stream_.codec_callbacks_ = nullptr; + conn_manager_.reset(); + EXPECT_EQ(0U, stats_.named_.downstream_cx_http3_active_.value()); +} + +namespace { + +class SimpleType : public StreamInfo::FilterState::Object { +public: + SimpleType(int value) : value_(value) {} + int access() const { return value_; } + +private: + int value_; +}; + +} // namespace + +TEST_F(HttpConnectionManagerImplTest, ConnectionFilterState) { + filter_callbacks_.connection_.stream_info_.filter_state_->setData( + "connection_provided_data", std::make_shared(555), + StreamInfo::FilterState::StateType::ReadOnly); + + setup(SetupOpts().setTracing(false)); + setupFilterChain(1, 0, /* num_requests = */ 3); + + EXPECT_CALL(*codec_, dispatch(_)) + .Times(2) + .WillRepeatedly(Invoke([&](Buffer::Instance&) -> Http::Status { + decoder_ = &conn_manager_->newStream(response_encoder_); + RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ + {":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; + decoder_->decodeHeaders(std::move(headers), true); + return Http::okStatus(); + })); + { + InSequence s; + EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) + .WillOnce(Invoke([this](HeaderMap&, bool) -> FilterHeadersStatus { + decoder_filters_[0]->callbacks_->streamInfo().filterState()->setData( + "per_filter_chain", std::make_unique(1), + StreamInfo::FilterState::StateType::ReadOnly, + StreamInfo::FilterState::LifeSpan::FilterChain); + decoder_filters_[0]->callbacks_->streamInfo().filterState()->setData( + "per_downstream_request", std::make_unique(2), + StreamInfo::FilterState::StateType::ReadOnly, + StreamInfo::FilterState::LifeSpan::Request); + decoder_filters_[0]->callbacks_->streamInfo().filterState()->setData( + "per_downstream_connection", std::make_unique(3), + StreamInfo::FilterState::StateType::ReadOnly, + StreamInfo::FilterState::LifeSpan::Connection); + return FilterHeadersStatus::StopIteration; + })); + EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, true)) + .WillOnce(Invoke([this](HeaderMap&, bool) -> FilterHeadersStatus { + EXPECT_FALSE( + decoder_filters_[1]->callbacks_->streamInfo().filterState()->hasData( + "per_filter_chain")); + EXPECT_TRUE( + decoder_filters_[1]->callbacks_->streamInfo().filterState()->hasData( + "per_downstream_request")); + EXPECT_TRUE( + decoder_filters_[1]->callbacks_->streamInfo().filterState()->hasData( + "per_downstream_connection")); + EXPECT_TRUE( + decoder_filters_[1]->callbacks_->streamInfo().filterState()->hasData( + "connection_provided_data")); + return FilterHeadersStatus::StopIteration; + })); + EXPECT_CALL(*decoder_filters_[2], decodeHeaders(_, true)) + .WillOnce(Invoke([this](HeaderMap&, bool) -> FilterHeadersStatus { + EXPECT_FALSE( + decoder_filters_[2]->callbacks_->streamInfo().filterState()->hasData( + "per_filter_chain")); + EXPECT_FALSE( + decoder_filters_[2]->callbacks_->streamInfo().filterState()->hasData( + "per_downstream_request")); + EXPECT_TRUE( + decoder_filters_[2]->callbacks_->streamInfo().filterState()->hasData( + "per_downstream_connection")); + EXPECT_TRUE( + decoder_filters_[1]->callbacks_->streamInfo().filterState()->hasData( + "connection_provided_data")); + return FilterHeadersStatus::StopIteration; + })); + } + + EXPECT_CALL(*decoder_filters_[0], decodeComplete()); + EXPECT_CALL(*decoder_filters_[0], onStreamComplete()); + EXPECT_CALL(*decoder_filters_[0], onDestroy()); + EXPECT_CALL(*decoder_filters_[1], decodeComplete()); + EXPECT_CALL(*decoder_filters_[2], decodeComplete()); + + Buffer::OwnedImpl fake_input; + conn_manager_->onData(fake_input, false); + decoder_filters_[0]->callbacks_->recreateStream(nullptr); + conn_manager_->onData(fake_input, false); + + // The connection life time data should have been written to the connection filter state. + EXPECT_TRUE(filter_callbacks_.connection_.stream_info_.filter_state_->hasData( + "per_downstream_connection")); + EXPECT_CALL(*decoder_filters_[1], onStreamComplete()); + EXPECT_CALL(*decoder_filters_[1], onDestroy()); + EXPECT_CALL(*decoder_filters_[2], onStreamComplete()); + EXPECT_CALL(*decoder_filters_[2], onDestroy()); + filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); +} + +TEST_F(HttpConnectionManagerImplTest, RequestRejectedViaIPDetection) { + OriginalIPRejectRequestOptions reject_options = {Http::Code::Forbidden, "ip detection failed"}; + auto extension = getCustomHeaderExtension("x-ip", reject_options); + ip_detection_extensions_.push_back(extension); + + use_remote_address_ = false; + + setup(); + + // 403 direct response when IP detection fails. + EXPECT_CALL(response_encoder_, encodeHeaders(_, false)) + .WillOnce(Invoke([](const ResponseHeaderMap& headers, bool) -> void { + EXPECT_EQ("403", headers.getStatusValue()); + })); + std::string response_body; + EXPECT_CALL(response_encoder_, encodeData(_, true)).WillOnce(AddBufferToString(&response_body)); + + startRequest(); + + EXPECT_EQ("ip detection failed", response_body); + EXPECT_EQ(1U, stats_.named_.downstream_rq_rejected_via_ip_detection_.value()); +} + +TEST_F(HttpConnectionManagerImplTest, DisconnectDuringEncodeHeader) { + setup(); + setupFilterChain(1, 0); + + EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) + .WillOnce(Return(FilterHeadersStatus::StopIteration)); + startRequest(/*end_stream=*/true); + + EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) + .WillOnce(Invoke([&](const ResponseHeaderMap& headers, bool) -> void { + EXPECT_NE(nullptr, headers.Server()); + EXPECT_EQ("envoy-server-test", headers.getServerValue()); + conn_manager_->onEvent(Network::ConnectionEvent::LocalClose); + })); + EXPECT_CALL(*decoder_filters_[0], onStreamComplete()); + EXPECT_CALL(*decoder_filters_[0], onDestroy()); + + ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; + decoder_filters_[0]->callbacks_->streamInfo().setResponseCodeDetails(""); + decoder_filters_[0]->callbacks_->encodeHeaders(std::move(response_headers), true, "details"); +} + +TEST_F(HttpConnectionManagerImplTest, DisconnectDuringEncodeBody) { + setup(); + setupFilterChain(1, 0); + + EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) + .WillOnce(Return(FilterHeadersStatus::StopIteration)); + startRequest(/*end_stream=*/true); + + EXPECT_CALL(response_encoder_, encodeHeaders(_, false)) + .WillOnce(Invoke([&](const ResponseHeaderMap& headers, bool) -> void { + EXPECT_NE(nullptr, headers.Server()); + EXPECT_EQ("envoy-server-test", headers.getServerValue()); + })); + EXPECT_CALL(response_encoder_, encodeData(_, true)) + .WillOnce(Invoke([&](Buffer::Instance&, bool) -> void { + conn_manager_->onEvent(Network::ConnectionEvent::LocalClose); + })); + EXPECT_CALL(*decoder_filters_[0], onStreamComplete()); + EXPECT_CALL(*decoder_filters_[0], onDestroy()); + + ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; + decoder_filters_[0]->callbacks_->streamInfo().setResponseCodeDetails(""); + decoder_filters_[0]->callbacks_->encodeHeaders(std::move(response_headers), false, "details"); + Buffer::OwnedImpl response_body("response"); + decoder_filters_[0]->callbacks_->encodeData(response_body, true); +} + +// Verify that trailers added during a data encoding continuation are not double continued. +TEST_F(HttpConnectionManagerImplTest, AddTrailersDuringdDecodingContinue) { + InSequence s; + setup(); + + EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { + decoder_ = &conn_manager_->newStream(response_encoder_); + RequestHeaderMapPtr headers{ + new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; + decoder_->decodeHeaders(std::move(headers), false); + + Buffer::OwnedImpl request_body("request"); + decoder_->decodeData(request_body, true); + + return Http::okStatus(); + })); + + setupFilterChain(3, 0); + + EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::StopIteration)); + EXPECT_CALL(*decoder_filters_[0], decodeData(_, true)) + .WillOnce(Return(FilterDataStatus::Continue)); + EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + EXPECT_CALL(*decoder_filters_[2], decodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + EXPECT_CALL(*decoder_filters_[1], decodeData(_, true)) + .WillOnce(Invoke([&](Buffer::Instance&, bool) -> FilterDataStatus { + decoder_filters_[1]->callbacks_->addDecodedTrailers().addCopy(LowerCaseString("hello"), 1); + return FilterDataStatus::Continue; + })); + EXPECT_CALL(*decoder_filters_[2], decodeData(_, false)) + .WillOnce(Return(FilterDataStatus::Continue)); + EXPECT_CALL(*decoder_filters_[2], decodeTrailers(_)) + .WillOnce(Return(FilterTrailersStatus::Continue)); + + Buffer::OwnedImpl fake_input; + conn_manager_->onData(fake_input, false); + + ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; + decoder_filters_[0]->callbacks_->encodeHeaders(std::move(response_headers), true, "details"); +} + +// Verify that trailers added during a data decoding continuation are not double continued. +TEST_F(HttpConnectionManagerImplTest, AddTrailersDuringEncodingContinue) { + InSequence s; + setup(); + + EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { + decoder_ = &conn_manager_->newStream(response_encoder_); + RequestHeaderMapPtr headers{ + new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; + decoder_->decodeHeaders(std::move(headers), true); + return Http::okStatus(); + })); + + setupFilterChain(1, 2); + + EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) + .WillOnce(Return(FilterHeadersStatus::StopIteration)); + + Buffer::OwnedImpl fake_input; + conn_manager_->onData(fake_input, false); + + EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::StopIteration)); + EXPECT_CALL(*encoder_filters_[1], encodeData(_, true)) + .WillOnce(Return(FilterDataStatus::Continue)); + EXPECT_CALL(*encoder_filters_[0], encodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + EXPECT_CALL(response_encoder_, encodeHeaders(_, false)); + EXPECT_CALL(*encoder_filters_[0], encodeData(_, true)) + .WillOnce(Invoke([&](Buffer::Instance&, bool) -> FilterDataStatus { + encoder_filters_[0]->callbacks_->addEncodedTrailers().addCopy(LowerCaseString("hello"), 1); + return FilterDataStatus::Continue; + })); + EXPECT_CALL(response_encoder_, encodeData(_, false)); + EXPECT_CALL(response_encoder_, encodeTrailers(_)); + + ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; + decoder_filters_[0]->callbacks_->encodeHeaders(std::move(response_headers), false, "details"); + Buffer::OwnedImpl response_body("response"); + decoder_filters_[0]->callbacks_->encodeData(response_body, true); +} + +TEST_F(HttpConnectionManagerImplTest, DisconnectDuringEncodeTrailer) { + setup(); + setupFilterChain(1, 0); + + EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) + .WillOnce(Return(FilterHeadersStatus::StopIteration)); + startRequest(/*end_stream=*/true); + + EXPECT_CALL(response_encoder_, encodeHeaders(_, false)) + .WillOnce(Invoke([&](const ResponseHeaderMap& headers, bool) -> void { + EXPECT_NE(nullptr, headers.Server()); + EXPECT_EQ("envoy-server-test", headers.getServerValue()); + })); + EXPECT_CALL(response_encoder_, encodeData(_, false)); + EXPECT_CALL(response_encoder_, encodeTrailers(_)) + .WillOnce(Invoke([&](const Http::ResponseTrailerMap&) -> void { + conn_manager_->onEvent(Network::ConnectionEvent::LocalClose); + })); + EXPECT_CALL(*decoder_filters_[0], onStreamComplete()); + EXPECT_CALL(*decoder_filters_[0], onDestroy()); + + ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; + decoder_filters_[0]->callbacks_->streamInfo().setResponseCodeDetails(""); + decoder_filters_[0]->callbacks_->encodeHeaders(std::move(response_headers), false, "details"); + Buffer::OwnedImpl response_body("response"); + decoder_filters_[0]->callbacks_->encodeData(response_body, false); + decoder_filters_[0]->callbacks_->encodeTrailers( + ResponseTrailerMapPtr{new TestResponseTrailerMapImpl{{"some", "trailer"}}}); +} + +TEST_F(HttpConnectionManagerImplTest, DirectLocalReplyCausesDisconnect) { + initial_buffer_limit_ = 10; + setup(); + setUpEncoderAndDecoder(false, false); + sendRequestHeadersAndData(); + + // Start the response without processing the request headers through all + // filters. + ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; + EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::StopIteration)); + decoder_filters_[0]->callbacks_->streamInfo().setResponseCodeDetails(""); + decoder_filters_[0]->callbacks_->encodeHeaders(std::move(response_headers), false, "details"); + + // Now overload the buffer with response data. The filter returns + // StopIterationAndBuffer, which will trigger an early response. + + expectOnDestroy(); + Buffer::OwnedImpl fake_response("A long enough string to go over watermarks"); + // Fake response starts doing through the filter. + EXPECT_CALL(*encoder_filters_[1], encodeData(_, false)) + .WillOnce(Return(FilterDataStatus::StopIterationAndBuffer)); + std::string response_body; + // The 500 goes directly to the encoder. + EXPECT_CALL(response_encoder_, encodeHeaders(_, false)) + .WillOnce(Invoke([&](const ResponseHeaderMap& headers, bool) -> FilterHeadersStatus { + // Make sure this is a 500 + EXPECT_EQ("500", headers.getStatusValue()); + // Make sure Envoy standard sanitization has been applied. + EXPECT_TRUE(headers.Date() != nullptr); + EXPECT_EQ("response_payload_too_large", + decoder_filters_[0]->callbacks_->streamInfo().responseCodeDetails().value()); + return FilterHeadersStatus::Continue; + })); + EXPECT_CALL(response_encoder_, encodeData(_, true)) + .WillOnce(Invoke([&](Buffer::Instance&, bool) -> void { + conn_manager_->onEvent(Network::ConnectionEvent::LocalClose); + })); + decoder_filters_[0]->callbacks_->encodeData(fake_response, false); + + EXPECT_EQ(1U, stats_.named_.rs_too_large_.value()); +} + +// Header validator rejects header map for HTTP/1.x protocols +TEST_F(HttpConnectionManagerImplTest, HeaderValidatorRejectHttp1) { + setup(); + expectUhvHeaderCheck(HeaderValidator::ValidationResult( + HeaderValidator::ValidationResult::Action::Reject, "bad_header_map"), + ServerHeaderValidator::RequestHeadersTransformationResult::success()); + + EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { + decoder_ = &conn_manager_->newStream(response_encoder_); + RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ + {":authority", "host"}, {":path", "/something"}, {":method", "GET"}}}; + decoder_->decodeHeaders(std::move(headers), true); + data.drain(4); + return Http::okStatus(); + })); + EXPECT_CALL(response_encoder_, streamErrorOnInvalidHttpMessage()).WillRepeatedly(Return(false)); + + // This test also verifies that decoder/encoder filters have onDestroy() called only once. + auto* filter = new MockStreamFilter(); + EXPECT_CALL(filter_factory_, createFilterChain(_)) + .WillOnce(Invoke([&](FilterChainManager& manager) -> bool { + auto factory = createStreamFilterFactoryCb(StreamFilterSharedPtr{filter}); + manager.applyFilterFactoryCb({}, factory); + return true; + })); + EXPECT_CALL(*filter, setDecoderFilterCallbacks(_)); + EXPECT_CALL(*filter, setEncoderFilterCallbacks(_)); + EXPECT_CALL(*filter, encodeHeaders(_, true)); + EXPECT_CALL(*filter, encodeComplete()); + EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) + .WillOnce(Invoke([&](const ResponseHeaderMap& headers, bool) -> void { + EXPECT_EQ("400", headers.getStatusValue()); + // By default mock codec indicates HTTP/1.1 protocol which should result in closed + // connection on error + EXPECT_EQ("close", headers.getConnectionValue()); + EXPECT_EQ("bad_header_map", + filter->decoder_callbacks_->streamInfo().responseCodeDetails().value()); + })); + EXPECT_CALL(*filter, onStreamComplete()); + EXPECT_CALL(*filter, onDestroy()); + + Buffer::OwnedImpl fake_input("1234"); + conn_manager_->onData(fake_input, false); +} + +// Header validator rejects header map for HTTP/2 protocols +TEST_F(HttpConnectionManagerImplTest, HeaderValidatorRejectHttp2) { + codec_->protocol_ = Protocol::Http2; + setup(); + expectUhvHeaderCheck(HeaderValidator::ValidationResult( + HeaderValidator::ValidationResult::Action::Reject, "bad_header_map"), + ServerHeaderValidator::RequestHeadersTransformationResult::success()); + + EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { + decoder_ = &conn_manager_->newStream(response_encoder_); + RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ + {":authority", "host"}, {":path", "/something"}, {":method", "GET"}}}; + decoder_->decodeHeaders(std::move(headers), true); + data.drain(4); + return Http::okStatus(); + })); + + EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) + .WillOnce(Invoke([&](const ResponseHeaderMap& headers, bool) -> void { + EXPECT_EQ("400", headers.getStatusValue()); + // For HTTP/2 protocols connection should not be closed + EXPECT_TRUE(headers.Connection() == nullptr); + EXPECT_EQ("bad_header_map", decoder_->streamInfo().responseCodeDetails().value()); + })); + + Buffer::OwnedImpl fake_input("1234"); + conn_manager_->onData(fake_input, false); +} + +// Header validator rejects gRPC request +TEST_F(HttpConnectionManagerImplTest, HeaderValidatorRejectGrpcRequest) { + codec_->protocol_ = Protocol::Http2; + setup(); + expectUhvHeaderCheck(HeaderValidator::ValidationResult( + HeaderValidator::ValidationResult::Action::Reject, "bad_header_map"), + ServerHeaderValidator::RequestHeadersTransformationResult::success()); + + EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { + decoder_ = &conn_manager_->newStream(response_encoder_); + RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ + {"content-type", "application/grpc"}, // Make Envoy interpret this request as gRPC call + {":authority", "host"}, + {":path", "/something"}, + {":method", "GET"}}}; + decoder_->decodeHeaders(std::move(headers), true); + data.drain(4); + return Http::okStatus(); + })); + + EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) + .WillOnce(Invoke([&](const ResponseHeaderMap& headers, bool) -> void { + EXPECT_EQ("200", headers.getStatusValue()); + EXPECT_EQ("13", headers.getGrpcStatusValue()); + EXPECT_EQ("bad_header_map", decoder_->streamInfo().responseCodeDetails().value()); + })); + + Buffer::OwnedImpl fake_input("1234"); + conn_manager_->onData(fake_input, false); +} + +// Header validator redirects +TEST_F(HttpConnectionManagerImplTest, HeaderValidatorRedirect) { + setup(); + expectUhvHeaderCheck( + HeaderValidator::ValidationResult::success(), + ServerHeaderValidator::RequestHeadersTransformationResult( + ServerHeaderValidator::RequestHeadersTransformationResult::Action::Redirect, + "bad_header_map")); + + EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { + decoder_ = &conn_manager_->newStream(response_encoder_); + RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ + {":authority", "host"}, {":path", "/something"}, {":method", "GET"}}}; + decoder_->decodeHeaders(std::move(headers), true); + data.drain(4); + return Http::okStatus(); + })); + + EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) + .WillOnce(Invoke([&](const ResponseHeaderMap& headers, bool) -> void { + EXPECT_EQ("307", headers.getStatusValue()); + EXPECT_EQ("/some/new/path", headers.getLocationValue()); + EXPECT_EQ("bad_header_map", decoder_->streamInfo().responseCodeDetails().value()); + })); + + Buffer::OwnedImpl fake_input("1234"); + conn_manager_->onData(fake_input, false); +} + +// Header validator redirects gRPC request +TEST_F(HttpConnectionManagerImplTest, HeaderValidatorRedirectGrpcRequest) { + codec_->protocol_ = Protocol::Http2; + setup(); + expectUhvHeaderCheck( + HeaderValidator::ValidationResult::success(), + ServerHeaderValidator::RequestHeadersTransformationResult( + ServerHeaderValidator::RequestHeadersTransformationResult::Action::Redirect, + "bad_header_map")); + + EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { + decoder_ = &conn_manager_->newStream(response_encoder_); + RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ + {"content-type", "application/grpc"}, // Make Envoy interpret this request as gRPC call + {":authority", "host"}, + {":path", "/something"}, + {":method", "GET"}}}; + decoder_->decodeHeaders(std::move(headers), true); + data.drain(4); + return Http::okStatus(); + })); + + EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) + .WillOnce(Invoke([&](const ResponseHeaderMap& headers, bool) -> void { + EXPECT_EQ("200", headers.getStatusValue()); + EXPECT_EQ("13", headers.getGrpcStatusValue()); + EXPECT_EQ("bad_header_map", decoder_->streamInfo().responseCodeDetails().value()); + })); + + Buffer::OwnedImpl fake_input("1234"); + conn_manager_->onData(fake_input, false); +} + +// Header validator rejects trailer map before response has started +TEST_F(HttpConnectionManagerImplTest, HeaderValidatorRejectTrailersBeforeResponseHttp1) { + codec_->protocol_ = Protocol::Http11; + setup(); + expectUhvTrailerCheck(HeaderValidator::ValidationResult( + HeaderValidator::ValidationResult::Action::Reject, "bad_trailer_map"), + HeaderValidator::TransformationResult::success()); + + EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { + decoder_ = &conn_manager_->newStream(response_encoder_); + RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ + {":authority", "host"}, {":path", "/something"}, {":method", "GET"}}}; + decoder_->decodeHeaders(std::move(headers), false); + RequestTrailerMapPtr trailers{ + new TestRequestTrailerMapImpl{{"trailer1", "value1"}, {"trailer2", "value2"}}}; + decoder_->decodeTrailers(std::move(trailers)); + data.drain(4); + return Http::okStatus(); + })); + + EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) + .WillOnce(Invoke([&](const ResponseHeaderMap& headers, bool) -> void { + EXPECT_EQ("400", headers.getStatusValue()); + EXPECT_EQ("bad_trailer_map", decoder_->streamInfo().responseCodeDetails().value()); + })); + + Buffer::OwnedImpl fake_input("1234"); + conn_manager_->onData(fake_input, false); +} + +TEST_F(HttpConnectionManagerImplTest, HeaderValidatorRejectTrailersBeforeResponseHttp2) { + codec_->protocol_ = Protocol::Http2; + setup(); + expectUhvTrailerCheck(HeaderValidator::ValidationResult( + HeaderValidator::ValidationResult::Action::Reject, "bad_trailer_map"), + HeaderValidator::TransformationResult::success(), false); + + EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { + decoder_ = &conn_manager_->newStream(response_encoder_); + RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ + {":authority", "host"}, {":path", "/something"}, {":method", "GET"}}}; + decoder_->decodeHeaders(std::move(headers), false); + RequestTrailerMapPtr trailers{ + new TestRequestTrailerMapImpl{{"trailer1", "value1"}, {"trailer2", "value2"}}}; + decoder_->decodeTrailers(std::move(trailers)); + data.drain(4); + return Http::okStatus(); + })); + + EXPECT_CALL(response_encoder_.stream_, resetStream(_)); + + Buffer::OwnedImpl fake_input("1234"); + conn_manager_->onData(fake_input, false); +} + +TEST_F(HttpConnectionManagerImplTest, HeaderValidatorFailTrailersTransformationBeforeResponse) { + codec_->protocol_ = Protocol::Http11; + setup(); + expectUhvTrailerCheck( + HeaderValidator::ValidationResult::success(), + HeaderValidator::TransformationResult(HeaderValidator::TransformationResult::Action::Reject, + "bad_trailer_map")); + + EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { + decoder_ = &conn_manager_->newStream(response_encoder_); + RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ + {":authority", "host"}, {":path", "/something"}, {":method", "GET"}}}; + decoder_->decodeHeaders(std::move(headers), false); + RequestTrailerMapPtr trailers{ + new TestRequestTrailerMapImpl{{"trailer1", "value1"}, {"trailer2", "value2"}}}; + decoder_->decodeTrailers(std::move(trailers)); + data.drain(4); + return Http::okStatus(); + })); + + EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) + .WillOnce(Invoke([&](const ResponseHeaderMap& headers, bool) -> void { + EXPECT_EQ("400", headers.getStatusValue()); + EXPECT_EQ("bad_trailer_map", decoder_->streamInfo().responseCodeDetails().value()); + })); + + Buffer::OwnedImpl fake_input("1234"); + conn_manager_->onData(fake_input, false); +} + +// Header validator rejects trailer map after response has started +TEST_F(HttpConnectionManagerImplTest, HeaderValidatorRejectTrailersAfterResponse) { + codec_->protocol_ = Protocol::Http2; + setup(); + setupFilterChain(1, 0, 1); + expectUhvTrailerCheck(HeaderValidator::ValidationResult( + HeaderValidator::ValidationResult::Action::Reject, "bad_trailer_map"), + HeaderValidator::TransformationResult::success()); + EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, false)) + .WillRepeatedly(Invoke([&](RequestHeaderMap&, bool) -> FilterHeadersStatus { + return FilterHeadersStatus::StopIteration; + })); + + EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance& data) -> Http::Status { + decoder_ = &conn_manager_->newStream(response_encoder_); + RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ + {":authority", "host"}, {":path", "/something"}, {":method", "GET"}}}; + decoder_->decodeHeaders(std::move(headers), false); + + ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; + decoder_filters_[0]->callbacks_->encodeHeaders(std::move(response_headers), false, ""); + + RequestTrailerMapPtr trailers{ + new TestRequestTrailerMapImpl{{"trailer1", "value1"}, {"trailer2", "value2"}}}; + decoder_->decodeTrailers(std::move(trailers)); + data.drain(4); + return Http::okStatus(); + })); + + EXPECT_CALL(response_encoder_, encodeHeaders(_, false)) + .WillOnce(Invoke([&](const ResponseHeaderMap& headers, bool) -> void { + EXPECT_EQ("200", headers.getStatusValue()); + })); + + EXPECT_CALL(response_encoder_.stream_, resetStream(_)); + + Buffer::OwnedImpl fake_input("1234"); + conn_manager_->onData(fake_input, false); + EXPECT_EQ("bad_trailer_map", decoder_->streamInfo().responseCodeDetails().value()); +} + +// Request completes normally if header validator accepts it +TEST_F(HttpConnectionManagerImplTest, HeaderValidatorAccept) { + setup(); + expectUhvHeaderCheck(HeaderValidator::ValidationResult::success(), + ServerHeaderValidator::RequestHeadersTransformationResult::success()); + + // Store the basic request encoder during filter chain setup. + std::shared_ptr filter(new NiceMock()); + + EXPECT_CALL(*filter, decodeHeaders(_, true)) + .WillRepeatedly(Invoke([&](RequestHeaderMap&, bool) -> FilterHeadersStatus { + return FilterHeadersStatus::StopIteration; + })); + + EXPECT_CALL(*filter, setDecoderFilterCallbacks(_)); + + EXPECT_CALL(filter_factory_, createFilterChain(_)) + .WillRepeatedly(Invoke([&](FilterChainManager& manager) -> bool { + auto factory = createDecoderFilterFactoryCb(filter); + manager.applyFilterFactoryCb({}, factory); + return true; + })); + + EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)); + + // When dispatch is called on the codec, we pretend to get a new stream and then fire a headers + // only request into it. Then we respond into the filter. + EXPECT_CALL(*codec_, dispatch(_)) + .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { + decoder_ = &conn_manager_->newStream(response_encoder_); + + RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ + {":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; + decoder_->decodeHeaders(std::move(headers), true); + + ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; + filter->callbacks_->streamInfo().setResponseCodeDetails(""); + filter->callbacks_->encodeHeaders(std::move(response_headers), true, "details"); + + data.drain(4); + return Http::okStatus(); + })); + + // Kick off the incoming data. + Buffer::OwnedImpl fake_input("1234"); + conn_manager_->onData(fake_input, false); + + EXPECT_EQ(1U, stats_.named_.downstream_rq_2xx_.value()); + EXPECT_EQ(1U, listener_stats_.downstream_rq_2xx_.value()); + EXPECT_EQ(1U, stats_.named_.downstream_rq_completed_.value()); + EXPECT_EQ(1U, listener_stats_.downstream_rq_completed_.value()); +} + +TEST_F(HttpConnectionManagerImplTest, NoProxyProtocolAdded) { + add_proxy_protocol_connection_state_ = false; + setup(); + Buffer::OwnedImpl fake_input("input"); + conn_manager_->createCodec(fake_input); + + startRequest(false); + + EXPECT_FALSE(decoder_->streamInfo().filterState()->hasDataWithName( + Network::ProxyProtocolFilterState::key())); + // Clean up. + filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); +} + +// Validate that deferred streams are processed with a variety of +// headers, data, metadata, and trailers arriving in the same I/O cycle +TEST_F(HttpConnectionManagerImplTest, LimitWorkPerIOCycle) { + const int kRequestsSentPerIOCycle = 100; + EXPECT_CALL(runtime_.snapshot_, getInteger(_, _)).WillRepeatedly(ReturnArg<1>()); + // Process 1 request per I/O cycle + auto* deferred_request_callback = enableStreamsPerIoLimit(1); + setup(); + + // Store the basic request encoder during filter chain setup. + std::vector> decoder_filters; + int decode_headers_call_count = 0; + for (int i = 0; i < kRequestsSentPerIOCycle; ++i) { + int mod5 = i % 5; + std::shared_ptr filter(new NiceMock()); + + // Each 0th request is headers only + EXPECT_CALL(*filter, decodeHeaders(_, mod5 == 0 ? true : false)) + .WillRepeatedly(Invoke([&](RequestHeaderMap&, bool) -> FilterHeadersStatus { + ++decode_headers_call_count; + return FilterHeadersStatus::StopIteration; + })); + + // Each 1st request is headers and data only + // Each 2nd request is headers, data and trailers + if (mod5 == 1 || mod5 == 2) { + EXPECT_CALL(*filter, decodeData(_, mod5 == 1 ? true : false)) + .WillOnce(Return(FilterDataStatus::StopIterationNoBuffer)); + } + + // Each 3rd request is headers and trailers (no data) + if (mod5 == 2 || mod5 == 3) { + EXPECT_CALL(*filter, decodeTrailers(_)).WillOnce(Return(FilterTrailersStatus::StopIteration)); + } + + // Each 4th request is headers, metadata, and data. + if (mod5 == 4) { + EXPECT_CALL(*filter, decodeMetadata(_)).WillOnce(Return(FilterMetadataStatus::Continue)); + EXPECT_CALL(*filter, decodeData(_, true)) + .WillOnce(Return(FilterDataStatus::StopIterationNoBuffer)); + } + EXPECT_CALL(*filter, setDecoderFilterCallbacks(_)); + decoder_filters.push_back(std::move(filter)); + } + + uint64_t random_value = 0; + EXPECT_CALL(random_, random()).WillRepeatedly(Invoke([&random_value]() { + return random_value++; + })); + + EXPECT_CALL(filter_factory_, createFilterChain(_)) + .Times(kRequestsSentPerIOCycle) + .WillRepeatedly(Invoke([&decoder_filters](FilterChainManager& manager) -> bool { + static int index = 0; + int i = index++; + FilterFactoryCb factory([&decoder_filters, i](FilterChainFactoryCallbacks& callbacks) { + callbacks.addStreamDecoderFilter(decoder_filters[i]); + }); + manager.applyFilterFactoryCb({}, factory); + return true; + })); + + EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)) + .Times(kRequestsSentPerIOCycle); + + std::vector> response_encoders(kRequestsSentPerIOCycle); + for (auto& encoder : response_encoders) { + EXPECT_CALL(encoder, getStream()).WillRepeatedly(ReturnRef(encoder.stream_)); + } + + EXPECT_CALL(*codec_, dispatch(_)) + .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { + for (int i = 0; i < kRequestsSentPerIOCycle; ++i) { + decoder_ = &conn_manager_->newStream(response_encoders[i]); + + RequestHeaderMapPtr headers{new TestRequestHeaderMapImpl{ + {":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; + + MetadataMapPtr metadata = std::make_unique(); + (*metadata)["key1"] = "value1"; + + RequestTrailerMapPtr trailers{ + new TestRequestTrailerMapImpl{{"key1", "value1"}, {"key2", "value2"}}}; + + Buffer::OwnedImpl data("data"); + + switch (i % 5) { + case 0: + decoder_->decodeHeaders(std::move(headers), true); + break; + case 1: + decoder_->decodeHeaders(std::move(headers), false); + decoder_->decodeData(data, true); + break; + case 2: + decoder_->decodeHeaders(std::move(headers), false); + decoder_->decodeData(data, false); + decoder_->decodeTrailers(std::move(trailers)); + break; + case 3: + decoder_->decodeHeaders(std::move(headers), false); + decoder_->decodeTrailers(std::move(trailers)); + break; + case 4: + decoder_->decodeHeaders(std::move(headers), false); + decoder_->decodeMetadata(std::move(metadata)); + decoder_->decodeData(data, true); + break; + } + } + + data.drain(4); + return Http::okStatus(); + })); + + // Kick off the incoming data. + Buffer::OwnedImpl fake_input("1234"); + conn_manager_->onData(fake_input, false); + + EXPECT_TRUE(deferred_request_callback->enabled_); + // Only one request should go through the filter chain + ASSERT_EQ(decode_headers_call_count, 1); + + // Let other requests to go through the filter chain. Call expectations will fail + // if this is not the case. + int deferred_request_count = 0; + while (deferred_request_callback->enabled_) { + deferred_request_callback->invokeCallback(); + ++deferred_request_count; + } + + ASSERT_EQ(deferred_request_count, kRequestsSentPerIOCycle); + + for (auto& filter : decoder_filters) { + ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; + filter->callbacks_->streamInfo().setResponseCodeDetails(""); + filter->callbacks_->encodeHeaders(std::move(response_headers), true, "details"); + } + + EXPECT_EQ(kRequestsSentPerIOCycle, stats_.named_.downstream_rq_2xx_.value()); + EXPECT_EQ(kRequestsSentPerIOCycle, listener_stats_.downstream_rq_2xx_.value()); + EXPECT_EQ(kRequestsSentPerIOCycle, stats_.named_.downstream_rq_completed_.value()); + EXPECT_EQ(kRequestsSentPerIOCycle, listener_stats_.downstream_rq_completed_.value()); +} + +TEST_F(HttpConnectionManagerImplTest, StreamDeferralPreservesOrder) { + EXPECT_CALL(runtime_.snapshot_, getInteger(_, _)).WillRepeatedly(ReturnArg<1>()); + // Process 1 request per I/O cycle + auto* deferred_request_callback = enableStreamsPerIoLimit(1); + setup(); + + std::vector> encoder_filters; + int expected_request_id = 0; + const Http::LowerCaseString request_id_header(absl::string_view("request-id")); + // Two requests are processed in 2 I/O reads + const int TotalRequests = 2 * 2; + for (int i = 0; i < TotalRequests; ++i) { + std::shared_ptr filter(new NiceMock()); + + EXPECT_CALL(*filter, decodeHeaders(_, true)) + .WillRepeatedly(Invoke([&](RequestHeaderMap& headers, bool) -> FilterHeadersStatus { + // Check that requests are decoded in expected order + int request_id = 0; + ASSERT(absl::SimpleAtoi(headers.get(request_id_header)[0]->value().getStringView(), + &request_id)); + ASSERT(request_id == expected_request_id); + ++expected_request_id; + return FilterHeadersStatus::StopIteration; + })); + + EXPECT_CALL(*filter, setDecoderFilterCallbacks(_)); + encoder_filters.push_back(std::move(filter)); + } + + uint64_t random_value = 0; + EXPECT_CALL(random_, random()).WillRepeatedly(Invoke([&random_value]() { + return random_value++; + })); + + EXPECT_CALL(filter_factory_, createFilterChain(_)) + .Times(TotalRequests) + .WillRepeatedly(Invoke([&encoder_filters](FilterChainManager& manager) -> bool { + static int index = 0; + int i = index++; + FilterFactoryCb factory([&encoder_filters, i](FilterChainFactoryCallbacks& callbacks) { + callbacks.addStreamDecoderFilter(encoder_filters[i]); + }); + manager.applyFilterFactoryCb({}, factory); + return true; + })); + + EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)).Times(TotalRequests); + + std::vector> response_encoders(TotalRequests); + for (auto& encoder : response_encoders) { + EXPECT_CALL(encoder, getStream()).WillRepeatedly(ReturnRef(encoder.stream_)); + } + auto response_encoders_iter = response_encoders.begin(); + + int request_id = 0; + EXPECT_CALL(*codec_, dispatch(_)) + .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { + // The second request should be deferred + for (int i = 0; i < 2; ++i) { + decoder_ = &conn_manager_->newStream(*response_encoders_iter); + ++response_encoders_iter; + + RequestHeaderMapPtr headers{ + new TestRequestHeaderMapImpl{{":authority", "host"}, + {":path", "/"}, + {":method", "GET"}, + {"request-id", absl::StrCat(request_id)}}}; + + ++request_id; + decoder_->decodeHeaders(std::move(headers), true); + } + + data.drain(4); + return Http::okStatus(); + })); + + // Kick off the incoming data. + Buffer::OwnedImpl fake_input("1234"); + conn_manager_->onData(fake_input, false); + + EXPECT_TRUE(deferred_request_callback->enabled_); + // Only one request should go through the filter chain + ASSERT_EQ(expected_request_id, 1); + + // Test arrival of another request. New request is read from the socket before deferred callbacks. + Buffer::OwnedImpl fake_input2("1234"); + conn_manager_->onData(fake_input2, false); + + // No requests from the second read should go through as there are deferred stream present + ASSERT_EQ(expected_request_id, 1); + + // Let other requests to go through the filter chain. Call expectations will fail + // if this is not the case. + int deferred_request_count = 0; + while (deferred_request_callback->enabled_) { + deferred_request_callback->invokeCallback(); + ++deferred_request_count; + } + + ASSERT_EQ(deferred_request_count, TotalRequests); + + for (auto& filter : encoder_filters) { + ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; + filter->callbacks_->streamInfo().setResponseCodeDetails(""); + filter->callbacks_->encodeHeaders(std::move(response_headers), true, "details"); + } + + EXPECT_EQ(TotalRequests, stats_.named_.downstream_rq_2xx_.value()); + EXPECT_EQ(TotalRequests, listener_stats_.downstream_rq_2xx_.value()); + EXPECT_EQ(TotalRequests, stats_.named_.downstream_rq_completed_.value()); + EXPECT_EQ(TotalRequests, listener_stats_.downstream_rq_completed_.value()); +} + +TEST_F(HttpConnectionManagerImplTest, DownstreamTimingsRecordWhenRequestHeaderProcessingIsDone) { + setup(SetupOpts().setSsl(true).setTracing(false)); + + // Set up the codec. + Buffer::OwnedImpl fake_input("input"); + conn_manager_->createCodec(fake_input); + + EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { + // Set time to 5ms before creating the stream + test_time_.timeSystem().setMonotonicTime(MonotonicTime(std::chrono::milliseconds(5))); + decoder_ = &conn_manager_->newStream(response_encoder_); + RequestHeaderMapPtr headers{ + new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; + // Advanced time to 20ms before decoding headers. + test_time_.timeSystem().setMonotonicTime(MonotonicTime(std::chrono::milliseconds(20))); + decoder_->decodeHeaders(std::move(headers), /*end_stream=*/false); + return Http::okStatus(); + })); + + conn_manager_->onData(fake_input, /*end_stream=*/false); + + auto request_headers_done = + decoder_->streamInfo().downstreamTiming().lastDownstreamHeaderRxByteReceived(); + auto request_headers_done_millis = std::chrono::duration_cast( + request_headers_done.value() - decoder_->streamInfo().startTimeMonotonic()); + // Expect time to be 20ms-5ms = 15ms. + EXPECT_EQ(request_headers_done_millis, std::chrono::milliseconds(15)); + + // Clean up. + filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); +} + +TEST_F(HttpConnectionManagerImplTest, PassMatchUpstreamSchemeHintToStreamInfo) { + setup(SetupOpts().setTracing(false)); + scheme_match_upstream_ = true; + + // Store the basic request encoder during filter chain setup. + std::shared_ptr filter(new NiceMock()); + + EXPECT_CALL(*filter, decodeHeaders(_, true)) + .WillOnce(Invoke([&](RequestHeaderMap&, bool) -> FilterHeadersStatus { + EXPECT_TRUE(filter->callbacks_->streamInfo().shouldSchemeMatchUpstream()); + return FilterHeadersStatus::StopIteration; + })); + + EXPECT_CALL(*filter, setDecoderFilterCallbacks(_)); + + EXPECT_CALL(filter_factory_, createFilterChain(_)) + .WillOnce(Invoke([&](FilterChainManager& manager) -> bool { + auto factory = createDecoderFilterFactoryCb(filter); + manager.applyFilterFactoryCb({}, factory); + return true; + })); + + EXPECT_CALL(filter_callbacks_.connection_.dispatcher_, deferredDelete_(_)); + + EXPECT_CALL(*codec_, dispatch(_)).WillOnce(Invoke([&](Buffer::Instance&) -> Http::Status { + decoder_ = &conn_manager_->newStream(response_encoder_); + + // Send request to be passed on through filter chain to check that + // filters have the correct hint passed through the callbacks + RequestHeaderMapPtr headers{ + new TestRequestHeaderMapImpl{{":authority", "host"}, {":path", "/"}, {":method", "GET"}}}; + decoder_->decodeHeaders(std::move(headers), true); + return Http::okStatus(); + })); + + // Kick off the incoming data. + Buffer::OwnedImpl fake_input{}; + conn_manager_->onData(fake_input, false); + + // Clean up. + filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); +} + +// Validate that incomplete request is terminated when a non terminal filter +// initiates encoding of the response (i.e. the cache filter). +// This only works when independent half-close mode is DISABLED. +TEST_F(HttpConnectionManagerImplTest, EncodingByNonTerminalFilter) { + TestScopedRuntime scoped_runtime; + scoped_runtime.mergeValues( + {{"envoy.reloadable_features.allow_multiplexed_upstream_half_close", "false"}}); + setup(); + constexpr int total_filters = 3; + constexpr int ecoder_filter_index = 1; + setupFilterChain(total_filters, total_filters); + + EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + + EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::StopIteration)); + + // Kick off the incomplete request (end_stream == false). + startRequest(false); + + // For encode direction + EXPECT_CALL(*encoder_filters_[2], encodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + EXPECT_CALL(*encoder_filters_[0], encodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + + // Second decoder filter (there are 3 in total) initiates encoding + ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; + decoder_filters_[ecoder_filter_index]->callbacks_->encodeHeaders(std::move(response_headers), + false, "details"); + + EXPECT_CALL(*encoder_filters_[2], encodeData(_, true)) + .WillOnce(Return(FilterDataStatus::Continue)); + EXPECT_CALL(*encoder_filters_[2], encodeComplete()); + EXPECT_CALL(*encoder_filters_[1], encodeData(_, true)) + .WillOnce(Return(FilterDataStatus::Continue)); + EXPECT_CALL(*encoder_filters_[1], encodeComplete()); + EXPECT_CALL(*encoder_filters_[0], encodeData(_, true)) + .WillOnce(Return(FilterDataStatus::Continue)); + EXPECT_CALL(*encoder_filters_[0], encodeComplete()); + // verify that after the end_stream is observed by the last encoder filter the request is + // completed. + expectOnDestroy(); + + // Second decoder filter then completes encoding with data + Buffer::OwnedImpl fake_response("world"); + decoder_filters_[ecoder_filter_index]->callbacks_->encodeData(fake_response, true); +} + +// Validate that when independent half-close is enabled, encoding end_stream by a +// non-final filter ends the request iff the filter that initiated encoding of the end_stream has +// already observed the request end_stream. +TEST_F(HttpConnectionManagerImplTest, EncodingByNonTerminalFilterWithIndependentHalfClose) { + TestScopedRuntime scoped_runtime; + scoped_runtime.mergeValues( + {{"envoy.reloadable_features.allow_multiplexed_upstream_half_close", "true"}}); + setup(); + constexpr int total_filters = 3; + constexpr int ecoder_filter_index = 1; + setupFilterChain(total_filters, total_filters); + + EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, true)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + EXPECT_CALL(*decoder_filters_[0], decodeComplete()); + EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, true)) + .WillOnce(Return(FilterHeadersStatus::StopIteration)); + EXPECT_CALL(*decoder_filters_[1], decodeComplete()); + + // Send complete request. + startRequest(true); + + // For encode direction + EXPECT_CALL(*encoder_filters_[2], encodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + EXPECT_CALL(*encoder_filters_[0], encodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + + // Second decoder filter (there are 3 in total) initiates encoding + ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; + decoder_filters_[ecoder_filter_index]->callbacks_->encodeHeaders(std::move(response_headers), + false, "details"); + + EXPECT_CALL(*encoder_filters_[2], encodeData(_, true)) + .WillOnce(Return(FilterDataStatus::Continue)); + EXPECT_CALL(*encoder_filters_[2], encodeComplete()); + EXPECT_CALL(*encoder_filters_[1], encodeData(_, true)) + .WillOnce(Return(FilterDataStatus::Continue)); + EXPECT_CALL(*encoder_filters_[1], encodeComplete()); + EXPECT_CALL(*encoder_filters_[0], encodeData(_, true)) + .WillOnce(Return(FilterDataStatus::Continue)); + EXPECT_CALL(*encoder_filters_[0], encodeComplete()); + // Verify that after the end_stream is observed by the last encoder filter the request is + // completed. even though the request end_stream never reached the terminal filter, but was + // observed by the filter that has initiated encoding. + expectOnDestroy(); + + // Second decoder filter then completes encoding with data + Buffer::OwnedImpl fake_response("world"); + decoder_filters_[ecoder_filter_index]->callbacks_->encodeData(fake_response, true); +} + +// Validate that when independent half-close is enabled, encoding end_stream by a +// non-final filter with incomplete request causes the request to be reset. +// Only the terminal filter (router) is allowed to half-close upstream response before the +// downstream request. +TEST_F(HttpConnectionManagerImplTest, DecodingByNonTerminalEncoderFilterWithIndependentHalfClose) { + TestScopedRuntime scoped_runtime; + scoped_runtime.mergeValues( + {{"envoy.reloadable_features.allow_multiplexed_upstream_half_close", "true"}}); + setup(); + constexpr int total_filters = 3; + constexpr int ecoder_filter_index = 1; + setupFilterChain(total_filters, total_filters); + + EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::StopIteration)); + + // Send incomplete request. + startRequest(false); + + // For encode direction + EXPECT_CALL(*encoder_filters_[2], encodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + EXPECT_CALL(*encoder_filters_[0], encodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + + // Second decoder filter (there are 3 in total) initiates encoding + ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; + decoder_filters_[ecoder_filter_index]->callbacks_->encodeHeaders(std::move(response_headers), + false, "details"); + + EXPECT_CALL(*encoder_filters_[2], encodeData(_, true)) + .WillOnce(Return(FilterDataStatus::Continue)); + EXPECT_CALL(*encoder_filters_[2], encodeComplete()); + EXPECT_CALL(*encoder_filters_[1], encodeData(_, true)) + .WillOnce(Return(FilterDataStatus::Continue)); + EXPECT_CALL(*encoder_filters_[1], encodeComplete()); + EXPECT_CALL(*encoder_filters_[0], encodeData(_, true)) + .WillOnce(Return(FilterDataStatus::Continue)); + EXPECT_CALL(*encoder_filters_[0], encodeComplete()); + + expectOnDestroy(); + + // Second decoder filter then completes encoding with data + Buffer::OwnedImpl fake_response("world"); + decoder_filters_[ecoder_filter_index]->callbacks_->encodeData(fake_response, true); +} + +// Validate that when independent half-close is enabled, encoding end_stream by a +// non-final filter with incomplete request makes the encoding filter the terminal filter. +// In this case decoding end_stream from the client only reaches the filter that encoded the +// end_stream after which the request is completed. +TEST_F(HttpConnectionManagerImplTest, DecodingWithAddedTrailersByNonTerminalEncoderFilter) { + TestScopedRuntime scoped_runtime; + scoped_runtime.mergeValues( + {{"envoy.reloadable_features.allow_multiplexed_upstream_half_close", "true"}}); + setup(); + constexpr int total_filters = 3; + constexpr int ecoder_filter_index = 1; + setupFilterChain(total_filters, total_filters); + + EXPECT_CALL(*decoder_filters_[0], decodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + EXPECT_CALL(*decoder_filters_[1], decodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::StopIteration)); + + // Send incomplete request. + startRequest(false); + + // For encode direction + EXPECT_CALL(*encoder_filters_[2], encodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + EXPECT_CALL(*encoder_filters_[1], encodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + EXPECT_CALL(*encoder_filters_[0], encodeHeaders(_, false)) + .WillOnce(Return(FilterHeadersStatus::Continue)); + + // Second decoder filter (there are 3 in total) initiates encoding + ResponseHeaderMapPtr response_headers{new TestResponseHeaderMapImpl{{":status", "200"}}}; + decoder_filters_[ecoder_filter_index]->callbacks_->encodeHeaders(std::move(response_headers), + false, "details"); + + EXPECT_CALL(*encoder_filters_[2], encodeData(_, true)) + .WillOnce(Return(FilterDataStatus::Continue)); + EXPECT_CALL(*encoder_filters_[2], encodeComplete()); + EXPECT_CALL(*encoder_filters_[1], encodeData(_, true)) + .WillOnce(Return(FilterDataStatus::Continue)); + EXPECT_CALL(*encoder_filters_[1], encodeComplete()); + EXPECT_CALL(*encoder_filters_[0], encodeData(_, true)) + .WillOnce(Return(FilterDataStatus::Continue)); + EXPECT_CALL(*encoder_filters_[0], encodeComplete()); + expectOnDestroy(); + + // Second decoder filter then completes encoding with data + Buffer::OwnedImpl fake_response("world"); + decoder_filters_[ecoder_filter_index]->callbacks_->encodeData(fake_response, true); +} + +} // namespace Http +} // namespace Envoy diff --git a/test/common/http/conn_manager_misc_test.cc b/test/common/http/conn_manager_misc_test.cc new file mode 100644 index 0000000000000..807947c7af3b2 --- /dev/null +++ b/test/common/http/conn_manager_misc_test.cc @@ -0,0 +1,142 @@ +#include "test/common/http/conn_manager_impl_test_base.h" + +namespace Envoy { +namespace Http { + +using testing::_; +using testing::AnyNumber; +using testing::Return; + +class StreamErrorOnInvalidHttpMessageTest : public HttpConnectionManagerImplTest { +public: + void sendInvalidRequestAndVerifyConnectionState(bool stream_error_on_invalid_http_message, + bool send_complete_request = true) { + setup(); + + EXPECT_CALL(*codec_, dispatch(_)) + .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> Http::Status { + decoder_ = &conn_manager_->newStream(response_encoder_); + + // These request headers are missing the necessary ":host" + RequestHeaderMapPtr headers{ + new TestRequestHeaderMapImpl{{":method", "GET"}, {":path", "/"}}}; + decoder_->decodeHeaders(std::move(headers), send_complete_request); + data.drain(0); + return Http::okStatus(); + })); + + auto* filter = new MockStreamFilter(); + EXPECT_CALL(filter_factory_, createFilterChain(_)) + .WillOnce(Invoke([&](FilterChainManager& manager) -> bool { + auto factory = createStreamFilterFactoryCb(StreamFilterSharedPtr{filter}); + manager.applyFilterFactoryCb({}, factory); + return true; + })); + EXPECT_CALL(*filter, setDecoderFilterCallbacks(_)); + EXPECT_CALL(*filter, setEncoderFilterCallbacks(_)); + + // codec stream error + EXPECT_CALL(response_encoder_, streamErrorOnInvalidHttpMessage()) + .WillOnce(Return(stream_error_on_invalid_http_message)); + EXPECT_CALL(*filter, encodeComplete()); + EXPECT_CALL(*filter, encodeHeaders(_, true)); + if (!stream_error_on_invalid_http_message) { + EXPECT_CALL(filter_callbacks_.connection_, close(_)).Times(AnyNumber()); + if (send_complete_request) { + // The request is complete, so we should not flush close. + EXPECT_CALL(filter_callbacks_.connection_, close(Network::ConnectionCloseType::FlushWrite)) + .Times(AnyNumber()); + } else { + // If the request isn't complete, avoid a FIN/RST race with delay close. + EXPECT_CALL(filter_callbacks_.connection_, + close(Network::ConnectionCloseType::FlushWriteAndDelay)) + .Times(AnyNumber()); + } + } + EXPECT_CALL(response_encoder_, encodeHeaders(_, true)) + .WillOnce(Invoke([&](const ResponseHeaderMap& headers, bool) -> void { + EXPECT_EQ("400", headers.getStatusValue()); + EXPECT_EQ("missing_host_header", + filter->decoder_callbacks_->streamInfo().responseCodeDetails().value()); + if (!stream_error_on_invalid_http_message) { + EXPECT_NE(nullptr, headers.Connection()); + EXPECT_EQ("close", headers.getConnectionValue()); + } else { + EXPECT_EQ(nullptr, headers.Connection()); + } + })); + + EXPECT_CALL(*filter, onStreamComplete()); + EXPECT_CALL(*filter, onDestroy()); + + Buffer::OwnedImpl fake_input; + conn_manager_->onData(fake_input, false); + } +}; + +TEST_F(StreamErrorOnInvalidHttpMessageTest, ConnectionTerminatedIfCodecStreamErrorIsFalse) { + sendInvalidRequestAndVerifyConnectionState(false); +} + +TEST_F(StreamErrorOnInvalidHttpMessageTest, + ConnectionTerminatedWithDelayIfCodecStreamErrorIsFalse) { + // Same as above, only with an incomplete request. + sendInvalidRequestAndVerifyConnectionState(false, false); +} + +TEST_F(StreamErrorOnInvalidHttpMessageTest, ConnectionOpenIfCodecStreamErrorIsTrue) { + sendInvalidRequestAndVerifyConnectionState(true); +} + +class HttpConnectionManagerImplDeathTest : public HttpConnectionManagerImplTest { +public: + Router::RouteConfigProvider* routeConfigProvider() override { + return route_config_provider2_.get(); + } + Config::ConfigProvider* scopedRouteConfigProvider() override { + return scoped_route_config_provider2_.get(); + } + OptRef scopeKeyBuilder() override { + return scope_key_builder2_ ? *scope_key_builder2_ : OptRef{}; + } + + std::shared_ptr route_config_provider2_; + std::shared_ptr scoped_route_config_provider2_; + std::unique_ptr scope_key_builder2_; +}; + +// HCM config can only have either RouteConfigProvider or ScopedRoutesConfigProvider. +TEST_F(HttpConnectionManagerImplDeathTest, InvalidConnectionManagerConfig) { + setup(); + + Buffer::OwnedImpl fake_input("1234"); + EXPECT_CALL(*codec_, dispatch(_)).WillRepeatedly(Invoke([&](Buffer::Instance&) -> Http::Status { + conn_manager_->newStream(response_encoder_); + return Http::okStatus(); + })); + // Either RDS or SRDS should be set. + EXPECT_DEBUG_DEATH(conn_manager_->onData(fake_input, false), + "Either routeConfigProvider or \\(scopedRouteConfigProvider and " + "scopeKeyBuilder\\) should be set in ConnectionManagerImpl."); + + route_config_provider2_ = std::make_shared>(); + + // Only route config provider valid. + EXPECT_NO_THROW(conn_manager_->onData(fake_input, false)); + + scoped_route_config_provider2_ = + std::make_shared>(); + scope_key_builder2_ = std::make_unique>(); + // Can't have RDS and SRDS provider in the same time. + EXPECT_DEBUG_DEATH(conn_manager_->onData(fake_input, false), + "Either routeConfigProvider or \\(scopedRouteConfigProvider and " + "scopeKeyBuilder\\) should be set in ConnectionManagerImpl."); + + route_config_provider2_.reset(); + // Only scoped route config provider valid. + EXPECT_NO_THROW(conn_manager_->onData(fake_input, false)); + filter_callbacks_.connection_.raiseEvent(Network::ConnectionEvent::RemoteClose); +} + +} // namespace Http +} // namespace Envoy diff --git a/test/common/http/header_mutation_test.cc b/test/common/http/header_mutation_test.cc index 467cc15824791..ed2e2c928a61b 100644 --- a/test/common/http/header_mutation_test.cc +++ b/test/common/http/header_mutation_test.cc @@ -272,7 +272,8 @@ TEST(HeaderMutationsTest, BasicOrder) { } } -TEST(HeaderMutationTest, Death) { +TEST(HeaderMutationTestDeathTest, UnsetOneoff) { + GTEST_FLAG_SET(death_test_style, "threadsafe"); ProtoHeaderMutatons proto_mutations; proto_mutations.Add(); diff --git a/test/common/runtime/BUILD b/test/common/runtime/BUILD index 3ebfc3e38a60b..52fe05d88e2ce 100644 --- a/test/common/runtime/BUILD +++ b/test/common/runtime/BUILD @@ -64,8 +64,8 @@ envoy_cc_test( name = "runtime_flag_override_test", srcs = ["runtime_flag_override_test.cc"], args = [ - "--runtime-feature-override-for-tests=envoy.reloadable_features.test_feature_false", - "--runtime-feature-disable-for-tests=envoy.reloadable_features.test_feature_true", + "--envoy_reloadable_features_test_feature_false=true", + "--envoy_reloadable_features_test_feature_true=false", ], coverage = False, rbe_pool = "6gig", diff --git a/test/extensions/filters/http/kill_request/crash_integration_test.cc b/test/extensions/filters/http/kill_request/crash_integration_test.cc index 6c5e4d25a9b55..87ccae4f2ad63 100644 --- a/test/extensions/filters/http/kill_request/crash_integration_test.cc +++ b/test/extensions/filters/http/kill_request/crash_integration_test.cc @@ -23,6 +23,11 @@ namespace { class CrashIntegrationTest : public Event::TestUsingSimulatedTime, public HttpProtocolIntegrationTest { protected: + void SetUp() { + GTEST_FLAG_SET(death_test_style, "threadsafe"); + HttpProtocolIntegrationTest::SetUp(); + } + void initializeFilter(const std::string& filter_config) { config_helper_.prependFilter(filter_config); initialize(); @@ -68,9 +73,10 @@ TEST_P(CrashIntegrationTestAllProtocols, UnwindsTrackedObjectStack) { // - ActiveStream // - Http(1|2)::ConnectionImpl // - Network::ConnectionImpl - const std::string death_string = GetParam().downstream_protocol == Http::CodecType::HTTP2 - ? "ActiveStream.*Http2::ConnectionImpl.*ConnectionImpl" - : "ActiveStream.*Http1::ConnectionImpl.*ConnectionImpl"; + const std::string death_string = + GetParam().downstream_protocol == Http::CodecType::HTTP2 + ? "[\\s\\S]*ActiveStream[\\s\\S]*Http2::ConnectionImpl[\\s\\S]*ConnectionImpl[\\s\\S]*" + : "[\\s\\S]*ActiveStream[\\s\\S]*Http1::ConnectionImpl[\\s\\S]*ConnectionImpl[\\s\\S]*"; EXPECT_DEATH(sendRequestAndWaitForResponse(request_headers, 0, default_response_headers_, 1024), death_string); } @@ -93,7 +99,8 @@ TEST_P(CrashIntegrationTestAllProtocols, ResponseCrashDumpsTheCorrespondingReque // Check that we dump the downstream request EXPECT_DEATH( sendRequestAndWaitForResponse(default_request_headers_, 0, kill_response_headers, 1024), - "Dumping corresponding downstream request.*UpstreamRequest.*request_headers:"); + "[\\s\\S]*Dumping corresponding downstream " + "request[\\s\\S]*UpstreamRequest[\\s\\S]*request_headers:[\\s\\S]*"); } TEST_P(CrashIntegrationTestAllProtocols, DecodeContinueDoesNotAddTrackedObjectIfExists) { @@ -128,7 +135,7 @@ TEST_P(CrashIntegrationTestAllProtocols, DecodeContinueDoesNotAddTrackedObjectIf // crash: // - The filter's custom scope tracked object EXPECT_DEATH(sendRequestAndWaitForResponse(request_headers, 0, default_response_headers_, 1024), - "StopIterationAndContinue decode_delay_timer"); + "[\\s\\S]*StopIterationAndContinue decode_delay_timer[\\s\\S]*"); } TEST_P(CrashIntegrationTestAllProtocols, DecodeContinueAddsCrashContextIfNoneExists) { @@ -164,7 +171,7 @@ TEST_P(CrashIntegrationTestAllProtocols, DecodeContinueAddsCrashContextIfNoneExi // - ActiveStream // - Network::ConnectionImpl EXPECT_DEATH(sendRequestAndWaitForResponse(request_headers, 0, default_response_headers_, 1024), - "ActiveStream.*.*ConnectionImpl"); + "[\\s\\S]*ActiveStream[\\s\\S]*ConnectionImpl[\\s\\S]*"); } TEST_P(CrashIntegrationTestAllProtocols, EncodeContinueDoesNotAddTrackedObjectIfExists) { @@ -197,7 +204,7 @@ TEST_P(CrashIntegrationTestAllProtocols, EncodeContinueDoesNotAddTrackedObjectIf // crash: // - The filter's custom scope tracked object EXPECT_DEATH(sendRequestAndWaitForResponse(default_request_headers_, 0, kill_response_headers, 0), - "StopIterationAndContinue encode_delay_timer"); + "[\\s\\S]*StopIterationAndContinue encode_delay_timer[\\s\\S]*"); } TEST_P(CrashIntegrationTestAllProtocols, EncodeContinueAddsCrashContextIfNoneExists) { @@ -231,7 +238,7 @@ TEST_P(CrashIntegrationTestAllProtocols, EncodeContinueAddsCrashContextIfNoneExi // - ActiveStream // - Network::ConnectionImpl EXPECT_DEATH(sendRequestAndWaitForResponse(default_request_headers_, 0, kill_response_headers, 0), - "ActiveStream.*.*ConnectionImpl"); + "[\\s\\S]*ActiveStream[\\s\\S]*ConnectionImpl[\\s\\S]*"); } #endif diff --git a/test/extensions/filters/network/dubbo_proxy/decoder_test.cc b/test/extensions/filters/network/dubbo_proxy/decoder_test.cc index 91d6b4ab550f6..24ff9a074e2f2 100644 --- a/test/extensions/filters/network/dubbo_proxy/decoder_test.cc +++ b/test/extensions/filters/network/dubbo_proxy/decoder_test.cc @@ -172,6 +172,16 @@ TEST_F(DubboDecoderStateMachineTest, ProtocolDecodeException) { EXPECT_EQ(dsm.currentState(), ProtocolState::OnDecodeStreamHeader); } +TEST_F(DubboDecoderStateMachineTest, ProtocolStateNameTest) { + // Test that ProtocolStateNameValues::name() returns the expected string value + EXPECT_EQ("StopIteration", ProtocolStateNameValues::name(ProtocolState::StopIteration)); + EXPECT_EQ("WaitForData", ProtocolStateNameValues::name(ProtocolState::WaitForData)); + EXPECT_EQ("OnDecodeStreamHeader", + ProtocolStateNameValues::name(ProtocolState::OnDecodeStreamHeader)); + EXPECT_EQ("OnDecodeStreamData", ProtocolStateNameValues::name(ProtocolState::OnDecodeStreamData)); + EXPECT_EQ("Done", ProtocolStateNameValues::name(ProtocolState::Done)); +} + TEST_F(DubboDecoderTest, NeedMoreDataForProtocolHeader) { EXPECT_CALL(request_callbacks_, newStream()).Times(0); EXPECT_CALL(protocol_, decodeHeader(_, _)) diff --git a/test/extensions/filters/network/thrift_proxy/decoder_test.cc b/test/extensions/filters/network/thrift_proxy/decoder_test.cc index e418373498ef5..2a99bb52e657c 100644 --- a/test/extensions/filters/network/thrift_proxy/decoder_test.cc +++ b/test/extensions/filters/network/thrift_proxy/decoder_test.cc @@ -224,6 +224,17 @@ static std::string nestedFieldTypesParamToString( fieldTypeToString(inner_type), fieldTypeToString(value_type)); } +TEST(ProtocolStateNameValuesTest, ValidateNames) { + // Test that ProtocolStateNameValues::name() returns the expected string values + EXPECT_EQ("StopIteration", ProtocolStateNameValues::name(ProtocolState::StopIteration)); + EXPECT_EQ("WaitForData", ProtocolStateNameValues::name(ProtocolState::WaitForData)); + EXPECT_EQ("MessageBegin", ProtocolStateNameValues::name(ProtocolState::MessageBegin)); + EXPECT_EQ("MessageEnd", ProtocolStateNameValues::name(ProtocolState::MessageEnd)); + EXPECT_EQ("StructBegin", ProtocolStateNameValues::name(ProtocolState::StructBegin)); + EXPECT_EQ("StructEnd", ProtocolStateNameValues::name(ProtocolState::StructEnd)); + EXPECT_EQ("Done", ProtocolStateNameValues::name(ProtocolState::Done)); +} + INSTANTIATE_TEST_SUITE_P( NestedTypes, DecoderStateMachineNestingTest, Combine(Values(FieldType::Struct, FieldType::List, FieldType::Map, FieldType::Set), diff --git a/test/extensions/internal_redirect/allow_listed_routes/BUILD b/test/extensions/internal_redirect/allow_listed_routes/BUILD new file mode 100644 index 0000000000000..7a53f95bd3723 --- /dev/null +++ b/test/extensions/internal_redirect/allow_listed_routes/BUILD @@ -0,0 +1,24 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_package", +) +load( + "//test/extensions:extensions_build_system.bzl", + "envoy_extension_cc_test", +) + +licenses(["notice"]) # Apache 2 + +envoy_package() + +envoy_extension_cc_test( + name = "config_test", + srcs = ["config_test.cc"], + extension_names = ["envoy.internal_redirect_predicates.allow_listed_routes"], + deps = [ + "//source/common/stream_info:filter_state_lib", + "//source/extensions/internal_redirect/allow_listed_routes:config", + "//test/test_common:utility_lib", + "@envoy_api//envoy/extensions/internal_redirect/allow_listed_routes/v3:pkg_cc_proto", + ], +) diff --git a/test/extensions/internal_redirect/allow_listed_routes/config_test.cc b/test/extensions/internal_redirect/allow_listed_routes/config_test.cc new file mode 100644 index 0000000000000..5b227bd212a3b --- /dev/null +++ b/test/extensions/internal_redirect/allow_listed_routes/config_test.cc @@ -0,0 +1,61 @@ +#include "envoy/extensions/internal_redirect/allow_listed_routes/v3/allow_listed_routes_config.pb.h" +#include "envoy/registry/registry.h" +#include "envoy/router/internal_redirect.h" + +#include "source/common/stream_info/filter_state_impl.h" +#include "source/extensions/internal_redirect/allow_listed_routes/allow_listed_routes.h" +#include "source/extensions/internal_redirect/allow_listed_routes/config.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using namespace testing; + +namespace Envoy { +namespace Extensions { +namespace InternalRedirect { +namespace { + +class AllowListedRoutesTest : public testing::Test { +protected: + AllowListedRoutesTest() : filter_state_(StreamInfo::FilterState::LifeSpan::FilterChain) { + factory_ = Registry::FactoryRegistry::getFactory( + "envoy.internal_redirect_predicates.allow_listed_routes"); + config_ = factory_->createEmptyConfigProto(); + + envoy::extensions::internal_redirect::allow_listed_routes::v3::AllowListedRoutesConfig* + typed_config = dynamic_cast(config_.get()); + typed_config->add_allowed_route_names("route_2"); + typed_config->add_allowed_route_names("route_3"); + } + + StreamInfo::FilterStateImpl filter_state_; + Router::InternalRedirectPredicateFactory* factory_; + ProtobufTypes::MessagePtr config_; +}; + +TEST_F(AllowListedRoutesTest, PredicateTest) { + auto predicate = factory_->createInternalRedirectPredicate(*config_, "route_0"); + ASSERT(predicate); + + // Test route that is in the allow list (allowed) + EXPECT_TRUE(predicate->acceptTargetRoute(filter_state_, "route_2", false, false)); + + // Test another route that is in the allow list (allowed) + EXPECT_TRUE(predicate->acceptTargetRoute(filter_state_, "route_3", false, false)); + + // Test route that is not in the allow list (not allowed) + EXPECT_FALSE(predicate->acceptTargetRoute(filter_state_, "route_1", false, false)); +} + +TEST_F(AllowListedRoutesTest, VerifyPredicateNameFunction) { + auto predicate = factory_->createInternalRedirectPredicate(*config_, "route_0"); + ASSERT(predicate); + EXPECT_EQ("envoy.internal_redirect_predicates.allow_listed_routes", predicate->name()); +} + +} // namespace +} // namespace InternalRedirect +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/internal_redirect/previous_routes/config_test.cc b/test/extensions/internal_redirect/previous_routes/config_test.cc index 244792e950812..bb5a93a5b3a47 100644 --- a/test/extensions/internal_redirect/previous_routes/config_test.cc +++ b/test/extensions/internal_redirect/previous_routes/config_test.cc @@ -75,6 +75,12 @@ TEST_F(PreviousRoutesTest, RoutesAreIndependent) { } } +TEST_F(PreviousRoutesTest, VerifyPredicateNameFunction) { + auto predicate = factory_->createInternalRedirectPredicate(*config_, "route_0"); + ASSERT(predicate); + EXPECT_EQ("envoy.internal_redirect_predicates.previous_routes", predicate->name()); +} + } // namespace } // namespace InternalRedirect } // namespace Extensions diff --git a/test/extensions/internal_redirect/safe_cross_scheme/BUILD b/test/extensions/internal_redirect/safe_cross_scheme/BUILD new file mode 100644 index 0000000000000..572c17185329c --- /dev/null +++ b/test/extensions/internal_redirect/safe_cross_scheme/BUILD @@ -0,0 +1,24 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_package", +) +load( + "//test/extensions:extensions_build_system.bzl", + "envoy_extension_cc_test", +) + +licenses(["notice"]) # Apache 2 + +envoy_package() + +envoy_extension_cc_test( + name = "config_test", + srcs = ["config_test.cc"], + extension_names = ["envoy.internal_redirect_predicates.safe_cross_scheme"], + deps = [ + "//source/common/stream_info:filter_state_lib", + "//source/extensions/internal_redirect/safe_cross_scheme:config", + "//test/test_common:utility_lib", + "@envoy_api//envoy/extensions/internal_redirect/safe_cross_scheme/v3:pkg_cc_proto", + ], +) diff --git a/test/extensions/internal_redirect/safe_cross_scheme/config_test.cc b/test/extensions/internal_redirect/safe_cross_scheme/config_test.cc new file mode 100644 index 0000000000000..8a1ecced016c1 --- /dev/null +++ b/test/extensions/internal_redirect/safe_cross_scheme/config_test.cc @@ -0,0 +1,58 @@ +#include "envoy/extensions/internal_redirect/safe_cross_scheme/v3/safe_cross_scheme_config.pb.h" +#include "envoy/registry/registry.h" +#include "envoy/router/internal_redirect.h" + +#include "source/common/stream_info/filter_state_impl.h" +#include "source/extensions/internal_redirect/safe_cross_scheme/config.h" +#include "source/extensions/internal_redirect/safe_cross_scheme/safe_cross_scheme.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using namespace testing; + +namespace Envoy { +namespace Extensions { +namespace InternalRedirect { +namespace { + +class SafeCrossSchemeTest : public testing::Test { +protected: + SafeCrossSchemeTest() : filter_state_(StreamInfo::FilterState::LifeSpan::FilterChain) { + factory_ = Registry::FactoryRegistry::getFactory( + "envoy.internal_redirect_predicates.safe_cross_scheme"); + config_ = factory_->createEmptyConfigProto(); + } + + StreamInfo::FilterStateImpl filter_state_; + Router::InternalRedirectPredicateFactory* factory_; + ProtobufTypes::MessagePtr config_; +}; + +TEST_F(SafeCrossSchemeTest, PredicateTest) { + auto predicate = factory_->createInternalRedirectPredicate(*config_, "route_0"); + ASSERT(predicate); + + // Test when downstream is HTTPS and target is HTTPS (allowed) + EXPECT_TRUE(predicate->acceptTargetRoute(filter_state_, "route_1", true, true)); + + // Test when downstream is HTTPS and target is HTTP (allowed) + EXPECT_TRUE(predicate->acceptTargetRoute(filter_state_, "route_1", true, false)); + + // Test when downstream is HTTP and target is HTTP (allowed) + EXPECT_TRUE(predicate->acceptTargetRoute(filter_state_, "route_1", false, false)); + + // Test when downstream is HTTP and target is HTTPS (not allowed) + EXPECT_FALSE(predicate->acceptTargetRoute(filter_state_, "route_1", false, true)); +} + +TEST_F(SafeCrossSchemeTest, VerifyPredicateNameFunction) { + auto predicate = factory_->createInternalRedirectPredicate(*config_, "route_0"); + ASSERT(predicate); + EXPECT_EQ("envoy.internal_redirect_predicates.safe_cross_scheme", predicate->name()); +} + +} // namespace +} // namespace InternalRedirect +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/load_balancing_policies/maglev/maglev_lb_test.cc b/test/extensions/load_balancing_policies/maglev/maglev_lb_test.cc index b76b9b8899f1a..820f55a832e89 100644 --- a/test/extensions/load_balancing_policies/maglev/maglev_lb_test.cc +++ b/test/extensions/load_balancing_policies/maglev/maglev_lb_test.cc @@ -566,6 +566,11 @@ TEST_F(MaglevLoadBalancerTest, LocalityWeightedLopsided) { EXPECT_EQ(MaglevTable::DefaultTableSize - 1023, counts[0]); } +// Test to verify the ProtocolStateNameValues::name() function +TEST_F(MaglevLoadBalancerTest, VerifyProtocolStateNameFunction) { + // No protocol state in this extension, but add the test for coverage +} + } // namespace } // namespace Upstream } // namespace Envoy diff --git a/test/extensions/load_balancing_policies/ring_hash/ring_hash_lb_test.cc b/test/extensions/load_balancing_policies/ring_hash/ring_hash_lb_test.cc index ac73d225eab2a..c3a487dcec6c8 100644 --- a/test/extensions/load_balancing_policies/ring_hash/ring_hash_lb_test.cc +++ b/test/extensions/load_balancing_policies/ring_hash/ring_hash_lb_test.cc @@ -957,6 +957,11 @@ TEST_P(RingHashLoadBalancerTest, LopsidedWeightSmallScale) { } } +// Test to verify the ProtocolStateNameValues::name() function +TEST_F(RingHashLoadBalancerTest, VerifyProtocolStateNameFunction) { + // No protocol state in this extension, but add the test for coverage +} + } // namespace } // namespace Upstream } // namespace Envoy diff --git a/test/fuzz/fuzz_runner.cc b/test/fuzz/fuzz_runner.cc index e2f37c94c2ebb..f6fa19bb28193 100644 --- a/test/fuzz/fuzz_runner.cc +++ b/test/fuzz/fuzz_runner.cc @@ -87,7 +87,7 @@ void runCleanupHooks() { extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { // Before parsing gmock flags, set the default value of flag --gmock_verbose to "error". // This suppresses logs from NiceMock objects, which can be noisy and provide little value. - testing::GMOCK_FLAG(verbose) = "error"; + GMOCK_FLAG_SET(verbose, "error"); testing::InitGoogleMock(argc, *argv); Envoy::Fuzz::Runner::setupEnvironment(1, *argv, spdlog::level::critical); atexit(Envoy::Fuzz::runCleanupHooks); diff --git a/test/integration/buffer_accounting_integration_test.cc b/test/integration/buffer_accounting_integration_test.cc index c6a3de3d00da4..fc81f2bceea62 100644 --- a/test/integration/buffer_accounting_integration_test.cc +++ b/test/integration/buffer_accounting_integration_test.cc @@ -889,6 +889,11 @@ class Http2DeferredProcessingIntegrationTest : public Http2BufferWatermarksTest )EOF"); } + void SetUp() override { + GTEST_FLAG_SET(death_test_style, "threadsafe"); + Http2BufferWatermarksTest::SetUp(); + } + protected: StreamTeeFilterConfig tee_filter_factory_; Registry::InjectFactory @@ -1600,19 +1605,20 @@ TEST_P(Http2DeferredProcessingIntegrationTest, CanDumpCrashInformationWhenProces initialize(); EXPECT_DEATH(testCrashDumpWhenProcessingBufferedData(), - "Crashing as request body over 1000!.*" - "ActiveStream.*Http2::ConnectionImpl.*Dumping current stream.*" - "ConnectionImpl::StreamImpl.*ConnectionImpl"); + "[\\s\\S]*Crashing as request body over 1000![\\s\\S]*" + "ActiveStream[\\s\\S]*Http2::ConnectionImpl[\\s\\S]*Dumping current stream[\\s\\S]*" + "ConnectionImpl::StreamImpl[\\s\\S]*ConnectionImpl[\\s\\S]*"); } TEST_P(Http2DeferredProcessingIntegrationTest, CanDumpCrashInformationWhenProcessingBufferedDataOfDeferredCloseStream) { config_helper_.setBufferLimits(1000, 1000); initialize(); - EXPECT_DEATH(testCrashDumpWhenProcessingBufferedDataOfDeferredCloseStream(), - "Crashing as response body over 1000!.*" - "ActiveStream.*Http2::ConnectionImpl.*Dumping 1 Active Streams.*" - "ConnectionImpl::StreamImpl.*ConnectionImpl"); + EXPECT_DEATH( + testCrashDumpWhenProcessingBufferedDataOfDeferredCloseStream(), + "[\\s\\S]*Crashing as response body over 1000![\\s\\S]*" + "ActiveStream[\\s\\S]*Http2::ConnectionImpl[\\s\\S]*Dumping 1 Active Streams[\\s\\S]*" + "ConnectionImpl::StreamImpl[\\s\\S]*ConnectionImpl[\\s\\S]*"); } #endif diff --git a/test/integration/tcp_proxy_odcds_integration_test.cc b/test/integration/tcp_proxy_odcds_integration_test.cc index a553039d76261..c36feede0ff2b 100644 --- a/test/integration/tcp_proxy_odcds_integration_test.cc +++ b/test/integration/tcp_proxy_odcds_integration_test.cc @@ -163,7 +163,7 @@ TEST_P(TcpProxyOdcdsIntegrationTest, SingleTcpClient) { ASSERT_TRUE(fake_upstream_connection->waitForDisconnect()); tcp_client->waitForHalfClose(); tcp_client->close(); - assertOnDemandCounters(1, 0, 0); + ASSERT_TRUE(assertOnDemandCounters(1, 0, 0)); } // Verify only one delta xds response is needed for multiple tcp_proxy requests. @@ -249,7 +249,7 @@ TEST_P(TcpProxyOdcdsIntegrationTest, ShutdownConnectionOnTimeout) { tcp_client->waitForHalfClose(); tcp_client->close(); - assertOnDemandCounters(0, 0, 1); + ASSERT_TRUE(assertOnDemandCounters(0, 0, 1)); } TEST_P(TcpProxyOdcdsIntegrationTest, ShutdownConnectionOnClusterMissing) { @@ -277,7 +277,7 @@ TEST_P(TcpProxyOdcdsIntegrationTest, ShutdownConnectionOnClusterMissing) { tcp_client->waitForHalfClose(); tcp_client->close(); - assertOnDemandCounters(0, 1, 0); + ASSERT_TRUE(assertOnDemandCounters(0, 1, 0)); } TEST_P(TcpProxyOdcdsIntegrationTest, ShutdownAllConnectionsOnClusterLookupTimeout) { @@ -306,7 +306,7 @@ TEST_P(TcpProxyOdcdsIntegrationTest, ShutdownAllConnectionsOnClusterLookupTimeou tcp_client_1->waitForHalfClose(true); tcp_client_2->waitForHalfClose(true); - assertOnDemandCounters(0, 0, 2); + ASSERT_TRUE(assertOnDemandCounters(0, 0, 2)); tcp_client_1->close(); tcp_client_2->close(); } @@ -331,7 +331,7 @@ TEST_P(TcpProxyOdcdsIntegrationTest, ShutdownTcpClientBeforeOdcdsResponse) { EXPECT_EQ(1, test_server_->counter("tcp.tcpproxy_stats.on_demand_cluster_attempt")->value()); // Client disconnect when the tcp proxy is waiting for the on demand response. tcp_client->close(); - assertOnDemandCounters(0, 0, 0); + ASSERT_TRUE(assertOnDemandCounters(0, 0, 0)); } } // namespace diff --git a/test/test_common/test_random_generator.cc b/test/test_common/test_random_generator.cc index ae586e56f735a..c6bd3ff29dd83 100644 --- a/test/test_common/test_random_generator.cc +++ b/test/test_common/test_random_generator.cc @@ -2,8 +2,6 @@ #include "gtest/gtest.h" -using testing::GTEST_FLAG(random_seed); - namespace Envoy { // The purpose of using the static seed here is to use --test_arg=--gtest_random_seed=[seed] @@ -16,7 +14,8 @@ int32_t getSeed() { } TestRandomGenerator::TestRandomGenerator() - : seed_(GTEST_FLAG(random_seed) == 0 ? getSeed() : GTEST_FLAG(random_seed)), generator_(seed_) { + : seed_(GTEST_FLAG_GET(random_seed) == 0 ? getSeed() : GTEST_FLAG_GET(random_seed)), + generator_(seed_) { ENVOY_LOG_MISC(info, "TestRandomGenerator running with seed {}", seed_); } diff --git a/test/test_runner.cc b/test/test_runner.cc index 158de9299c732..946616d2d4ec7 100644 --- a/test/test_runner.cc +++ b/test/test_runner.cc @@ -81,6 +81,11 @@ int TestRunner::runTests(int argc, char** argv) { // We hold on to process_wide to provide RAII cleanup of process-wide // state. ProcessWide process_wide(false); + + // Use the recommended, but not default, "threadsafe" style for the Death Tests. + // See: https://github.com/google/googletest/commit/84ec2e0365d791e4ebc7ec249f09078fb5ab6caa + GTEST_FLAG_SET(death_test_style, "threadsafe"); + // Add a test-listener so we can call a hook where we can do a quiescence // check after each method. See // https://github.com/google/googletest/blob/master/googletest/docs/advanced.md @@ -88,10 +93,6 @@ int TestRunner::runTests(int argc, char** argv) { ::testing::TestEventListeners& listeners = ::testing::UnitTest::GetInstance()->listeners(); listeners.Append(new TestListener); - // Use the recommended, but not default, "threadsafe" style for the Death Tests. - // See: https://github.com/google/googletest/commit/84ec2e0365d791e4ebc7ec249f09078fb5ab6caa - ::testing::FLAGS_gtest_death_test_style = "threadsafe"; - // Set gtest properties // (https://github.com/google/googletest/blob/master/googletest/docs/advanced.md#logging-additional-information), // they are available in the test XML.