feat(transport): Add service multiplexing/routing (#99)
* feat(transport): Add service multiplexing/routing
This change introduces a new "router" built on top of
`transport::Server` that allows one to run multiple
gRPC services on the same socket.
```rust
Server::builder()
.add_service(greeter)
.add_service(echo)
.serve(addr)
.await?;
```
There is also a new `multiplex` example showcasing
server side service multiplexing and client side
service multiplexing.
BREAKING CHANGES: `Server::serve` is now crate private
and all services must be added via `Server::add_service`.
Codegen also returns just a `Service` now instead of a
`MakeService` pair.
Closes #29
Signed-off-by: Lucio Franco [email protected]
This commit is contained in:
+40
-38
@@ -7,63 +7,32 @@ use syn::{Ident, Lit, LitStr};
|
||||
pub(crate) fn generate(service: &Service, proto_path: &str) -> TokenStream {
|
||||
let methods = generate_methods(&service, proto_path);
|
||||
|
||||
let server_make_service = quote::format_ident!("{}Server", service.name);
|
||||
let server_service = quote::format_ident!("{}ServerSvc", service.name);
|
||||
let server_service = quote::format_ident!("{}Server", service.name);
|
||||
let server_trait = quote::format_ident!("{}", service.name);
|
||||
let generated_trait = generate_trait(service, proto_path, server_trait.clone());
|
||||
let service_doc = generate_doc_comments(&service.comments.leading);
|
||||
let server_new_doc = generate_doc_comment(&format!(
|
||||
"Create a new {} from a type that implements {}.",
|
||||
server_make_service, server_trait
|
||||
));
|
||||
|
||||
// Transport based implementations
|
||||
let path = format!("{}.{}", service.package, service.proto_name);
|
||||
let transport = generate_transport(&server_service, &server_trait, &path);
|
||||
|
||||
quote! {
|
||||
#generated_trait
|
||||
|
||||
#service_doc
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct #server_make_service<T: #server_trait> {
|
||||
inner: Arc<T>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Debug)]
|
||||
#[doc(hidden)]
|
||||
pub struct #server_service<T: #server_trait> {
|
||||
inner: Arc<T>,
|
||||
}
|
||||
|
||||
impl<T: #server_trait> #server_make_service<T> {
|
||||
#server_new_doc
|
||||
impl<T: #server_trait> #server_service<T> {
|
||||
pub fn new(inner: T) -> Self {
|
||||
let inner = Arc::new(inner);
|
||||
Self::from_shared(inner)
|
||||
}
|
||||
|
||||
pub fn from_shared(inner: Arc<T>) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: #server_trait> #server_service<T> {
|
||||
pub fn new(inner: Arc<T>) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: #server_trait, R> Service<R> for #server_make_service<T> {
|
||||
type Response = #server_service<T>;
|
||||
type Error = Never;
|
||||
type Future = Ready<Result<Self::Response, Self::Error>>;
|
||||
|
||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, _: R) -> Self::Future {
|
||||
ok(#server_service::new(self.inner.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: #server_trait> Service<http::Request<HyperBody>> for #server_service<T> {
|
||||
type Response = http::Response<tonic::body::BoxBody>;
|
||||
type Error = Never;
|
||||
@@ -89,6 +58,15 @@ pub(crate) fn generate(service: &Service, proto_path: &str) -> TokenStream {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: #server_trait> Clone for #server_service<T> {
|
||||
fn clone(&self) -> Self {
|
||||
let inner = self.inner.clone();
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
#transport
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,6 +159,30 @@ fn generate_trait_methods(service: &Service, proto_path: &str) -> TokenStream {
|
||||
stream
|
||||
}
|
||||
|
||||
#[cfg(feature = "transport")]
|
||||
fn generate_transport(
|
||||
server_service: &syn::Ident,
|
||||
server_trait: &syn::Ident,
|
||||
service_name: &str,
|
||||
) -> TokenStream {
|
||||
let service_name = syn::LitStr::new(service_name, proc_macro2::Span::call_site());
|
||||
|
||||
quote! {
|
||||
impl<T: #server_trait> tonic::transport::ServiceName for #server_service<T> {
|
||||
const NAME: &'static str = #service_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "transport"))]
|
||||
fn generate_transport(
|
||||
_server_service: &syn::Ident,
|
||||
_server_trait: &syn::Ident,
|
||||
_service_name: &str,
|
||||
) -> TokenStream {
|
||||
TokenStream::new()
|
||||
}
|
||||
|
||||
fn generate_methods(service: &Service, proto_path: &str) -> TokenStream {
|
||||
let mut stream = TokenStream::new();
|
||||
|
||||
|
||||
@@ -52,6 +52,14 @@ path = "src/tls_client_auth/server.rs"
|
||||
name = "tls-client-auth-client"
|
||||
path = "src/tls_client_auth/client.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "multiplex-server"
|
||||
path = "src/multiplex/server.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "multiplex-client"
|
||||
path = "src/multiplex/client.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "gcp-client"
|
||||
path = "src/gcp/client.rs"
|
||||
|
||||
@@ -191,7 +191,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let greeter = MyGreeter {};
|
||||
|
||||
Server::builder()
|
||||
.serve(addr, GreeterServer::new(greeter))
|
||||
.add_service(GreeterServer::new(greeter))
|
||||
.serve(addr)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
@@ -236,7 +237,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let greeter = MyGreeter {};
|
||||
|
||||
Server::builder()
|
||||
.serve(addr, GreeterServer::new(greeter))
|
||||
.add_service(GreeterServer::new(greeter))
|
||||
.serve(addr)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -558,7 +558,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
let svc = server::RouteGuideServer::new(route_guide);
|
||||
|
||||
Server::builder().serve(addr, svc).await?;
|
||||
Server::builder()
|
||||
.add_service(svc)
|
||||
.serve(addr)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -78,8 +78,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
}
|
||||
})
|
||||
.clone()
|
||||
.serve(addr, pb::server::EchoServer::new(server))
|
||||
.add_service(pb::server::EchoServer::new(server))
|
||||
.serve(addr)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -33,7 +33,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let greeter = MyGreeter::default();
|
||||
|
||||
Server::builder()
|
||||
.serve(addr, GreeterServer::new(greeter))
|
||||
.add_service(GreeterServer::new(greeter))
|
||||
.serve(addr)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -60,7 +60,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut tx = tx.clone();
|
||||
|
||||
let server = EchoServer { addr };
|
||||
let serve = Server::builder().serve(addr, pb::server::EchoServer::new(server));
|
||||
let serve = Server::builder()
|
||||
.add_service(pb::server::EchoServer::new(server))
|
||||
.serve(addr);
|
||||
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = serve.await {
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
pub mod hello_world {
|
||||
tonic::include_proto!("helloworld");
|
||||
}
|
||||
|
||||
pub mod echo {
|
||||
tonic::include_proto!("grpc.examples.echo");
|
||||
}
|
||||
|
||||
use echo::{client::EchoClient, EchoRequest};
|
||||
use hello_world::{client::GreeterClient, HelloRequest};
|
||||
use tonic::transport::Endpoint;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let channel = Endpoint::from_static("http://[::1]:50051").channel();
|
||||
|
||||
let mut greeter_client = GreeterClient::new(channel.clone());
|
||||
let mut echo_client = EchoClient::new(channel);
|
||||
|
||||
let request = tonic::Request::new(HelloRequest {
|
||||
name: "Tonic".into(),
|
||||
});
|
||||
|
||||
let response = greeter_client.say_hello(request).await?;
|
||||
|
||||
println!("GREETER RESPONSE={:?}", response);
|
||||
|
||||
let request = tonic::Request::new(EchoRequest {
|
||||
message: "hello".into(),
|
||||
});
|
||||
|
||||
let response = echo_client.unary_echo(request).await?;
|
||||
|
||||
println!("ECHO RESPONSE={:?}", response);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
use std::collections::VecDeque;
|
||||
use tonic::{transport::Server, Request, Response, Status};
|
||||
|
||||
pub mod hello_world {
|
||||
tonic::include_proto!("helloworld");
|
||||
}
|
||||
|
||||
pub mod echo {
|
||||
tonic::include_proto!("grpc.examples.echo");
|
||||
}
|
||||
|
||||
use hello_world::{
|
||||
server::{Greeter, GreeterServer},
|
||||
HelloReply, HelloRequest,
|
||||
};
|
||||
|
||||
use echo::{
|
||||
server::{Echo, EchoServer},
|
||||
EchoRequest, EchoResponse,
|
||||
};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let addr = "[::1]:50051".parse().unwrap();
|
||||
|
||||
let greeter = GreeterServer::new(MyGreeter::default());
|
||||
let echo = EchoServer::new(MyEcho::default());
|
||||
|
||||
Server::builder()
|
||||
.add_service(greeter)
|
||||
.add_service(echo)
|
||||
.serve(addr)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MyGreeter {}
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl Greeter for MyGreeter {
|
||||
async fn say_hello(
|
||||
&self,
|
||||
request: Request<HelloRequest>,
|
||||
) -> Result<Response<HelloReply>, Status> {
|
||||
let reply = hello_world::HelloReply {
|
||||
message: format!("Hello {}!", request.into_inner().name).into(),
|
||||
};
|
||||
Ok(Response::new(reply))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MyEcho;
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl Echo for MyEcho {
|
||||
async fn unary_echo(
|
||||
&self,
|
||||
request: Request<EchoRequest>,
|
||||
) -> Result<Response<EchoResponse>, Status> {
|
||||
let message = request.into_inner().message;
|
||||
Ok(Response::new(EchoResponse { message }))
|
||||
}
|
||||
|
||||
type ServerStreamingEchoStream = VecDeque<Result<EchoResponse, Status>>;
|
||||
type BidirectionalStreamingEchoStream = VecDeque<Result<EchoResponse, Status>>;
|
||||
}
|
||||
@@ -155,7 +155,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
let svc = server::RouteGuideServer::new(route_guide);
|
||||
|
||||
Server::builder().serve(addr, svc).await?;
|
||||
Server::builder().add_service(svc).serve(addr).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -61,7 +61,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
Server::builder()
|
||||
.tls_config(ServerTlsConfig::with_rustls().identity(identity))
|
||||
.clone()
|
||||
.serve(addr, pb::server::EchoServer::new(server))
|
||||
.add_service(pb::server::EchoServer::new(server))
|
||||
.serve(addr)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -13,7 +13,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let client_key = tokio::fs::read("tonic-examples/data/tls/client1.key").await?;
|
||||
let client_identity = Identity::from_pem(client_cert, client_key);
|
||||
|
||||
let tls = ClientTlsConfig::with_openssl()
|
||||
let tls = ClientTlsConfig::with_rustls()
|
||||
.domain_name("localhost")
|
||||
.ca_certificate(server_root_ca_cert)
|
||||
.identity(client_identity)
|
||||
|
||||
@@ -44,8 +44,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
Server::builder()
|
||||
.tls_config(&tls)
|
||||
.clone()
|
||||
.serve(addr, pb::server::EchoServer::new(server))
|
||||
.add_service(pb::server::EchoServer::new(server))
|
||||
.serve(addr)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -57,106 +57,15 @@ async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
});
|
||||
|
||||
let test_service = server::TestServiceServer::new(server::TestService::default());
|
||||
let unimplemented_service =
|
||||
server::UnimplementedServiceServer::new(server::UnimplementedService::default());
|
||||
|
||||
builder
|
||||
.serve(
|
||||
addr,
|
||||
router::Router {
|
||||
test_service: std::sync::Arc::new(server::TestService),
|
||||
unimplemented_service: std::sync::Arc::new(server::UnimplementedService),
|
||||
},
|
||||
)
|
||||
.add_service(test_service)
|
||||
.add_service(unimplemented_service)
|
||||
.serve(addr)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
mod router {
|
||||
use futures_util::future;
|
||||
use http::{Request, Response};
|
||||
use std::sync::Arc;
|
||||
use std::{
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use tonic::{body::BoxBody, transport::Body};
|
||||
use tonic_interop::server::{
|
||||
TestService, TestServiceServer, UnimplementedService, UnimplementedServiceServer,
|
||||
};
|
||||
use tower::Service;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Router {
|
||||
pub test_service: Arc<TestService>,
|
||||
pub unimplemented_service: Arc<UnimplementedService>,
|
||||
}
|
||||
|
||||
impl Service<()> for Router {
|
||||
type Response = Router;
|
||||
type Error = Never;
|
||||
type Future = future::Ready<Result<Self::Response, Self::Error>>;
|
||||
|
||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Ok(()).into()
|
||||
}
|
||||
|
||||
fn call(&mut self, _req: ()) -> Self::Future {
|
||||
future::ok(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Service<Request<Body>> for Router {
|
||||
type Response = Response<BoxBody>;
|
||||
type Error = Never;
|
||||
type Future =
|
||||
Pin<Box<dyn Future<Output = Result<Response<BoxBody>, Never>> + Send + 'static>>;
|
||||
|
||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Ok(()).into()
|
||||
}
|
||||
|
||||
fn call(&mut self, req: Request<Body>) -> Self::Future {
|
||||
let mut segments = req.uri().path().split("/");
|
||||
segments.next();
|
||||
let service = segments.next().unwrap();
|
||||
|
||||
match service {
|
||||
"grpc.testing.TestService" => {
|
||||
let me = self.clone();
|
||||
Box::pin(async move {
|
||||
let mut svc = TestServiceServer::from_shared(me.test_service);
|
||||
let mut svc = svc.call(()).await.unwrap();
|
||||
|
||||
let res = svc.call(req).await.unwrap();
|
||||
Ok(res)
|
||||
})
|
||||
}
|
||||
|
||||
"grpc.testing.UnimplementedService" => {
|
||||
let me = self.clone();
|
||||
Box::pin(async move {
|
||||
let mut svc =
|
||||
UnimplementedServiceServer::from_shared(me.unimplemented_service);
|
||||
let mut svc = svc.call(()).await.unwrap();
|
||||
|
||||
let res = svc.call(req).await.unwrap();
|
||||
Ok(res)
|
||||
})
|
||||
}
|
||||
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Never {}
|
||||
|
||||
impl std::fmt::Display for Never {
|
||||
fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match *self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Never {}
|
||||
}
|
||||
|
||||
@@ -150,6 +150,7 @@ impl pb::server::TestService for TestService {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct UnimplementedService;
|
||||
|
||||
#[tonic::async_trait]
|
||||
|
||||
@@ -50,7 +50,23 @@
|
||||
//! # use futures_util::future::{err, ok};
|
||||
//! # #[cfg(feature = "rustls")]
|
||||
//! # async fn do_thing() -> Result<(), Box<dyn std::error::Error>> {
|
||||
//! # let my_svc = service_fn(|_| ok::<_, tonic::Status>(service_fn(|req| err(tonic::Status::unimplemented("")))));
|
||||
//! # #[derive(Clone)]
|
||||
//! # pub struct Svc;
|
||||
//! # impl Service<hyper::Request<hyper::Body>> for Svc {
|
||||
//! # type Response = hyper::Response<tonic::body::BoxBody>;
|
||||
//! # type Error = tonic::Status;
|
||||
//! # type Future = futures_util::future::Ready<Result<Self::Response, Self::Error>>;
|
||||
//! # fn poll_ready(&mut self, _cx: &mut std::task::Context<'_>) -> std::task::Poll<Result<(), Self::Error>> {
|
||||
//! # Ok(()).into()
|
||||
//! # }
|
||||
//! # fn call(&mut self, _req: hyper::Request<hyper::Body>) -> Self::Future {
|
||||
//! # unimplemented!()
|
||||
//! # }
|
||||
//! # }
|
||||
//! # impl tonic::transport::ServiceName for Svc {
|
||||
//! # const NAME: &'static str = "some_svc";
|
||||
//! # }
|
||||
//! # let my_svc = Svc;
|
||||
//! let cert = std::fs::read_to_string("server.pem")?;
|
||||
//! let key = std::fs::read_to_string("server.key")?;
|
||||
//!
|
||||
@@ -64,8 +80,8 @@
|
||||
//! println!("Request: {:?}", req);
|
||||
//! svc.call(req)
|
||||
//! })
|
||||
//! .clone()
|
||||
//! .serve(addr, my_svc)
|
||||
//! .add_service(my_svc)
|
||||
//! .serve(addr)
|
||||
//! .await?;
|
||||
//!
|
||||
//! # Ok(())
|
||||
@@ -88,7 +104,7 @@ pub use self::channel::Channel;
|
||||
pub use self::endpoint::Endpoint;
|
||||
pub use self::error::Error;
|
||||
#[doc(inline)]
|
||||
pub use self::server::Server;
|
||||
pub use self::server::{Server, ServiceName};
|
||||
pub use self::tls::{Certificate, Identity};
|
||||
pub use hyper::Body;
|
||||
|
||||
|
||||
+138
-22
@@ -1,6 +1,6 @@
|
||||
//! Server implementation and builder.
|
||||
|
||||
use super::service::{layer_fn, BoxedIo, ServiceBuilderExt};
|
||||
use super::service::{layer_fn, BoxedIo, Or, Routes, ServiceBuilderExt};
|
||||
#[cfg(feature = "tls")]
|
||||
use super::{
|
||||
service::TlsAcceptor,
|
||||
@@ -9,7 +9,7 @@ use super::{
|
||||
};
|
||||
use crate::body::BoxBody;
|
||||
use futures_core::Stream;
|
||||
use futures_util::{ready, try_future::MapErr, TryFutureExt, TryStreamExt};
|
||||
use futures_util::{future, ready, try_future::MapErr, TryFutureExt, TryStreamExt};
|
||||
use http::{Request, Response};
|
||||
use hyper::{
|
||||
server::{accept::Accept, conn},
|
||||
@@ -31,7 +31,6 @@ use tower::{
|
||||
Service,
|
||||
ServiceBuilder,
|
||||
};
|
||||
use tower_make::MakeService;
|
||||
#[cfg(feature = "tls")]
|
||||
use tracing::error;
|
||||
|
||||
@@ -58,6 +57,22 @@ pub struct Server {
|
||||
max_concurrent_streams: Option<u32>,
|
||||
}
|
||||
|
||||
/// A stack based `Service` router.
|
||||
#[derive(Debug)]
|
||||
pub struct Router<A, B> {
|
||||
server: Server,
|
||||
routes: Routes<A, B, Request<Body>>,
|
||||
}
|
||||
|
||||
/// A trait to provide a static reference to the service's
|
||||
/// name. This is used for routing service's within the router.
|
||||
pub trait ServiceName {
|
||||
/// The `Service-Name` as described [here].
|
||||
///
|
||||
/// [here]: https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests
|
||||
const NAME: &'static str;
|
||||
}
|
||||
|
||||
impl Server {
|
||||
/// Create a new server builder that can configure a [`Server`].
|
||||
pub fn builder() -> Self {
|
||||
@@ -149,14 +164,26 @@ impl Server {
|
||||
self
|
||||
}
|
||||
|
||||
/// Consume this [`Server`] creating a future that will execute the server
|
||||
/// on [`tokio`]'s default executor.
|
||||
pub async fn serve<M, S>(self, addr: SocketAddr, svc: M) -> Result<(), super::Error>
|
||||
/// Create a router with the `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_service<S>(&mut self, svc: S) -> Router<S, Unimplemented>
|
||||
where
|
||||
M: Service<(), Response = S>,
|
||||
M::Error: Into<crate::Error> + Send + 'static,
|
||||
M::Future: Send + 'static,
|
||||
S: Service<Request<Body>, Response = Response<BoxBody>> + Send + 'static,
|
||||
S: Service<Request<Body>, Response = Response<BoxBody>>
|
||||
+ ServiceName
|
||||
+ Clone
|
||||
+ Send
|
||||
+ 'static,
|
||||
S::Future: Send + 'static,
|
||||
S::Error: Into<crate::Error> + Send,
|
||||
{
|
||||
Router::new(self.clone(), svc)
|
||||
}
|
||||
|
||||
pub(crate) async fn serve<S>(self, addr: SocketAddr, svc: S) -> Result<(), super::Error>
|
||||
where
|
||||
S: Service<Request<Body>, Response = Response<BoxBody>> + Clone + Send + 'static,
|
||||
S::Future: Send + 'static,
|
||||
S::Error: Into<crate::Error> + Send,
|
||||
{
|
||||
@@ -210,6 +237,74 @@ impl Server {
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Router<S, Unimplemented> {
|
||||
pub(crate) fn new(server: Server, svc: S) -> Self
|
||||
where
|
||||
S: Service<Request<Body>, Response = Response<BoxBody>>
|
||||
+ ServiceName
|
||||
+ Clone
|
||||
+ Send
|
||||
+ 'static,
|
||||
S::Future: Send + 'static,
|
||||
S::Error: Into<crate::Error> + Send,
|
||||
{
|
||||
let svc_name = <S as ServiceName>::NAME;
|
||||
let svc_route = format!("/{}", svc_name);
|
||||
let pred = move |req: &Request<Body>| {
|
||||
let path = req.uri().path();
|
||||
|
||||
path.starts_with(&svc_route)
|
||||
};
|
||||
Self {
|
||||
server,
|
||||
routes: Routes::new(pred, svc, Unimplemented::default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> Router<A, B>
|
||||
where
|
||||
A: Service<Request<Body>, Response = Response<BoxBody>> + Clone + Send + 'static,
|
||||
A::Future: Send + 'static,
|
||||
A::Error: Into<crate::Error> + Send,
|
||||
B: Service<Request<Body>, Response = Response<BoxBody>> + Clone + Send + 'static,
|
||||
B::Future: Send + 'static,
|
||||
B::Error: Into<crate::Error> + Send,
|
||||
{
|
||||
/// Add a new service to this router.
|
||||
pub fn add_service<S>(self, svc: S) -> Router<S, Or<A, B, Request<Body>>>
|
||||
where
|
||||
S: Service<Request<Body>, Response = Response<BoxBody>>
|
||||
+ ServiceName
|
||||
+ Clone
|
||||
+ Send
|
||||
+ 'static,
|
||||
S::Future: Send + 'static,
|
||||
S::Error: Into<crate::Error> + Send,
|
||||
{
|
||||
let Self { routes, server } = self;
|
||||
|
||||
let svc_name = <S as ServiceName>::NAME;
|
||||
let svc_route = format!("/{}", svc_name);
|
||||
let pred = move |req: &Request<Body>| {
|
||||
let path = req.uri().path();
|
||||
|
||||
path.starts_with(&svc_route)
|
||||
};
|
||||
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.
|
||||
///
|
||||
/// [`Server`]: struct.Server.html
|
||||
pub async fn serve(self, addr: SocketAddr) -> Result<(), super::Error> {
|
||||
self.server.serve(addr, self.routes).await
|
||||
}
|
||||
}
|
||||
|
||||
fn map_err(e: impl Into<crate::Error>) -> super::Error {
|
||||
super::Error::from_source(super::ErrorKind::Server, e.into())
|
||||
}
|
||||
@@ -371,19 +466,16 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
struct MakeSvc<M> {
|
||||
struct MakeSvc<S> {
|
||||
interceptor: Option<Interceptor>,
|
||||
concurrency_limit: Option<usize>,
|
||||
// timeout: Option<Duration>,
|
||||
inner: M,
|
||||
inner: S,
|
||||
}
|
||||
|
||||
impl<M, S, T> Service<T> for MakeSvc<M>
|
||||
impl<S, T> Service<T> for MakeSvc<S>
|
||||
where
|
||||
M: Service<(), Response = S>,
|
||||
M::Error: Into<crate::Error> + Send,
|
||||
M::Future: Send + 'static,
|
||||
S: Service<Request<Body>, Response = Response<BoxBody>> + Send + 'static,
|
||||
S: Service<Request<Body>, Response = Response<BoxBody>> + Clone + Send + 'static,
|
||||
S::Future: Send + 'static,
|
||||
S::Error: Into<crate::Error> + Send,
|
||||
{
|
||||
@@ -392,19 +484,17 @@ where
|
||||
type Future =
|
||||
Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>>;
|
||||
|
||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
MakeService::poll_ready(&mut self.inner, cx).map_err(Into::into)
|
||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Ok(()).into()
|
||||
}
|
||||
|
||||
fn call(&mut self, _: T) -> Self::Future {
|
||||
let interceptor = self.interceptor.clone();
|
||||
let make = self.inner.make_service(());
|
||||
let svc = self.inner.clone();
|
||||
let concurrency_limit = self.concurrency_limit;
|
||||
// let timeout = self.timeout.clone();
|
||||
|
||||
Box::pin(async move {
|
||||
let svc = make.await.map_err(Into::into)?;
|
||||
|
||||
let svc = ServiceBuilder::new()
|
||||
.optional_layer(concurrency_limit.map(ConcurrencyLimitLayer::new))
|
||||
// .optional_layer(timeout.map(TimeoutLayer::new))
|
||||
@@ -421,3 +511,29 @@ where
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Debug)]
|
||||
#[doc(hidden)]
|
||||
pub struct Unimplemented {
|
||||
_p: (),
|
||||
}
|
||||
|
||||
impl Service<Request<Body>> for Unimplemented {
|
||||
type Response = Response<BoxBody>;
|
||||
type Error = crate::Error;
|
||||
type Future = future::Ready<Result<Self::Response, Self::Error>>;
|
||||
|
||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Ok(()).into()
|
||||
}
|
||||
|
||||
fn call(&mut self, _req: Request<Body>) -> Self::Future {
|
||||
future::ok(
|
||||
http::Response::builder()
|
||||
.status(200)
|
||||
.header("grpc-status", "12")
|
||||
.body(BoxBody::empty())
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ mod discover;
|
||||
mod either;
|
||||
mod io;
|
||||
mod layer;
|
||||
mod router;
|
||||
#[cfg(feature = "tls")]
|
||||
mod tls;
|
||||
|
||||
@@ -14,5 +15,6 @@ pub(crate) use self::connector::connector;
|
||||
pub(crate) use self::discover::ServiceList;
|
||||
pub(crate) use self::io::BoxedIo;
|
||||
pub(crate) use self::layer::{layer_fn, ServiceBuilderExt};
|
||||
pub(crate) use self::router::{Or, Routes};
|
||||
#[cfg(feature = "tls")]
|
||||
pub(crate) use self::tls::{TlsAcceptor, TlsConnector};
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
use futures_util::{
|
||||
future::Either,
|
||||
try_future::{MapErr, TryFutureExt},
|
||||
};
|
||||
use std::{
|
||||
fmt,
|
||||
sync::Arc,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use tower_service::Service;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Routes<A, B, Request> {
|
||||
routes: Or<A, B, Request>,
|
||||
}
|
||||
|
||||
impl<A, B, Request> Routes<A, B, Request> {
|
||||
pub(crate) fn new(
|
||||
predicate: impl Fn(&Request) -> bool + Send + Sync + 'static,
|
||||
a: A,
|
||||
b: B,
|
||||
) -> Self {
|
||||
let routes = Or::new(predicate, a, b);
|
||||
Self { routes }
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B, Request> Routes<A, B, Request> {
|
||||
pub(crate) fn push<C>(
|
||||
self,
|
||||
predicate: impl Fn(&Request) -> bool + Send + Sync + 'static,
|
||||
route: C,
|
||||
) -> Routes<C, Or<A, B, Request>, Request> {
|
||||
let routes = Or::new(predicate, route, self.routes);
|
||||
Routes { routes }
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B, Request> Service<Request> for Routes<A, B, Request>
|
||||
where
|
||||
A: Service<Request>,
|
||||
A::Future: Send + 'static,
|
||||
A::Error: Into<crate::Error>,
|
||||
B: Service<Request, Response = A::Response>,
|
||||
B::Future: Send + 'static,
|
||||
B::Error: Into<crate::Error>,
|
||||
{
|
||||
type Response = A::Response;
|
||||
type Error = crate::Error;
|
||||
type Future = <Or<A, B, Request> as Service<Request>>::Future;
|
||||
|
||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Ok(()).into()
|
||||
}
|
||||
|
||||
fn call(&mut self, req: Request) -> Self::Future {
|
||||
self.routes.call(req)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Clone, B: Clone, Request> Clone for Routes<A, B, Request> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
routes: self.routes.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct Or<A, B, Request> {
|
||||
predicate: Arc<dyn Fn(&Request) -> bool + Send + Sync + 'static>,
|
||||
a: A,
|
||||
b: B,
|
||||
}
|
||||
|
||||
impl<A, B, Request> Or<A, B, Request> {
|
||||
pub(crate) fn new<F>(predicate: F, a: A, b: B) -> Self
|
||||
where
|
||||
F: Fn(&Request) -> bool + Send + Sync + 'static,
|
||||
{
|
||||
let predicate = Arc::new(predicate);
|
||||
Self { predicate, a, b }
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B, Request> Service<Request> for Or<A, B, Request>
|
||||
where
|
||||
A: Service<Request>,
|
||||
A::Future: Send + 'static,
|
||||
A::Error: Into<crate::Error>,
|
||||
B: Service<Request, Response = A::Response>,
|
||||
B::Future: Send + 'static,
|
||||
B::Error: Into<crate::Error>,
|
||||
{
|
||||
type Response = A::Response;
|
||||
type Error = crate::Error;
|
||||
type Future = Either<
|
||||
MapErr<A::Future, fn(A::Error) -> crate::Error>,
|
||||
MapErr<B::Future, fn(B::Error) -> crate::Error>,
|
||||
>;
|
||||
|
||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Ok(()).into()
|
||||
}
|
||||
|
||||
fn call(&mut self, req: Request) -> Self::Future {
|
||||
if (self.predicate)(&req) {
|
||||
Either::Left(self.a.call(req).map_err(|e| e.into()))
|
||||
} else {
|
||||
Either::Right(self.b.call(req).map_err(|e| e.into()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Clone, B: Clone, Request> Clone for Or<A, B, Request> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
predicate: self.predicate.clone(),
|
||||
a: self.a.clone(),
|
||||
b: self.b.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B, Request> fmt::Debug for Or<A, B, Request> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Or {{ .. }}")
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,6 @@ use tokio_rustls::{
|
||||
webpki::DNSNameRef,
|
||||
TlsAcceptor as RustlsAcceptor, TlsConnector as RustlsConnector,
|
||||
};
|
||||
use tracing::trace;
|
||||
|
||||
/// h2 alpn in wire format for openssl.
|
||||
#[cfg(feature = "openssl")]
|
||||
@@ -137,7 +136,7 @@ impl TlsConnector {
|
||||
let tls = tokio_openssl::connect(config, &self.domain, io).await?;
|
||||
|
||||
match tls.ssl().selected_alpn_protocol() {
|
||||
Some(b) if b == b"h2" => trace!("HTTP/2 succesfully negotiated."),
|
||||
Some(b) if b == b"h2" => tracing::trace!("HTTP/2 succesfully negotiated."),
|
||||
_ => return Err(TlsError::H2NotNegotiated.into()),
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user