1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
//! # grpc-webnext-client
//!
//! A gRPC client for **Rust WASM frontends**, speaking real gRPC to a
//! [grpc-webnext](https://github.com/debdattabasu/grpc-webnext) endpoint over an
//! [h2ts](https://github.com/debdattabasu/h2ts) WebSocket tunnel.
//!
//! **No tonic, no hyper, no tokio.** The wire is real HTTP/2 — trailers,
//! multiplexing, flow control — so there is nothing to translate: framing plus a
//! status read off the trailers is the whole client. Everything is single-threaded
//! (`Rc`, `!Send`), which is what a browser actually is; nothing here asserts
//! `Send` to satisfy a runtime that does not exist on this target.
//!
//! ```no_run
//! # async fn demo(client: grpc_webnext_client::Client) -> Result<(), grpc_webnext_client::Status> {
//! use grpc_webnext_client::CallOptions;
//!
//! let reply = client
//! .unary("/helloworld.Greeter/SayHello", encoded_request(), CallOptions::new())
//! .await?;
//! let _ = reply.message; // your codec decodes these bytes
//! # Ok(()) }
//! # fn encoded_request() -> Vec<u8> { Vec::new() }
//! ```
//!
//! ## Codec
//!
//! The client deals in **message bytes**, so it is codec-agnostic: encode with
//! `prost`, or anything else. The `prost` feature adds typed helpers over
//! [`prost::Message`]; there is no generated service stub layer yet, so a service
//! is a handful of thin wrappers over [`Client::unary`] and friends.
//!
//! ## Streaming
//!
//! All four cardinalities. Backpressure on the response is real and costs nothing
//! here: `h2ts-client` replenishes the HTTP/2 receive window only as the body is
//! polled, so a consumer that stops reading stops the *server* rather than filling
//! memory — the same property the TypeScript client gets, for the same reason.
//!
//! Dropping a [`Streaming`] cancels the RPC: the HTTP/2 stream is reset, so the
//! server stops work it has no reader for. That is also how a deadline stops a
//! stream, and why [`CallOptions::timeout`] covers a stream's whole lifetime rather
//! than only its opening.
//!
//! The streaming cardinalities need `h2ts-client` **0.1.2**: at 0.1.1
//! `Response::into_body` took `self` while `trailers()` needed `&self`, so a caller
//! could stream the body or read the trailers, never both — and gRPC's terminal
//! status lives in the trailers. A streaming client built on 0.1.1 could not tell a
//! failed stream from a successfully empty one. `Response::into_parts` fixes that.
//!
//! ## Reconnect
//!
//! [`Client`] is a gRPC **channel**, not a handle to a socket: the tunnel opens on
//! the first call and **reopens if it drops**, the way `tonic::transport::Channel`
//! does. The call that discovers a dead tunnel reports the failure and the next one
//! reconnects — the transport never silently replays a request the server may
//! already have seen, because that is a retry policy decision and not its to make.
//!
//! [`Client::state`] and [`Client::state_changes`] surface where the channel is
//! (gRPC's connectivity states, `WaitForStateChange` as a stream), so an app can
//! say something useful instead of guessing from a failed call.
//!
//! There is no reconnect **backoff**: like tonic, a redial happens when a call asks
//! for one, so the call rate bounds the dial rate. A client built with
//! [`Client::over_transport`] cannot redial at all — the transport is consumed —
//! and says so rather than pretending to be live.
pub use ;
pub use Connector;
pub use ConnectivityState;
pub use ;
pub use ;
pub use ;
// Re-exported so callers can tune the tunnel — or supply their own — without
// depending on h2ts-client directly. `TransportError` belongs here too: a caller
// building a `Transport` has to name the error type its sink produces, so leaving it
// out meant `Client::over_transport` could not be used without adding h2ts-client as
// a second dependency.
pub use ;
/// The WebSocket subprotocol an h2ts client offers.
pub const H2TS_SUBPROTOCOL: &str = DEFAULT_SUBPROTOCOL;
pub use connect;
pub use ;