-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
Adding MultiActions for composite filters. WIP #26251
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,77 +10,83 @@ namespace HttpFilters { | |
namespace Composite { | ||
|
||
namespace { | ||
// Helper that returns the last `filter->func(args...)` result in the list of filters, if the list | ||
// is empty, return `rval`. | ||
template <class FilterPtrT, class FuncT, class RValT, class... Args> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm wondering at the C++ code level why we need to distinguish single action vs. multiple actions with templates etc. Why not have the core implementation support multiple action, and when we instantiation the single action extension it sets N=1? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When I added this originally I looked into supporting delegating to multiple filters, but found that in order to preserve the existing filter API behavior you need to manage pausing iteration which blows up the complexity of this. Maybe now with the FilterManager being extracted out of the HCM this is something we can do, though I suspect it will make this much more complicated than it is right now There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @htuch this had occurred to me when implementing the vectorized version of this class. I think that if we can define the type of behavior to expect when iterating over multiple filters, i.e. some default behavior. We can add upon that in the future and create more complex logic if necessary. Something I can discuss with @tyxia. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah. Not suggesting we do the full thing that @snowp points out, but what I'm suggesting is to simplify your existing implementation. You have some template and conditional logic on whether this is a single or multi action. Why not just have it always the second case with N=1? |
||
RValT delegateAllFilterActionOr(std::vector<FilterPtrT>& filter_list, FuncT func, RValT rval, | ||
Args&&... args) { | ||
RValT value = rval; | ||
for (auto filter = filter_list.rbegin(); filter != filter_list.rend(); ++filter) { | ||
value = ((**filter).*func)(std::forward<Args>(args)...); | ||
} | ||
return value; | ||
} | ||
// Helper that returns `filter->func(args...)` if the filter is not null, returning `rval` | ||
// otherwise. | ||
template <class FilterPtrT, class FuncT, class RValT, class... Args> | ||
RValT delegateFilterActionOr(FilterPtrT& filter, FuncT func, RValT rval, Args&&... args) { | ||
if (filter) { | ||
return ((*filter).*func)(std::forward<Args>(args)...); | ||
} | ||
|
||
return rval; | ||
} | ||
} // namespace | ||
Http::FilterHeadersStatus Filter::decodeHeaders(Http::RequestHeaderMap& headers, bool end_stream) { | ||
decoded_headers_ = true; | ||
|
||
return delegateFilterActionOr(delegated_filter_, &StreamDecoderFilter::decodeHeaders, | ||
Http::FilterHeadersStatus::Continue, headers, end_stream); | ||
return delegateAllFilterActionOr(delegated_filter_list_, &StreamDecoderFilter::decodeHeaders, | ||
Http::FilterHeadersStatus::Continue, headers, end_stream); | ||
} | ||
|
||
Http::FilterDataStatus Filter::decodeData(Buffer::Instance& data, bool end_stream) { | ||
return delegateFilterActionOr(delegated_filter_, &StreamDecoderFilter::decodeData, | ||
Http::FilterDataStatus::Continue, data, end_stream); | ||
return delegateAllFilterActionOr(delegated_filter_list_, &StreamDecoderFilter::decodeData, | ||
Http::FilterDataStatus::Continue, data, end_stream); | ||
} | ||
|
||
Http::FilterTrailersStatus Filter::decodeTrailers(Http::RequestTrailerMap& trailers) { | ||
return delegateFilterActionOr(delegated_filter_, &StreamDecoderFilter::decodeTrailers, | ||
Http::FilterTrailersStatus::Continue, trailers); | ||
return delegateAllFilterActionOr(delegated_filter_list_, &StreamDecoderFilter::decodeTrailers, | ||
Http::FilterTrailersStatus::Continue, trailers); | ||
} | ||
|
||
Http::FilterMetadataStatus Filter::decodeMetadata(Http::MetadataMap& metadata_map) { | ||
return delegateFilterActionOr(delegated_filter_, &StreamDecoderFilter::decodeMetadata, | ||
Http::FilterMetadataStatus::Continue, metadata_map); | ||
return delegateAllFilterActionOr(delegated_filter_list_, &StreamDecoderFilter::decodeMetadata, | ||
Http::FilterMetadataStatus::Continue, metadata_map); | ||
} | ||
|
||
void Filter::decodeComplete() { | ||
if (delegated_filter_) { | ||
delegated_filter_->decodeComplete(); | ||
for (auto delegated_filter : delegated_filter_list_) { | ||
delegated_filter->decodeComplete(); | ||
} | ||
} | ||
|
||
Http::Filter1xxHeadersStatus Filter::encode1xxHeaders(Http::ResponseHeaderMap& headers) { | ||
return delegateFilterActionOr(delegated_filter_, &StreamEncoderFilter::encode1xxHeaders, | ||
Http::Filter1xxHeadersStatus::Continue, headers); | ||
return delegateAllFilterActionOr(delegated_filter_list_, &StreamEncoderFilter::encode1xxHeaders, | ||
Http::Filter1xxHeadersStatus::Continue, headers); | ||
} | ||
|
||
Http::FilterHeadersStatus Filter::encodeHeaders(Http::ResponseHeaderMap& headers, bool end_stream) { | ||
return delegateFilterActionOr(delegated_filter_, &StreamEncoderFilter::encodeHeaders, | ||
Http::FilterHeadersStatus::Continue, headers, end_stream); | ||
return delegateAllFilterActionOr(delegated_filter_list_, &StreamEncoderFilter::encodeHeaders, | ||
Http::FilterHeadersStatus::Continue, headers, end_stream); | ||
} | ||
Http::FilterDataStatus Filter::encodeData(Buffer::Instance& data, bool end_stream) { | ||
return delegateFilterActionOr(delegated_filter_, &StreamEncoderFilter::encodeData, | ||
Http::FilterDataStatus::Continue, data, end_stream); | ||
return delegateAllFilterActionOr(delegated_filter_list_, &StreamEncoderFilter::encodeData, | ||
Http::FilterDataStatus::Continue, data, end_stream); | ||
} | ||
Http::FilterTrailersStatus Filter::encodeTrailers(Http::ResponseTrailerMap& trailers) { | ||
return delegateFilterActionOr(delegated_filter_, &StreamEncoderFilter::encodeTrailers, | ||
Http::FilterTrailersStatus::Continue, trailers); | ||
return delegateAllFilterActionOr(delegated_filter_list_, &StreamEncoderFilter::encodeTrailers, | ||
Http::FilterTrailersStatus::Continue, trailers); | ||
} | ||
Http::FilterMetadataStatus Filter::encodeMetadata(Http::MetadataMap& metadata_map) { | ||
return delegateFilterActionOr(delegated_filter_, &StreamEncoderFilter::encodeMetadata, | ||
Http::FilterMetadataStatus::Continue, metadata_map); | ||
return delegateAllFilterActionOr(delegated_filter_list_, &StreamEncoderFilter::encodeMetadata, | ||
Http::FilterMetadataStatus::Continue, metadata_map); | ||
} | ||
void Filter::encodeComplete() { | ||
if (delegated_filter_) { | ||
delegated_filter_->encodeComplete(); | ||
for (auto delegated_filter : delegated_filter_list_) { | ||
delegated_filter->encodeComplete(); | ||
} | ||
} | ||
void Filter::onMatchCallback(const Matcher::Action& action) { | ||
const auto& composite_action = action.getTyped<ExecuteFilterAction>(); | ||
|
||
FactoryCallbacksWrapper wrapper(*this, dispatcher_); | ||
composite_action.createFilters(wrapper); | ||
|
||
void Filter::ApplyWrapper(FactoryCallbacksWrapper& wrapper) { | ||
if (!wrapper.errors_.empty()) { | ||
stats_.filter_delegation_error_.inc(); | ||
ENVOY_LOG(debug, "failed to create delegated filter {}", | ||
|
@@ -91,19 +97,21 @@ void Filter::onMatchCallback(const Matcher::Action& action) { | |
|
||
if (wrapper.filter_to_inject_) { | ||
stats_.filter_delegation_success_.inc(); | ||
Http::StreamFilterSharedPtr delegated_filter; | ||
if (absl::holds_alternative<Http::StreamDecoderFilterSharedPtr>(*wrapper.filter_to_inject_)) { | ||
delegated_filter_ = std::make_shared<StreamFilterWrapper>( | ||
delegated_filter = std::make_shared<StreamFilterWrapper>( | ||
absl::get<Http::StreamDecoderFilterSharedPtr>(*wrapper.filter_to_inject_)); | ||
} else if (absl::holds_alternative<Http::StreamEncoderFilterSharedPtr>( | ||
*wrapper.filter_to_inject_)) { | ||
delegated_filter_ = std::make_shared<StreamFilterWrapper>( | ||
delegated_filter = std::make_shared<StreamFilterWrapper>( | ||
absl::get<Http::StreamEncoderFilterSharedPtr>(*wrapper.filter_to_inject_)); | ||
} else { | ||
delegated_filter_ = absl::get<Http::StreamFilterSharedPtr>(*wrapper.filter_to_inject_); | ||
delegated_filter = absl::get<Http::StreamFilterSharedPtr>(*wrapper.filter_to_inject_); | ||
} | ||
|
||
delegated_filter_->setDecoderFilterCallbacks(*decoder_callbacks_); | ||
delegated_filter_->setEncoderFilterCallbacks(*encoder_callbacks_); | ||
delegated_filter->setDecoderFilterCallbacks(*decoder_callbacks_); | ||
delegated_filter->setEncoderFilterCallbacks(*encoder_callbacks_); | ||
delegated_filter_list_.push_back(delegated_filter); | ||
|
||
// Size should be small, so a copy should be fine. | ||
access_loggers_.insert(access_loggers_.end(), wrapper.access_loggers_.begin(), | ||
|
@@ -114,6 +122,23 @@ void Filter::onMatchCallback(const Matcher::Action& action) { | |
// either directly or via some return status. | ||
} | ||
|
||
void Filter::onMatchCallback(const Matcher::Action& action) { | ||
if (action.typeUrl() == ExecuteFilterAction::staticTypeUrl()) { | ||
const auto& composite_action = action.getTyped<ExecuteFilterAction>(); | ||
FactoryCallbacksWrapper wrapper(*this, dispatcher_); | ||
composite_action.createFilters(wrapper); | ||
ApplyWrapper(wrapper); | ||
} else if (action.typeUrl() == ExecuteFilterMultiAction::staticTypeUrl()) { | ||
const auto& composite_multi_action = action.getTyped<ExecuteFilterMultiAction>(); | ||
std::function<void(Http::FilterFactoryCb&)> parse_wrapper = [this](Http::FilterFactoryCb& cb) { | ||
FactoryCallbacksWrapper wrapper(*this, dispatcher_); | ||
cb(wrapper); | ||
ApplyWrapper(wrapper); | ||
}; | ||
composite_multi_action.createFilters(parse_wrapper); | ||
} | ||
} | ||
|
||
Http::FilterHeadersStatus | ||
Filter::StreamFilterWrapper::decodeHeaders(Http::RequestHeaderMap& headers, bool end_stream) { | ||
return delegateFilterActionOr(decoder_filter_, &StreamDecoderFilter::decodeHeaders, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel we should have some better documentation here explaining what this is, why you would use it, how etc.