Skip to content
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

filter: expose predicate check as methods on Filter and AsyncFilter #521

Merged
merged 4 commits into from
Jan 11, 2021

Conversation

hawkw
Copy link
Member

@hawkw hawkw commented Jan 11, 2021

Motivation

In Linkerd 2, we have our own RequestFilter middleware. Now that Tower
0.4 has been released, including a new synchronous request filtering
middleware, we'd like to migrate to Tower's filter middleware. However,
there's one hitch: we implement additional traits for our filtering
middleware --- in particular, we use a NewService trait, which is
essentially a synchronous version of MakeService, for services that
can be constructed immediately. We'd like to be able to implement that
trait for tower::filter::Filter services as well.

Solution

This branch adds new Filter::check and AsyncFilter::check methods
which expose the inner Predicate::check and AsyncPredicate::check
methods on the filter service's predicate. This can be used to call into
the predicate directly, allowing external traits to be implemented for
Filter/AsyncFilter in downstream code. Of course, the additional
traits must be defined in the crate providing implementaitons for
tower::filter, since Filter and AsyncFilter would be foreign
types, but in our use case, at least, this isn't an issue.

Motivation
----------

In Linkerd 2, we have our own `RequestFilter` middleware. Now that Tower
0.4 has been released, including a new synchronous request filtering
middleware, we'd like to migrate to Tower's filter middleware. However,
there's one hitch: we implement additional traits for our filtering
middleware --- in particular, we use a `NewService` trait, which is
essentially a synchronous version of `MakeService`, for services that
can be constructed immediately. We'd like to be able to implement that
trait for `tower::filter::Filter` services as well.

Solution
--------

This branch adds new `Filter::check` and `AsyncFilter::check` methods
which expose the inner `Predicate::check` and `AsyncPredicate::check`
methods on the filter service's predicate. This can be used to call into
the predicate directly, allowing external traits to be implemented for
`Filter`/`AsyncFilter` in downstream code. Of course, the additional
traits must be defined in the crate providing implementaitons for
`tower::filter`, since `Filter` and `AsyncFilter` would be foreign
types, but in our use case, at least, this isn't an issue.
@@ -72,6 +72,14 @@ impl<T, U> Filter<T, U> {
pub fn layer(predicate: U) -> FilterLayer<U> {
FilterLayer::new(predicate)
}

/// Check a `Request` value against this filter's predicate.
pub fn check<Request>(&mut self, request: Request) -> Result<U::Request, BoxError>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

only nit I have is we have a lot of Request in here, maybe change the generic to be R?

@hawkw hawkw merged commit 7aecf78 into master Jan 11, 2021
@hawkw hawkw deleted the eliza/expose-filter-checks branch January 11, 2021 20:39
hawkw added a commit that referenced this pull request Jan 11, 2021
This branch adds methods to access the inner service of a `Filter` or an
`AsyncFilter`. These are identical to the similarly-named method
provided by many other middleware services.

Along with the changes in #521, this is also necessary to implement
filtered versions of foreign traits in downstream code. I probably
should've added this in that PR, but I wasn't thinking it through...
hawkw added a commit that referenced this pull request Jan 11, 2021
This branch adds methods to access the inner service of a `Filter` or an
`AsyncFilter`. These are identical to the similarly-named method
provided by many other middleware services.

Along with the changes in #521, this is also necessary to implement
filtered versions of foreign traits in downstream code. I probably
should've added this in that PR, but I wasn't thinking it through...
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants