diff --git a/.bazelrc b/.bazelrc index c01fb9a246..637ac2a593 100644 --- a/.bazelrc +++ b/.bazelrc @@ -91,7 +91,7 @@ build:clang-pch --spawn_strategy=local build:clang-pch --define=ENVOY_CLANG_PCH=1 # Use gold linker for gcc compiler. -build:gcc --linkopt=-fuse-ld=gold +build:gcc --linkopt=-fuse-ld=gold --host_linkopt=-fuse-ld=gold build:gcc --test_env=HEAPCHECK= build:gcc --action_env=BAZEL_COMPILER=gcc build:gcc --action_env=CC=gcc --action_env=CXX=g++ diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index b5233988c6..503403a344 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -153,7 +153,7 @@ REPOSITORY_LOCATIONS_SPEC = dict( # # !!! NOTE !!! # Anytime the FIPS BoringSSL version is upgraded, `bazel/external/boringssl_fips.genrule_cmd` must be updated to use the toolchain - # specified in the associated accredidation certificate, which can be found linked from + # specified in the associated accreditation certificate, which can be found linked from # https://boringssl.googlesource.com/boringssl/+/refs/heads/master/crypto/fipsmodule/FIPS.md, for example # https://csrc.nist.gov/projects/cryptographic-module-validation-program/certificate/4735. version = "fips-20220613", @@ -1208,12 +1208,12 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "QUICHE", project_desc = "QUICHE (QUIC, HTTP/2, Etc) is Google‘s implementation of QUIC and related protocols", project_url = "https://github.com/google/quiche", - version = "171f6f89a6a119e8763f1216f8d85347f997cd3b", - sha256 = "3e0fec32dfa9c7568d4703516ee14c9e2316379e0a35f723d17a988be178e532", + version = "eaeaa74b2b4bf4cd9f7a2f44ba8f323fdc55f66a", + sha256 = "1383267a64cb18fca62868e7b54118c223e164d9c0533b11a9a31c779c626f95", urls = ["https://github.com/google/quiche/archive/{version}.tar.gz"], strip_prefix = "quiche-{version}", use_category = ["controlplane", "dataplane_core"], - release_date = "2024-09-26", + release_date = "2024-10-02", cpe = "N/A", license = "BSD-3-Clause", license_url = "https://github.com/google/quiche/blob/{version}/LICENSE", diff --git a/envoy/stream_info/filter_state.h b/envoy/stream_info/filter_state.h index 7e7cfd7fde..f43ffe00eb 100644 --- a/envoy/stream_info/filter_state.h +++ b/envoy/stream_info/filter_state.h @@ -87,6 +87,8 @@ class FilterState { class Object { public: + using FieldType = absl::variant; + virtual ~Object() = default; /** @@ -102,21 +104,17 @@ class FilterState { * This method can be used to get an unstructured serialization result. */ virtual absl::optional serializeAsString() const { return absl::nullopt; } - }; - - /** - * Generic reflection support for the filter state objects. - */ - class ObjectReflection { - public: - virtual ~ObjectReflection() = default; - using FieldType = absl::variant; + /** + * @return bool true if the object supports field access. False if the object does not support + * field access. Default implementation returns false. + */ + virtual bool hasFieldSupport() const { return false; } /** - * @return FieldType a field value for a field name. + * @return FieldType a single state property or field value for a name. */ - virtual FieldType getField(absl::string_view) const PURE; + virtual FieldType getField(absl::string_view) const { return absl::monostate{}; } }; /** @@ -134,12 +132,6 @@ class FilterState { * is malformed. */ virtual std::unique_ptr createFromBytes(absl::string_view data) const PURE; - - /** - * @return std::unique_ptr for the runtime object - * Note that the reflection object is a view and should not outlive the object. - */ - virtual std::unique_ptr reflect(const Object*) const { return nullptr; } }; struct FilterObject { diff --git a/mobile/test/kotlin/integration/proxying/ProxyPollPerformHTTPRequestWithoutUsingPACProxyTest.kt b/mobile/test/kotlin/integration/proxying/ProxyPollPerformHTTPRequestWithoutUsingPACProxyTest.kt index 354ef87073..6cfc8ce170 100644 --- a/mobile/test/kotlin/integration/proxying/ProxyPollPerformHTTPRequestWithoutUsingPACProxyTest.kt +++ b/mobile/test/kotlin/integration/proxying/ProxyPollPerformHTTPRequestWithoutUsingPACProxyTest.kt @@ -53,7 +53,7 @@ class ProxyPollPerformHTTPRequestWithoutUsingPACProxyTest { HttpTestServerFactory.start( HttpTestServerFactory.Type.HTTP1_WITHOUT_TLS, 0, - // http://go/mdn/HTTP/Proxy_servers_and_tunneling/Proxy_Auto-Configuration_PAC_file + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Proxy_servers_and_tunneling/Proxy_Auto-Configuration_PAC_file mapOf("Content-Type" to "application/x-ns-proxy-autoconfig"), """ function FindProxyForURL(url, host) { diff --git a/source/common/formatter/stream_info_formatter.cc b/source/common/formatter/stream_info_formatter.cc index c7f532db00..5ff679a731 100644 --- a/source/common/formatter/stream_info_formatter.cc +++ b/source/common/formatter/stream_info_formatter.cc @@ -192,7 +192,6 @@ FilterStateFormatter::FilterStateFormatter(absl::string_view key, absl::optional if (!field_name.empty()) { format_ = FilterStateFormat::Field; field_name_ = std::string(field_name); - factory_ = Registry::FactoryRegistry::getFactory(key); } else if (serialize_as_string) { format_ = FilterStateFormat::String; } else { @@ -264,14 +263,7 @@ FilterStateFormatter::format(const StreamInfo::StreamInfo& stream_info) const { #endif } case FilterStateFormat::Field: { - if (!factory_) { - return absl::nullopt; - } - const auto reflection = factory_->reflect(state); - if (!reflection) { - return absl::nullopt; - } - auto field_value = reflection->getField(field_name_); + auto field_value = state->getField(field_name_); auto string_value = absl::visit(StringFieldVisitor(), field_value); if (!string_value) { return absl::nullopt; @@ -315,14 +307,7 @@ FilterStateFormatter::formatValue(const StreamInfo::StreamInfo& stream_info) con return SubstitutionFormatUtils::unspecifiedValue(); } case FilterStateFormat::Field: { - if (!factory_) { - return SubstitutionFormatUtils::unspecifiedValue(); - } - const auto reflection = factory_->reflect(state); - if (!reflection) { - return SubstitutionFormatUtils::unspecifiedValue(); - } - auto field_value = reflection->getField(field_name_); + auto field_value = state->getField(field_name_); auto string_value = absl::visit(StringFieldVisitor(), field_value); if (!string_value) { return SubstitutionFormatUtils::unspecifiedValue(); diff --git a/source/common/formatter/stream_info_formatter.h b/source/common/formatter/stream_info_formatter.h index 0cc80e0d91..10f4288c9d 100644 --- a/source/common/formatter/stream_info_formatter.h +++ b/source/common/formatter/stream_info_formatter.h @@ -110,7 +110,6 @@ class FilterStateFormatter : public StreamInfoFormatterProvider { const bool is_upstream_; FilterStateFormat format_; std::string field_name_; - StreamInfo::FilterState::ObjectFactory* factory_; }; class CommonDurationFormatter : public StreamInfoFormatterProvider { diff --git a/source/common/http/filter_manager.cc b/source/common/http/filter_manager.cc index 35066ddb88..89c58323ef 100644 --- a/source/common/http/filter_manager.cc +++ b/source/common/http/filter_manager.cc @@ -1696,6 +1696,10 @@ bool ActiveStreamDecoderFilter::recreateStream(const ResponseHeaderMap* headers) return false; } + parent_.state_.decoder_filter_chain_aborted_ = true; + parent_.state_.encoder_filter_chain_aborted_ = true; + parent_.state_.recreated_stream_ = true; + parent_.streamInfo().setResponseCodeDetails( StreamInfo::ResponseCodeDetails::get().InternalRedirect); @@ -1762,6 +1766,12 @@ void ActiveStreamEncoderFilter::drainSavedResponseMetadata() { } void ActiveStreamEncoderFilter::handleMetadataAfterHeadersCallback() { + if (parent_.state_.recreated_stream_) { + // The stream has been recreated. In this case, there's no reason to encode saved metadata. + getSavedResponseMetadata()->clear(); + return; + } + // If we drain accumulated metadata, the iteration must start with the current filter. const bool saved_state = iterate_from_current_filter_; iterate_from_current_filter_ = true; diff --git a/source/common/http/filter_manager.h b/source/common/http/filter_manager.h index 1798106f86..94500f13d1 100644 --- a/source/common/http/filter_manager.h +++ b/source/common/http/filter_manager.h @@ -894,6 +894,8 @@ class FilterManager : public ScopeTrackedObject, bool decoder_filter_chain_aborted_{}; bool encoder_filter_chain_aborted_{}; bool saw_downstream_reset_{}; + // True when the stream was recreated. + bool recreated_stream_{}; // The following 3 members are booleans rather than part of the space-saving bitfield as they // are passed as arguments to functions expecting bools. Extend State using the bitfield diff --git a/source/common/network/filter_state_dst_address.cc b/source/common/network/filter_state_dst_address.cc index b164573e5f..231f3e0b1c 100644 --- a/source/common/network/filter_state_dst_address.cc +++ b/source/common/network/filter_state_dst_address.cc @@ -9,39 +9,25 @@ absl::optional AddressObject::hash() const { return HashUtil::xxHash64(address_->asStringView()); } -class AddressObjectReflection : public StreamInfo::FilterState::ObjectReflection { -public: - AddressObjectReflection(const AddressObject* object) : object_(object) {} - FieldType getField(absl::string_view field_name) const override { - const auto* ip = object_->address_->ip(); - if (!ip) { - return {}; - } - if (field_name == "ip") { - return ip->addressAsString(); - } else if (field_name == "port") { - return int64_t(ip->port()); - } +StreamInfo::FilterState::Object::FieldType +AddressObject::getField(absl::string_view field_name) const { + const auto* ip = address_->ip(); + if (!ip) { return {}; } - -private: - const AddressObject* object_; -}; + if (field_name == "ip") { + return ip->addressAsString(); + } else if (field_name == "port") { + return int64_t(ip->port()); + } + return {}; +} std::unique_ptr BaseAddressObjectFactory::createFromBytes(absl::string_view data) const { const auto address = Utility::parseInternetAddressAndPortNoThrow(std::string(data)); return address ? std::make_unique(address) : nullptr; } -std::unique_ptr -BaseAddressObjectFactory::reflect(const StreamInfo::FilterState::Object* data) const { - const auto* object = dynamic_cast(data); - if (object) { - return std::make_unique(object); - } - return nullptr; -} } // namespace Network } // namespace Envoy diff --git a/source/common/network/filter_state_dst_address.h b/source/common/network/filter_state_dst_address.h index ec6c565fd6..b35cb25b9f 100644 --- a/source/common/network/filter_state_dst_address.h +++ b/source/common/network/filter_state_dst_address.h @@ -17,13 +17,15 @@ class AddressObject : public StreamInfo::FilterState::Object, public Hashable { absl::optional serializeAsString() const override { return address_ ? absl::make_optional(address_->asString()) : absl::nullopt; } + bool hasFieldSupport() const override { return true; } + FieldType getField(absl::string_view field_name) const override; + // Implements hashing interface because the value is applied once per upstream connection. // Multiple streams sharing the upstream connection must have the same address object. absl::optional hash() const override; private: const Network::Address::InstanceConstSharedPtr address_; - friend class AddressObjectReflection; }; /** @@ -33,8 +35,6 @@ class BaseAddressObjectFactory : public StreamInfo::FilterState::ObjectFactory { public: std::unique_ptr createFromBytes(absl::string_view data) const override; - std::unique_ptr - reflect(const StreamInfo::FilterState::Object* data) const override; }; } // namespace Network diff --git a/source/extensions/filters/common/expr/context.cc b/source/extensions/filters/common/expr/context.cc index 10438dff4d..a13a39656a 100644 --- a/source/extensions/filters/common/expr/context.cc +++ b/source/extensions/filters/common/expr/context.cc @@ -300,13 +300,12 @@ absl::optional PeerWrapper::operator[](CelValue key) const { class FilterStateObjectWrapper : public google::api::expr::runtime::CelMap { public: - FilterStateObjectWrapper(const StreamInfo::FilterState::ObjectReflection* reflection) - : reflection_(reflection) {} + FilterStateObjectWrapper(const StreamInfo::FilterState::Object* object) : object_(object) {} absl::optional operator[](CelValue key) const override { - if (reflection_ == nullptr || !key.IsString()) { + if (object_ == nullptr || !key.IsString()) { return {}; } - auto field_value = reflection_->getField(key.StringOrDie().value()); + auto field_value = object_->getField(key.StringOrDie().value()); return absl::visit(Visitor{}, field_value); } // Default stubs. @@ -325,7 +324,7 @@ class FilterStateObjectWrapper : public google::api::expr::runtime::CelMap { } absl::optional operator()(absl::monostate) { return {}; } }; - const StreamInfo::FilterState::ObjectReflection* reflection_; + const StreamInfo::FilterState::Object* object_; }; absl::optional FilterStateWrapper::operator[](CelValue key) const { @@ -339,17 +338,11 @@ absl::optional FilterStateWrapper::operator[](CelValue key) const { if (cel_state) { return cel_state->exprValue(&arena_, false); } else if (object != nullptr) { - // Attempt to find the reflection object. - auto factory = - Registry::FactoryRegistry::getFactory(value); - if (factory) { - auto reflection = factory->reflect(object); - if (reflection) { - auto* raw_reflection = reflection.release(); - arena_.Own(raw_reflection); - return CelValue::CreateMap( - ProtobufWkt::Arena::Create(&arena_, raw_reflection)); - } + // TODO(wbpcode): the implementation of cannot handle the case where the object has provided + // field support, but callers only want to access the whole object. + if (object->hasFieldSupport()) { + return CelValue::CreateMap( + ProtobufWkt::Arena::Create(&arena_, object)); } absl::optional serialized = object->serializeAsString(); if (serialized.has_value()) { diff --git a/test/common/formatter/substitution_formatter_test.cc b/test/common/formatter/substitution_formatter_test.cc index 41b3e2fcba..11bfd2135c 100644 --- a/test/common/formatter/substitution_formatter_test.cc +++ b/test/common/formatter/substitution_formatter_test.cc @@ -123,18 +123,10 @@ class TestSerializedStringFilterState : public StreamInfo::FilterState::Object { message->set_value(raw_string_ + " By TYPED"); return message; } - -private: - std::string raw_string_; - friend class TestSerializedStringReflection; -}; - -class TestSerializedStringReflection : public StreamInfo::FilterState::ObjectReflection { -public: - TestSerializedStringReflection(const TestSerializedStringFilterState* data) : data_(data) {} + bool hasFieldSupport() const override { return true; } FieldType getField(absl::string_view field_name) const override { if (field_name == "test_field") { - return data_->raw_string_; + return raw_string_; } else if (field_name == "test_num") { return 137; } @@ -142,25 +134,9 @@ class TestSerializedStringReflection : public StreamInfo::FilterState::ObjectRef } private: - const TestSerializedStringFilterState* data_; -}; - -class TestSerializedStringFilterStateFactory : public StreamInfo::FilterState::ObjectFactory { -public: - std::string name() const override { return "test_key"; } - std::unique_ptr - createFromBytes(absl::string_view) const override { - return nullptr; - } - std::unique_ptr - reflect(const StreamInfo::FilterState::Object* data) const override { - return std::make_unique( - dynamic_cast(data)); - } + std::string raw_string_; }; -REGISTER_FACTORY(TestSerializedStringFilterStateFactory, StreamInfo::FilterState::ObjectFactory); - // Test tests multiple versions of variadic template method parseSubcommand // extracting tokens. TEST(SubstitutionFormatParser, commandParser) { diff --git a/test/extensions/clusters/original_dst/original_dst_cluster_test.cc b/test/extensions/clusters/original_dst/original_dst_cluster_test.cc index d0e1cd8e82..ba0777efa7 100644 --- a/test/extensions/clusters/original_dst/original_dst_cluster_test.cc +++ b/test/extensions/clusters/original_dst/original_dst_cluster_test.cc @@ -1163,10 +1163,8 @@ TEST(DestinationAddress, ObjectFactory) { auto object = factory->createFromBytes(address); ASSERT_NE(nullptr, object); EXPECT_EQ(address, object->serializeAsString()); - auto mirror = factory->reflect(object.get()); - ASSERT_NE(nullptr, mirror); - EXPECT_THAT(mirror->getField("ip"), testing::VariantWith("10.0.0.10")); - EXPECT_THAT(mirror->getField("port"), testing::VariantWith(8080)); + EXPECT_THAT(object->getField("ip"), testing::VariantWith("10.0.0.10")); + EXPECT_THAT(object->getField("port"), testing::VariantWith(8080)); EXPECT_EQ(nullptr, factory->createFromBytes("foo")); } diff --git a/test/extensions/filters/common/expr/context_test.cc b/test/extensions/filters/common/expr/context_test.cc index c766d47e25..f7cec4fb53 100644 --- a/test/extensions/filters/common/expr/context_test.cc +++ b/test/extensions/filters/common/expr/context_test.cc @@ -777,6 +777,8 @@ TEST(Context, FilterStateAttributes) { StreamInfo::FilterStateImpl filter_state(StreamInfo::FilterState::LifeSpan::FilterChain); ProtobufWkt::Arena arena; FilterStateWrapper wrapper(arena, filter_state); + auto status_or = wrapper.ListKeys(&arena); + EXPECT_EQ(status_or.status().message(), "ListKeys() is not implemented"); const std::string key = "filter_state_key"; const std::string serialized = "filter_state_value"; @@ -941,6 +943,21 @@ TEST(Context, XDSAttributes) { } } +TEST(Context, EmptyXdsWrapper) { + Protobuf::Arena arena; + XDSWrapper wrapper(arena, nullptr, nullptr); + + { + const auto value = wrapper[CelValue::CreateStringView(Node)]; + EXPECT_FALSE(value.has_value()); + } + + { + const auto value = wrapper[CelValue::CreateStringView(ClusterName)]; + EXPECT_FALSE(value.has_value()); + } +} + } // namespace } // namespace Expr } // namespace Common diff --git a/test/integration/BUILD b/test/integration/BUILD index 58a33e4a52..374b9f7b3f 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -798,13 +798,16 @@ envoy_cc_test( "//source/common/http:header_map_lib", "//source/extensions/filters/http/buffer:config", "//test/integration/filters:add_body_filter_config_lib", + "//test/integration/filters:add_encode_metadata_filter_lib", "//test/integration/filters:add_invalid_data_filter_lib", "//test/integration/filters:assert_non_reentrant_filter_lib", "//test/integration/filters:buffer_continue_filter_lib", "//test/integration/filters:continue_after_local_reply_filter_lib", "//test/integration/filters:continue_headers_only_inject_body", "//test/integration/filters:encoder_decoder_buffer_filter_lib", + "//test/integration/filters:encoder_recreate_stream_filter_lib", "//test/integration/filters:invalid_header_filter_lib", + "//test/integration/filters:local_reply_during_decoding_filter_lib", "//test/integration/filters:local_reply_during_encoding_data_filter_lib", "//test/integration/filters:local_reply_during_encoding_filter_lib", "//test/integration/filters:local_reply_with_metadata_filter_lib", diff --git a/test/integration/filter_integration_test.cc b/test/integration/filter_integration_test.cc index e83a9d2311..5028b5eca5 100644 --- a/test/integration/filter_integration_test.cc +++ b/test/integration/filter_integration_test.cc @@ -1575,5 +1575,85 @@ TEST_P(FilterIntegrationTest, FilterAddsDataToHeaderOnlyRequestWithIndependentHa testFilterAddsDataAndTrailersToHeaderOnlyRequest(); } +// Add metadata in the first filter before recreate the stream in the second filter, +// on response path. +TEST_P(FilterIntegrationTest, RecreateStreamAfterEncodeMetadata) { + // recreateStream is not supported in Upstream filter chain. + if (!testing_downstream_filter_) { + return; + } + + prependFilter("{ name: add-metadata-encode-headers-filter }"); + prependFilter("{ name: encoder-recreate-stream-filter }"); + config_helper_.addConfigModifier( + [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& + hcm) -> void { hcm.mutable_http2_protocol_options()->set_allow_metadata(true); }); + + initialize(); + + codec_client_ = makeHttpConnection(lookupPort("http")); + auto response = codec_client_->makeHeaderOnlyRequest(default_request_headers_); + waitForNextUpstreamRequest(); + upstream_request_->encodeHeaders(default_response_headers_, true); + + // Second upstream request is triggered by recreateStream. + FakeStreamPtr upstream_request_2; + // Wait for the next stream on the upstream connection. + ASSERT_TRUE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_2)); + // Wait for the stream to be completely received. + ASSERT_TRUE(upstream_request_2->waitForEndStream(*dispatcher_)); + upstream_request_2->encodeHeaders(default_response_headers_, true); + + // Wait for the response to be completely received. + ASSERT_TRUE(response->waitForEndStream()); + ASSERT_TRUE(response->complete()); + + // Verify the metadata is received. + std::set expected_metadata_keys = {"headers", "duplicate"}; + EXPECT_EQ(response->metadataMap().size(), expected_metadata_keys.size()); + for (const auto& key : expected_metadata_keys) { + // keys are the same as their corresponding values. + auto it = response->metadataMap().find(key); + ASSERT_FALSE(it == response->metadataMap().end()) << "key: " << key; + EXPECT_EQ(response->metadataMap().find(key)->second, key); + } +} + +// Add metadata in the first filter on local reply path. +TEST_P(FilterIntegrationTest, EncodeMetadataOnLocalReply) { + // Local replies are not seen by upstream HTTP filters. add-metadata-encode-headers-filter will + // not be invoked if it is installed in upstream filter chain. + // Thus, this test is only applicable to downstream filter chain. + if (!testing_downstream_filter_) { + return; + } + + prependFilter("{ name: local-reply-during-decode }"); + prependFilter("{ name: add-metadata-encode-headers-filter }"); + + config_helper_.addConfigModifier( + [&](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& + hcm) -> void { hcm.mutable_http2_protocol_options()->set_allow_metadata(true); }); + + initialize(); + + codec_client_ = makeHttpConnection(lookupPort("http")); + auto response = codec_client_->makeHeaderOnlyRequest(default_request_headers_); + + ASSERT_TRUE(response->waitForEndStream()); + EXPECT_TRUE(response->complete()); + EXPECT_EQ("500", response->headers().getStatusValue()); + + // Verify the metadata is received. + std::set expected_metadata_keys = {"headers", "duplicate"}; + EXPECT_EQ(response->metadataMap().size(), expected_metadata_keys.size()); + for (const auto& key : expected_metadata_keys) { + // keys are the same as their corresponding values. + auto it = response->metadataMap().find(key); + ASSERT_FALSE(it == response->metadataMap().end()) << "key: " << key; + EXPECT_EQ(response->metadataMap().find(key)->second, key); + } +} + } // namespace } // namespace Envoy diff --git a/test/integration/filters/BUILD b/test/integration/filters/BUILD index 35e062867d..6dd174614b 100644 --- a/test/integration/filters/BUILD +++ b/test/integration/filters/BUILD @@ -1013,3 +1013,36 @@ envoy_cc_test_library( "//test/extensions/filters/http/common:empty_http_filter_config_lib", ], ) + +envoy_cc_test_library( + name = "add_encode_metadata_filter_lib", + srcs = [ + "add_encode_metadata_filter.cc", + ], + deps = [ + ":common_lib", + "//envoy/event:timer_interface", + "//envoy/http:filter_interface", + "//envoy/registry", + "//envoy/server:filter_config_interface", + "//source/extensions/filters/http/common:pass_through_filter_lib", + "//test/extensions/filters/http/common:empty_http_filter_config_lib", + ], +) + +envoy_cc_test_library( + name = "encoder_recreate_stream_filter_lib", + srcs = [ + "encoder_recreate_stream_filter.cc", + ], + deps = [ + ":common_lib", + "//envoy/event:timer_interface", + "//envoy/http:filter_interface", + "//envoy/registry", + "//envoy/server:filter_config_interface", + "//source/common/router:string_accessor_lib", + "//source/extensions/filters/http/common:pass_through_filter_lib", + "//test/extensions/filters/http/common:empty_http_filter_config_lib", + ], +) diff --git a/test/integration/filters/add_encode_metadata_filter.cc b/test/integration/filters/add_encode_metadata_filter.cc new file mode 100644 index 0000000000..5f56ac39b0 --- /dev/null +++ b/test/integration/filters/add_encode_metadata_filter.cc @@ -0,0 +1,41 @@ +#include +#include + +#include "envoy/event/timer.h" +#include "envoy/http/filter.h" +#include "envoy/registry/registry.h" +#include "envoy/server/filter_config.h" + +#include "source/common/buffer/buffer_impl.h" +#include "source/extensions/filters/http/common/pass_through_filter.h" + +#include "test/extensions/filters/http/common/empty_http_filter_config.h" +#include "test/integration/filters/common.h" + +#include "gtest/gtest.h" + +namespace Envoy { + +// A filter add encoded metadata in encodeHeaders. +class AddEncodeMetadataFilter : public Http::PassThroughFilter { +public: + constexpr static char name[] = "add-metadata-encode-headers-filter"; + + Http::FilterHeadersStatus encodeHeaders(Http::ResponseHeaderMap&, bool) override { + Http::MetadataMap metadata_map = {{"headers", "headers"}, {"duplicate", "duplicate"}}; + Http::MetadataMapPtr metadata_map_ptr = std::make_unique(metadata_map); + encoder_callbacks_->addEncodedMetadata(std::move(metadata_map_ptr)); + return Http::FilterHeadersStatus::Continue; + } + + Http::FilterDataStatus encodeData(Buffer::Instance&, bool) override { + return Http::FilterDataStatus::Continue; + } +}; + +constexpr char AddEncodeMetadataFilter::name[]; +static Registry::RegisterFactory, + Server::Configuration::NamedHttpFilterConfigFactory> + register_; + +} // namespace Envoy diff --git a/test/integration/filters/encoder_recreate_stream_filter.cc b/test/integration/filters/encoder_recreate_stream_filter.cc new file mode 100644 index 0000000000..ebcbb4e40e --- /dev/null +++ b/test/integration/filters/encoder_recreate_stream_filter.cc @@ -0,0 +1,55 @@ +#include +#include + +#include "envoy/event/timer.h" +#include "envoy/http/filter.h" +#include "envoy/registry/registry.h" +#include "envoy/server/filter_config.h" + +#include "source/common/buffer/buffer_impl.h" +#include "source/common/router/string_accessor_impl.h" +#include "source/extensions/filters/http/common/pass_through_filter.h" + +#include "test/extensions/filters/http/common/empty_http_filter_config.h" +#include "test/integration/filters/common.h" + +#include "gtest/gtest.h" + +namespace Envoy { + +class EncoderRecreateStreamFilter : public Http::PassThroughFilter { +public: + constexpr static char name[] = "encoder-recreate-stream-filter"; + + Http::FilterHeadersStatus encodeHeaders(Http::ResponseHeaderMap&, bool) override { + const auto* filter_state = + decoder_callbacks_->streamInfo().filterState()->getDataReadOnly( + "test_key"); + + if (filter_state != nullptr) { + return ::Envoy::Http::FilterHeadersStatus::Continue; + } + + decoder_callbacks_->streamInfo().filterState()->setData( + "test_key", std::make_unique("test_value"), + StreamInfo::FilterState::StateType::ReadOnly, StreamInfo::FilterState::LifeSpan::Request); + + if (decoder_callbacks_->recreateStream(nullptr)) { + return ::Envoy::Http::FilterHeadersStatus::StopIteration; + } + + return ::Envoy::Http::FilterHeadersStatus::Continue; + } + + void setDecoderFilterCallbacks(Http::StreamDecoderFilterCallbacks& callbacks) override { + decoder_callbacks_ = &callbacks; + } +}; + +// perform static registration +constexpr char EncoderRecreateStreamFilter::name[]; +static Registry::RegisterFactory, + Server::Configuration::NamedHttpFilterConfigFactory> + register_; + +} // namespace Envoy