From 38817a987e16b35d7b05fa55baa17b8975c16a84 Mon Sep 17 00:00:00 2001 From: boxdot Date: Thu, 12 Nov 2020 09:47:27 +0100 Subject: [PATCH] Quick and dirty patch to compile client-only transport on wasm32. --- tonic/Cargo.toml | 21 +++++++--- tonic/src/lib.rs | 2 +- tonic/src/request.rs | 6 +-- tonic/src/transport/channel/endpoint.rs | 3 ++ tonic/src/transport/channel/mod.rs | 23 +++++++---- tonic/src/transport/mod.rs | 20 ++++++++++ tonic/src/transport/server/conn.rs | 19 +-------- tonic/src/transport/server/mod.rs | 2 +- tonic/src/transport/service/connection.rs | 47 +++++++++++++++++------ tonic/src/transport/service/io.rs | 10 ++++- tonic/src/transport/service/mod.rs | 6 +++ 11 files changed, 113 insertions(+), 46 deletions(-) diff --git a/tonic/Cargo.toml b/tonic/Cargo.toml index 3a820d19b..7d0677c7a 100644 --- a/tonic/Cargo.toml +++ b/tonic/Cargo.toml @@ -27,12 +27,20 @@ default = ["transport", "codegen", "prost"] codegen = ["async-trait"] transport = [ "h2", - "hyper", - "tokio", + "hyper/full", + "tokio/net", "tower", + "tower/balance", "tracing-futures", "tokio/macros" ] +client = [ + "h2", + "hyper/client", + "hyper/http2", + "tokio", + "tower", +] tls = ["transport", "tokio-rustls"] tls-roots = ["tls", "rustls-native-certs"] prost = ["prost1", "prost-derive"] @@ -65,16 +73,19 @@ async-trait = { version = "0.1.13", optional = true } # transport h2 = { version = "0.3", optional = true } -hyper = { version = "0.14.2", features = ["full"], optional = true } -tokio = { version = "1.0.1", features = ["net"], optional = true } +hyper = { version = "0.14.2", default-features = false, optional = true } +tokio = { version = "1.0.1", default-features = false, optional = true } tokio-stream = "0.1" -tower = { version = "0.4", features = ["balance", "buffer", "discover", "limit", "load", "make", "timeout", "util"], optional = true} +tower = { version = "0.4", features = ["buffer", "discover", "limit", "load", "make", "timeout", "util"], optional = true} tracing-futures = { version = "0.2", optional = true } # rustls tokio-rustls = { version = "0.22", optional = true } rustls-native-certs = { version = "0.5", optional = true } +[target.'cfg(target_arch = "wasm32")'.dependencies] +wasm-bindgen-futures = "0.4.18" + [dev-dependencies] tokio = { version = "1.0", features = ["rt", "macros"] } static_assertions = "1.0" diff --git a/tonic/src/lib.rs b/tonic/src/lib.rs index f2e1b363d..cf61680ba 100644 --- a/tonic/src/lib.rs +++ b/tonic/src/lib.rs @@ -82,7 +82,7 @@ pub mod codec; pub mod metadata; pub mod server; -#[cfg(feature = "transport")] +#[cfg(any(feature = "transport", feature = "client"))] #[cfg_attr(docsrs, doc(cfg(feature = "transport")))] pub mod transport; diff --git a/tonic/src/request.rs b/tonic/src/request.rs index f2f047ffc..fbdf48b3e 100644 --- a/tonic/src/request.rs +++ b/tonic/src/request.rs @@ -1,10 +1,10 @@ use crate::metadata::MetadataMap; -#[cfg(feature = "transport")] +#[cfg(any(feature = "transport", feature = "client"))] use crate::transport::Certificate; use futures_core::Stream; use http::Extensions; use std::net::SocketAddr; -#[cfg(feature = "transport")] +#[cfg(any(feature = "transport", feature = "client"))] use std::sync::Arc; /// A gRPC request and metadata from an RPC call. @@ -18,7 +18,7 @@ pub struct Request { #[derive(Clone)] pub(crate) struct ConnectionInfo { pub(crate) remote_addr: Option, - #[cfg(feature = "transport")] + #[cfg(any(feature = "transport", feature = "client"))] pub(crate) peer_certs: Option>>, } diff --git a/tonic/src/transport/channel/endpoint.rs b/tonic/src/transport/channel/endpoint.rs index 13eb4b986..d4e43d184 100644 --- a/tonic/src/transport/channel/endpoint.rs +++ b/tonic/src/transport/channel/endpoint.rs @@ -43,6 +43,7 @@ impl Endpoint { // FIXME: determine if we want to expose this or not. This is really // just used in codegen for a shortcut. #[doc(hidden)] + #[cfg(feature = "transport")] pub fn new(dst: D) -> Result where D: TryInto, @@ -231,6 +232,7 @@ impl Endpoint { } /// Create a channel from this config. + #[cfg(feature = "transport")] pub async fn connect(&self) -> Result { let mut http = hyper::client::connect::HttpConnector::new(); http.enforce_http(false); @@ -250,6 +252,7 @@ impl Endpoint { /// /// The channel returned by this method does not attempt to connect to the endpoint until first /// use. + #[cfg(feature = "transport")] pub fn connect_lazy(&self) -> Result { let mut http = hyper::client::connect::HttpConnector::new(); http.enforce_http(false); diff --git a/tonic/src/transport/channel/mod.rs b/tonic/src/transport/channel/mod.rs index eca92ddd8..70d3b3040 100644 --- a/tonic/src/transport/channel/mod.rs +++ b/tonic/src/transport/channel/mod.rs @@ -9,7 +9,9 @@ pub use endpoint::Endpoint; #[cfg(feature = "tls")] pub use tls::ClientTlsConfig; -use super::service::{Connection, DynamicServiceStream}; +use super::service::Connection; +#[cfg(feature = "transport")] +use super::service::DynamicServiceStream; use crate::body::BoxBody; use bytes::Bytes; use http::{ @@ -17,22 +19,25 @@ use http::{ Request, Response, }; use hyper::client::connect::Connection as HyperConnection; +#[cfg(feature = "transport")] +use std::hash::Hash; use std::{ fmt, future::Future, - hash::Hash, pin::Pin, task::{Context, Poll}, }; -use tokio::{ - io::{AsyncRead, AsyncWrite}, - sync::mpsc::{channel, Sender}, -}; +use tokio::io::{AsyncRead, AsyncWrite}; +#[cfg(feature = "transport")] +use tokio::sync::mpsc::{channel, Sender}; + +#[cfg(feature = "transport")] use tower::balance::p2c::Balance; +#[cfg(feature = "transport")] +use tower::discover::{Change, Discover}; use tower::{ buffer::{self, Buffer}, - discover::{Change, Discover}, util::{BoxService, Either}, Service, }; @@ -108,6 +113,7 @@ impl Channel { /// /// This creates a [`Channel`] that will load balance accross all the /// provided endpoints. + #[cfg(feature = "transport")] pub fn balance_list(list: impl Iterator) -> Self { let (channel, tx) = Self::balance_channel(DEFAULT_BUFFER_SIZE); list.for_each(|endpoint| { @@ -121,6 +127,7 @@ impl Channel { /// Balance a list of [`Endpoint`]'s. /// /// This creates a [`Channel`] that will listen to a stream of change events and will add or remove provided endpoints. + #[cfg(feature = "transport")] pub fn balance_channel(capacity: usize) -> (Self, Sender>) where K: Hash + Eq + Send + Clone + 'static, @@ -130,6 +137,7 @@ impl Channel { (Self::balance(list, DEFAULT_BUFFER_SIZE), tx) } + #[cfg(feature = "transport")] pub(crate) fn new(connector: C, endpoint: Endpoint) -> Self where C: Service + Send + 'static, @@ -162,6 +170,7 @@ impl Channel { Ok(Channel { svc }) } + #[cfg(feature = "transport")] pub(crate) fn balance(discover: D, buffer_size: usize) -> Self where D: Discover + Unpin + Send + 'static, diff --git a/tonic/src/transport/mod.rs b/tonic/src/transport/mod.rs index b91767aa3..52fac0a29 100644 --- a/tonic/src/transport/mod.rs +++ b/tonic/src/transport/mod.rs @@ -87,8 +87,26 @@ //! [rustls]: https://docs.rs/rustls/0.16.0/rustls/ pub mod channel; +#[cfg(feature = "transport")] pub mod server; +/// Trait that connected IO resources implement. +/// +/// The goal for this trait is to allow users to implement +/// custom IO types that can still provide the same connection +/// metadata. +pub trait Connected { + /// Return the remote address this IO resource is connected too. + fn remote_addr(&self) -> Option { + None + } + + /// Return the set of connected peer TLS certificates. + fn peer_certs(&self) -> Option> { + None + } +} + mod error; mod service; mod tls; @@ -96,6 +114,7 @@ mod tls; #[doc(inline)] pub use self::channel::{Channel, Endpoint}; pub use self::error::Error; +#[cfg(feature = "transport")] #[doc(inline)] pub use self::server::{NamedService, Server}; pub use self::tls::{Certificate, Identity}; @@ -105,6 +124,7 @@ pub use hyper::{Body, Uri}; #[cfg_attr(docsrs, doc(cfg(feature = "tls")))] pub use self::channel::ClientTlsConfig; #[cfg(feature = "tls")] +#[cfg(feature = "transport")] #[cfg_attr(docsrs, doc(cfg(feature = "tls")))] pub use self::server::ServerTlsConfig; diff --git a/tonic/src/transport/server/conn.rs b/tonic/src/transport/server/conn.rs index f5bbcfc08..c36f90e64 100644 --- a/tonic/src/transport/server/conn.rs +++ b/tonic/src/transport/server/conn.rs @@ -1,27 +1,12 @@ +#[cfg(feature = "tls")] use crate::transport::Certificate; +use crate::transport::Connected; use hyper::server::conn::AddrStream; use std::net::SocketAddr; use tokio::net::TcpStream; #[cfg(feature = "tls")] use tokio_rustls::{rustls::Session, server::TlsStream}; -/// Trait that connected IO resources implement. -/// -/// The goal for this trait is to allow users to implement -/// custom IO types that can still provide the same connection -/// metadata. -pub trait Connected { - /// Return the remote address this IO resource is connected too. - fn remote_addr(&self) -> Option { - None - } - - /// Return the set of connected peer TLS certificates. - fn peer_certs(&self) -> Option> { - None - } -} - impl Connected for AddrStream { fn remote_addr(&self) -> Option { Some(self.remote_addr()) diff --git a/tonic/src/transport/server/mod.rs b/tonic/src/transport/server/mod.rs index f13b46b1c..a3ede5051 100644 --- a/tonic/src/transport/server/mod.rs +++ b/tonic/src/transport/server/mod.rs @@ -6,7 +6,7 @@ mod incoming; #[cfg_attr(docsrs, doc(cfg(feature = "tls")))] mod tls; -pub use conn::Connected; +pub use super::Connected; #[cfg(feature = "tls")] pub use tls::ServerTlsConfig; diff --git a/tonic/src/transport/service/connection.rs b/tonic/src/transport/service/connection.rs index ed23aac45..4a861c8c7 100644 --- a/tonic/src/transport/service/connection.rs +++ b/tonic/src/transport/service/connection.rs @@ -27,6 +27,22 @@ pub(crate) struct Connection { inner: BoxService, } +#[cfg(target_arch = "wasm32")] +mod wasm { + use std::future::Future; + use std::pin::Pin; + + type BoxSendFuture = Pin + Send>>; + + pub(crate) struct Executor; + + impl hyper::rt::Executor for Executor { + fn execute(&self, fut: BoxSendFuture) { + wasm_bindgen_futures::spawn_local(fut) + } + } +} + impl Connection { fn new(connector: C, endpoint: Endpoint, is_lazy: bool) -> Self where @@ -35,21 +51,29 @@ impl Connection { C::Future: Unpin + Send, C::Response: AsyncRead + AsyncWrite + HyperConnection + Unpin + Send + 'static, { - let mut settings = Builder::new() - .http2_initial_stream_window_size(endpoint.init_stream_window_size) - .http2_initial_connection_window_size(endpoint.init_connection_window_size) - .http2_only(true) - .http2_keep_alive_interval(endpoint.http2_keep_alive_interval) - .clone(); + let mut settings = Builder::new(); - if let Some(val) = endpoint.http2_keep_alive_timeout { - settings.http2_keep_alive_timeout(val); + #[cfg(target_arch = "wasm32")] + { + settings.executor(wasm::Executor); } - - if let Some(val) = endpoint.http2_keep_alive_while_idle { - settings.http2_keep_alive_while_idle(val); + #[cfg(feature = "transport")] + { + settings.http2_keep_alive_interval(endpoint.http2_keep_alive_interval); + if let Some(val) = endpoint.http2_keep_alive_timeout { + settings.http2_keep_alive_timeout(val); + } + + if let Some(val) = endpoint.http2_keep_alive_while_idle { + settings.http2_keep_alive_while_idle(val); + } } + settings + .http2_initial_stream_window_size(endpoint.init_stream_window_size) + .http2_initial_connection_window_size(endpoint.init_connection_window_size) + .http2_only(true); + let stack = ServiceBuilder::new() .layer_fn(|s| AddOrigin::new(s, endpoint.uri.clone())) .layer_fn(|s| UserAgent::new(s, endpoint.user_agent.clone())) @@ -78,6 +102,7 @@ impl Connection { Self::new(connector, endpoint, false).ready_oneshot().await } + #[cfg(feature = "transport")] pub(crate) fn lazy(connector: C, endpoint: Endpoint) -> Self where C: Service + Send + 'static, diff --git a/tonic/src/transport/service/io.rs b/tonic/src/transport/service/io.rs index 761c8ece9..56cc2d621 100644 --- a/tonic/src/transport/service/io.rs +++ b/tonic/src/transport/service/io.rs @@ -1,6 +1,9 @@ -use crate::transport::{server::Connected, Certificate}; +#[cfg(feature = "transport")] +use crate::transport::Certificate; +use crate::transport::Connected; use hyper::client::connect::{Connected as HyperConnected, Connection}; use std::io; +#[cfg(feature = "transport")] use std::net::SocketAddr; use std::pin::Pin; use std::task::{Context, Poll}; @@ -61,14 +64,17 @@ pub(in crate::transport) trait ConnectedIo: Io + Connected {} impl ConnectedIo for T where T: Io + Connected {} +#[cfg(feature = "transport")] pub(crate) struct ServerIo(Pin>); +#[cfg(feature = "transport")] impl ServerIo { pub(in crate::transport) fn new(io: I) -> Self { ServerIo(Box::pin(io)) } } +#[cfg(feature = "transport")] impl Connected for ServerIo { fn remote_addr(&self) -> Option { (&*self.0).remote_addr() @@ -79,6 +85,7 @@ impl Connected for ServerIo { } } +#[cfg(feature = "transport")] impl AsyncRead for ServerIo { fn poll_read( mut self: Pin<&mut Self>, @@ -89,6 +96,7 @@ impl AsyncRead for ServerIo { } } +#[cfg(feature = "transport")] impl AsyncWrite for ServerIo { fn poll_write( mut self: Pin<&mut Self>, diff --git a/tonic/src/transport/service/mod.rs b/tonic/src/transport/service/mod.rs index eab3b40ef..a3332bce5 100644 --- a/tonic/src/transport/service/mod.rs +++ b/tonic/src/transport/service/mod.rs @@ -1,10 +1,12 @@ mod add_origin; mod connection; mod connector; +#[cfg(feature = "transport")] mod discover; mod io; mod layer; mod reconnect; +#[cfg(feature = "transport")] mod router; #[cfg(feature = "tls")] mod tls; @@ -13,9 +15,13 @@ mod user_agent; pub(crate) use self::add_origin::AddOrigin; pub(crate) use self::connection::Connection; pub(crate) use self::connector::connector; +#[cfg(feature = "transport")] pub(crate) use self::discover::DynamicServiceStream; +#[cfg(feature = "transport")] pub(crate) use self::io::ServerIo; +#[cfg(feature = "transport")] pub(crate) use self::layer::ServiceBuilderExt; +#[cfg(feature = "transport")] pub(crate) use self::router::{Or, Routes}; #[cfg(feature = "tls")] pub(crate) use self::tls::{TlsAcceptor, TlsConnector};