From 8c8f4d12515643050f47227894c98e226b01f924 Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Tue, 27 Jul 2021 18:11:51 +0300 Subject: [PATCH] feat(tonic): add `Interceptor` trait (#713) * Expose `Interceptor` trait. * Revert changes to public API surface. * Rename types, add deprecated alias for old names. --- examples/src/tower/client.rs | 2 +- examples/src/tower/server.rs | 2 +- tonic-build/src/client.rs | 2 +- tonic-build/src/server.rs | 2 +- tonic/src/extensions.rs | 4 +- tonic/src/request.rs | 4 +- tonic/src/service/interceptor.rs | 74 +++++++++++++++++++++++++------ tonic/src/service/mod.rs | 3 +- tonic/src/transport/server/mod.rs | 8 ++-- 9 files changed, 75 insertions(+), 26 deletions(-) diff --git a/examples/src/tower/client.rs b/examples/src/tower/client.rs index 3f5d410ef..0a33fffae 100644 --- a/examples/src/tower/client.rs +++ b/examples/src/tower/client.rs @@ -15,7 +15,7 @@ async fn main() -> Result<(), Box> { let channel = ServiceBuilder::new() // Interceptors can be also be applied as middleware - .layer(tonic::service::interceptor_fn(intercept)) + .layer(tonic::service::interceptor(intercept)) .layer_fn(AuthSvc::new) .service(channel); diff --git a/examples/src/tower/server.rs b/examples/src/tower/server.rs index 2f3e36fcb..59d884ad8 100644 --- a/examples/src/tower/server.rs +++ b/examples/src/tower/server.rs @@ -47,7 +47,7 @@ async fn main() -> Result<(), Box> { // Apply our own middleware .layer(MyMiddlewareLayer::default()) // Interceptors can be also be applied as middleware - .layer(tonic::service::interceptor_fn(intercept)) + .layer(tonic::service::interceptor(intercept)) .into_inner(); Server::builder() diff --git a/tonic-build/src/client.rs b/tonic-build/src/client.rs index 7eaa09417..9cbc5e721 100644 --- a/tonic-build/src/client.rs +++ b/tonic-build/src/client.rs @@ -68,7 +68,7 @@ pub fn generate( pub fn with_interceptor(inner: T, interceptor: F) -> #service_ident> where - F: FnMut(tonic::Request<()>) -> Result, tonic::Status>, + F: tonic::service::Interceptor, T: tonic::codegen::Service< http::Request, Response = http::Response<>::ResponseBody> diff --git a/tonic-build/src/server.rs b/tonic-build/src/server.rs index 1b2f61f73..9c650666d 100644 --- a/tonic-build/src/server.rs +++ b/tonic-build/src/server.rs @@ -104,7 +104,7 @@ pub fn generate( pub fn with_interceptor(inner: T, interceptor: F) -> InterceptedService where - F: FnMut(tonic::Request<()>) -> Result, tonic::Status>, + F: tonic::service::Interceptor, { InterceptedService::new(Self::new(inner), interceptor) } diff --git a/tonic/src/extensions.rs b/tonic/src/extensions.rs index c7a40ffb6..aef1f7ebc 100644 --- a/tonic/src/extensions.rs +++ b/tonic/src/extensions.rs @@ -2,10 +2,10 @@ use std::fmt; /// A type map of protocol extensions. /// -/// `Extensions` can be used by [`interceptor_fn`] and [`Request`] to store extra data derived from +/// `Extensions` can be used by [`Interceptor`] and [`Request`] to store extra data derived from /// the underlying protocol. /// -/// [`interceptor_fn`]: crate::service::interceptor_fn +/// [`Interceptor`]: crate::service::Interceptor /// [`Request`]: crate::Request pub struct Extensions { inner: http::Extensions, diff --git a/tonic/src/request.rs b/tonic/src/request.rs index b5aae8c03..612648d45 100644 --- a/tonic/src/request.rs +++ b/tonic/src/request.rs @@ -300,13 +300,13 @@ impl Request { /// Extensions can be set in interceptors: /// /// ```no_run - /// use tonic::{Request, service::interceptor_fn}; + /// use tonic::{Request, service::interceptor}; /// /// struct MyExtension { /// some_piece_of_data: String, /// } /// - /// interceptor_fn(|mut request: Request<()>| { + /// interceptor(|mut request: Request<()>| { /// request.extensions_mut().insert(MyExtension { /// some_piece_of_data: "foo".to_string(), /// }); diff --git a/tonic/src/service/interceptor.rs b/tonic/src/service/interceptor.rs index 278ebd181..1daaf83a3 100644 --- a/tonic/src/service/interceptor.rs +++ b/tonic/src/service/interceptor.rs @@ -1,6 +1,6 @@ //! gRPC interceptors which are a kind of middleware. //! -//! See [`interceptor_fn`] for more details. +//! See [`Interceptor`] for more details. use crate::{request::SanitizeHeaders, Status}; use pin_project::pin_project; @@ -13,12 +13,15 @@ use std::{ use tower_layer::Layer; use tower_service::Service; -/// Create a new interceptor from a function. +/// A gRPC incerceptor. /// /// gRPC interceptors are similar to middleware but have less flexibility. An interceptor allows /// you to do two main things, one is to add/remove/check items in the `MetadataMap` of each /// request. Two, cancel a request with a `Status`. /// +/// Any function that satisfies the bound `FnMut(Request<()>) -> Result, Status>` can be +/// used as an `Interceptor`. +/// /// An interceptor can be used on both the server and client side through the `tonic-build` crate's /// generated structs. /// @@ -35,24 +38,56 @@ use tower_service::Service; /// [tower]: https://crates.io/crates/tower /// [example]: https://github.com/hyperium/tonic/tree/master/examples/src/interceptor /// [tower-example]: https://github.com/hyperium/tonic/tree/master/examples/src/tower -pub fn interceptor_fn(f: F) -> InterceptorFn +pub trait Interceptor { + /// Intercept a request before it is sent, optionally cancelling it. + fn call(&mut self, request: crate::Request<()>) -> Result, Status>; +} + +impl Interceptor for F where F: FnMut(crate::Request<()>) -> Result, Status>, { - InterceptorFn { f } + fn call(&mut self, request: crate::Request<()>) -> Result, Status> { + self(request) + } +} + +/// Create a new interceptor layer. +/// +/// See [`Interceptor`] for more details. +pub fn interceptor(f: F) -> InterceptorLayer +where + F: Interceptor, +{ + InterceptorLayer { f } } -/// An interceptor created from a function. +#[deprecated( + since = "0.5.1", + note = "Please use the `interceptor` function instead" +)] +/// Create a new interceptor layer. /// -/// See [`interceptor_fn`] for more details. +/// See [`Interceptor`] for more details. +pub fn interceptor_fn(f: F) -> InterceptorLayer +where + F: Interceptor, +{ + interceptor(f) +} + +/// A gRPC interceptor that can be used as a [`Layer`], +/// created by calling [`interceptor`]. +/// +/// See [`Interceptor`] for more details. #[derive(Debug, Clone, Copy)] -pub struct InterceptorFn { +pub struct InterceptorLayer { f: F, } -impl Layer for InterceptorFn +impl Layer for InterceptorLayer where - F: FnMut(crate::Request<()>) -> Result, Status> + Clone, + F: Interceptor + Clone, { type Service = InterceptedService; @@ -61,9 +96,19 @@ where } } +#[deprecated( + since = "0.5.1", + note = "Please use the `InterceptorLayer` type instead" +)] +/// A gRPC interceptor that can be used as a [`Layer`], +/// created by calling [`interceptor`]. +/// +/// See [`Interceptor`] for more details. +pub type InterceptorFn = InterceptorLayer; + /// A service wrapped in an interceptor middleware. /// -/// See [`interceptor_fn`] for more details. +/// See [`Interceptor`] for more details. #[derive(Clone, Copy)] pub struct InterceptedService { inner: S, @@ -75,7 +120,7 @@ impl InterceptedService { /// function `F`. pub fn new(service: S, f: F) -> Self where - F: FnMut(crate::Request<()>) -> Result, Status>, + F: Interceptor, { Self { inner: service, f } } @@ -95,7 +140,7 @@ where impl Service> for InterceptedService where - F: FnMut(crate::Request<()>) -> Result, Status>, + F: Interceptor, S: Service, Response = http::Response>, S::Error: Into, { @@ -113,7 +158,10 @@ where let req = crate::Request::from_http(req); let (metadata, extensions, msg) = req.into_parts(); - match (self.f)(crate::Request::from_parts(metadata, extensions, ())) { + match self + .f + .call(crate::Request::from_parts(metadata, extensions, ())) + { Ok(req) => { let (metadata, extensions, _) = req.into_parts(); let req = crate::Request::from_parts(metadata, extensions, msg); diff --git a/tonic/src/service/mod.rs b/tonic/src/service/mod.rs index 2a6ab273a..125ac7980 100644 --- a/tonic/src/service/mod.rs +++ b/tonic/src/service/mod.rs @@ -3,4 +3,5 @@ pub mod interceptor; #[doc(inline)] -pub use self::interceptor::interceptor_fn; +#[allow(deprecated)] +pub use self::interceptor::{interceptor, interceptor_fn, Interceptor}; diff --git a/tonic/src/transport/server/mod.rs b/tonic/src/transport/server/mod.rs index 8a061ae03..c0f27a0ca 100644 --- a/tonic/src/transport/server/mod.rs +++ b/tonic/src/transport/server/mod.rs @@ -395,7 +395,7 @@ impl Server { /// # use tower_service::Service; /// use tower::ServiceBuilder; /// use std::time::Duration; - /// use tonic::{Request, Status, service::interceptor_fn}; + /// use tonic::{Request, Status, service::interceptor}; /// /// fn auth_interceptor(request: Request<()>) -> Result, Status> { /// if valid_credentials(&request) { @@ -417,8 +417,8 @@ impl Server { /// let layer = ServiceBuilder::new() /// .load_shed() /// .timeout(Duration::from_secs(30)) - /// .layer(interceptor_fn(auth_interceptor)) - /// .layer(interceptor_fn(some_other_interceptor)) + /// .layer(interceptor(auth_interceptor)) + /// .layer(interceptor(some_other_interceptor)) /// .into_inner(); /// /// Server::builder().layer(layer); @@ -428,7 +428,7 @@ impl Server { /// [`Layer`]: tower::layer::Layer /// [eco]: https://github.com/tower-rs /// [`ServiceBuilder`]: tower::ServiceBuilder - /// [interceptors]: crate::service::interceptor_fn + /// [interceptors]: crate::service::Interceptor pub fn layer(self, new_layer: NewLayer) -> Server { Server { layer: new_layer,