Skip to content

Commit a9db219

Browse files
authoredMar 20, 2023
fix(web): Fix enable and update docs (#1326)
* fix(web): Fix `enable` and update docs * fix example, update deny * fix namedservice path
1 parent 69ce71e commit a9db219

File tree

3 files changed

+63
-23
lines changed

3 files changed

+63
-23
lines changed
 

‎deny.toml

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ skip-tree = [
1818
{ name = "rustls-pemfile" },
1919
{ name = "windows-sys" },
2020
{ name = "hermit-abi" },
21+
{ name = "syn" },
2122
]
2223

2324
[licenses]

‎examples/src/grpc-web/server.rs

+1-9
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ use tonic::{transport::Server, Request, Response, Status};
22

33
use hello_world::greeter_server::{Greeter, GreeterServer};
44
use hello_world::{HelloReply, HelloRequest};
5-
use tonic_web::GrpcWebLayer;
6-
use tower_http::cors::CorsLayer;
75

86
pub mod hello_world {
97
tonic::include_proto!("helloworld");
@@ -41,13 +39,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
4139
Server::builder()
4240
// GrpcWeb is over http1 so we must enable it.
4341
.accept_http1(true)
44-
// Use the Cors layer from `tower-http`.
45-
.layer(CorsLayer::new())
46-
// Apply the tonic-web layer to convert
47-
// http1 requests into something that
48-
// the core tonic code can understand.
49-
.layer(GrpcWebLayer::new())
50-
.add_service(greeter)
42+
.add_service(tonic_web::enable(greeter))
5143
.serve(addr)
5244
.await?;
5345

‎tonic-web/src/lib.rs

+61-14
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,6 @@
55
//! with a [tower] service that performs the translation between protocols and handles `cors`
66
//! requests.
77
//!
8-
//! ## Getting Started
9-
//!
10-
//! ```toml
11-
//! [dependencies]
12-
//! tonic_web = "0.1"
13-
//! ```
14-
//!
158
//! ## Enabling tonic services
169
//!
1710
//! The easiest way to get started, is to call the [`enable`] function with your tonic service
@@ -31,12 +24,29 @@
3124
//!
3225
//! Ok(())
3326
//! }
34-
//!
3527
//! ```
3628
//! This will apply a default configuration that works well with grpc-web clients out of the box.
3729
//!
3830
//! You can customize the CORS configuration composing the [`GrpcWebLayer`] with the cors layer of your choice.
3931
//!
32+
//! ```ignore
33+
//! #[tokio::main]
34+
//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
35+
//! let addr = "[::1]:50051".parse().unwrap();
36+
//! let greeter = GreeterServer::new(MyGreeter::default());
37+
//!
38+
//! Server::builder()
39+
//! .accept_http1(true)
40+
//! // This will apply the gRPC-Web translation layer
41+
//! .layer(GrpcWebLayer::new())
42+
//! .add_service(greeter)
43+
//! .serve(addr)
44+
//! .await?;
45+
//!
46+
//! Ok(())
47+
//! }
48+
//! ```
49+
//!
4050
//! Alternatively, if you have a tls enabled server, you could skip setting `accept_http1` to `true`.
4151
//! This works because the browser will handle `ALPN`.
4252
//!
@@ -96,8 +106,8 @@ mod service;
96106

97107
use http::header::HeaderName;
98108
use std::time::Duration;
99-
use tonic::body::BoxBody;
100-
use tower_http::cors::{AllowOrigin, Cors, CorsLayer};
109+
use tonic::{body::BoxBody, server::NamedService};
110+
use tower_http::cors::{AllowOrigin, CorsLayer};
101111
use tower_layer::Layer;
102112
use tower_service::Service;
103113

@@ -112,14 +122,14 @@ type BoxError = Box<dyn std::error::Error + Send + Sync>;
112122
/// Enable a tonic service to handle grpc-web requests with the default configuration.
113123
///
114124
/// You can customize the CORS configuration composing the [`GrpcWebLayer`] with the cors layer of your choice.
115-
pub fn enable<S>(service: S) -> Cors<GrpcWebService<S>>
125+
pub fn enable<S>(service: S) -> CorsGrpcWeb<S>
116126
where
117127
S: Service<http::Request<hyper::Body>, Response = http::Response<BoxBody>>,
118128
S: Clone + Send + 'static,
119129
S::Future: Send + 'static,
120130
S::Error: Into<BoxError> + Send,
121131
{
122-
CorsLayer::new()
132+
let cors = CorsLayer::new()
123133
.allow_origin(AllowOrigin::mirror_request())
124134
.allow_credentials(true)
125135
.max_age(DEFAULT_MAX_AGE)
@@ -136,8 +146,45 @@ where
136146
.cloned()
137147
.map(HeaderName::from_static)
138148
.collect::<Vec<HeaderName>>(),
139-
)
140-
.layer(GrpcWebService::new(service))
149+
);
150+
151+
tower_layer::layer_fn(|s| CorsGrpcWeb(cors.layer(s))).layer(GrpcWebService::new(service))
152+
}
153+
154+
/// A newtype wrapper around [`GrpcWebLayer`] and [`tower_http::cors::CorsLayer`] to allow
155+
/// `tonic_web::enable` to implement the [`NamedService`] trait.
156+
#[derive(Debug, Clone)]
157+
pub struct CorsGrpcWeb<S>(tower_http::cors::Cors<GrpcWebService<S>>);
158+
159+
impl<S> Service<http::Request<hyper::Body>> for CorsGrpcWeb<S>
160+
where
161+
S: Service<http::Request<hyper::Body>, Response = http::Response<BoxBody>>,
162+
S: Clone + Send + 'static,
163+
S::Future: Send + 'static,
164+
S::Error: Into<BoxError> + Send,
165+
{
166+
type Response = S::Response;
167+
type Error = S::Error;
168+
type Future =
169+
<tower_http::cors::Cors<GrpcWebService<S>> as Service<http::Request<hyper::Body>>>::Future;
170+
171+
fn poll_ready(
172+
&mut self,
173+
cx: &mut std::task::Context<'_>,
174+
) -> std::task::Poll<Result<(), Self::Error>> {
175+
self.0.poll_ready(cx)
176+
}
177+
178+
fn call(&mut self, req: http::Request<hyper::Body>) -> Self::Future {
179+
self.0.call(req)
180+
}
181+
}
182+
183+
impl<S> NamedService for CorsGrpcWeb<S>
184+
where
185+
S: NamedService,
186+
{
187+
const NAME: &'static str = S::NAME;
141188
}
142189

143190
pub(crate) mod util {

0 commit comments

Comments
 (0)
Please sign in to comment.