feat(tonic): add Interceptor trait (#713)

* Expose `Interceptor` trait.

* Revert changes to public API surface.

* Rename types, add deprecated alias for old names.
This commit is contained in:
Yotam Ofek
2021-07-27 11:11:51 -04:00
committed by GitHub
parent 0e33a0241e
commit 8c8f4d1251
9 changed files with 75 additions and 26 deletions
+1 -1
View File
@@ -15,7 +15,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let channel = ServiceBuilder::new() let channel = ServiceBuilder::new()
// Interceptors can be also be applied as middleware // Interceptors can be also be applied as middleware
.layer(tonic::service::interceptor_fn(intercept)) .layer(tonic::service::interceptor(intercept))
.layer_fn(AuthSvc::new) .layer_fn(AuthSvc::new)
.service(channel); .service(channel);
+1 -1
View File
@@ -47,7 +47,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Apply our own middleware // Apply our own middleware
.layer(MyMiddlewareLayer::default()) .layer(MyMiddlewareLayer::default())
// Interceptors can be also be applied as middleware // Interceptors can be also be applied as middleware
.layer(tonic::service::interceptor_fn(intercept)) .layer(tonic::service::interceptor(intercept))
.into_inner(); .into_inner();
Server::builder() Server::builder()
+1 -1
View File
@@ -68,7 +68,7 @@ pub fn generate<T: Service>(
pub fn with_interceptor<F>(inner: T, interceptor: F) -> #service_ident<InterceptedService<T, F>> pub fn with_interceptor<F>(inner: T, interceptor: F) -> #service_ident<InterceptedService<T, F>>
where where
F: FnMut(tonic::Request<()>) -> Result<tonic::Request<()>, tonic::Status>, F: tonic::service::Interceptor,
T: tonic::codegen::Service< T: tonic::codegen::Service<
http::Request<tonic::body::BoxBody>, http::Request<tonic::body::BoxBody>,
Response = http::Response<<T as tonic::client::GrpcService<tonic::body::BoxBody>>::ResponseBody> Response = http::Response<<T as tonic::client::GrpcService<tonic::body::BoxBody>>::ResponseBody>
+1 -1
View File
@@ -104,7 +104,7 @@ pub fn generate<T: Service>(
pub fn with_interceptor<F>(inner: T, interceptor: F) -> InterceptedService<Self, F> pub fn with_interceptor<F>(inner: T, interceptor: F) -> InterceptedService<Self, F>
where where
F: FnMut(tonic::Request<()>) -> Result<tonic::Request<()>, tonic::Status>, F: tonic::service::Interceptor,
{ {
InterceptedService::new(Self::new(inner), interceptor) InterceptedService::new(Self::new(inner), interceptor)
} }
+2 -2
View File
@@ -2,10 +2,10 @@ use std::fmt;
/// A type map of protocol extensions. /// 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. /// the underlying protocol.
/// ///
/// [`interceptor_fn`]: crate::service::interceptor_fn /// [`Interceptor`]: crate::service::Interceptor
/// [`Request`]: crate::Request /// [`Request`]: crate::Request
pub struct Extensions { pub struct Extensions {
inner: http::Extensions, inner: http::Extensions,
+2 -2
View File
@@ -300,13 +300,13 @@ impl<T> Request<T> {
/// Extensions can be set in interceptors: /// Extensions can be set in interceptors:
/// ///
/// ```no_run /// ```no_run
/// use tonic::{Request, service::interceptor_fn}; /// use tonic::{Request, service::interceptor};
/// ///
/// struct MyExtension { /// struct MyExtension {
/// some_piece_of_data: String, /// some_piece_of_data: String,
/// } /// }
/// ///
/// interceptor_fn(|mut request: Request<()>| { /// interceptor(|mut request: Request<()>| {
/// request.extensions_mut().insert(MyExtension { /// request.extensions_mut().insert(MyExtension {
/// some_piece_of_data: "foo".to_string(), /// some_piece_of_data: "foo".to_string(),
/// }); /// });
+61 -13
View File
@@ -1,6 +1,6 @@
//! gRPC interceptors which are a kind of middleware. //! 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 crate::{request::SanitizeHeaders, Status};
use pin_project::pin_project; use pin_project::pin_project;
@@ -13,12 +13,15 @@ use std::{
use tower_layer::Layer; use tower_layer::Layer;
use tower_service::Service; 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 /// 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 /// 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`. /// request. Two, cancel a request with a `Status`.
/// ///
/// Any function that satisfies the bound `FnMut(Request<()>) -> Result<Request<()>, 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 /// An interceptor can be used on both the server and client side through the `tonic-build` crate's
/// generated structs. /// generated structs.
/// ///
@@ -35,24 +38,56 @@ use tower_service::Service;
/// [tower]: https://crates.io/crates/tower /// [tower]: https://crates.io/crates/tower
/// [example]: https://github.com/hyperium/tonic/tree/master/examples/src/interceptor /// [example]: https://github.com/hyperium/tonic/tree/master/examples/src/interceptor
/// [tower-example]: https://github.com/hyperium/tonic/tree/master/examples/src/tower /// [tower-example]: https://github.com/hyperium/tonic/tree/master/examples/src/tower
pub fn interceptor_fn<F>(f: F) -> InterceptorFn<F> pub trait Interceptor {
/// Intercept a request before it is sent, optionally cancelling it.
fn call(&mut self, request: crate::Request<()>) -> Result<crate::Request<()>, Status>;
}
impl<F> Interceptor for F
where where
F: FnMut(crate::Request<()>) -> Result<crate::Request<()>, Status>, F: FnMut(crate::Request<()>) -> Result<crate::Request<()>, Status>,
{ {
InterceptorFn { f } fn call(&mut self, request: crate::Request<()>) -> Result<crate::Request<()>, Status> {
self(request)
}
} }
/// An interceptor created from a function. /// Create a new interceptor layer.
/// ///
/// See [`interceptor_fn`] for more details. /// See [`Interceptor`] for more details.
pub fn interceptor<F>(f: F) -> InterceptorLayer<F>
where
F: Interceptor,
{
InterceptorLayer { f }
}
#[deprecated(
since = "0.5.1",
note = "Please use the `interceptor` function instead"
)]
/// Create a new interceptor layer.
///
/// See [`Interceptor`] for more details.
pub fn interceptor_fn<F>(f: F) -> InterceptorLayer<F>
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)] #[derive(Debug, Clone, Copy)]
pub struct InterceptorFn<F> { pub struct InterceptorLayer<F> {
f: F, f: F,
} }
impl<S, F> Layer<S> for InterceptorFn<F> impl<S, F> Layer<S> for InterceptorLayer<F>
where where
F: FnMut(crate::Request<()>) -> Result<crate::Request<()>, Status> + Clone, F: Interceptor + Clone,
{ {
type Service = InterceptedService<S, F>; type Service = InterceptedService<S, F>;
@@ -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<F> = InterceptorLayer<F>;
/// A service wrapped in an interceptor middleware. /// A service wrapped in an interceptor middleware.
/// ///
/// See [`interceptor_fn`] for more details. /// See [`Interceptor`] for more details.
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct InterceptedService<S, F> { pub struct InterceptedService<S, F> {
inner: S, inner: S,
@@ -75,7 +120,7 @@ impl<S, F> InterceptedService<S, F> {
/// function `F`. /// function `F`.
pub fn new(service: S, f: F) -> Self pub fn new(service: S, f: F) -> Self
where where
F: FnMut(crate::Request<()>) -> Result<crate::Request<()>, Status>, F: Interceptor,
{ {
Self { inner: service, f } Self { inner: service, f }
} }
@@ -95,7 +140,7 @@ where
impl<S, F, ReqBody, ResBody> Service<http::Request<ReqBody>> for InterceptedService<S, F> impl<S, F, ReqBody, ResBody> Service<http::Request<ReqBody>> for InterceptedService<S, F>
where where
F: FnMut(crate::Request<()>) -> Result<crate::Request<()>, Status>, F: Interceptor,
S: Service<http::Request<ReqBody>, Response = http::Response<ResBody>>, S: Service<http::Request<ReqBody>, Response = http::Response<ResBody>>,
S::Error: Into<crate::Error>, S::Error: Into<crate::Error>,
{ {
@@ -113,7 +158,10 @@ where
let req = crate::Request::from_http(req); let req = crate::Request::from_http(req);
let (metadata, extensions, msg) = req.into_parts(); 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) => { Ok(req) => {
let (metadata, extensions, _) = req.into_parts(); let (metadata, extensions, _) = req.into_parts();
let req = crate::Request::from_parts(metadata, extensions, msg); let req = crate::Request::from_parts(metadata, extensions, msg);
+2 -1
View File
@@ -3,4 +3,5 @@
pub mod interceptor; pub mod interceptor;
#[doc(inline)] #[doc(inline)]
pub use self::interceptor::interceptor_fn; #[allow(deprecated)]
pub use self::interceptor::{interceptor, interceptor_fn, Interceptor};
+4 -4
View File
@@ -395,7 +395,7 @@ impl<L> Server<L> {
/// # use tower_service::Service; /// # use tower_service::Service;
/// use tower::ServiceBuilder; /// use tower::ServiceBuilder;
/// use std::time::Duration; /// use std::time::Duration;
/// use tonic::{Request, Status, service::interceptor_fn}; /// use tonic::{Request, Status, service::interceptor};
/// ///
/// fn auth_interceptor(request: Request<()>) -> Result<Request<()>, Status> { /// fn auth_interceptor(request: Request<()>) -> Result<Request<()>, Status> {
/// if valid_credentials(&request) { /// if valid_credentials(&request) {
@@ -417,8 +417,8 @@ impl<L> Server<L> {
/// let layer = ServiceBuilder::new() /// let layer = ServiceBuilder::new()
/// .load_shed() /// .load_shed()
/// .timeout(Duration::from_secs(30)) /// .timeout(Duration::from_secs(30))
/// .layer(interceptor_fn(auth_interceptor)) /// .layer(interceptor(auth_interceptor))
/// .layer(interceptor_fn(some_other_interceptor)) /// .layer(interceptor(some_other_interceptor))
/// .into_inner(); /// .into_inner();
/// ///
/// Server::builder().layer(layer); /// Server::builder().layer(layer);
@@ -428,7 +428,7 @@ impl<L> Server<L> {
/// [`Layer`]: tower::layer::Layer /// [`Layer`]: tower::layer::Layer
/// [eco]: https://github.com/tower-rs /// [eco]: https://github.com/tower-rs
/// [`ServiceBuilder`]: tower::ServiceBuilder /// [`ServiceBuilder`]: tower::ServiceBuilder
/// [interceptors]: crate::service::interceptor_fn /// [interceptors]: crate::service::Interceptor
pub fn layer<NewLayer>(self, new_layer: NewLayer) -> Server<NewLayer> { pub fn layer<NewLayer>(self, new_layer: NewLayer) -> Server<NewLayer> {
Server { Server {
layer: new_layer, layer: new_layer,