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 request info dynamic metadata api #3627

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from 7 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
54 changes: 54 additions & 0 deletions docs/root/configuration/http_filters/lua_filter.rst
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,11 @@ under the filter name i.e. *envoy.lua*. Below is an example of a *metadata* in a

Returns a :ref:`metadata object <config_http_filters_lua_metadata_wrapper>`.

requestInfo()
^^^^^^^^^^^^^

Returns a :ref:`request info object <config_http_filters_lua_request_info_wrapper>`.

.. _config_http_filters_lua_header_wrapper:

Header object API
Expand Down Expand Up @@ -403,3 +408,52 @@ __pairs()

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

.. _config_http_filters_lua_request_info_wrapper:

Request Info object API
-----------------------

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

Returns a :ref:`dynamicMetadata object <config_http_filters_lua_dynamic_metadata_wrapper>`.

.. _config_http_filters_lua_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.
2 changes: 2 additions & 0 deletions source/extensions/filters/common/lua/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ envoy_cc_library(
deps = [
":lua_lib",
"//include/envoy/buffer:buffer_interface",
"//include/envoy/request_info:request_info_interface",
"//source/common/protobuf",
"//source/common/protobuf:utility_lib",
],
)
89 changes: 85 additions & 4 deletions source/extensions/filters/common/lua/wrappers.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "extensions/filters/common/lua/wrappers.h"

#include "common/protobuf/utility.h"

namespace Envoy {
namespace Extensions {
namespace Filters {
Expand All @@ -26,7 +28,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 +78,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 +100,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 +113,7 @@ int MetadataMapWrapper::luaGet(lua_State* state) {
return 0;
}

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

Expand All @@ -125,6 +127,85 @@ int MetadataMapWrapper::luaPairs(lua_State* state) {
return 1;
}

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

int RequestInfoWrapper::luaProtocol(lua_State* state) {
switch (request_info_.protocol().value()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I have not looked at the entire code. But one quick comment here, this whole switch can be replaced with this method call

Copy link
Member Author

Choose a reason for hiding this comment

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

Gotcha! Thanks @ramaraochavali!

case Http::Protocol::Http10:
lua_pushstring(state, "HTTP10");
break;
case Http::Protocol::Http11:
lua_pushstring(state, "HTTP11");
break;
case Http::Protocol::Http2:
lua_pushstring(state, "HTTP2");
break;
default:
lua_pushstring(state, "UNKNOWN");
}

return 1;
}

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

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

lua_pushstring(state, current_->first.c_str());
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 = request_info_.dynamicMetadata().filter_metadata();
const auto filter_it = metadata.find(filter_name);
if (filter_it == metadata.end()) {
return 0;
}

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

int DynamicMetadataMapWrapper::luaSet(lua_State* state) {
// 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);
request_info_.setDynamicMetadata(filter_name, MessageUtil::keyValueStruct(key, value));
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;
}

int ConnectionWrapper::luaSecure(lua_State* state) {
lua_pushboolean(state, connection_->ssl() != nullptr);
return 1;
}

} // namespace Lua
} // namespace Common
} // namespace Filters
Expand Down
120 changes: 116 additions & 4 deletions source/extensions/filters/common/lua/wrappers.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include "envoy/buffer/buffer.h"
#include "envoy/request_info/request_info.h"

#include "common/protobuf/protobuf.h"

Expand Down Expand Up @@ -42,6 +43,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,16 +97,120 @@ 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_;

friend class MetadataMapIterator;
};

class DynamicMetadataMapWrapper;

/**
* Iterator over a dynamic metadata map.
*/
class DynamicMetadataMapIterator : public 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_;
};

class DynamicMetadataMapWrapper : public BaseLuaObject<DynamicMetadataMapWrapper> {
public:
DynamicMetadataMapWrapper(RequestInfo::RequestInfo& request_info) : request_info_{request_info} {}

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();
}

RequestInfo::RequestInfo& request_info_;
LuaDeathRef<DynamicMetadataMapIterator> iterator_;

friend class DynamicMetadataMapIterator;
};

class RequestInfoWrapper : public BaseLuaObject<RequestInfoWrapper> {
public:
RequestInfoWrapper(RequestInfo::RequestInfo& request_info) : request_info_{request_info} {}
static ExportedFunctions exportedFunctions() {
return {{"dynamicMetadata", static_luaDynamicMetadata}, {"protocol", static_luaProtocol}};
}

private:
/**
* Get a dynamic metadata value from the map.
* @return value if found or nil.
*/
DECLARE_LUA_FUNCTION(RequestInfoWrapper, luaDynamicMetadata);

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

// Envoy::Lua::BaseLuaObject
void onMarkDead() override {
// TODO(dio): Check if it is required to always reset in here.
metadata_wrapper_.reset();
}

LuaDeathRef<DynamicMetadataMapWrapper> metadata_wrapper_;
RequestInfo::RequestInfo& request_info_;
};

typedef std::shared_ptr<const Network::Connection> NetworkConnectionSharedPtr;

class ConnectionWrapper : public BaseLuaObject<ConnectionWrapper> {
public:
ConnectionWrapper(const Network::Connection* connection) : connection_{connection} {}
static ExportedFunctions exportedFunctions() { return {{"secure", static_luaSecure}}; }

private:
/**
* Check if the connection is secured or not.
* @return boolean true if secure and false if not.
*/
DECLARE_LUA_FUNCTION(ConnectionWrapper, luaSecure);

const NetworkConnectionSharedPtr connection_;
};

} // namespace Lua
} // namespace Common
} // namespace Filters
Expand Down
26 changes: 26 additions & 0 deletions source/extensions/filters/http/lua/lua_filter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,28 @@ int StreamHandleWrapper::luaMetadata(lua_State* state) {
return 1;
}

int StreamHandleWrapper::luaRequestInfo(lua_State* state) {
ASSERT(state_ == State::Running);
if (request_info_wrapper_.get() != nullptr) {
request_info_wrapper_.pushStack();
} else {
request_info_wrapper_.reset(
Filters::Common::Lua::RequestInfoWrapper::create(state, callbacks_.requestInfo()), true);
}
return 1;
}

int StreamHandleWrapper::luaConnection(lua_State* state) {
ASSERT(state_ == State::Running);
if (connection_wrapper_.get() != nullptr) {
connection_wrapper_.pushStack();
} else {
connection_wrapper_.reset(
Filters::Common::Lua::ConnectionWrapper::create(state, callbacks_.connection()), true);
}
return 1;
}

int StreamHandleWrapper::luaLogTrace(lua_State* state) {
const char* message = luaL_checkstring(state, 2);
filter_.scriptLog(spdlog::level::trace, message);
Expand Down Expand Up @@ -409,6 +431,10 @@ FilterConfig::FilterConfig(const std::string& lua_code, ThreadLocal::SlotAllocat
lua_state_.registerType<Filters::Common::Lua::BufferWrapper>();
lua_state_.registerType<Filters::Common::Lua::MetadataMapWrapper>();
lua_state_.registerType<Filters::Common::Lua::MetadataMapIterator>();
lua_state_.registerType<Filters::Common::Lua::DynamicMetadataMapWrapper>();
lua_state_.registerType<Filters::Common::Lua::DynamicMetadataMapIterator>();
lua_state_.registerType<Filters::Common::Lua::RequestInfoWrapper>();
lua_state_.registerType<Filters::Common::Lua::ConnectionWrapper>();
lua_state_.registerType<HeaderMapWrapper>();
lua_state_.registerType<HeaderMapIterator>();
lua_state_.registerType<StreamHandleWrapper>();
Expand Down
Loading