-
Notifications
You must be signed in to change notification settings - Fork 5.5k
http: unified the filter chain creation in the FilterManager #37112
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
Changes from 2 commits
d28e7cf
b37f5b7
4a73cb7
64eb0f9
0308ca1
e13c9a9
dcd4bdf
2bd2592
f2958c5
b7c67b4
8e4b115
cd2e199
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 |
|---|---|---|
|
|
@@ -1036,7 +1036,7 @@ void DownstreamFilterManager::prepareLocalReplyViaFilterChain( | |
| // For early error handling, do a best-effort attempt to create a filter chain | ||
| // to ensure access logging. If the filter chain already exists this will be | ||
| // a no-op. | ||
| createFilterChain(); | ||
| createDownstreamFilterChain(); | ||
|
|
||
| if (prepared_local_reply_) { | ||
| return; | ||
|
|
@@ -1077,6 +1077,11 @@ void DownstreamFilterManager::executeLocalReplyIfPrepared() { | |
| Utility::encodeLocalReply(state_.destroyed_, std::move(prepared_local_reply_)); | ||
| } | ||
|
|
||
| bool DownstreamFilterManager::createDownstreamFilterChain() { | ||
| return createFilterChain(filter_chain_factory_, /*allow_upgrade_filter_chain=*/true, false) | ||
| .upgrade_rejected; | ||
| } | ||
|
|
||
| void DownstreamFilterManager::sendLocalReplyViaFilterChain( | ||
| bool is_grpc_request, Code code, absl::string_view body, | ||
| const std::function<void(ResponseHeaderMap& headers)>& modify_headers, bool is_head_request, | ||
|
|
@@ -1086,7 +1091,7 @@ void DownstreamFilterManager::sendLocalReplyViaFilterChain( | |
| // For early error handling, do a best-effort attempt to create a filter chain | ||
| // to ensure access logging. If the filter chain already exists this will be | ||
| // a no-op. | ||
| createFilterChain(); | ||
| createDownstreamFilterChain(); | ||
|
|
||
| Utility::sendLocalReply( | ||
| state_.destroyed_, | ||
|
|
@@ -1623,11 +1628,9 @@ void FilterManager::contextOnContinue(ScopeTrackedObjectStack& tracked_object_st | |
| tracked_object_stack.add(filter_manager_callbacks_.scope()); | ||
| } | ||
|
|
||
| bool FilterManager::createFilterChain() { | ||
| if (state_.created_filter_chain_) { | ||
| return false; | ||
| } | ||
| bool upgrade_rejected = false; | ||
| FilterManager::CreateFilterChainResult | ||
| FilterManager::createUpgradeFilterChain(const FilterChainFactory& filter_chain_factory, | ||
| const FilterChainOptionsImpl& options) { | ||
| const HeaderEntry* upgrade = nullptr; | ||
| if (filter_manager_callbacks_.requestHeaders()) { | ||
| upgrade = filter_manager_callbacks_.requestHeaders()->Upgrade(); | ||
|
|
@@ -1638,28 +1641,55 @@ bool FilterManager::createFilterChain() { | |
| } | ||
| } | ||
|
|
||
| if (upgrade == nullptr) { | ||
| // No upgrade header, no upgrade filter chain. | ||
| return {false, false}; | ||
| } | ||
|
|
||
| const Router::RouteEntry::UpgradeMap* upgrade_map = filter_manager_callbacks_.upgradeMap(); | ||
| if (filter_chain_factory.createUpgradeFilterChain(upgrade->value().getStringView(), upgrade_map, | ||
| *this, options)) { | ||
| filter_manager_callbacks_.upgradeFilterChainCreated(); | ||
| // The upgrade filter chain is created successfully. | ||
| return {true, false}; | ||
| } | ||
|
|
||
| // The upgrade filter chain is rejected. Fall through to the default filter chain. | ||
| // The default filter chain will be used to handle the upgrade failure local reply. | ||
| return {false, true}; | ||
| } | ||
|
|
||
| FilterManager::CreateFilterChainResult | ||
| FilterManager::createFilterChain(const FilterChainFactory& filter_chain_factory, | ||
| bool allow_upgrade_filter_chain, bool only_create_if_configured) { | ||
| if (state_.created_filter_chain_) { | ||
| return {true, false}; | ||
|
Contributor
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. is this necessarily correct? Doesn't it depend on what happened previously? maybe at least comment if this is preserving legacy behavior
Member
Author
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, this is mainly for preserving legacy behavior. But I think you are right because the
Member
Author
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 found we have added a |
||
| } | ||
|
|
||
| // This filter chain options is only used for the downstream HTTP filter chains for now. So, try | ||
| // to set valid initial route only when the downstream callbacks is available. | ||
| FilterChainOptionsImpl options( | ||
| filter_manager_callbacks_.downstreamCallbacks().has_value() ? streamInfo().route() : nullptr); | ||
|
|
||
| state_.created_filter_chain_ = true; | ||
| if (upgrade != nullptr) { | ||
| const Router::RouteEntry::UpgradeMap* upgrade_map = filter_manager_callbacks_.upgradeMap(); | ||
| bool upgrade_rejected = false; | ||
|
|
||
| if (filter_chain_factory_.createUpgradeFilterChain(upgrade->value().getStringView(), | ||
| upgrade_map, *this, options)) { | ||
| filter_manager_callbacks_.upgradeFilterChainCreated(); | ||
| return true; | ||
| } else { | ||
| upgrade_rejected = true; | ||
| // Fall through to the default filter chain. The function calling this | ||
| // will send a local reply indicating that the upgrade failed. | ||
| if (allow_upgrade_filter_chain) { | ||
| const auto upgrade_result = createUpgradeFilterChain(filter_chain_factory, options); | ||
| if (upgrade_result.created) { | ||
| ASSERT(!upgrade_result.upgrade_rejected); | ||
| state_.created_filter_chain_ = true; | ||
| return upgrade_result; | ||
| } | ||
| // The upgrade filter chain may be unnecessary or be rejected. Fall through to the default | ||
| // filter chain anyway. | ||
| upgrade_rejected = upgrade_result.upgrade_rejected; | ||
| } | ||
|
|
||
| filter_chain_factory_.createFilterChain(*this, false, options); | ||
| return !upgrade_rejected; | ||
| const bool created = | ||
| filter_chain_factory.createFilterChain(*this, only_create_if_configured, options); | ||
| state_.created_filter_chain_ = created; | ||
|
|
||
| return {created, upgrade_rejected}; | ||
| } | ||
|
|
||
| void ActiveStreamDecoderFilter::requestDataDrained() { | ||
|
|
@@ -1714,11 +1744,11 @@ bool ActiveStreamDecoderFilter::recreateStream(const ResponseHeaderMap* headers) | |
|
|
||
| if (headers != nullptr) { | ||
| // The call to setResponseHeaders is needed to ensure that the headers are properly logged in | ||
| // access logs before the stream is destroyed. Since the function expects a ResponseHeaderPtr&&, | ||
| // ownership of the headers must be passed. This cannot happen earlier in the flow (such as in | ||
| // the call to setupRedirect) because at that point it is still possible for the headers to be | ||
| // used in a different logical branch. We work around this by creating a copy and passing | ||
| // ownership of the copy instead. | ||
| // access logs before the stream is destroyed. Since the function expects a | ||
| // ResponseHeaderPtr&&, ownership of the headers must be passed. This cannot happen earlier in | ||
|
wbpcode marked this conversation as resolved.
|
||
| // the flow (such as in the call to setupRedirect) because at that point it is still possible | ||
| // for the headers to be used in a different logical branch. We work around this by creating a | ||
| // copy and passing ownership of the copy instead. | ||
| ResponseHeaderMapPtr headers_copy = createHeaderMap<ResponseHeaderMapImpl>(*headers); | ||
| parent_.filter_manager_callbacks_.setResponseHeaders(std::move(headers_copy)); | ||
| parent_.filter_manager_callbacks_.chargeStats(*headers); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -639,11 +639,10 @@ class FilterManager : public ScopeTrackedObject, | |
| FilterManager(FilterManagerCallbacks& filter_manager_callbacks, Event::Dispatcher& dispatcher, | ||
| OptRef<const Network::Connection> connection, uint64_t stream_id, | ||
| Buffer::BufferMemoryAccountSharedPtr account, bool proxy_100_continue, | ||
| uint32_t buffer_limit, const FilterChainFactory& filter_chain_factory) | ||
| uint32_t buffer_limit) | ||
| : filter_manager_callbacks_(filter_manager_callbacks), dispatcher_(dispatcher), | ||
| connection_(connection), stream_id_(stream_id), account_(std::move(account)), | ||
| proxy_100_continue_(proxy_100_continue), buffer_limit_(buffer_limit), | ||
| filter_chain_factory_(filter_chain_factory) {} | ||
| proxy_100_continue_(proxy_100_continue), buffer_limit_(buffer_limit) {} | ||
|
|
||
| ~FilterManager() override { | ||
| ASSERT(state_.destroyed_); | ||
|
|
@@ -837,8 +836,23 @@ class FilterManager : public ScopeTrackedObject, | |
| virtual StreamInfo::StreamInfo& streamInfo() PURE; | ||
| virtual const StreamInfo::StreamInfo& streamInfo() const PURE; | ||
|
|
||
| // Set up the Encoder/Decoder filter chain. | ||
| bool createFilterChain(); | ||
| struct CreateFilterChainResult { | ||
| bool created{}; | ||
| bool upgrade_rejected{}; | ||
| }; | ||
|
|
||
| /** | ||
| * Set up the Encoder/Decoder filter chain. | ||
| * @param filter_chain_factory the factory to create the filter chain. | ||
| * @param allow_upgrade_filter_chain whether to allow the creation of an upgrade filter chain. | ||
| * This only should be set to true for downstream HTTP filter chain. | ||
| * @param only_create_if_configured whether to only create the filter chain if it is configured | ||
| * explicitly. This only makes sense for upstream HTTP filter chain. | ||
|
Contributor
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. ooc is it ever not true for upstream filter chain? I'm wondering if we can infer this as we do the upgrade allowed option if they're just the inverse of each other
Member
Author
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...it will be false when we try to create filters from router and cluster at second time. As I said before, i think we should remove this by providing a default filter chain factory for upstream filter chain.
Contributor
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. Would it be worth doing that now? default filter chain is just codec filter right?
Member
Author
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 think it deserves. It not just make code simple also reduce memory overhead of cluster because we now need keep a default filter chain for every cluster. |
||
| * | ||
| */ | ||
| CreateFilterChainResult createFilterChain(const FilterChainFactory& filter_chain_factory, | ||
|
Contributor
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. Can you comment a bit more about the motivation for this refactor?
Member
Author
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.
The initial motivation is I wanna to refactor the http filter chain to use vector rather than list as the container of filters because the filter chain needn't O(1) modification anyway. And rather than This refactoring requires us to set filter callbacks at the end of filter chain creation. So I need to ensure the filter chain creation is under the control of And even not for that, make sure all filter chain creation logic in same way is better choice.
Yeah, I can make the create filter chain a shared private method and create a wrapper method for upstream filter chain creation.
Member
Author
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 found I acutally needn't the upgrade option, because we can check whether the downstream stream callbacks is valid or not to determine to try upgrade or not.
Member
Author
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. By the way, after a check to the upstream filter chain creation, I think even This way should be much cleaner. (If it's ok to you, I can also do a minor refactoring to this with a new PR). |
||
| bool allow_upgrade_filter_chain, | ||
| bool only_create_if_configured); | ||
|
|
||
| OptRef<const Network::Connection> connection() const { return connection_; } | ||
|
|
||
|
|
@@ -966,6 +980,9 @@ class FilterManager : public ScopeTrackedObject, | |
| // Indicates which filter to start the iteration with. | ||
| enum class FilterIterationStartState { AlwaysStartFromNext, CanStartFromCurrent }; | ||
|
|
||
| CreateFilterChainResult createUpgradeFilterChain(const FilterChainFactory& filter_chain_factory, | ||
| const FilterChainOptionsImpl& options); | ||
|
|
||
| // Returns the encoder filter to start iteration with. | ||
| std::list<ActiveStreamEncoderFilterPtr>::iterator | ||
| commonEncodePrefix(ActiveStreamEncoderFilter* filter, bool end_stream, | ||
|
|
@@ -1053,7 +1070,6 @@ class FilterManager : public ScopeTrackedObject, | |
| std::make_shared<Network::Socket::Options>(); | ||
| absl::optional<Upstream::LoadBalancerContext::OverrideHost> upstream_override_host_; | ||
|
|
||
| const FilterChainFactory& filter_chain_factory_; | ||
| // TODO(snowp): Once FM has been moved to its own file we'll make these private classes of FM, | ||
| // at which point they no longer need to be friends. | ||
| friend ActiveStreamFilterBase; | ||
|
|
@@ -1108,11 +1124,11 @@ class DownstreamFilterManager : public FilterManager { | |
| StreamInfo::FilterStateSharedPtr parent_filter_state, | ||
| Server::OverloadManager& overload_manager) | ||
| : FilterManager(filter_manager_callbacks, dispatcher, connection, stream_id, account, | ||
| proxy_100_continue, buffer_limit, filter_chain_factory), | ||
| proxy_100_continue, buffer_limit), | ||
| stream_info_(protocol, time_source, connection.connectionInfoProviderSharedPtr(), | ||
| StreamInfo::FilterState::LifeSpan::FilterChain, | ||
| std::move(parent_filter_state)), | ||
| local_reply_(local_reply), | ||
| local_reply_(local_reply), filter_chain_factory_(filter_chain_factory), | ||
| downstream_filter_load_shed_point_(overload_manager.getLoadShedPoint( | ||
| Server::LoadShedPointName::get().HttpDownstreamFilterCheck)), | ||
| use_filter_manager_state_for_downstream_end_stream_(Runtime::runtimeFeatureEnabled( | ||
|
|
@@ -1136,6 +1152,8 @@ class DownstreamFilterManager : public FilterManager { | |
| stream_info_.setDownstreamRemoteAddress(downstream_remote_address); | ||
| } | ||
|
|
||
| bool createDownstreamFilterChain(); | ||
|
|
||
| /** | ||
| * Called before local reply is made by the filter manager. | ||
| * @param data the data associated with the local reply. | ||
|
|
@@ -1212,6 +1230,7 @@ class DownstreamFilterManager : public FilterManager { | |
| private: | ||
| OverridableRemoteConnectionInfoSetterStreamInfo stream_info_; | ||
| const LocalReply::LocalReply& local_reply_; | ||
| const FilterChainFactory& filter_chain_factory_; | ||
| Utility::PreparedLocalReplyPtr prepared_local_reply_{nullptr}; | ||
| Server::LoadShedPoint* downstream_filter_load_shed_point_{nullptr}; | ||
| // Set by the envoy.reloadable_features.use_filter_manager_state_for_downstream_end_stream runtime | ||
|
|
||
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.
why change this one to CreateFilterChainResult? If it creates isn't the upgrade automatically not rejected? Seems simpler as a boolean
Uh oh!
There was an error while loading. Please reload this page.
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.
But the
createdmay be true when the upgrade is rejected. And thecreatedstatus is necessary by the upstream filter chain creation.#37150 will make thing become more complex. So, I think I have to make some changes here to make it's easy to work with #37150