Skip to content

Commit 4f0596e

Browse files
committed
feat(tonic): Add client feature flag to support connect_with_connector in wasm32 targets
This adds a new feature that enables compilation of connect_with_connector and the Endpoint and Channel struct for wasm. Related: hyperium#491
1 parent e2c506a commit 4f0596e

File tree

9 files changed

+218
-109
lines changed

9 files changed

+218
-109
lines changed

tonic/Cargo.toml

+19-3
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,24 @@ transport = [
3838
"dep:async-stream",
3939
"channel",
4040
"dep:h2",
41-
"dep:hyper", "dep:hyper-util", "dep:hyper-timeout",
41+
"dep:hyper",
42+
"dep:hyper-util",
43+
"dep:hyper-timeout",
4244
"dep:socket2",
43-
"dep:tokio", "tokio?/macros", "tokio?/net", "tokio?/time",
45+
"dep:tokio",
46+
"tokio?/macros",
47+
"tokio?/net",
48+
"tokio?/time",
4449
"dep:tower",
4550
]
51+
client = [
52+
"dep:h2",
53+
"hyper/client",
54+
"hyper/http2",
55+
"dep:tokio",
56+
"dep:tower",
57+
"dep:hyper-timeout",
58+
]
4659
channel = []
4760

4861
# [[bench]]
@@ -72,7 +85,7 @@ async-trait = {version = "0.1.13", optional = true}
7285
async-stream = {version = "0.3", optional = true}
7386
h2 = {version = "0.4", optional = true}
7487
hyper = {version = "1", features = ["full"], optional = true}
75-
hyper-util = { version = ">=0.1.4, <0.2", features = ["full"], optional = true }
88+
hyper-util = { version = ">=0.1.4, <0.2", default-features = false, optional = true }
7689
hyper-timeout = {version = "0.5", optional = true}
7790
socket2 = { version = ">=0.4.7, <0.6.0", optional = true, features = ["all"] }
7891
tokio = {version = "1", default-features = false, optional = true}
@@ -90,6 +103,9 @@ webpki-roots = { version = "0.26", optional = true }
90103
flate2 = {version = "1.0", optional = true}
91104
zstd = { version = "0.13.0", optional = true }
92105

106+
[target.'cfg(target_arch = "wasm32")'.dependencies]
107+
wasm-bindgen-futures = "0.4.38"
108+
93109
[dev-dependencies]
94110
bencher = "0.1.5"
95111
quickcheck = "1.0"

tonic/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ pub mod metadata;
101101
pub mod server;
102102
pub mod service;
103103

104-
#[cfg(feature = "transport")]
104+
#[cfg(any(feature = "transport", feature = "client"))]
105105
#[cfg_attr(docsrs, doc(cfg(feature = "transport")))]
106106
pub mod transport;
107107

tonic/src/transport/channel/endpoint.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,7 @@ impl Endpoint {
333333
}
334334

335335
/// Create a channel from this config.
336+
#[cfg(feature = "transport")]
336337
pub async fn connect(&self) -> Result<Channel, Error> {
337338
let mut http = HttpConnector::new();
338339
http.enforce_http(false);
@@ -349,6 +350,7 @@ impl Endpoint {
349350
///
350351
/// The channel returned by this method does not attempt to connect to the endpoint until first
351352
/// use.
353+
#[cfg(feature = "transport")]
352354
pub fn connect_lazy(&self) -> Channel {
353355
let mut http = HttpConnector::new();
354356
http.enforce_http(false);
@@ -447,7 +449,7 @@ impl From<Uri> for Endpoint {
447449
http2_keep_alive_while_idle: None,
448450
connect_timeout: None,
449451
http2_adaptive_window: None,
450-
executor: SharedExec::tokio(),
452+
executor: SharedExec::default_exec(),
451453
}
452454
}
453455
}

tonic/src/transport/channel/mod.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ pub use endpoint::Endpoint;
99
#[cfg(feature = "tls")]
1010
pub use tls::ClientTlsConfig;
1111

12-
use super::service::{Connection, DynamicServiceStream, SharedExec};
12+
use super::service::{Connection};
13+
#[cfg(feature = "transport")]
14+
use super::service::{DynamicServiceStream, SharedExec};
1315
use crate::body::BoxBody;
1416
use crate::transport::Executor;
1517
use bytes::Bytes;
@@ -107,6 +109,7 @@ impl Channel {
107109
///
108110
/// This creates a [`Channel`] that will load balance across all the
109111
/// provided endpoints.
112+
#[cfg(feature = "transport")]
110113
pub fn balance_list(list: impl Iterator<Item = Endpoint>) -> Self {
111114
let (channel, tx) = Self::balance_channel(DEFAULT_BUFFER_SIZE);
112115
list.for_each(|endpoint| {
@@ -120,18 +123,20 @@ impl Channel {
120123
/// Balance a list of [`Endpoint`]'s.
121124
///
122125
/// This creates a [`Channel`] that will listen to a stream of change events and will add or remove provided endpoints.
126+
#[cfg(feature = "transport")]
123127
pub fn balance_channel<K>(capacity: usize) -> (Self, Sender<Change<K, Endpoint>>)
124128
where
125129
K: Hash + Eq + Send + Clone + 'static,
126130
{
127-
Self::balance_channel_with_executor(capacity, SharedExec::tokio())
131+
Self::balance_channel_with_executor(capacity, SharedExec::default_exec())
128132
}
129133

130134
/// Balance a list of [`Endpoint`]'s.
131135
///
132136
/// This creates a [`Channel`] that will listen to a stream of change events and will add or remove provided endpoints.
133137
///
134138
/// The [`Channel`] will use the given executor to spawn async tasks.
139+
#[cfg(feature = "transport")]
135140
pub fn balance_channel_with_executor<K, E>(
136141
capacity: usize,
137142
executor: E,

tonic/src/transport/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
//! [rustls]: https://docs.rs/rustls/0.16.0/rustls/
9191
9292
pub mod channel;
93+
#[cfg(feature = "transport")]
9394
pub mod server;
9495

9596
mod error;
@@ -102,6 +103,7 @@ mod tls;
102103
#[cfg_attr(docsrs, doc(cfg(feature = "channel")))]
103104
pub use self::channel::{Channel, Endpoint};
104105
pub use self::error::Error;
106+
#[cfg(feature = "transport")]
105107
#[doc(inline)]
106108
pub use self::server::Server;
107109
#[doc(inline)]
@@ -111,6 +113,7 @@ pub(crate) use self::service::ConnectError;
111113
#[cfg(feature = "tls")]
112114
#[cfg_attr(docsrs, doc(cfg(feature = "tls")))]
113115
pub use self::tls::Certificate;
116+
#[cfg(feature = "transport")]
114117
pub use axum::{body::Body as AxumBoxBody, Router as AxumRouter};
115118
pub use hyper::{body::Body, Uri};
116119
#[cfg(feature = "tls")]

tonic/src/transport/service/connection.rs

+49-6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::{
44
body::{boxed, BoxBody},
55
transport::{BoxFuture, Endpoint},
66
};
7+
78
use http::Uri;
89
use hyper::rt;
910
use hyper::{client::conn::http2::Builder, rt::Executor};
@@ -16,11 +17,19 @@ use tower::load::Load;
1617
use tower::{
1718
layer::Layer,
1819
limit::{concurrency::ConcurrencyLimitLayer, rate::RateLimitLayer},
19-
util::BoxService,
20-
ServiceBuilder, ServiceExt,
20+
ServiceBuilder,
21+
ServiceExt, util::BoxService,
2122
};
23+
use tower::load::Load;
2224
use tower_service::Service;
2325

26+
use crate::{
27+
body::BoxBody,
28+
transport::{BoxFuture, Endpoint},
29+
};
30+
31+
use super::{AddOrigin, grpc_timeout::GrpcTimeout, reconnect::Reconnect, UserAgent};
32+
2433
pub(crate) type Response<B = BoxBody> = http::Response<B>;
2534
pub(crate) type Request<B = BoxBody> = http::Request<B>;
2635

@@ -43,18 +52,36 @@ impl Connection {
4352
.timer(TokioTimer::new())
4453
.clone();
4554

46-
if let Some(val) = endpoint.http2_keep_alive_timeout {
47-
settings.keep_alive_timeout(val);
55+
if let Some(val) = endpoint.http2_adaptive_window {
56+
settings.http2_adaptive_window(val);
4857
}
4958

50-
if let Some(val) = endpoint.http2_keep_alive_while_idle {
51-
settings.keep_alive_while_idle(val);
59+
#[cfg(feature = "transport")]
60+
{
61+
settings
62+
.http_keep_alive_interval(endpoint.http2_keep_alive_interval);
63+
64+
if let Some(val) = endpoint.http2_keep_alive_timeout {
65+
settings.keep_alive_timeout(val);
66+
}
67+
68+
if let Some(val) = endpoint.http2_keep_alive_while_idle {
69+
settings.keep_alive_while_idle(val);
70+
}
5271
}
5372

5473
if let Some(val) = endpoint.http2_adaptive_window {
5574
settings.adaptive_window(val);
5675
}
5776

77+
78+
#[cfg(target_arch = "wasm32")]
79+
{
80+
settings.executor(wasm::Executor)
81+
// reset streams require `Instant::now` which is not available on wasm
82+
.http_max_concurrent_reset_streams(0);
83+
}
84+
5885
let stack = ServiceBuilder::new()
5986
.layer_fn(|s| {
6087
let origin = endpoint.origin.as_ref().unwrap_or(&endpoint.uri).clone();
@@ -209,3 +236,19 @@ where
209236
})
210237
}
211238
}
239+
240+
#[cfg(target_arch = "wasm32")]
241+
mod wasm {
242+
use std::future::Future;
243+
use std::pin::Pin;
244+
245+
type BoxSendFuture = Pin<Box<dyn Future<Output = ()> + Send>>;
246+
247+
pub(crate) struct Executor;
248+
249+
impl hyper::rt::Executor<BoxSendFuture> for Executor {
250+
fn execute(&self, fut: BoxSendFuture) {
251+
wasm_bindgen_futures::spawn_local(fut)
252+
}
253+
}
254+
}

tonic/src/transport/service/executor.rs

+22-2
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ use std::{future::Future, sync::Arc};
33

44
pub(crate) use hyper::rt::Executor;
55

6+
#[cfg(not(target_arch = "wasm32"))]
67
#[derive(Copy, Clone)]
78
struct TokioExec;
89

10+
#[cfg(not(target_arch = "wasm32"))]
911
impl<F> Executor<F> for TokioExec
1012
where
1113
F: Future + Send + 'static,
@@ -16,6 +18,21 @@ where
1618
}
1719
}
1820

21+
#[cfg(target_arch = "wasm32")]
22+
#[derive(Copy, Clone)]
23+
struct WasmBindgenExec;
24+
25+
#[cfg(target_arch = "wasm32")]
26+
impl<F> Executor<F> for WasmBindgenExec
27+
where
28+
F: Future + 'static,
29+
F::Output: 'static,
30+
{
31+
fn execute(&self, fut: F) {
32+
wasm_bindgen_futures::spawn_local(async move {fut.await;});
33+
}
34+
}
35+
1936
#[derive(Clone)]
2037
pub(crate) struct SharedExec {
2138
inner: Arc<dyn Executor<BoxFuture<'static, ()>> + Send + Sync + 'static>,
@@ -31,8 +48,11 @@ impl SharedExec {
3148
}
3249
}
3350

34-
pub(crate) fn tokio() -> Self {
35-
Self::new(TokioExec)
51+
pub(crate) fn default_exec() -> Self {
52+
#[cfg(not(target_arch = "wasm32"))]
53+
return Self::new(TokioExec);
54+
#[cfg(target_arch = "wasm32")]
55+
Self::new(WasmBindgenExec)
3656
}
3757
}
3858

0 commit comments

Comments
 (0)