teimuraz / tonic-middleware

Async middleware and interceptor for Tonic gRPC services
https://crates.io/crates/tonic-middleware
MIT License
47 stars 1 forks source link

`MiddlewareFor` does not support `tower::layer::util::Stack` #21

Closed lcmgh closed 6 days ago

lcmgh commented 3 weeks ago

I am not able to use

.add_service(MiddlewareFor::new(my_service, auth_layer))

for middleware created via https://docs.rs/tower/latest/tower/layer/util/struct.Stack.html

95 |         .add_service(MiddlewareFor::new(amdemo_service, auth_layer))
   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Middleware<MyServiceServer<ServerState>>` is not implemented for `Stack<Either<Stack<SetAuthenticatedExtLayer, AuthorizationLayer<CustomClaims>>, Identity>, ...>`

Or is it because I have not implemented Middleware. Why is that even necessary?

teimuraz commented 3 weeks ago

MiddlewareFor and Middleware are struct and trait from tonic-middleware library.

MiddlewareFor is a service wrapper that pairs a middleware with its target service. The service should implement Middleware trait to be used with MiddlewareFor

Why is that even necessary?

You can use directly Tower's middleware, however the purpose of this library is to simplify usage of asynchronous interceptors / middleware with tonic + tower (it's not super straight forward to find how to make it work)

Tonic provides a solid foundation for developing gRPC services in Rust, and while it offers a range of features, extending it with asynchronous interceptors and middleware requires a bit more effort. That's where tonic-middleware comes in, this library simplifies adding custom asynchronous processing to the tonic service stack.

Btw, probably in your case (if you don't need to modify response) it's even easier to use RequestInterceptor (see example here https://github.com/teimuraz/tonic-middleware/blob/main/example/src/server.rs#L67)

lcmgh commented 3 days ago

Could you provide an example that shows how to simply use existing layers for a specific service? I found that the examples all do not use existing layers but reimplement the layer logic if I am not mistaken.

These are the layers I want to add to one service only:

.layer(ConcurrencyLimitLayer::new(100))
.map_result(|result: Result<_, BoxError>| match result {
    Ok(resp) => Ok(resp),
    Err(err) => {
        if err.is::<tower::load_shed::error::Overloaded>() {
            Ok(Status::unavailable("overloaded").into_http())
        } else {
            Err(err)
        }
    }
})
// Returns with error if next inner service is not ready -> when rate limit is reached
.load_shed()