Skip to content

Commit 7e8f7fa

Browse files
wbpcodeggreenway
andauthored
lua: filter context implementation (envoyproxy#39263)
Commit Message: lua: filter context implementation Additional Description: Risk Level: low. Testing: unit. Docs Changes: added. Release Notes: added. Platform Specific Features: n/a. --------- Signed-off-by: wangbaiping(wbpcode) <[email protected]> Signed-off-by: code <[email protected]> Co-authored-by: Greg Greenway <[email protected]>
1 parent cfce55d commit 7e8f7fa

File tree

6 files changed

+225
-46
lines changed

6 files changed

+225
-46
lines changed

api/envoy/extensions/filters/http/lua/v3/lua.proto

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,6 @@ message Lua {
9292

9393
message LuaPerRoute {
9494
oneof override {
95-
option (validate.required) = true;
96-
9795
// Disable the Lua filter for this particular vhost or route. If disabled is specified in
9896
// multiple per-filter-configs, the most specific one will be used.
9997
bool disabled = 1 [(validate.rules).bool = {const: true}];
@@ -111,12 +109,12 @@ message LuaPerRoute {
111109
// For example:
112110
//
113111
// .. code-block:: lua
112+
//
114113
// function envoy_on_request(request_handle)
115114
// local filter_context = request_handle:filterContext()
116115
// local filter_context_value = filter_context["key"]
117116
// -- Do something with filter_context_value.
118117
// end
119118
//
120-
// [#not-implemented-hide:]
121119
google.protobuf.Struct filter_context = 4;
122120
}

changelogs/current.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ removed_config_or_runtime:
8080
Removed runtime guard ``envoy.reloadable_features.lua_flow_control_while_http_call`` and legacy code paths.
8181
8282
new_features:
83+
- area: lua
84+
change: |
85+
Added support for accessing filter context.
86+
See :ref:`filterContext() <config_http_filters_lua_stream_handle_api_filter_context>` for more details.
8387
- area: resource_monitors
8488
change: |
8589
added new cgroup memory resource monitor that reads memory usage/limit from cgroup v1/v2 subsystems and calculates

docs/root/configuration/http/http_filters/lua_filter.rst

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,40 @@ Example:
582582
request_handle:clearRouteCache()
583583
end
584584
585+
.. _config_http_filters_lua_stream_handle_api_filter_context:
586+
587+
``filterContext()``
588+
^^^^^^^^^^^^^^^^^^^
589+
590+
.. code-block:: lua
591+
592+
local filter_context = handle:filterContext()
593+
594+
Returns the filter context that configured in the the
595+
:ref:`filter_context <envoy_v3_api_field_extensions.filters.http.lua.v3.LuaPerRoute.filter_context>`.
596+
597+
For example, given the following filter context in the route entry:
598+
599+
.. code-block:: yaml
600+
601+
typed_per_filter_config:
602+
"lua-filter-name":
603+
"@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.LuaPerRoute
604+
filter_context:
605+
key: xxxxxx
606+
607+
The filter context can be accessed in the related Lua script as follows:
608+
609+
.. code-block:: lua
610+
611+
function envoy_on_request(request_handle)
612+
-- Get the filter context
613+
local filter_context = request_handle:filterContext()
614+
615+
-- Access the filter context data
616+
local value = filter_context["key"]
617+
end
618+
585619
``importPublicKey()``
586620
^^^^^^^^^^^^^^^^^^^^^
587621

source/extensions/filters/http/lua/lua_filter.cc

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -822,14 +822,16 @@ FilterConfig::FilterConfig(const envoy::extensions::filters::http::lua::v3::Lua&
822822
FilterConfigPerRoute::FilterConfigPerRoute(
823823
const envoy::extensions::filters::http::lua::v3::LuaPerRoute& config,
824824
Server::Configuration::ServerFactoryContext& context)
825-
: disabled_(config.disabled()), name_(config.name()) {
825+
: disabled_(config.disabled()), name_(config.name()), filter_context_(config.filter_context()) {
826826
if (disabled_ || !name_.empty()) {
827-
return;
827+
return; // Filter is disabled or explicit script name is provided.
828+
}
829+
if (config.has_source_code()) {
830+
// Read and parse the inline Lua code defined in the route configuration.
831+
const std::string code_str = THROW_OR_RETURN_VALUE(
832+
Config::DataSource::read(config.source_code(), true, context.api()), std::string);
833+
per_lua_code_setup_ptr_ = std::make_unique<PerLuaCodeSetup>(code_str, context.threadLocal());
828834
}
829-
// Read and parse the inline Lua code defined in the route configuration.
830-
const std::string code_str = THROW_OR_RETURN_VALUE(
831-
Config::DataSource::read(config.source_code(), true, context.api()), std::string);
832-
per_lua_code_setup_ptr_ = std::make_unique<PerLuaCodeSetup>(code_str, context.threadLocal());
833835
}
834836

835837
void Filter::onDestroy() {
@@ -933,6 +935,17 @@ int StreamHandleWrapper::luaClearRouteCache(lua_State*) {
933935
return 0;
934936
}
935937

938+
int StreamHandleWrapper::luaFilterContext(lua_State* state) {
939+
ASSERT(state_ == State::Running);
940+
if (filter_context_wrapper_.get() != nullptr) {
941+
filter_context_wrapper_.pushStack();
942+
} else {
943+
filter_context_wrapper_.reset(
944+
Filters::Common::Lua::MetadataMapWrapper::create(state, callbacks_.filterContext()), true);
945+
}
946+
return 1;
947+
}
948+
936949
void Filter::DecoderCallbacks::respond(Http::ResponseHeaderMapPtr&& headers, Buffer::Instance* body,
937950
lua_State*) {
938951
uint64_t status = Http::Utility::getResponseStatus(*headers);

source/extensions/filters/http/lua/lua_filter.h

Lines changed: 61 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,13 @@ class FilterCallbacks {
122122
* Clear the route cache explicitly.
123123
*/
124124
virtual void clearRouteCache() PURE;
125+
126+
/**
127+
* @return const ProtobufWkt::Struct& the filter context from the most specific filter config
128+
* from the route or virtual host. Empty struct will be returned if no route or virtual host is
129+
* found.
130+
*/
131+
virtual const ProtobufWkt::Struct& filterContext() const PURE;
125132
};
126133

127134
class Filter;
@@ -194,7 +201,8 @@ class StreamHandleWrapper : public Filters::Common::Lua::BaseLuaObject<StreamHan
194201
{"timestampString", static_luaTimestampString},
195202
{"connectionStreamInfo", static_luaConnectionStreamInfo},
196203
{"setUpstreamOverrideHost", static_luaSetUpstreamOverrideHost},
197-
{"clearRouteCache", static_luaClearRouteCache}};
204+
{"clearRouteCache", static_luaClearRouteCache},
205+
{"filterContext", static_luaFilterContext}};
198206
}
199207

200208
private:
@@ -326,6 +334,11 @@ class StreamHandleWrapper : public Filters::Common::Lua::BaseLuaObject<StreamHan
326334
*/
327335
DECLARE_LUA_FUNCTION(StreamHandleWrapper, luaClearRouteCache);
328336

337+
/**
338+
* Get the filter context.
339+
*/
340+
DECLARE_LUA_FUNCTION(StreamHandleWrapper, luaFilterContext);
341+
329342
enum Timestamp::Resolution getTimestampResolution(absl::string_view unit_parameter);
330343

331344
int doHttpCall(lua_State* state, const HttpCallOptions& options);
@@ -345,6 +358,7 @@ class StreamHandleWrapper : public Filters::Common::Lua::BaseLuaObject<StreamHan
345358
body_wrapper_.reset();
346359
trailers_wrapper_.reset();
347360
metadata_wrapper_.reset();
361+
filter_context_wrapper_.reset();
348362
stream_info_wrapper_.reset();
349363
connection_wrapper_.reset();
350364
public_key_wrapper_.reset();
@@ -372,6 +386,8 @@ class StreamHandleWrapper : public Filters::Common::Lua::BaseLuaObject<StreamHan
372386
Filters::Common::Lua::LuaDeathRef<Filters::Common::Lua::BufferWrapper> body_wrapper_;
373387
Filters::Common::Lua::LuaDeathRef<HeaderMapWrapper> trailers_wrapper_;
374388
Filters::Common::Lua::LuaDeathRef<Filters::Common::Lua::MetadataMapWrapper> metadata_wrapper_;
389+
Filters::Common::Lua::LuaDeathRef<Filters::Common::Lua::MetadataMapWrapper>
390+
filter_context_wrapper_;
375391
Filters::Common::Lua::LuaDeathRef<StreamInfoWrapper> stream_info_wrapper_;
376392
Filters::Common::Lua::LuaDeathRef<ConnectionStreamInfoWrapper> connection_stream_info_wrapper_;
377393
Filters::Common::Lua::LuaDeathRef<Filters::Common::Lua::ConnectionWrapper> connection_wrapper_;
@@ -446,43 +462,17 @@ class FilterConfigPerRoute : public Router::RouteSpecificFilterConfig {
446462
Server::Configuration::ServerFactoryContext& context);
447463

448464
bool disabled() const { return disabled_; }
449-
const std::string& name() const { return name_; }
465+
absl::string_view name() const { return name_; }
450466
PerLuaCodeSetup* perLuaCodeSetup() const { return per_lua_code_setup_ptr_.get(); }
467+
const ProtobufWkt::Struct& filterContext() const { return filter_context_; }
451468

452469
private:
453470
const bool disabled_;
454471
const std::string name_;
455472
PerLuaCodeSetupPtr per_lua_code_setup_ptr_;
473+
const ProtobufWkt::Struct filter_context_;
456474
};
457475

458-
namespace {
459-
460-
PerLuaCodeSetup* getPerLuaCodeSetup(const FilterConfig* filter_config,
461-
Http::StreamFilterCallbacks* callbacks) {
462-
const FilterConfigPerRoute* config_per_route = nullptr;
463-
if (callbacks && callbacks->route()) {
464-
config_per_route =
465-
Http::Utility::resolveMostSpecificPerFilterConfig<FilterConfigPerRoute>(callbacks);
466-
}
467-
468-
if (config_per_route != nullptr) {
469-
if (config_per_route->disabled()) {
470-
return nullptr;
471-
}
472-
if (!config_per_route->name().empty()) {
473-
ASSERT(filter_config);
474-
return filter_config->perLuaCodeSetup(config_per_route->name());
475-
}
476-
return config_per_route->perLuaCodeSetup();
477-
}
478-
ASSERT(filter_config);
479-
return filter_config->perLuaCodeSetup();
480-
}
481-
482-
} // namespace
483-
484-
// TODO(mattklein123): Filter stats.
485-
486476
/**
487477
* The HTTP Lua filter. Allows scripts to run in both the request an response flow.
488478
*/
@@ -500,7 +490,7 @@ class Filter : public Http::StreamFilter, private Filters::Common::Lua::LuaLogga
500490
// Http::StreamDecoderFilter
501491
Http::FilterHeadersStatus decodeHeaders(Http::RequestHeaderMap& headers,
502492
bool end_stream) override {
503-
PerLuaCodeSetup* setup = getPerLuaCodeSetup(config_.get(), decoder_callbacks_.callbacks_);
493+
PerLuaCodeSetup* setup = getPerLuaCodeSetup();
504494
const int function_ref = setup ? setup->requestFunctionRef() : LUA_REFNIL;
505495
return doHeaders(request_stream_wrapper_, request_coroutine_, decoder_callbacks_, function_ref,
506496
setup, headers, end_stream);
@@ -521,7 +511,7 @@ class Filter : public Http::StreamFilter, private Filters::Common::Lua::LuaLogga
521511
}
522512
Http::FilterHeadersStatus encodeHeaders(Http::ResponseHeaderMap& headers,
523513
bool end_stream) override {
524-
PerLuaCodeSetup* setup = getPerLuaCodeSetup(config_.get(), decoder_callbacks_.callbacks_);
514+
PerLuaCodeSetup* setup = getPerLuaCodeSetup();
525515
const int function_ref = setup ? setup->responseFunctionRef() : LUA_REFNIL;
526516
return doHeaders(response_stream_wrapper_, response_coroutine_, encoder_callbacks_,
527517
function_ref, setup, headers, end_stream);
@@ -574,6 +564,7 @@ class Filter : public Http::StreamFilter, private Filters::Common::Lua::LuaLogga
574564
cb->clearRouteCache();
575565
}
576566
}
567+
const ProtobufWkt::Struct& filterContext() const override { return parent_.filterContext(); }
577568

578569
Filter& parent_;
579570
Http::StreamDecoderFilterCallbacks* callbacks_{};
@@ -602,13 +593,43 @@ class Filter : public Http::StreamFilter, private Filters::Common::Lua::LuaLogga
602593
UNREFERENCED_PARAMETER(host_and_strict);
603594
}
604595
void clearRouteCache() override {}
596+
const ProtobufWkt::Struct& filterContext() const override { return parent_.filterContext(); }
605597

606598
Filter& parent_;
607599
Http::StreamEncoderFilterCallbacks* callbacks_{};
608600
};
609601

610602
using StreamHandleRef = Filters::Common::Lua::LuaDeathRef<StreamHandleWrapper>;
611603

604+
PerLuaCodeSetup* getPerLuaCodeSetup() {
605+
if (decoder_callbacks_.callbacks_ != nullptr) {
606+
per_route_config_ = Http::Utility::resolveMostSpecificPerFilterConfig<FilterConfigPerRoute>(
607+
decoder_callbacks_.callbacks_);
608+
}
609+
610+
if (per_route_config_ != nullptr) {
611+
// The filter is disabled by the route configuration explicitly.
612+
if (per_route_config_->disabled()) {
613+
return nullptr;
614+
}
615+
// The filter should execute the script specified by the script name if exist.
616+
if (!per_route_config_->name().empty()) {
617+
return config_->perLuaCodeSetup(per_route_config_->name());
618+
}
619+
// The filter should execute the script specified by the inline code if exist.
620+
if (auto inline_code = per_route_config_->perLuaCodeSetup(); inline_code != nullptr) {
621+
return inline_code;
622+
}
623+
}
624+
625+
return config_->perLuaCodeSetup();
626+
}
627+
628+
const ProtobufWkt::Struct& filterContext() const {
629+
return per_route_config_ == nullptr ? ProtobufWkt::Struct::default_instance()
630+
: per_route_config_->filterContext();
631+
}
632+
612633
Http::FilterHeadersStatus doHeaders(StreamHandleRef& handle,
613634
Filters::Common::Lua::CoroutinePtr& coroutine,
614635
FilterCallbacks& callbacks, int function_ref,
@@ -618,11 +639,8 @@ class Filter : public Http::StreamFilter, private Filters::Common::Lua::LuaLogga
618639
Http::FilterTrailersStatus doTrailers(StreamHandleRef& handle, Http::HeaderMap& trailers);
619640

620641
FilterConfigConstSharedPtr config_;
621-
DecoderCallbacks decoder_callbacks_{*this};
622-
EncoderCallbacks encoder_callbacks_{*this};
623-
StreamHandleRef request_stream_wrapper_;
624-
StreamHandleRef response_stream_wrapper_;
625-
bool destroyed_{};
642+
const FilterConfigPerRoute* per_route_config_{};
643+
626644
TimeSource& time_source_;
627645
LuaFilterStats stats_;
628646

@@ -640,6 +658,12 @@ class Filter : public Http::StreamFilter, private Filters::Common::Lua::LuaLogga
640658
// seems like a safer fix for now.
641659
Filters::Common::Lua::CoroutinePtr request_coroutine_;
642660
Filters::Common::Lua::CoroutinePtr response_coroutine_;
661+
662+
DecoderCallbacks decoder_callbacks_{*this};
663+
EncoderCallbacks encoder_callbacks_{*this};
664+
StreamHandleRef request_stream_wrapper_;
665+
StreamHandleRef response_stream_wrapper_;
666+
bool destroyed_{};
643667
};
644668

645669
} // namespace Lua

0 commit comments

Comments
 (0)