musli_web/tungstenite029.rs
1//! Client side implementation for [`tokio-tungstenite`] `0.29.x`.
2//!
3//! This allows non-browser clients to talk to the same websocket API which is
4//! served by [`ws::Server`], such as through the [`axum08`] integration.
5//!
6//! [`axum08`]: <https://docs.rs/musli-web/latest/musli_web/axum08/>
7//! [`tokio-tungstenite`]: <https://docs.rs/tokio-tungstenite/0.29>
8//! [`ws::Server`]: <https://docs.rs/musli-web/latest/musli_web/ws/struct.Server.html>
9//!
10//! # Examples
11//!
12//! ```no_run
13//! use musli_web::tungstenite029::prelude::*;
14//!
15//! mod api {
16//! use musli::{Decode, Encode};
17//! use musli_web::api;
18//!
19//! #[derive(Encode, Decode)]
20//! pub struct HelloRequest<'de> {
21//! pub message: &'de str,
22//! }
23//!
24//! #[derive(Encode, Decode)]
25//! pub struct HelloResponse<'de> {
26//! pub message: &'de str,
27//! }
28//!
29//! api::define! {
30//! pub type Hello;
31//!
32//! impl Endpoint for Hello {
33//! impl<'de> Request for HelloRequest<'de>;
34//! type Response<'de> = HelloResponse<'de>;
35//! }
36//! }
37//! }
38//!
39//! # async fn example() -> Result<(), Box<dyn core::error::Error>> {
40//! let mut service = ws::connect("ws://localhost:3000/ws")
41//! .on_error(|error| {
42//! tracing::error!("WebSocket error: {error}");
43//! })
44//! .build();
45//!
46//! let handle = service.handle().clone();
47//!
48//! tokio::spawn(async move {
49//! if let Err(error) = service.run().await {
50//! tracing::error!("WebSocket service error: {error}");
51//! }
52//! });
53//!
54//! handle.wait_until_open().await?;
55//!
56//! let packet = handle
57//! .request()
58//! .body(api::HelloRequest { message: "Hello!" })
59//! .send()
60//! .await?;
61//!
62//! let response = packet.decode()?;
63//! println!("Response: {}", response.message);
64//! # Ok(())
65//! # }
66//! ```
67//!
68//! # Sending headers with the handshake
69//!
70//! A server which authenticates the websocket upgrade, through a session
71//! cookie or a token, reads it from the headers of the HTTP request which opens
72//! the websocket. [`ServiceBuilder::header`] adds such a header, and it is sent
73//! again every time the service reconnects:
74//!
75//! ```no_run
76//! use musli_web::tungstenite029::prelude::*;
77//!
78//! # async fn example(token: &str) -> Result<(), Box<dyn core::error::Error>> {
79//! let mut service = ws::connect("ws://localhost:3000/ws")
80//! .header("Cookie", "session=4b0c2d")
81//! .header("Authorization", format!("Bearer {token}"))
82//! .build();
83//!
84//! tokio::spawn(async move {
85//! if let Err(error) = service.run().await {
86//! tracing::error!("WebSocket service error: {error}");
87//! }
88//! });
89//! # Ok(())
90//! # }
91//! ```
92//!
93//! A header which cannot be sent, such as one with a line break in its value,
94//! fails [`Service::run`] and [`Service::try_connect`] right away rather than
95//! being retried, see [`Error::is_invalid_header`].
96//!
97//! [`ServiceBuilder::header`]: crate::client::ServiceBuilder::header
98//! [`Service::run`]: crate::client::Service::run
99//! [`Error::is_invalid_header`]: crate::client::Error::is_invalid_header
100//!
101//! # Connecting over any stream
102//!
103//! [`connect_with()`] establishes the websocket over a stream the caller
104//! opens, such as a [`UnixStream`], instead of connecting to the url. Combined
105//! with [`Service::try_connect`] a caller can fail fast if there is nothing to
106//! connect to, rather than retrying in the background:
107//!
108//! ```no_run
109//! use musli_web::tungstenite029::prelude::*;
110//!
111//! # async fn example() -> Result<(), Box<dyn core::error::Error>> {
112//! let mut service = ws::connect_with("ws://localhost/ws", || {
113//! tokio::net::UnixStream::connect("/run/user/1000/app.sock")
114//! })
115//! .reconnect(false)
116//! .build();
117//!
118//! service.try_connect().await?;
119//!
120//! tokio::spawn(async move {
121//! if let Err(error) = service.run().await {
122//! tracing::error!("WebSocket service error: {error}");
123//! }
124//! });
125//! # Ok(())
126//! # }
127//! ```
128//!
129//! [`UnixStream`]: <https://docs.rs/tokio/1/tokio/net/struct.UnixStream.html>
130//! [`Service::try_connect`]: crate::client::Service::try_connect
131
132tungstenite_impl!(tokio_tungstenite029, Tungstenite029Impl, "0.29.x");