Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(tonic): expose Interceptor trait #713

Merged
merged 3 commits into from
Jul 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/src/tower/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {

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);

Expand Down
2 changes: 1 addition & 1 deletion examples/src/tower/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 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()
Expand Down
2 changes: 1 addition & 1 deletion tonic-build/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ pub fn generate<T: Service>(

pub fn with_interceptor<F>(inner: T, interceptor: F) -> #service_ident<InterceptedService<T, F>>
where
F: FnMut(tonic::Request<()>) -> Result<tonic::Request<()>, tonic::Status>,
F: tonic::service::Interceptor,
T: tonic::codegen::Service<
http::Request<tonic::body::BoxBody>,
Response = http::Response<<T as tonic::client::GrpcService<tonic::body::BoxBody>>::ResponseBody>
Expand Down
2 changes: 1 addition & 1 deletion tonic-build/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ pub fn generate<T: Service>(

pub fn with_interceptor<F>(inner: T, interceptor: F) -> InterceptedService<Self, F>
where
F: FnMut(tonic::Request<()>) -> Result<tonic::Request<()>, tonic::Status>,
F: tonic::service::Interceptor,
{
InterceptedService::new(Self::new(inner), interceptor)
}
Expand Down
4 changes: 2 additions & 2 deletions tonic/src/extensions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions tonic/src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,13 +300,13 @@ impl<T> Request<T> {
/// 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(),
/// });
Expand Down
74 changes: 61 additions & 13 deletions tonic/src/service/interceptor.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<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
/// generated structs.
///
Expand All @@ -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: 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
F: FnMut(crate::Request<()>) -> Result<crate::Request<()>, Status>,
{
InterceptorFn { f }
fn call(&mut self, request: crate::Request<()>) -> Result<crate::Request<()>, Status> {
self(request)
}
}

/// Create a new interceptor layer.
///
/// See [`Interceptor`] for more details.
pub fn interceptor<F>(f: F) -> InterceptorLayer<F>
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: 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)]
pub struct InterceptorFn<F> {
pub struct InterceptorLayer<F> {
f: F,
}

impl<S, F> Layer<S> for InterceptorFn<F>
impl<S, F> Layer<S> for InterceptorLayer<F>
where
F: FnMut(crate::Request<()>) -> Result<crate::Request<()>, Status> + Clone,
F: Interceptor + Clone,
{
type Service = InterceptedService<S, F>;

Expand All @@ -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.
///
/// See [`interceptor_fn`] for more details.
/// See [`Interceptor`] for more details.
#[derive(Clone, Copy)]
pub struct InterceptedService<S, F> {
inner: S,
Expand All @@ -75,7 +120,7 @@ impl<S, F> InterceptedService<S, F> {
/// function `F`.
pub fn new(service: S, f: F) -> Self
where
F: FnMut(crate::Request<()>) -> Result<crate::Request<()>, Status>,
F: Interceptor,
{
Self { inner: service, f }
}
Expand All @@ -95,7 +140,7 @@ where

impl<S, F, ReqBody, ResBody> Service<http::Request<ReqBody>> for InterceptedService<S, F>
where
F: FnMut(crate::Request<()>) -> Result<crate::Request<()>, Status>,
F: Interceptor,
S: Service<http::Request<ReqBody>, Response = http::Response<ResBody>>,
S::Error: Into<crate::Error>,
{
Expand All @@ -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);
Expand Down
3 changes: 2 additions & 1 deletion tonic/src/service/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
8 changes: 4 additions & 4 deletions tonic/src/transport/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ impl<L> Server<L> {
/// # 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<Request<()>, Status> {
/// if valid_credentials(&request) {
Expand All @@ -417,8 +417,8 @@ impl<L> Server<L> {
/// 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);
Expand All @@ -428,7 +428,7 @@ impl<L> Server<L> {
/// [`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<NewLayer>(self, new_layer: NewLayer) -> Server<NewLayer> {
Server {
layer: new_layer,
Expand Down