Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
Signed-off-by: Kuat Yessenov <[email protected]>
  • Loading branch information
kyessenov committed Sep 29, 2023
1 parent 0ea3605 commit 664648a
Show file tree
Hide file tree
Showing 4 changed files with 282 additions and 4 deletions.
6 changes: 3 additions & 3 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -196,9 +196,9 @@ extensions/filters/http/oauth2 @derekargueta @mattklein123
/*/extensions/http/original_ip_detection/custom_header @alyssawilk @mattklein123
/*/extensions/http/original_ip_detection/xff @alyssawilk @mattklein123
# set_filter_state extension
/*/extensions/filters/common/set_filter_state @kyessenov
/*/extensions/filters/http/set_filter_state @kyessenov
/*/extensions/filters/network/set_filter_state @kyessenov
/*/extensions/filters/common/set_filter_state @kyessenov @wbpcode
/*/extensions/filters/http/set_filter_state @kyessenov @wbpcode
/*/extensions/filters/network/set_filter_state @kyessenov @wbpcode
# set_metadata extension
/*/extensions/filters/http/set_metadata @aguinet @mattklein123
# Formatters
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Config::parse(const Protobuf::RepeatedPtrField<
rule.factory_ =
Registry::FactoryRegistry<StreamInfo::FilterState::ObjectFactory>::getFactory(rule.key_);
if (rule.factory_ == nullptr) {
throw EnvoyException(fmt::format("{} does not have an object factory", rule.key_));
throw EnvoyException(fmt::format("'{}' does not have an object factory", rule.key_));
}
rule.state_type_ = proto_rule.read_only() ? StateType::ReadOnly : StateType::Mutable;
switch (proto_rule.shared_with_upstream()) {
Expand Down
21 changes: 21 additions & 0 deletions test/extensions/filters/common/set_filter_state/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
load(
"//bazel:envoy_build_system.bzl",
"envoy_cc_test",
"envoy_package",
)

licenses(["notice"]) # Apache 2

envoy_package()

envoy_cc_test(
name = "filter_config_test",
srcs = ["filter_config_test.cc"],
deps = [
"//source/common/router:string_accessor_lib",
"//source/extensions/filters/common/set_filter_state:filter_config_lib",
"//test/mocks/server:factory_context_mocks",
"//test/mocks/stream_info:stream_info_mocks",
"//test/test_common:utility_lib",
],
)
257 changes: 257 additions & 0 deletions test/extensions/filters/common/set_filter_state/filter_config_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
#include "source/common/router/string_accessor_impl.h"
#include "source/extensions/filters/common/set_filter_state/filter_config.h"

#include "test/mocks/server/factory_context.h"
#include "test/mocks/stream_info/mocks.h"
#include "test/test_common/utility.h"

#include "gmock/gmock.h"
#include "gtest/gtest.h"

namespace Envoy {
namespace Extensions {
namespace Filters {
namespace Common {
namespace SetFilterState {
namespace {

class ObjectBarFactory : public StreamInfo::FilterState::ObjectFactory {
public:
std::string name() const override { return "bar"; }
std::unique_ptr<StreamInfo::FilterState::Object>
createFromBytes(absl::string_view data) const override {
return std::make_unique<Router::StringAccessorImpl>(data);
}
};

class ObjectFooFactory : public StreamInfo::FilterState::ObjectFactory {
public:
std::string name() const override { return "foo"; }
std::unique_ptr<StreamInfo::FilterState::Object>
createFromBytes(absl::string_view data) const override {
if (data == "BAD_VALUE") {
return nullptr;
}
return std::make_unique<Router::StringAccessorImpl>(data);
}
};

REGISTER_FACTORY(ObjectBarFactory, StreamInfo::FilterState::ObjectFactory);
REGISTER_FACTORY(ObjectFooFactory, StreamInfo::FilterState::ObjectFactory);

class ConfigTest : public testing::Test {
public:
void initialize(const std::vector<std::string>& rules,
LifeSpan life_span = LifeSpan::FilterChain) {
using ProtoRule = envoy::extensions::filters::common::set_filter_state::v3::Rule;
std::vector<ProtoRule> proto_rules;
proto_rules.reserve(rules.size());
for (const auto& rule : rules) {
ProtoRule proto_rule;
TestUtility::loadFromYaml(rule, proto_rule);
proto_rules.push_back(proto_rule);
}
config_ = std::make_shared<Config>(
Protobuf::RepeatedPtrField<ProtoRule>(proto_rules.begin(), proto_rules.end()), life_span,
context_);
}
void update() { config_->updateFilterState({&header_map_}, info_); }
NiceMock<Server::Configuration::MockFactoryContext> context_;
Http::TestRequestHeaderMapImpl header_map_{{"test-header", "test-value"}};
NiceMock<StreamInfo::MockStreamInfo> info_;
ConfigSharedPtr config_;
};

TEST_F(ConfigTest, SetValue) {
initialize({R"YAML(
key: foo
format_string:
text_format_source:
inline_string: "XXX"
)YAML"});
update();
EXPECT_FALSE(info_.filterState()->hasDataAtOrAboveLifeSpan(LifeSpan::Request));
const auto* foo = info_.filterState()->getDataReadOnly<Router::StringAccessor>("foo");
ASSERT_NE(nullptr, foo);
EXPECT_EQ(foo->serializeAsString(), "XXX");
EXPECT_EQ(0, info_.filterState()->objectsSharedWithUpstreamConnection()->size());
}

TEST_F(ConfigTest, SetValueConnection) {
initialize({R"YAML(
key: foo
format_string:
text_format_source:
inline_string: "XXX"
)YAML"},
LifeSpan::Connection);
update();
EXPECT_TRUE(info_.filterState()->hasDataAtOrAboveLifeSpan(LifeSpan::Request));
const auto* foo = info_.filterState()->getDataReadOnly<Router::StringAccessor>("foo");
ASSERT_NE(nullptr, foo);
EXPECT_EQ(foo->serializeAsString(), "XXX");
EXPECT_EQ(0, info_.filterState()->objectsSharedWithUpstreamConnection()->size());
}

TEST_F(ConfigTest, UpdateValue) {
initialize({R"YAML(
key: foo
format_string:
text_format_source:
inline_string: "XXX"
)YAML"});
info_.filterState()->setData("foo", std::make_unique<Router::StringAccessorImpl>("OLD"),
StateType::Mutable);
update();
EXPECT_FALSE(info_.filterState()->hasDataAtOrAboveLifeSpan(LifeSpan::Request));
const auto* foo = info_.filterState()->getDataReadOnly<Router::StringAccessor>("foo");
ASSERT_NE(nullptr, foo);
EXPECT_EQ(foo->serializeAsString(), "XXX");
EXPECT_EQ(0, info_.filterState()->objectsSharedWithUpstreamConnection()->size());
}

TEST_F(ConfigTest, SetValueFromHeader) {
initialize({R"YAML(
key: foo
format_string:
text_format_source:
inline_string: "%REQ(test-header)%"
)YAML"});
update();
EXPECT_FALSE(info_.filterState()->hasDataAtOrAboveLifeSpan(LifeSpan::Request));
const auto* foo = info_.filterState()->getDataReadOnly<Router::StringAccessor>("foo");
ASSERT_NE(nullptr, foo);
EXPECT_EQ(foo->serializeAsString(), "test-value");
EXPECT_EQ(0, info_.filterState()->objectsSharedWithUpstreamConnection()->size());
}

TEST_F(ConfigTest, MultipleRules) {
initialize({R"YAML(
key: foo
format_string:
text_format_source:
inline_string: "XXX"
)YAML",
R"YAML(
key: foo
format_string:
text_format_source:
inline_string: "YYY"
)YAML",
R"YAML(
key: bar
format_string:
text_format_source:
inline_string: "ZZZ"
)YAML"});
update();
const auto* foo = info_.filterState()->getDataReadOnly<Router::StringAccessor>("foo");
ASSERT_NE(nullptr, foo);
const auto* bar = info_.filterState()->getDataReadOnly<Router::StringAccessor>("bar");
ASSERT_NE(nullptr, bar);
EXPECT_EQ(foo->serializeAsString(), "YYY");
EXPECT_EQ(bar->serializeAsString(), "ZZZ");
EXPECT_EQ(0, info_.filterState()->objectsSharedWithUpstreamConnection()->size());
}

TEST_F(ConfigTest, BadValue) {
initialize({R"YAML(
key: foo
format_string:
text_format_source:
inline_string: "BAD_VALUE"
)YAML"});
update();
EXPECT_FALSE(info_.filterState()->hasDataAtOrAboveLifeSpan(LifeSpan::Request));
const auto* foo = info_.filterState()->getDataReadOnly<Router::StringAccessor>("foo");
EXPECT_EQ(nullptr, foo);
}

TEST_F(ConfigTest, MissingKey) {
EXPECT_THROW_WITH_MESSAGE(initialize({R"YAML(
key: unknown_key
format_string:
text_format_source:
inline_string: "XXX"
)YAML"}),
EnvoyException, "'unknown_key' does not have an object factory");
}

TEST_F(ConfigTest, EmptyValue) {
initialize({R"YAML(
key: foo
format_string:
text_format_source:
inline_string: ""
)YAML"});
update();
EXPECT_FALSE(info_.filterState()->hasDataAtOrAboveLifeSpan(LifeSpan::Request));
const auto* foo = info_.filterState()->getDataReadOnly<Router::StringAccessor>("foo");
ASSERT_NE(nullptr, foo);
EXPECT_EQ(foo->serializeAsString(), "");
EXPECT_EQ(0, info_.filterState()->objectsSharedWithUpstreamConnection()->size());
}

TEST_F(ConfigTest, EmptyValueSkip) {
initialize({R"YAML(
key: foo
format_string:
text_format_source:
inline_string: ""
skip_if_empty: true
)YAML"});
update();
EXPECT_FALSE(info_.filterState()->hasDataAtOrAboveLifeSpan(LifeSpan::Request));
const auto* foo = info_.filterState()->getDataReadOnly<Router::StringAccessor>("foo");
EXPECT_EQ(nullptr, foo);
}

TEST_F(ConfigTest, SetValueUpstreamSharedOnce) {
initialize({R"YAML(
key: foo
format_string:
text_format_source:
inline_string: "XXX"
shared_with_upstream: ONCE
)YAML"});
update();
EXPECT_FALSE(info_.filterState()->hasDataAtOrAboveLifeSpan(LifeSpan::Request));
const auto* foo = info_.filterState()->getDataReadOnly<Router::StringAccessor>("foo");
ASSERT_NE(nullptr, foo);
EXPECT_EQ(foo->serializeAsString(), "XXX");
const auto objects = info_.filterState()->objectsSharedWithUpstreamConnection();
EXPECT_EQ(1, objects->size());
EXPECT_EQ(StreamSharing::None, objects->at(0).stream_sharing_);
EXPECT_EQ(StateType::Mutable, objects->at(0).state_type_);
EXPECT_EQ("foo", objects->at(0).name_);
EXPECT_EQ(foo, objects->at(0).data_.get());
}

TEST_F(ConfigTest, SetValueUpstreamSharedTransitive) {
initialize({R"YAML(
key: foo
format_string:
text_format_source:
inline_string: "XXX"
shared_with_upstream: TRANSITIVE
read_only: true
)YAML"});
update();
EXPECT_FALSE(info_.filterState()->hasDataAtOrAboveLifeSpan(LifeSpan::Request));
const auto* foo = info_.filterState()->getDataReadOnly<Router::StringAccessor>("foo");
ASSERT_NE(nullptr, foo);
EXPECT_EQ(foo->serializeAsString(), "XXX");
const auto objects = info_.filterState()->objectsSharedWithUpstreamConnection();
EXPECT_EQ(1, objects->size());
EXPECT_EQ(StreamSharing::SharedWithUpstreamConnection, objects->at(0).stream_sharing_);
EXPECT_EQ(StateType::ReadOnly, objects->at(0).state_type_);
EXPECT_EQ("foo", objects->at(0).name_);
EXPECT_EQ(foo, objects->at(0).data_.get());
}

} // namespace
} // namespace SetFilterState
} // namespace Common
} // namespace Filters
} // namespace Extensions
} // namespace Envoy

0 comments on commit 664648a

Please sign in to comment.