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
26 changes: 26 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,17 @@ 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()
^^^^^^^^^^^^^

.. code-block:: lua

requestInfo = handle:requestInfo()

Returns :repo:`information <include/request_info/request_info.h>` related to the current request.

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 +414,18 @@ __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
-----------------------

protocol()
^^^^^^^^^^

.. code-block:: lua

requestInfo: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*.
1 change: 1 addition & 0 deletions docs/root/intro/version_history.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Version history
1.8.0 (Pending)
===============
* http: response filters not applied to early error paths such as http_parser generated 400s.
* lua: added :ref:`requestInfo() <config_http_filters_lua_request_info_wrapper>` wrapper and *protocol()* API.
* 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.
Envoy can use either proto to send client requests to a ratelimit server with the use of the
Expand Down
2 changes: 2 additions & 0 deletions source/extensions/filters/http/lua/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ envoy_cc_library(
hdrs = ["wrappers.h"],
deps = [
"//include/envoy/http:header_map_interface",
"//include/envoy/request_info:request_info_interface",
"//source/common/http:utility_lib",
"//source/extensions/filters/common/lua:lua_lib",
],
)
Expand Down
11 changes: 11 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,16 @@ 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(RequestInfoWrapper::create(state, callbacks_.requestInfo()), 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 @@ -411,6 +421,7 @@ FilterConfig::FilterConfig(const std::string& lua_code, ThreadLocal::SlotAllocat
lua_state_.registerType<Filters::Common::Lua::MetadataMapIterator>();
lua_state_.registerType<HeaderMapWrapper>();
lua_state_.registerType<HeaderMapIterator>();
lua_state_.registerType<RequestInfoWrapper>();
lua_state_.registerType<StreamHandleWrapper>();

request_function_slot_ = lua_state_.registerGlobal("envoy_on_request");
Expand Down
17 changes: 16 additions & 1 deletion source/extensions/filters/http/lua/lua_filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ class FilterCallbacks {
* route entry.
*/
virtual const ProtobufWkt::Struct& metadata() const PURE;

/**
* @return RequestInfo::RequestInfo& the current request info handle. This handle is mutable to
* accomodate write API e.g. setDynamicMetadata().
*/
virtual RequestInfo::RequestInfo& requestInfo() PURE;
};

class Filter;
Expand Down Expand Up @@ -122,7 +128,7 @@ class StreamHandleWrapper : public Filters::Common::Lua::BaseLuaObject<StreamHan
{"logDebug", static_luaLogDebug}, {"logInfo", static_luaLogInfo},
{"logWarn", static_luaLogWarn}, {"logErr", static_luaLogErr},
{"logCritical", static_luaLogCritical}, {"httpCall", static_luaHttpCall},
{"respond", static_luaRespond}};
{"respond", static_luaRespond}, {"requestInfo", static_luaRequestInfo}};
}

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

/**
* @return a handle to the request info.
*/
DECLARE_LUA_FUNCTION(StreamHandleWrapper, luaRequestInfo);

/**
* Log a message to the Envoy log.
* @param 1 (string): The log message.
Expand All @@ -202,6 +213,7 @@ class StreamHandleWrapper : public Filters::Common::Lua::BaseLuaObject<StreamHan
body_wrapper_.reset();
trailers_wrapper_.reset();
metadata_wrapper_.reset();
request_info_wrapper_.reset();
}

// Http::AsyncClient::Callbacks
Expand All @@ -220,6 +232,7 @@ class StreamHandleWrapper : public Filters::Common::Lua::BaseLuaObject<StreamHan
Filters::Common::Lua::LuaDeathRef<HeaderMapWrapper> headers_wrapper_;
Filters::Common::Lua::LuaDeathRef<Filters::Common::Lua::BufferWrapper> body_wrapper_;
Filters::Common::Lua::LuaDeathRef<HeaderMapWrapper> trailers_wrapper_;
Filters::Common::Lua::LuaDeathRef<RequestInfoWrapper> request_info_wrapper_;
Filters::Common::Lua::LuaDeathRef<Filters::Common::Lua::MetadataMapWrapper> metadata_wrapper_;
State state_{State::Running};
std::function<void()> yield_callback_;
Expand Down Expand Up @@ -310,6 +323,7 @@ class Filter : public Http::StreamFilter, Logger::Loggable<Logger::Id::lua> {
void respond(Http::HeaderMapPtr&& headers, Buffer::Instance* body, lua_State* state) override;

const ProtobufWkt::Struct& metadata() const override { return getMetadata(callbacks_); }
RequestInfo::RequestInfo& requestInfo() override { return callbacks_->requestInfo(); }

Filter& parent_;
Http::StreamDecoderFilterCallbacks* callbacks_{};
Expand All @@ -328,6 +342,7 @@ class Filter : public Http::StreamFilter, Logger::Loggable<Logger::Id::lua> {
void respond(Http::HeaderMapPtr&& headers, Buffer::Instance* body, lua_State* state) override;

const ProtobufWkt::Struct& metadata() const override { return getMetadata(callbacks_); }
RequestInfo::RequestInfo& requestInfo() override { return callbacks_->requestInfo(); }

Filter& parent_;
Http::StreamEncoderFilterCallbacks* callbacks_{};
Expand Down
7 changes: 7 additions & 0 deletions source/extensions/filters/http/lua/wrappers.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "extensions/filters/http/lua/wrappers.h"

#include "common/http/utility.h"

namespace Envoy {
namespace Extensions {
namespace HttpFilters {
Expand Down Expand Up @@ -100,6 +102,11 @@ void HeaderMapWrapper::checkModifiable(lua_State* state) {
}
}

int RequestInfoWrapper::luaProtocol(lua_State* state) {
lua_pushstring(state, Http::Utility::getProtocolString(request_info_.protocol().value()).c_str());
return 1;
}

} // namespace Lua
} // namespace HttpFilters
} // namespace Extensions
Expand Down
18 changes: 18 additions & 0 deletions source/extensions/filters/http/lua/wrappers.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include "envoy/http/header_map.h"
#include "envoy/request_info/request_info.h"

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

Expand Down Expand Up @@ -98,6 +99,23 @@ class HeaderMapWrapper : public Filters::Common::Lua::BaseLuaObject<HeaderMapWra
friend class HeaderMapIterator;
};

/**
* 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}}; }

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

} // namespace Lua
} // namespace HttpFilters
} // namespace Extensions
Expand Down
1 change: 1 addition & 0 deletions test/extensions/filters/http/lua/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ envoy_extension_cc_test(
deps = [
"//source/extensions/filters/http/lua:wrappers_lib",
"//test/extensions/filters/common/lua:lua_wrappers_lib",
"//test/mocks/request_info:request_info_mocks",
"//test/test_common:utility_lib",
],
)
Expand Down
22 changes: 22 additions & 0 deletions test/extensions/filters/http/lua/lua_filter_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ using testing::AtLeast;
using testing::InSequence;
using testing::Invoke;
using testing::Return;
using testing::ReturnPointee;
using testing::ReturnRef;
using testing::StrEq;
using testing::_;

Expand Down Expand Up @@ -79,6 +81,7 @@ class LuaHttpFilterTest : public testing::Test {
Http::MockStreamDecoderFilterCallbacks decoder_callbacks_;
Http::MockStreamEncoderFilterCallbacks encoder_callbacks_;
envoy::api::v2::core::Metadata metadata_;
NiceMock<Envoy::RequestInfo::MockRequestInfo> request_info_;

const std::string HEADER_ONLY_SCRIPT{R"EOF(
function envoy_on_request(request_handle)
Expand Down Expand Up @@ -1460,6 +1463,25 @@ TEST_F(LuaHttpFilterTest, GetMetadataFromHandleNoLuaMetadata) {
EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers, true));
}

// No available Lua metadata on route.
TEST_F(LuaHttpFilterTest, GetCurrentProtocol) {
const std::string SCRIPT{R"EOF(
function envoy_on_request(request_handle)
request_handle:logTrace(request_handle:requestInfo():protocol())
end
)EOF"};

InSequence s;
setup(SCRIPT);

EXPECT_CALL(decoder_callbacks_, requestInfo()).WillOnce(ReturnRef(request_info_));
EXPECT_CALL(request_info_, protocol()).WillOnce(Return(Http::Protocol::Http11));

Http::TestHeaderMapImpl request_headers{{":path", "/"}};
EXPECT_CALL(*filter_, scriptLog(spdlog::level::trace, StrEq("HTTP/1.1")));
EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers, true));
}

} // namespace Lua
} // namespace HttpFilters
} // namespace Extensions
Expand Down
8 changes: 8 additions & 0 deletions test/extensions/filters/http/lua/lua_integration_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ name: envoy.lua
request_handle:headers():add("request_body_size", body_length)
request_handle:headers():add("request_metadata_foo", metadata["foo"])
request_handle:headers():add("request_metadata_baz", metadata["baz"])
request_handle:headers():add("request_protocol", request_handle:requestInfo():protocol())
end

function envoy_on_response(response_handle)
Expand All @@ -117,6 +118,7 @@ name: envoy.lua
response_handle:headers():add("response_metadata_foo", metadata["foo"])
response_handle:headers():add("response_metadata_baz", metadata["baz"])
response_handle:headers():add("response_body_size", body_length)
response_handle:headers():add("request_protocol", response_handle:requestInfo():protocol())
response_handle:headers():remove("foo")
end
)EOF";
Expand Down Expand Up @@ -153,6 +155,10 @@ name: envoy.lua
->value()
.c_str());

EXPECT_STREQ(
"HTTP/1.1",
upstream_request_->headers().get(Http::LowerCaseString("request_protocol"))->value().c_str());

Http::TestHeaderMapImpl response_headers{{":status", "200"}, {"foo", "bar"}};
upstream_request_->encodeHeaders(response_headers, false);
Buffer::OwnedImpl response_data1("good");
Expand All @@ -170,6 +176,8 @@ name: envoy.lua
EXPECT_STREQ(
"bat",
response->headers().get(Http::LowerCaseString("response_metadata_baz"))->value().c_str());
EXPECT_STREQ("HTTP/1.1",
response->headers().get(Http::LowerCaseString("request_protocol"))->value().c_str());
EXPECT_EQ(nullptr, response->headers().get(Http::LowerCaseString("foo")));

cleanup();
Expand Down
40 changes: 40 additions & 0 deletions test/extensions/filters/http/lua/wrappers_test.cc
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
#include "common/http/utility.h"

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

#include "test/extensions/filters/common/lua/lua_wrappers.h"
#include "test/mocks/request_info/mocks.h"
#include "test/test_common/utility.h"

using testing::InSequence;
using testing::ReturnPointee;

namespace Envoy {
namespace Extensions {
Expand Down Expand Up @@ -210,6 +214,42 @@ TEST_F(LuaHeaderMapWrapperTest, IteratorAcrossYield) {
"[string \"...\"]:5: object used outside of proper scope");
}

class LuaRequestInfoWrapperTest
: public Filters::Common::Lua::LuaWrappersTestBase<RequestInfoWrapper> {
public:
virtual void setup(const std::string& script) {
Filters::Common::Lua::LuaWrappersTestBase<RequestInfoWrapper>::setup(script);
}

protected:
void expectToPrintCurrentProtocol(const absl::optional<Envoy::Http::Protocol>& protocol) {
const std::string SCRIPT{R"EOF(
function callMe(object)
testPrint(string.format("'%s'", object:protocol()))
end
)EOF"};

InSequence s;
setup(SCRIPT);

NiceMock<Envoy::RequestInfo::MockRequestInfo> request_info;
ON_CALL(request_info, protocol()).WillByDefault(ReturnPointee(&protocol));
Filters::Common::Lua::LuaDeathRef<RequestInfoWrapper> wrapper(
RequestInfoWrapper::create(coroutine_->luaState(), request_info), true);
EXPECT_CALL(*this,
testPrint(fmt::format("'{}'", Http::Utility::getProtocolString(protocol.value()))));
start("callMe");
wrapper.reset();
}
};

// Return the current request protocol.
TEST_F(LuaRequestInfoWrapperTest, ReturnCurrentProtocol) {
expectToPrintCurrentProtocol(Http::Protocol::Http10);
expectToPrintCurrentProtocol(Http::Protocol::Http11);
expectToPrintCurrentProtocol(Http::Protocol::Http2);
}

} // namespace Lua
} // namespace HttpFilters
} // namespace Extensions
Expand Down