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:
Lucio Franco
2019-10-29 16:32:04 -04:00
committed by GitHub
parent a17049f1f7
commit 5b4f4689a2
20 changed files with 473 additions and 176 deletions
+40 -38
View File
@@ -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();
+8
View File
@@ -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"
+4 -2
View File
@@ -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(())
+4 -1
View File
@@ -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(())
}
+2 -2
View File
@@ -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(())
+2 -1
View File
@@ -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(())
+3 -1
View File
@@ -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 {
+37
View File
@@ -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(())
}
+69
View File
@@ -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>>;
}
+1 -1
View File
@@ -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(())
}
+2 -1
View File
@@ -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(())
+1 -1
View File
@@ -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)
+2 -2
View File
@@ -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(())
+7 -98
View File
@@ -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 {}
}
+1
View File
@@ -150,6 +150,7 @@ impl pb::server::TestService for TestService {
}
}
#[derive(Default)]
pub struct UnimplementedService;
#[tonic::async_trait]
+20 -4
View File
@@ -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
View File
@@ -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(),
)
}
}
+2
View File
@@ -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};
+129
View File
@@ -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 {{ .. }}")
}
}
+1 -2
View File
@@ -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()),
};