From 1f95ef0cbfa126d9797bd87a1cd94ec7701dee3d Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Fri, 28 Feb 2020 19:45:36 +0700 Subject: [PATCH 1/6] Add add_optional_service method --- tonic/src/transport/server/mod.rs | 54 ++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/tonic/src/transport/server/mod.rs b/tonic/src/transport/server/mod.rs index 7751029f4..b38f36429 100644 --- a/tonic/src/transport/server/mod.rs +++ b/tonic/src/transport/server/mod.rs @@ -41,7 +41,8 @@ use std::{ }; use tokio::io::{AsyncRead, AsyncWrite}; use tower::{ - limit::concurrency::ConcurrencyLimitLayer, timeout::TimeoutLayer, Service, ServiceBuilder, + limit::concurrency::ConcurrencyLimitLayer, timeout::TimeoutLayer, util::Either, Service, + ServiceBuilder, }; use tracing_futures::{Instrument, Instrumented}; @@ -86,6 +87,10 @@ pub trait NamedService { const NAME: &'static str; } +impl NamedService for Either { + const NAME: &'static str = S::NAME; +} + impl Server { /// Create a new server builder that can configure a [`Server`]. pub fn builder() -> Self { @@ -231,6 +236,30 @@ impl Server { Router::new(self.clone(), svc) } + /// Create a router with the optional `S` typed service as the first service. + /// + /// This will clone the `Server` builder and create a router that will + /// route around different services. + pub fn add_optional_service( + &mut self, + svc: Option, + ) -> Router, Unimplemented> + where + S: Service, Response = Response> + + NamedService + + Clone + + Send + + 'static, + S::Future: Send + 'static, + S::Error: Into + Send, + { + let svc = match svc { + Some(some) => Either::A(some), + None => Either::B(EmptyService), + }; + Router::new(self.clone(), svc) + } + pub(crate) async fn serve_with_shutdown( self, svc: S, @@ -528,3 +557,26 @@ impl Service> for Unimplemented { ) } } + +#[derive(Clone)] +pub struct EmptyService; + +impl Service> for EmptyService { + type Response = Response; + type Error = crate::Error; + type Future = future::Ready>; + + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + Ok(()).into() + } + + fn call(&mut self, _req: Request) -> Self::Future { + future::ok( + http::Response::builder() + .status(200) + .header("grpc-status", "12") + .body(BoxBody::empty()) + .unwrap(), + ) + } +} From 8674b27803a3288560b804dc510fe2a0e4324dfe Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Fri, 26 Jun 2020 13:54:48 +0100 Subject: [PATCH 2/6] Add optional_service example --- examples/Cargo.toml | 4 +++ examples/src/optional/server.rs | 53 +++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 examples/src/optional/server.rs diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 211db44dd..24dcce2cb 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -122,6 +122,10 @@ path = "src/health/server.rs" name = "autoreload-server" path = "src/autoreload/server.rs" +[[bin]] +name = "optional-server" +path = "src/optional/server.rs" + [dependencies] tonic = { path = "../tonic", features = ["tls"] } prost = "0.6" diff --git a/examples/src/optional/server.rs b/examples/src/optional/server.rs new file mode 100644 index 000000000..2e50296a8 --- /dev/null +++ b/examples/src/optional/server.rs @@ -0,0 +1,53 @@ +use tonic::{transport::Server, Request, Response, Status}; +use std::env; + +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> { + println!("Got a request from {:?}", request.remote_addr()); + + let reply = hello_world::HelloReply { + message: format!("Hello {}!", request.into_inner().name), + }; + Ok(Response::new(reply)) + } +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + let args: Vec = env::args().collect(); + let enabled = args.get(1) == Some(&"enabled".to_string()); + + let addr = "[::1]:50051".parse().unwrap(); + let greeter = MyGreeter::default(); + + let optional_service = if enabled { + println!("MyGreeter enabled"); + Some(GreeterServer::new(greeter)) + } else { + println!("MyGreeter disabled"); + None + }; + + println!("GreeterServer listening on {}", addr); + + Server::builder() + .add_optional_service(optional_service) + .serve(addr) + .await?; + + Ok(()) +} From 7f0f4f35f77bdaa9aa6be7fc85e989c5b415402d Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Fri, 10 Jul 2020 16:39:38 +0100 Subject: [PATCH 3/6] Use Unimplemented service for optional service --- tonic/src/transport/server/mod.rs | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/tonic/src/transport/server/mod.rs b/tonic/src/transport/server/mod.rs index b38f36429..52b5c31ba 100644 --- a/tonic/src/transport/server/mod.rs +++ b/tonic/src/transport/server/mod.rs @@ -243,7 +243,7 @@ impl Server { pub fn add_optional_service( &mut self, svc: Option, - ) -> Router, Unimplemented> + ) -> Router, Unimplemented> where S: Service, Response = Response> + NamedService @@ -255,7 +255,7 @@ impl Server { { let svc = match svc { Some(some) => Either::A(some), - None => Either::B(EmptyService), + None => Either::B(Unimplemented::default()), }; Router::new(self.clone(), svc) } @@ -557,26 +557,3 @@ impl Service> for Unimplemented { ) } } - -#[derive(Clone)] -pub struct EmptyService; - -impl Service> for EmptyService { - type Response = Response; - type Error = crate::Error; - type Future = future::Ready>; - - fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { - Ok(()).into() - } - - fn call(&mut self, _req: Request) -> Self::Future { - future::ok( - http::Response::builder() - .status(200) - .header("grpc-status", "12") - .body(BoxBody::empty()) - .unwrap(), - ) - } -} From e895991030bfcbe31ace472bd5df331661f77ff9 Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Fri, 10 Jul 2020 17:07:58 +0100 Subject: [PATCH 4/6] Add add_optional_service to Router and improve documentation --- tonic/src/transport/server/mod.rs | 40 +++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tonic/src/transport/server/mod.rs b/tonic/src/transport/server/mod.rs index 52b5c31ba..8b2043851 100644 --- a/tonic/src/transport/server/mod.rs +++ b/tonic/src/transport/server/mod.rs @@ -240,6 +240,10 @@ impl Server { /// /// This will clone the `Server` builder and create a router that will /// route around different services. + /// + /// # Note + /// Even when the argument given is `None` this will capture *all* requests to this service name. + /// As a result, one cannot use this to toggle between two indentically named implementations. pub fn add_optional_service( &mut self, svc: Option, @@ -371,6 +375,42 @@ where Router { server, routes } } + /// Add a new optional service to this router. + /// + /// # Note + /// Even when the argument given is `None` this will capture *all* requests to this service name. + /// As a result, one cannot use this to toggle between two indentically named implementations. + pub fn add_optional_service( + self, + svc: Option, + ) -> Router, Or>> + where + S: Service, Response = Response> + + NamedService + + Clone + + Send + + 'static, + S::Future: Send + 'static, + S::Error: Into + Send, + { + let Self { routes, server } = self; + + let svc_name = ::NAME; + let svc_route = format!("/{}", svc_name); + let pred = move |req: &Request| { + let path = req.uri().path(); + + path.starts_with(&svc_route) + }; + let svc = match svc { + Some(some) => Either::A(some), + None => Either::B(Unimplemented::default()), + }; + let routes = routes.push(pred, svc); + + Router { server, routes } + } + /// Consume this [`Server`] creating a future that will execute the server /// on [`tokio`]'s default executor. /// From 1ba04557266d5a3d6cec15a6416e61b08c2aa3c8 Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Fri, 10 Jul 2020 17:25:14 +0100 Subject: [PATCH 5/6] Fix typo in optional example --- examples/src/optional/server.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/src/optional/server.rs b/examples/src/optional/server.rs index 2e50296a8..300010be1 100644 --- a/examples/src/optional/server.rs +++ b/examples/src/optional/server.rs @@ -29,7 +29,7 @@ impl Greeter for MyGreeter { #[tokio::main] async fn main() -> Result<(), Box> { let args: Vec = env::args().collect(); - let enabled = args.get(1) == Some(&"enabled".to_string()); + let enabled = args.get(1) == Some(&"enable".to_string()); let addr = "[::1]:50051".parse().unwrap(); let greeter = MyGreeter::default(); From ce7c725af3625eac3d4de86bf128e83f8abcae90 Mon Sep 17 00:00:00 2001 From: Harry Barber Date: Fri, 10 Jul 2020 17:25:41 +0100 Subject: [PATCH 6/6] cargo fmt --- examples/src/optional/server.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/src/optional/server.rs b/examples/src/optional/server.rs index 300010be1..f1d4fa685 100644 --- a/examples/src/optional/server.rs +++ b/examples/src/optional/server.rs @@ -1,5 +1,5 @@ -use tonic::{transport::Server, Request, Response, Status}; use std::env; +use tonic::{transport::Server, Request, Response, Status}; use hello_world::greeter_server::{Greeter, GreeterServer}; use hello_world::{HelloReply, HelloRequest};