Skip to main content

musli_web/
tungstenite030.rs

1//! Client side implementation for [`tokio-tungstenite`] `0.30.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.30>
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::tungstenite030::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::tungstenite030::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::tungstenite030::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_tungstenite030, Tungstenite030Impl, "0.30.x");