rewrite tower::filter#508
Merged
Merged
Conversation
Signed-off-by: Eliza Weisman <eliza@buoyant.io>
Signed-off-by: Eliza Weisman <eliza@buoyant.io>
Signed-off-by: Eliza Weisman <eliza@buoyant.io>
hawkw
commented
Jan 6, 2021
Comment on lines
+1
to
+24
| macro_rules! opaque_future { | ||
| ($(#[$m:meta])* pub type $name:ident<$($param:ident),+> = $actual:ty;) => { | ||
| #[pin_project::pin_project] | ||
| $(#[$m])* | ||
| pub struct $name<$($param),+>(#[pin] pub(crate) $actual); | ||
|
|
||
| impl<$($param),+> std::fmt::Debug for $name<$($param),+> { | ||
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
| f.debug_tuple(stringify!($name)).field(&format_args!("...")).finish() | ||
| } | ||
| } | ||
|
|
||
| impl<$($param),+> std::future::Future for $name<$($param),+> | ||
| where | ||
| $actual: std::future::Future, | ||
| { | ||
| type Output = <$actual as std::future::Future>::Output; | ||
| #[inline] | ||
| fn poll(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll<Self::Output> { | ||
| self.project().0.poll(cx) | ||
| } | ||
| } | ||
| } | ||
| } |
Member
Author
There was a problem hiding this comment.
The idea behind this thing is that we probably don't want to expose type aliases for futures_util futures in public APIs, for stability reasons. Since type aliases don't hide the aliased type, users could refer to these as the nested chain of futures_util combinator types. Thus, if we change the specific combinator futures, that's potentially a breaking change.
I'll wrap other exposed combinator futures in opaque_future! when this merges.
hawkw
commented
Jan 6, 2021
olix0r
reviewed
Jan 6, 2021
LucioFranco
approved these changes
Jan 6, 2021
|
|
||
| /// Conditionally reject requests based on `predicate`. | ||
| /// | ||
| /// `predicate` must implement the [`Predicate`] trait. |
Member
There was a problem hiding this comment.
Suggested change
| /// `predicate` must implement the [`Predicate`] trait. | |
| /// - `predicate` must implement the [`Predicate`] trait. |
minor nit: feel like this reads a bit better?
Member
Author
There was a problem hiding this comment.
i don't think it makes sense to use a bulleted list with only one entry?
|
|
||
| /// Conditionally reject requests based on an asynchronous `predicate`. | ||
| /// | ||
| /// `predicate` must implement the [`AsyncPredicate`] trait. |
Member
There was a problem hiding this comment.
Suggested change
| /// `predicate` must implement the [`AsyncPredicate`] trait. | |
| /// - `predicate` must implement the [`AsyncPredicate`] trait. |
Signed-off-by: Eliza Weisman <eliza@buoyant.io>
Signed-off-by: Eliza Weisman <eliza@buoyant.io>
hawkw
added a commit
that referenced
this pull request
Jan 6, 2021
For stability reasons, we probably don't want to expose future combinator types from the `futures_util` crate in public APIs. If we were to change which combinators are used to implement these futures, or if `futures_util` made breaking changes, this could cause API breakage. This branch wraps all publicly exposed `futures_util` types in the `opaque_future!` macro added in #508, to wrap them in newtypes that hide their internal details. This way, we can change these futures' internals whenever we like.
hawkw
added a commit
that referenced
this pull request
Jan 6, 2021
* make all combinator futures opaque For stability reasons, we probably don't want to expose future combinator types from the `futures_util` crate in public APIs. If we were to change which combinators are used to implement these futures, or if `futures_util` made breaking changes, this could cause API breakage. This branch wraps all publicly exposed `futures_util` types in the `opaque_future!` macro added in #508, to wrap them in newtypes that hide their internal details. This way, we can change these futures' internals whenever we like. Signed-off-by: Eliza Weisman <eliza@buoyant.io>
olix0r
pushed a commit
to linkerd/linkerd2-proxy
that referenced
this pull request
Jan 14, 2021
Tower provides a `tower::filter` utility for conditionally rejecting some requests and accepting others. Rather than using `tower`'s filter, we have a similar `linkerd_stack::request_filter`, due to two main issues with the filtering API in tower 0.3 and earlier: * `tower::filter`'s predicates returned futures, which we don't need --- all our filtering predicates are purely sunchronous * `tower::filter` didn't allow filters to return any error of their choosing --- they had to return a specific filter-rejected error. This doesn't work with other code where we downcast error types, like switch/fallback. However, Tower 0.4 includes a rewritten `Filter` API that no longer has these issues (tower-rs/tower#508). Therefore, we can switch to using the upstream implementation. This branch replaces `RequestFilter` with `tower::filter::Filter`. It was necessary to add an impl of `NewService` for `Filter`. This required an upstream change to expose access to the wrapped service, so that its `new_service` method can be called when it's a `NewService`, and to expose access to the `Predicate`. Therefore, this PR depends on tower-rs/tower#521 and tower-rs/tower#522, and introduces a Git dependency. The git dep can be removed when Tower 0.4.3 is released. Signed-off-by: Eliza Weisman <eliza@buoyant.io>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
It was pointed out that there is currently some overlap between the
try_withServicecombinator andtower::filtermiddleware (see #499 (comment) ).try_withsynchronously maps from aRequest->Result<DifferentRequest, Error>, whiletower::filterasynchronously maps from a
&Requestto aResult<(), Error>. Thekey differences are: -
try_withtakes a request by value, and allowsthe predicate to return a different request value -
try_withalsopermits changing the type of the request -
try_withis synchronous,while
tower::filteris asynchronous -tower::filterhas aPredicatetrait, which can be implemented by more than just functions.For example, a struct with a
HashSetcould implementPredicatebyfailing requests that match the values in the hashset.
It definitely seems like there's demand for both synchronous and
asynchronous request filtering. However, the APIs we have currently
differ pretty significantly. It would be nice to make them more
consistent with each other.
As an aside,
tower::filterdoes not seem all that widely used.Meanwhile,
linkerd2-proxydefines its ownRequestFiltermiddleware,using a predicate trait that's essentially in between
tower::filterandServiceExt::try_with: - it's synchronous, liketry_with- it allowsmodifying the type of the request, like
try_with- it uses a trait forpredicates, rather than a
Fn, liketower::filter- it uses a similarnaming scheme to
tower::filter("filtering" rather than "with"/"map").Solution
This branch rewrites
tower::filterto make the following changes:AsyncFiltertypeand an
AsyncPredicatetrait are available for predicates returningfutures.
Requesttype, allowingFilterandAsyncFilterto subsumetry_map_request.into
BoxErrors.Closes #502