Skip to content
Merged
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
48 changes: 48 additions & 0 deletions docs/root/configuration/http_filters/lua_filter.rst
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,54 @@ protocol()
Returns the string representation of :repo:`HTTP protocol <include/envoy/http/protocol.h>`
used by the current request. The possible values are: *HTTP/1.0*, *HTTP/1.1*, and *HTTP/2*.

dynamicMetadata()
^^^^^^^^^^^^^^^^^

.. code-block:: lua

requestInfo:dynamicMetadata()

Returns a :ref:`dynamic metadata object <config_http_filters_lua_request_info_dynamic_metadata_wrapper>`.

.. _config_http_filters_lua_request_info_dynamic_metadata_wrapper:

Dynamic metadata object API
---------------------------

get()
^^^^^

.. code-block:: lua

dynamicMetadata:get(filterName)

-- to get a value from a returned table.
dynamicMetadata:get(filterName)[key]

Gets an entry in dynamic metadata struct. *filterName* is a string that supplies the filter name, e.g. *envoy.lb*.
Returns the corresponding *table* of a given *filterName*.

set()
^^^^^

.. code-block:: lua

dynamicMetadata:set(filterName, key, value)

Sets key-value pair of a *filterName*'s metadata. *filterName* is a key specifying the target filter name,
e.g. *envoy.lb*. The type of *key* and *value* is *string*.

__pairs()
^^^^^^^^^

.. code-block:: lua

for key, value in pairs(dynamicMetadata) do
end

Iterates through every *dynamicMetadata* entry. *key* is a string that supplies a *dynamicMetadata*
key. *value* is *dynamicMetadata* entry value.

.. _config_http_filters_lua_connection_wrapper:

Connection object API
Expand Down
1 change: 1 addition & 0 deletions docs/root/intro/version_history.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Version history
:ref:`prefix_ranges <envoy_api_field_listener.FilterChainMatch.prefix_ranges>`.
* lua: added :ref:`connection() <config_http_filters_lua_connection_wrapper>` wrapper and *ssl()* API.
* lua: added :ref:`requestInfo() <config_http_filters_lua_request_info_wrapper>` wrapper and *protocol()* API.
* lua: added :ref:`requestInfo():dynamicMetadata() <config_http_filters_lua_request_info_dynamic_metadata_wrapper>` API.
* proxy_protocol: added support for HAProxy Proxy Protocol v2 (AF_INET/AF_INET6 only).
* ratelimit: added support for :repo:`api/envoy/service/ratelimit/v2/rls.proto`.
Lyft's reference implementation of the `ratelimit <https://github.com/lyft/ratelimit>`_ service also supports the data-plane-api proto as of v1.1.0.
Expand Down
8 changes: 4 additions & 4 deletions source/extensions/filters/common/lua/wrappers.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ int BufferWrapper::luaGetBytes(lua_State* state) {
return 1;
}

void MetadataMapWrapper::setValue(lua_State* state, const ProtobufWkt::Value& value) {
void MetadataMapHelper::setValue(lua_State* state, const ProtobufWkt::Value& value) {
ProtobufWkt::Value::KindCase kind = value.kind_case();

switch (kind) {
Expand Down Expand Up @@ -76,7 +76,7 @@ void MetadataMapWrapper::setValue(lua_State* state, const ProtobufWkt::Value& va
}
}

void MetadataMapWrapper::createTable(
void MetadataMapHelper::createTable(
lua_State* state,
const Protobuf::Map<Envoy::ProtobufTypes::String, ProtobufWkt::Value>& fields) {
lua_createtable(state, 0, fields.size());
Expand All @@ -98,7 +98,7 @@ int MetadataMapIterator::luaPairsIterator(lua_State* state) {
}

lua_pushstring(state, current_->first.c_str());
parent_.setValue(state, current_->second);
MetadataMapHelper::setValue(state, current_->second);

current_++;
return 2;
Expand All @@ -111,7 +111,7 @@ int MetadataMapWrapper::luaGet(lua_State* state) {
return 0;
}

setValue(state, filter_it->second);
MetadataMapHelper::setValue(state, filter_it->second);
return 1;
}

Expand Down
11 changes: 7 additions & 4 deletions source/extensions/filters/common/lua/wrappers.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ class BufferWrapper : public BaseLuaObject<BufferWrapper> {

class MetadataMapWrapper;

struct MetadataMapHelper {
static void setValue(lua_State* state, const ProtobufWkt::Value& value);
static void
createTable(lua_State* state,
const Protobuf::Map<Envoy::ProtobufTypes::String, ProtobufWkt::Value>& fields);
};

/**
* Iterator over a metadata map.
*/
Expand Down Expand Up @@ -89,10 +96,6 @@ class MetadataMapWrapper : public BaseLuaObject<MetadataMapWrapper> {
iterator_.reset();
}

void setValue(lua_State* state, const ProtobufWkt::Value& value);
void createTable(lua_State* state,
const Protobuf::Map<Envoy::ProtobufTypes::String, ProtobufWkt::Value>& fields);

const ProtobufWkt::Struct metadata_;
LuaDeathRef<MetadataMapIterator> iterator_;

Expand Down
1 change: 1 addition & 0 deletions source/extensions/filters/http/lua/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ envoy_cc_library(
"//include/envoy/request_info:request_info_interface",
"//source/common/http:utility_lib",
"//source/extensions/filters/common/lua:lua_lib",
"//source/extensions/filters/common/lua:wrappers_lib",
],
)

Expand Down
2 changes: 2 additions & 0 deletions source/extensions/filters/http/lua/lua_filter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,8 @@ FilterConfig::FilterConfig(const std::string& lua_code, ThreadLocal::SlotAllocat
lua_state_.registerType<HeaderMapWrapper>();
lua_state_.registerType<HeaderMapIterator>();
lua_state_.registerType<RequestInfoWrapper>();
lua_state_.registerType<DynamicMetadataMapWrapper>();
lua_state_.registerType<DynamicMetadataMapIterator>();
lua_state_.registerType<StreamHandleWrapper>();

request_function_slot_ = lua_state_.registerGlobal("envoy_on_request");
Expand Down
65 changes: 65 additions & 0 deletions source/extensions/filters/http/lua/wrappers.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#include "common/http/utility.h"

#include "extensions/filters/common/lua/wrappers.h"

namespace Envoy {
namespace Extensions {
namespace HttpFilters {
Expand Down Expand Up @@ -107,6 +109,69 @@ int RequestInfoWrapper::luaProtocol(lua_State* state) {
return 1;
}

int RequestInfoWrapper::luaDynamicMetadata(lua_State* state) {
if (dynamic_metadata_wrapper_.get() != nullptr) {
dynamic_metadata_wrapper_.pushStack();
} else {
dynamic_metadata_wrapper_.reset(DynamicMetadataMapWrapper::create(state, *this), true);
}
return 1;
}

DynamicMetadataMapIterator::DynamicMetadataMapIterator(DynamicMetadataMapWrapper& parent)
: parent_{parent}, current_{parent_.requestInfo().dynamicMetadata().filter_metadata().begin()} {
}

RequestInfo::RequestInfo& DynamicMetadataMapWrapper::requestInfo() { return parent_.request_info_; }

int DynamicMetadataMapIterator::luaPairsIterator(lua_State* state) {
if (current_ == parent_.requestInfo().dynamicMetadata().filter_metadata().end()) {
parent_.iterator_.reset();
return 0;
}

lua_pushstring(state, current_->first.c_str());
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 = requestInfo().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 DynamicMetadataMapWrapper::luaSet(lua_State* state) {
if (iterator_.get() != nullptr) {
luaL_error(state, "dynamic metadata map cannot be modified while iterating");
}

// TODO(dio): Allow to set dynamic metadata using a table.
const char* filter_name = luaL_checkstring(state, 2);
const char* key = luaL_checkstring(state, 3);
const char* value = luaL_checkstring(state, 4);
requestInfo().setDynamicMetadata(filter_name, MessageUtil::keyValueStruct(key, value));
Copy link
Member

Choose a reason for hiding this comment

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

Is this safe during iteration? Do you need to block a set operation while iterating via the stored iterator which might be invalidated?

Copy link
Member Author

Choose a reason for hiding this comment

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

To be honest, I'm not sure. Will have a test regarding that.

return 0;
}

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

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

} // namespace Lua
} // namespace HttpFilters
} // namespace Extensions
Expand Down
84 changes: 83 additions & 1 deletion source/extensions/filters/http/lua/wrappers.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,21 +99,103 @@ class HeaderMapWrapper : public Filters::Common::Lua::BaseLuaObject<HeaderMapWra
friend class HeaderMapIterator;
};

class DynamicMetadataMapWrapper;
class RequestInfoWrapper;

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

static ExportedFunctions exportedFunctions() { return {}; }

DECLARE_LUA_CLOSURE(DynamicMetadataMapIterator, luaPairsIterator);

private:
DynamicMetadataMapWrapper& parent_;
Protobuf::Map<Envoy::ProtobufTypes::String, ProtobufWkt::Struct>::const_iterator current_;
};

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

static ExportedFunctions exportedFunctions() {
return {{"get", static_luaGet}, {"set", static_luaSet}, {"__pairs", static_luaPairs}};
}

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

/**
* Get a metadata value from the map.
* @param 1 (string): filter name.
* @param 2 (string or table): key.
* @param 3 (string or table): value.
* @return nil.
*/
DECLARE_LUA_FUNCTION(DynamicMetadataMapWrapper, luaSet);

/**
* Implementation of the __pairs metamethod so a dynamic metadata wrapper can be iterated over
* using pairs().
*/
DECLARE_LUA_FUNCTION(DynamicMetadataMapWrapper, luaPairs);

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

// To get reference to parent's (RequestInfoWrapper) request info member.
RequestInfo::RequestInfo& requestInfo();

RequestInfoWrapper& parent_;
Filters::Common::Lua::LuaDeathRef<DynamicMetadataMapIterator> iterator_;

friend class DynamicMetadataMapIterator;
};

/**
* Lua wrapper for a request info.
*/
class RequestInfoWrapper : public Filters::Common::Lua::BaseLuaObject<RequestInfoWrapper> {
public:
RequestInfoWrapper(RequestInfo::RequestInfo& request_info) : request_info_{request_info} {}
static ExportedFunctions exportedFunctions() { return {{"protocol", static_luaProtocol}}; }
static ExportedFunctions exportedFunctions() {
return {{"protocol", static_luaProtocol}, {"dynamicMetadata", static_luaDynamicMetadata}};
}

private:
/**
* Get current protocol being used.
* @return string representation of Http::Protocol.
*/
DECLARE_LUA_FUNCTION(RequestInfoWrapper, luaProtocol);

/**
* Get reference to request info dynamic metadata object.
* @return DynamicMetadataMapWrapper representation of RequestInfo dynamic metadata.
*/
DECLARE_LUA_FUNCTION(RequestInfoWrapper, luaDynamicMetadata);

RequestInfo::RequestInfo& request_info_;
Filters::Common::Lua::LuaDeathRef<DynamicMetadataMapWrapper> dynamic_metadata_wrapper_;

friend class DynamicMetadataMapWrapper;
};

} // namespace Lua
Expand Down
2 changes: 2 additions & 0 deletions test/extensions/filters/http/lua/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ envoy_extension_cc_test(
srcs = ["lua_filter_test.cc"],
extension_name = "envoy.filters.http.lua",
deps = [
"//source/common/request_info:request_info_lib",
"//source/extensions/filters/http/lua:lua_filter_lib",
"//test/mocks/http:http_mocks",
"//test/mocks/network:network_mocks",
Expand All @@ -31,6 +32,7 @@ envoy_extension_cc_test(
srcs = ["wrappers_test.cc"],
extension_name = "envoy.filters.http.lua",
deps = [
"//source/common/request_info:request_info_lib",
"//source/extensions/filters/http/lua:wrappers_lib",
"//test/extensions/filters/common/lua:lua_wrappers_lib",
"//test/mocks/request_info:request_info_mocks",
Expand Down
28 changes: 28 additions & 0 deletions test/extensions/filters/http/lua/lua_filter_test.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "common/buffer/buffer_impl.h"
#include "common/http/message_impl.h"
#include "common/request_info/request_info_impl.h"

#include "extensions/filters/http/lua/lua_filter.h"

Expand Down Expand Up @@ -1516,6 +1517,33 @@ TEST_F(LuaHttpFilterTest, GetCurrentProtocol) {
EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers, true));
}

// Set and get request info dynamic metadata.
TEST_F(LuaHttpFilterTest, SetGetDynamicMetadata) {
const std::string SCRIPT{R"EOF(
function envoy_on_request(request_handle)
request_handle:requestInfo():dynamicMetadata():set("envoy.lb", "foo", "bar")
request_handle:logTrace(request_handle:requestInfo():dynamicMetadata():get("envoy.lb")["foo"])
end
)EOF"};

InSequence s;
setup(SCRIPT);

Http::TestHeaderMapImpl request_headers{{":path", "/"}};
RequestInfo::RequestInfoImpl request_info(Http::Protocol::Http2);
EXPECT_EQ(0, request_info.dynamicMetadata().filter_metadata_size());
EXPECT_CALL(decoder_callbacks_, requestInfo()).WillOnce(ReturnRef(request_info));
EXPECT_CALL(*filter_, scriptLog(spdlog::level::trace, StrEq("bar")));
EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers, true));
EXPECT_EQ(1, request_info.dynamicMetadata().filter_metadata_size());
EXPECT_EQ("bar", request_info.dynamicMetadata()
.filter_metadata()
.at("envoy.lb")
.fields()
.at("foo")
.string_value());
}

// Check the connection.
TEST_F(LuaHttpFilterTest, CheckConnection) {
const std::string SCRIPT{R"EOF(
Expand Down
Loading