diff --git a/deny.toml b/deny.toml index 0d2423ab4..ff2365b14 100644 --- a/deny.toml +++ b/deny.toml @@ -17,6 +17,8 @@ deny = [ skip-tree = [ { name = "winapi", version = "<= 0.3" }, { name = "autocfg", version = "<= 1" }, + { name = "base64", version = "<= 0.11" }, + { name = "version_check", version = "<= 0.9" } ] [licenses] diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 5a781b3e2..aba35312e 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -95,12 +95,12 @@ name = "interceptor-server" path = "src/interceptor/server.rs" [[bin]] -name = "hyper-client" -path = "src/hyper/client.rs" +name = "hyper-warp-client" +path = "src/hyper_warp/client.rs" [[bin]] -name = "hyper-server" -path = "src/hyper/server.rs" +name = "hyper-warp-server" +path = "src/hyper_warp/server.rs" [dependencies] tonic = { path = "../tonic", features = ["tls"] } @@ -122,6 +122,10 @@ tracing-futures = "0.2" prost-types = "0.6" # Hyper example hyper = "0.13" +warp = { version = "0.2", default-features = false } +http = "0.2" +http-body = "0.3" +pin-project = "0.4" [build-dependencies] tonic-build = { path = "../tonic-build" } diff --git a/examples/src/hyper/server.rs b/examples/src/hyper/server.rs deleted file mode 100644 index f2d725d75..000000000 --- a/examples/src/hyper/server.rs +++ /dev/null @@ -1,46 +0,0 @@ -use futures::future; -use hyper::{service::make_service_fn, Server}; -use std::convert::Infallible; -use tonic::{Request, Response, Status}; - -use hello_world::greeter_server::{Greeter, GreeterServer}; -use hello_world::{HelloReply, HelloRequest}; - -pub mod hello_world { - tonic::include_proto!("helloworld"); -} - -#[derive(Default)] -pub struct MyGreeter {} - -#[tonic::async_trait] -impl Greeter for MyGreeter { - async fn say_hello( - &self, - request: Request, - ) -> Result, Status> { - let reply = hello_world::HelloReply { - message: format!("Hello {}!", request.into_inner().name), - }; - Ok(Response::new(reply)) - } -} - -#[tokio::main] -async fn main() -> Result<(), Box> { - let addr = "[::1]:50051".parse().unwrap(); - let greeter = MyGreeter::default(); - - println!("GreeterServer listening on {}", addr); - - let svc = GreeterServer::new(greeter); - - Server::bind(&addr) - .http2_only(true) - .serve(make_service_fn(|_| { - future::ok::<_, Infallible>(svc.clone()) - })) - .await?; - - Ok(()) -} diff --git a/examples/src/hyper/client.rs b/examples/src/hyper_warp/client.rs similarity index 87% rename from examples/src/hyper/client.rs rename to examples/src/hyper_warp/client.rs index bc1719040..49091639e 100644 --- a/examples/src/hyper/client.rs +++ b/examples/src/hyper_warp/client.rs @@ -1,3 +1,8 @@ +//! To hit the gRPC endpoint you must run this client via: +//! `cargo run --bin hyper-warp-client +//! To hit the warp server you can run this command: +//! `curl localhost:50051/hello` + use hello_world::greeter_client::GreeterClient; use hello_world::HelloRequest; use hyper::{Client, Uri}; diff --git a/examples/src/hyper_warp/server.rs b/examples/src/hyper_warp/server.rs new file mode 100644 index 000000000..ba97e3716 --- /dev/null +++ b/examples/src/hyper_warp/server.rs @@ -0,0 +1,130 @@ +//! To hit the gRPC endpoint you must run this client via: +//! `cargo run --bin hyper-warp-client +//! To hit the warp server you can run this command: +//! `curl localhost:50051/hello` + +use futures::future::{self, Either, TryFutureExt}; +use http::version::Version; +use hyper::{service::make_service_fn, Server}; +use pin_project::{pin_project, project}; +use std::convert::Infallible; +use std::{ + pin::Pin, + task::{Context, Poll}, +}; +use tonic::{Request, Response, Status}; +use tower::Service; +use warp::Filter; + +use hello_world::greeter_server::{Greeter, GreeterServer}; +use hello_world::{HelloReply, HelloRequest}; + +type Error = Box; + +pub mod hello_world { + tonic::include_proto!("helloworld"); +} + +#[derive(Default)] +pub struct MyGreeter {} + +#[tonic::async_trait] +impl Greeter for MyGreeter { + async fn say_hello( + &self, + request: Request, + ) -> Result, Status> { + let reply = hello_world::HelloReply { + message: format!("Hello {}!", request.into_inner().name), + }; + Ok(Response::new(reply)) + } +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + let addr = "[::1]:50051".parse().unwrap(); + let greeter = MyGreeter::default(); + + println!("GreeterServer listening on {}", addr); + + let tonic = GreeterServer::new(greeter); + let warp = warp::service(warp::path("hello").map(|| "hello, world!")); + + Server::bind(&addr) + .serve(make_service_fn(move |_| { + let mut tonic = tonic.clone(); + let mut warp = warp.clone(); + future::ok::<_, Infallible>(tower::service_fn( + move |req: hyper::Request| match req.version() { + Version::HTTP_11 | Version::HTTP_10 => Either::Left( + warp.call(req) + .map_ok(|res| res.map(EitherBody::Left)) + .map_err(Error::from), + ), + Version::HTTP_2 => Either::Right( + tonic + .call(req) + .map_ok(|res| res.map(EitherBody::Right)) + .map_err(Error::from), + ), + _ => unimplemented!(), + }, + )) + })) + .await?; + + Ok(()) +} + +#[pin_project] +enum EitherBody { + Left(#[pin] A), + Right(#[pin] B), +} + +impl http_body::Body for EitherBody +where + A: http_body::Body + Send, + B: http_body::Body + Send, + A::Error: Into, + B::Error: Into, +{ + type Data = A::Data; + type Error = Box; + + fn is_end_stream(&self) -> bool { + match self { + EitherBody::Left(b) => b.is_end_stream(), + EitherBody::Right(b) => b.is_end_stream(), + } + } + + #[project] + fn poll_data( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll>> { + #[project] + match self.project() { + EitherBody::Left(b) => b.poll_data(cx).map(map_option_err), + EitherBody::Right(b) => b.poll_data(cx).map(map_option_err), + } + } + + #[project] + fn poll_trailers( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll, Self::Error>> { + #[project] + match self.project() { + EitherBody::Left(b) => b.poll_trailers(cx).map_err(Into::into), + EitherBody::Right(b) => b.poll_trailers(cx).map_err(Into::into), + } + } +} + +fn map_option_err>(err: Option>) -> Option> { + err.map(|e| e.map_err(Into::into)) +}