I developed middleware using Tower and have been applying it to tonic services via the layer method.
However layer applies middleware to all services.
I need to apply my middleware to particular service. With my current implementation grpc returns 12 error code (not implemented) for service being wrapped into middleware .add_service(AuthMiddleware::new(grpc_project_service), this middleware and grpc_project_service is not invoked this way (only when using via layer).
Is there a way to achieve this selective middleware application? I've been unable to find relevant documentation on this.
Middleware
use std::task::{Context, Poll};
use tower::{Layer, Service};
#[derive(Debug, Clone, Default)]
pub struct AuthMiddlewareLayer;
impl<S> Layer<S> for AuthMiddlewareLayer {
type Service = AuthMiddleware<S>;
fn layer(&self, inner: S) -> Self::Service {
AuthMiddleware::new(inner)
}
}
#[derive(Debug, Clone)]
pub struct AuthMiddleware<S> {
inner: S,
}
impl<S> AuthMiddleware<S> {
pub fn new(inner: S) -> Self {
AuthMiddleware {
inner
}
}
}
impl<S, Req> Service<Req> for AuthMiddleware<S>
where S: Service<Req>,
{
type Response = S::Response;
type Error = S::Error;
type Future = S::Future;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
println!("AuthMiddleware poll_ready called");
self.inner.poll_ready(cx)
}
fn call(&mut self, req: Req) -> Self::Future {
print!("Im here!!!");
self.inner.call(req)
}
}
Middleware usage (main.rs)
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
...
let project_api = ProjectApi { project_service };
let grpc_project_service = grpc_project::ProjectServiceServer::new(project_api);
let auth_api = AuthApi { auth_service };
let grpc_auth_service = grpc_auth::AuthServiceServer::new(auth_api);
...
let reflection_service = tonic_reflection::server::Builder::configure()
.register_encoded_file_descriptor_set(FILE_DESCRIPTOR_SET)
.build()
.unwrap();
Server::builder()
// Works, however applies to all services.
// .layer(AuthMiddlewareLayer::default())
.add_service(grpc_auth_service)
// Doesn't work, grpc returns 12 error code (not implemented), AuthMiddleware and grpc_project_service are not invoked at all.
.add_service(AuthMiddleware::new(grpc_project_service))
.add_service(reflection_service)
.serve(grpc_addr)
.await
.tap_err(|e| error!("Cannot start grpc server, error: {}", e))?;
Ok(())
}
impl NamedService for AuthMiddleware<ProjectServiceServer<ProjectApi>> {
const NAME: &'static str = "ProjectService";
}
Versions:
tonic = "0.11.0"
tower = "0.4.13"
hyper = "1.2.0"
Found the answer: Turns out I had to implemented NamedService for middleware, since tonic uses name under the hood to route requests...
Update Create small lib that simplifies creation of async interceptors and middlewares (Full interception of service call, adding custom logic before and after service call), using tower. https://github.com/teimuraz/tonic-middleware