Skip to content

Commit

Permalink
fix(web): Fix enable and update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
LucioFranco committed Mar 20, 2023
1 parent ddec9e3 commit 90b0445
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 21 deletions.
8 changes: 1 addition & 7 deletions examples/src/grpc-web/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
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?;

Expand Down
75 changes: 61 additions & 14 deletions tonic-web/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<dyn std::error::Error>> {
//! 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`.
//!
Expand Down Expand Up @@ -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, transport::NamedService};
use tower_http::cors::{AllowOrigin, CorsLayer};
use tower_layer::Layer;
use tower_service::Service;

Expand All @@ -112,14 +122,14 @@ type BoxError = Box<dyn std::error::Error + Send + Sync>;
/// 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<S>(service: S) -> Cors<GrpcWebService<S>>
pub fn enable<S>(service: S) -> CorsGrpcWeb<S>
where
S: Service<http::Request<hyper::Body>, Response = http::Response<BoxBody>>,
S: Clone + Send + 'static,
S::Future: Send + 'static,
S::Error: Into<BoxError> + Send,
{
CorsLayer::new()
let cors = CorsLayer::new()
.allow_origin(AllowOrigin::mirror_request())
.allow_credentials(true)
.max_age(DEFAULT_MAX_AGE)
Expand All @@ -136,8 +146,45 @@ where
.cloned()
.map(HeaderName::from_static)
.collect::<Vec<HeaderName>>(),
)
.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<S>(tower_http::cors::Cors<GrpcWebService<S>>);

impl<S> Service<http::Request<hyper::Body>> for CorsGrpcWeb<S>
where
S: Service<http::Request<hyper::Body>, Response = http::Response<BoxBody>>,
S: Clone + Send + 'static,
S::Future: Send + 'static,
S::Error: Into<BoxError> + Send,
{
type Response = S::Response;
type Error = S::Error;
type Future =
<tower_http::cors::Cors<GrpcWebService<S>> as Service<http::Request<hyper::Body>>>::Future;

fn poll_ready(
&mut self,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Result<(), Self::Error>> {
self.0.poll_ready(cx)
}

fn call(&mut self, req: http::Request<hyper::Body>) -> Self::Future {
self.0.call(req)
}
}

impl<S> NamedService for CorsGrpcWeb<S>
where
S: NamedService,
{
const NAME: &'static str = S::NAME;
}

pub(crate) mod util {
Expand Down

0 comments on commit 90b0445

Please sign in to comment.