tonic_middleware/request_interceptor.rs
1use std::task::{Context, Poll};
2
3use crate::ServiceBound;
4use async_trait::async_trait;
5use futures_util::future::BoxFuture;
6use tonic::body::Body;
7use tonic::codegen::http::Request;
8use tonic::codegen::Service;
9use tonic::server::NamedService;
10use tonic::Status;
11use tower::Layer;
12
13/// The `RequestInterceptor` trait is designed to enable the interception and processing of
14/// incoming requests within your service pipeline. This trait is particularly useful for
15/// performing operations such as authentication, enriching requests with additional metadata,
16/// or rejecting requests based on certain criteria before they reach the service logic.
17///
18/// If your requirements extend beyond request interception, and you need to interact with both the
19/// request and response or to perform actions after the service call has been made, you should
20/// consider implementing `Middleware`.
21///
22/// See [examples on GitHub](https://github.com/teimuraz/tonic-middleware/tree/main/example)
23
24#[async_trait]
25pub trait RequestInterceptor {
26 /// Intercepts an incoming request, allowing for inspection, modification, or early rejection
27 /// with a `Status` error.
28 ///
29 /// # Parameters
30 ///
31 /// * `req`: The incoming `Request` to be intercepted.
32 ///
33 /// # Returns
34 ///
35 /// Returns either the potentially modified request for further processing, or a `Status`
36 /// error to halt processing with a specific error response.
37 async fn intercept(&self, req: Request<Body>) -> Result<Request<Body>, Status>;
38}
39
40/// `InterceptorFor` wraps a service with a `RequestInterceptor`, enabling request-level
41/// interception before
42/// the request reaches the service logic.
43/// # Type Parameters
44///
45/// * `S`: The service being wrapped.
46/// * `I`: The `RequestInterceptor` that will preprocess the requests.
47#[derive(Clone)]
48pub struct InterceptorFor<S, I>
49where
50 I: RequestInterceptor,
51{
52 pub inner: S,
53 pub interceptor: I,
54}
55
56impl<S, I> InterceptorFor<S, I>
57where
58 I: RequestInterceptor,
59{
60 /// Creates a new `InterceptorFor` with the provided service and interceptor.
61 ///
62 /// # Parameters
63 ///
64 /// * `inner`: The service being wrapped.
65 /// * `interceptor`: The interceptor that will preprocess the requests.
66 pub fn new(inner: S, interceptor: I) -> Self {
67 InterceptorFor { inner, interceptor }
68 }
69}
70
71impl<S, I> Service<Request<Body>> for InterceptorFor<S, I>
72where
73 S: ServiceBound,
74 S::Future: Send,
75 I: RequestInterceptor + Send + Clone + 'static + Sync,
76{
77 type Response = S::Response;
78 type Error = S::Error;
79 type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
80
81 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
82 self.inner.poll_ready(cx)
83 }
84
85 fn call(&mut self, req: Request<Body>) -> Self::Future {
86 let interceptor = self.interceptor.clone();
87 let mut inner = self.inner.clone();
88 Box::pin(async move {
89 match interceptor.intercept(req).await {
90 Ok(req) => inner.call(req).await,
91 Err(status) => {
92 let response = status.into_http();
93 Ok(response)
94 }
95 }
96 })
97 }
98}
99
100impl<S, I> NamedService for InterceptorFor<S, I>
101where
102 S: NamedService,
103 I: RequestInterceptor,
104{
105 const NAME: &'static str = S::NAME;
106}
107
108/// `RequestInterceptorLayer` provides a way to wrap services with a specific interceptor using the tower `Layer` trait
109///
110/// # Type Parameters
111///
112/// * `I`: The `RequestInterceptor` implementation.
113#[derive(Clone)]
114pub struct RequestInterceptorLayer<I> {
115 interceptor: I,
116}
117
118impl<I> RequestInterceptorLayer<I> {
119 /// Creates a new `RequestInterceptorLayer` with the given interceptor.
120 ///
121 /// # Parameters
122 ///
123 /// * `interceptor`: The interceptor to apply to services.
124 pub fn new(interceptor: I) -> Self {
125 RequestInterceptorLayer { interceptor }
126 }
127}
128
129impl<S, I> Layer<S> for RequestInterceptorLayer<I>
130where
131 I: RequestInterceptor + Clone,
132{
133 type Service = InterceptorFor<S, I>;
134
135 fn layer(&self, inner: S) -> Self::Service {
136 InterceptorFor::new(inner, self.interceptor.clone())
137 }
138}