diff --git a/deny.toml b/deny.toml index ab45c04e8..aea2fb0de 100644 --- a/deny.toml +++ b/deny.toml @@ -18,6 +18,7 @@ skip-tree = [ { name = "rustls-pemfile" }, { name = "windows-sys" }, { name = "hermit-abi" }, + { name = "syn" }, ] [licenses] diff --git a/examples/src/grpc-web/server.rs b/examples/src/grpc-web/server.rs index 0e8d51f32..7734a7663 100644 --- a/examples/src/grpc-web/server.rs +++ b/examples/src/grpc-web/server.rs @@ -2,8 +2,6 @@ use tonic::{transport::Server, Request, Response, Status}; use hello_world::greeter_server::{Greeter, GreeterServer}; use hello_world::{HelloReply, HelloRequest}; -use tonic_web::GrpcWebLayer; -use tower_http::cors::CorsLayer; pub mod hello_world { tonic::include_proto!("helloworld"); @@ -41,13 +39,7 @@ async fn main() -> Result<(), Box> { Server::builder() // GrpcWeb is over http1 so we must enable it. .accept_http1(true) - // Use the Cors layer from `tower-http`. - .layer(CorsLayer::new()) - // Apply the tonic-web layer to convert - // http1 requests into something that - // the core tonic code can understand. - .layer(GrpcWebLayer::new()) - .add_service(greeter) + .add_service(tonic_web::enable(greeter)) .serve(addr) .await?; diff --git a/tonic-web/src/lib.rs b/tonic-web/src/lib.rs index 00c2326f9..94f5f0f29 100644 --- a/tonic-web/src/lib.rs +++ b/tonic-web/src/lib.rs @@ -5,13 +5,6 @@ //! with a [tower] service that performs the translation between protocols and handles `cors` //! requests. //! -//! ## Getting Started -//! -//! ```toml -//! [dependencies] -//! tonic_web = "0.1" -//! ``` -//! //! ## Enabling tonic services //! //! The easiest way to get started, is to call the [`enable`] function with your tonic service @@ -31,12 +24,29 @@ //! //! Ok(()) //! } -//! //! ``` //! This will apply a default configuration that works well with grpc-web clients out of the box. //! //! You can customize the CORS configuration composing the [`GrpcWebLayer`] with the cors layer of your choice. //! +//! ```ignore +//! #[tokio::main] +//! async fn main() -> Result<(), Box> { +//! let addr = "[::1]:50051".parse().unwrap(); +//! let greeter = GreeterServer::new(MyGreeter::default()); +//! +//! Server::builder() +//! .accept_http1(true) +//! // This will apply the gRPC-Web translation layer +//! .layer(GrpcWebLayer::new()) +//! .add_service(greeter) +//! .serve(addr) +//! .await?; +//! +//! Ok(()) +//! } +//! ``` +//! //! Alternatively, if you have a tls enabled server, you could skip setting `accept_http1` to `true`. //! This works because the browser will handle `ALPN`. //! @@ -96,8 +106,8 @@ mod service; use http::header::HeaderName; use std::time::Duration; -use tonic::body::BoxBody; -use tower_http::cors::{AllowOrigin, Cors, CorsLayer}; +use tonic::{body::BoxBody, server::NamedService}; +use tower_http::cors::{AllowOrigin, CorsLayer}; use tower_layer::Layer; use tower_service::Service; @@ -112,14 +122,14 @@ type BoxError = Box; /// Enable a tonic service to handle grpc-web requests with the default configuration. /// /// You can customize the CORS configuration composing the [`GrpcWebLayer`] with the cors layer of your choice. -pub fn enable(service: S) -> Cors> +pub fn enable(service: S) -> CorsGrpcWeb where S: Service, Response = http::Response>, S: Clone + Send + 'static, S::Future: Send + 'static, S::Error: Into + Send, { - CorsLayer::new() + let cors = CorsLayer::new() .allow_origin(AllowOrigin::mirror_request()) .allow_credentials(true) .max_age(DEFAULT_MAX_AGE) @@ -136,8 +146,45 @@ where .cloned() .map(HeaderName::from_static) .collect::>(), - ) - .layer(GrpcWebService::new(service)) + ); + + tower_layer::layer_fn(|s| CorsGrpcWeb(cors.layer(s))).layer(GrpcWebService::new(service)) +} + +/// A newtype wrapper around [`GrpcWebLayer`] and [`tower_http::cors::CorsLayer`] to allow +/// `tonic_web::enable` to implement the [`NamedService`] trait. +#[derive(Debug, Clone)] +pub struct CorsGrpcWeb(tower_http::cors::Cors>); + +impl Service> for CorsGrpcWeb +where + S: Service, Response = http::Response>, + S: Clone + Send + 'static, + S::Future: Send + 'static, + S::Error: Into + Send, +{ + type Response = S::Response; + type Error = S::Error; + type Future = + > as Service>>::Future; + + fn poll_ready( + &mut self, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + self.0.poll_ready(cx) + } + + fn call(&mut self, req: http::Request) -> Self::Future { + self.0.call(req) + } +} + +impl NamedService for CorsGrpcWeb +where + S: NamedService, +{ + const NAME: &'static str = S::NAME; } pub(crate) mod util {