Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lua: add new methods to access network connection streamInfo() & dynamicMetadata() #22246

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions source/extensions/filters/http/lua/lua_filter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ PerLuaCodeSetup::PerLuaCodeSetup(const std::string& lua_code, ThreadLocal::SlotA
lua_state_.registerType<DynamicMetadataMapIterator>();
lua_state_.registerType<StreamHandleWrapper>();
lua_state_.registerType<PublicKeyWrapper>();
lua_state_.registerType<ConnectionStreamInfoWrapper>();
lua_state_.registerType<ConnectionDynamicMetadataMapWrapper>();
lua_state_.registerType<ConnectionDynamicMetadataMapIterator>();
Comment on lines +137 to +139
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should the stream info and dynamic metadata need independent type?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good question. I wasn't aware of any implications for re-using the one we have for HTTP-level stream info object so I created separate ones for the listener-level stream info. If you think it's fine to reuse the existing ones then we can remove it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good question. I wasn't aware of any implications for re-using the one we have for HTTP-level stream info object so I created separate ones for the listener-level stream info. If you think it's fine to reuse the existing ones then we can remove it.

Yeah, I think it would be better to just reuse this wrapper.


const Filters::Common::Lua::InitializerList initializers(
// EnvoyTimestampResolution "enum".
Expand Down Expand Up @@ -511,6 +514,17 @@ int StreamHandleWrapper::luaStreamInfo(lua_State* state) {
return 1;
}

int StreamHandleWrapper::luaConnectionStreamInfo(lua_State* state) {
ASSERT(state_ == State::Running);
if (connection_stream_info_wrapper_.get() != nullptr) {
connection_stream_info_wrapper_.pushStack();
} else {
connection_stream_info_wrapper_.reset(
ConnectionStreamInfoWrapper::create(state, callbacks_.connection()->streamInfo()), true);
}
return 1;
}

int StreamHandleWrapper::luaConnection(lua_State* state) {
ASSERT(state_ == State::Running);
if (connection_wrapper_.get() != nullptr) {
Expand Down
10 changes: 9 additions & 1 deletion source/extensions/filters/http/lua/lua_filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,8 @@ class StreamHandleWrapper : public Filters::Common::Lua::BaseLuaObject<StreamHan
{"verifySignature", static_luaVerifySignature},
{"base64Escape", static_luaBase64Escape},
{"timestamp", static_luaTimestamp},
{"timestampString", static_luaTimestampString}};
{"timestampString", static_luaTimestampString},
{"connectionStreamInfo", static_luaConnectionStreamInfo}};
}

private:
Expand Down Expand Up @@ -231,6 +232,11 @@ class StreamHandleWrapper : public Filters::Common::Lua::BaseLuaObject<StreamHan
*/
DECLARE_LUA_FUNCTION(StreamHandleWrapper, luaConnection);

/**
* @return a handle to the network connection's stream info.
*/
DECLARE_LUA_FUNCTION(StreamHandleWrapper, luaConnectionStreamInfo);

/**
* Log a message to the Envoy log.
* @param 1 (string): The log message.
Expand Down Expand Up @@ -307,6 +313,7 @@ class StreamHandleWrapper : public Filters::Common::Lua::BaseLuaObject<StreamHan
stream_info_wrapper_.reset();
connection_wrapper_.reset();
public_key_wrapper_.reset();
connection_stream_info_wrapper_.reset();
}

// Http::AsyncClient::Callbacks
Expand All @@ -328,6 +335,7 @@ class StreamHandleWrapper : public Filters::Common::Lua::BaseLuaObject<StreamHan
Filters::Common::Lua::LuaDeathRef<HeaderMapWrapper> trailers_wrapper_;
Filters::Common::Lua::LuaDeathRef<Filters::Common::Lua::MetadataMapWrapper> metadata_wrapper_;
Filters::Common::Lua::LuaDeathRef<StreamInfoWrapper> stream_info_wrapper_;
Filters::Common::Lua::LuaDeathRef<ConnectionStreamInfoWrapper> connection_stream_info_wrapper_;
Filters::Common::Lua::LuaDeathRef<Filters::Common::Lua::ConnectionWrapper> connection_wrapper_;
Filters::Common::Lua::LuaDeathRef<PublicKeyWrapper> public_key_wrapper_;
State state_{State::Running};
Expand Down
56 changes: 56 additions & 0 deletions source/extensions/filters/http/lua/wrappers.cc
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,16 @@ int StreamInfoWrapper::luaDynamicMetadata(lua_State* state) {
return 1;
}

int ConnectionStreamInfoWrapper::luaConnectionDynamicMetadata(lua_State* state) {
if (connection_dynamic_metadata_wrapper_.get() != nullptr) {
connection_dynamic_metadata_wrapper_.pushStack();
} else {
connection_dynamic_metadata_wrapper_.reset(
ConnectionDynamicMetadataMapWrapper::create(state, *this), true);
}
return 1;
}

int StreamInfoWrapper::luaDownstreamSslConnection(lua_State* state) {
const auto& ssl = stream_info_.downstreamAddressProvider().sslConnection();
if (ssl != nullptr) {
Expand Down Expand Up @@ -174,6 +184,14 @@ DynamicMetadataMapIterator::DynamicMetadataMapIterator(DynamicMetadataMapWrapper

StreamInfo::StreamInfo& DynamicMetadataMapWrapper::streamInfo() { return parent_.stream_info_; }

ConnectionDynamicMetadataMapIterator::ConnectionDynamicMetadataMapIterator(
ConnectionDynamicMetadataMapWrapper& parent)
: parent_{parent}, current_{parent_.streamInfo().dynamicMetadata().filter_metadata().begin()} {}

const StreamInfo::StreamInfo& ConnectionDynamicMetadataMapWrapper::streamInfo() {
return parent_.connection_stream_info_;
}

int DynamicMetadataMapIterator::luaPairsIterator(lua_State* state) {
if (current_ == parent_.streamInfo().dynamicMetadata().filter_metadata().end()) {
parent_.iterator_.reset();
Expand All @@ -187,6 +205,20 @@ int DynamicMetadataMapIterator::luaPairsIterator(lua_State* state) {
return 2;
}

int ConnectionDynamicMetadataMapIterator::luaConnectionDynamicMetadataPairsIterator(
lua_State* state) {
if (current_ == parent_.streamInfo().dynamicMetadata().filter_metadata().end()) {
parent_.iterator_.reset();
return 0;
}

lua_pushlstring(state, current_->first.data(), current_->first.size());
Filters::Common::Lua::MetadataMapHelper::createTable(state, current_->second.fields());

current_++;
return 2;
}

int DynamicMetadataMapWrapper::luaGet(lua_State* state) {
const char* filter_name = luaL_checkstring(state, 2);
const auto& metadata = streamInfo().dynamicMetadata().filter_metadata();
Expand Down Expand Up @@ -230,6 +262,30 @@ int DynamicMetadataMapWrapper::luaPairs(lua_State* state) {
return 1;
}

int ConnectionDynamicMetadataMapWrapper::luaConnectionDynamicMetadataGet(lua_State* state) {
const char* filter_name = luaL_checkstring(state, 2);
const auto& metadata = streamInfo().dynamicMetadata().filter_metadata();
const auto filter_it = metadata.find(filter_name);
if (filter_it == metadata.end()) {
return 0;
}

Filters::Common::Lua::MetadataMapHelper::createTable(state, filter_it->second.fields());
return 1;
}

int ConnectionDynamicMetadataMapWrapper::luaConnectionDynamicMetadataPairs(lua_State* state) {
if (iterator_.get() != nullptr) {
luaL_error(state, "cannot create a second iterator before completing the first");
}

iterator_.reset(ConnectionDynamicMetadataMapIterator::create(state, *this), true);
lua_pushcclosure(
state, ConnectionDynamicMetadataMapIterator::static_luaConnectionDynamicMetadataPairsIterator,
1);
return 1;
}

int PublicKeyWrapper::luaGet(lua_State* state) {
if (public_key_.empty()) {
lua_pushnil(state);
Expand Down
91 changes: 91 additions & 0 deletions source/extensions/filters/http/lua/wrappers.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ class HeaderMapWrapper : public Filters::Common::Lua::BaseLuaObject<HeaderMapWra

class DynamicMetadataMapWrapper;
class StreamInfoWrapper;
class ConnectionDynamicMetadataMapWrapper;
class ConnectionStreamInfoWrapper;

/**
* Iterator over a dynamic metadata map.
Expand All @@ -140,6 +142,24 @@ class DynamicMetadataMapIterator
Protobuf::Map<std::string, ProtobufWkt::Struct>::const_iterator current_;
};

/**
* Iterator over a network filter dynamic metadata map.
*/
class ConnectionDynamicMetadataMapIterator
: public Filters::Common::Lua::BaseLuaObject<ConnectionDynamicMetadataMapIterator> {
public:
ConnectionDynamicMetadataMapIterator(ConnectionDynamicMetadataMapWrapper& parent);

static ExportedFunctions exportedFunctions() { return {}; }

DECLARE_LUA_CLOSURE(ConnectionDynamicMetadataMapIterator,
luaConnectionDynamicMetadataPairsIterator);

private:
ConnectionDynamicMetadataMapWrapper& parent_;
Protobuf::Map<std::string, ProtobufWkt::Struct>::const_iterator current_;
};

/**
* Lua wrapper for a dynamic metadata.
*/
Expand Down Expand Up @@ -190,6 +210,48 @@ class DynamicMetadataMapWrapper
friend class DynamicMetadataMapIterator;
};

/**
* Lua wrapper for a network filter dynamic metadata.
*/
class ConnectionDynamicMetadataMapWrapper
: public Filters::Common::Lua::BaseLuaObject<ConnectionDynamicMetadataMapWrapper> {
public:
ConnectionDynamicMetadataMapWrapper(ConnectionStreamInfoWrapper& parent) : parent_{parent} {}

static ExportedFunctions exportedFunctions() {
return {{"get", static_luaConnectionDynamicMetadataGet},
{"__pairs", static_luaConnectionDynamicMetadataPairs}};
}

private:
/**
* Get a metadata value from the map.
* @param 1 (string): filter name.
* @return value if found or nil.
*/
DECLARE_LUA_FUNCTION(ConnectionDynamicMetadataMapWrapper, luaConnectionDynamicMetadataGet);

/**
* Implementation of the __pairs meta method so a dynamic metadata wrapper can be iterated over
* using pairs().
*/
DECLARE_LUA_FUNCTION(ConnectionDynamicMetadataMapWrapper, luaConnectionDynamicMetadataPairs);

// Envoy::Lua::BaseLuaObject
void onMarkDead() override {
// Iterators do not survive yields.
iterator_.reset();
}

// To get reference to parent's (StreamInfoWrapper) stream info member.
const StreamInfo::StreamInfo& streamInfo();

ConnectionStreamInfoWrapper& parent_;
Filters::Common::Lua::LuaDeathRef<ConnectionDynamicMetadataMapIterator> iterator_;

friend class ConnectionDynamicMetadataMapIterator;
};

/**
* Lua wrapper for a stream info.
*/
Expand Down Expand Up @@ -257,6 +319,35 @@ class StreamInfoWrapper : public Filters::Common::Lua::BaseLuaObject<StreamInfoW
friend class DynamicMetadataMapWrapper;
};

/**
* Lua wrapper for a network connection's stream info.
*/
class ConnectionStreamInfoWrapper
: public Filters::Common::Lua::BaseLuaObject<ConnectionStreamInfoWrapper> {
public:
ConnectionStreamInfoWrapper(const StreamInfo::StreamInfo& connection_stream_info)
: connection_stream_info_{connection_stream_info} {}
static ExportedFunctions exportedFunctions() {
return {{"dynamicMetadata", static_luaConnectionDynamicMetadata}};
}

private:
/**
* Get reference to stream info dynamic metadata object.
* @return ConnectionDynamicMetadataMapWrapper representation of StreamInfo dynamic metadata.
*/
DECLARE_LUA_FUNCTION(ConnectionStreamInfoWrapper, luaConnectionDynamicMetadata);

// Envoy::Lua::BaseLuaObject
void onMarkDead() override { connection_dynamic_metadata_wrapper_.reset(); }

const StreamInfo::StreamInfo& connection_stream_info_;
Filters::Common::Lua::LuaDeathRef<ConnectionDynamicMetadataMapWrapper>
connection_dynamic_metadata_wrapper_;

friend class ConnectionDynamicMetadataMapWrapper;
};

/**
* Lua wrapper for key for accessing the imported public keys.
*/
Expand Down
49 changes: 49 additions & 0 deletions test/extensions/filters/http/lua/lua_filter_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1800,6 +1800,55 @@ TEST_F(LuaHttpFilterTest, GetRequestedServerName) {
EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers, true));
}

// Verify that network connection level streamInfo():dynamicMetadata() could be accessed using LUA.
TEST_F(LuaHttpFilterTest, GetConnectionDynamicMetadata) {
const std::string SCRIPT{R"EOF(
function envoy_on_request(request_handle)
local cx_metadata = request_handle:connectionStreamInfo():dynamicMetadata()
local filters_count = 0
for filter_name, _ in pairs(cx_metadata) do
filters_count = filters_count + 1
end
request_handle:logTrace('Filters Count: ' .. filters_count)

local pp_metadata_entries = cx_metadata:get("envoy.proxy_protocol")
for key, value in pairs(pp_metadata_entries) do
request_handle:logTrace('Key: ' .. key .. ', Value: ' .. value)
end

local lb_version = cx_metadata:get("envoy.lb")["version"]
request_handle:logTrace('Key: version, Value: ' .. lb_version)
end
)EOF"};

// Proxy Protocol Filter Metadata
ProtobufWkt::Value tlv_ea_value;
tlv_ea_value.set_string_value("vpce-064c279a4001a055f");
ProtobufWkt::Struct proxy_protocol_metadata;
proxy_protocol_metadata.mutable_fields()->insert({"tlv_ea", tlv_ea_value});
(*stream_info_.metadata_.mutable_filter_metadata())["envoy.proxy_protocol"] =
proxy_protocol_metadata;

// LB Filter Metadata
ProtobufWkt::Value lb_version_value;
lb_version_value.set_string_value("v1.0");
ProtobufWkt::Struct lb_metadata;
lb_metadata.mutable_fields()->insert({"version", lb_version_value});
(*stream_info_.metadata_.mutable_filter_metadata())["envoy.lb"] = lb_metadata;

InSequence s;
setup(SCRIPT);

Http::TestRequestHeaderMapImpl request_headers{{":path", "/"}};
EXPECT_CALL(decoder_callbacks_, connection()).WillOnce(Return(&connection_));
EXPECT_CALL(Const(connection_), streamInfo()).WillOnce(ReturnRef(stream_info_));
EXPECT_CALL(*filter_, scriptLog(spdlog::level::trace, StrEq("Filters Count: 2")));
EXPECT_CALL(*filter_,
scriptLog(spdlog::level::trace, StrEq("Key: tlv_ea, Value: vpce-064c279a4001a055f")));
EXPECT_CALL(*filter_, scriptLog(spdlog::level::trace, StrEq("Key: version, Value: v1.0")));
EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers, true));
}

// Verify that binary values could also be extracted from dynamicMetadata() in LUA filter.
TEST_F(LuaHttpFilterTest, GetDynamicMetadataBinaryData) {
const std::string SCRIPT{R"EOF(
Expand Down