musli_web/axum08.rs
1//! The server implementation for [axum].
2//!
3//! Use [`server()`] to set up the server and feed it incoming requests.
4//!
5//! [axum]: <https://docs.rs/axum>
6
7use alloc::string::String;
8
9use core::pin::Pin;
10use core::task::Poll;
11use core::task::{Context, ready};
12
13use bytes::Bytes;
14
15use axum_core05::Error;
16use axum08::extract::ws::{CloseFrame, Message, Utf8Bytes, WebSocket};
17use futures_core03::Stream;
18use futures_sink03::Sink;
19
20use crate::ws::{self, Connect, Handler, ServerImpl, SocketImpl};
21
22/// Construct a new axum connection with the specified handler.
23///
24/// The returned [`Connect`] cannot send anything. Call [`Connect::connect`] to
25/// perform the [negotiation protocol], which hands back the [`Server`] that
26/// can. This is what makes it impossible to write a message before the client
27/// has agreed on the [`Format`] it will be encoded with.
28///
29/// [`Format`]: crate::api::Format
30/// [`Server`]: crate::ws::Server
31/// [negotiation protocol]: crate::api#negotiating-the-format
32///
33/// # Examples
34///
35/// ```
36/// # extern crate axum08 as axum;
37/// use std::error::Error;
38/// use std::pin::pin;
39///
40/// use axum::Router;
41/// use axum::extract::State;
42/// use axum::extract::ws::{WebSocket, WebSocketUpgrade};
43/// use axum::response::Response;
44/// use axum::routing::any;
45/// use tokio::sync::broadcast::Sender;
46/// use tokio::time::{self, Duration};
47///
48/// use musli_web::api::MessageId;
49/// use musli_web::axum08;
50/// use musli_web::ws;
51///
52/// mod api {
53/// use musli::{Decode, Encode};
54/// use musli_web::api;
55///
56/// #[derive(Encode, Decode)]
57/// pub struct HelloRequest<'de> {
58/// pub message: &'de str,
59/// }
60///
61/// #[derive(Encode, Decode)]
62/// pub struct HelloResponse<'de> {
63/// pub message: &'de str,
64/// }
65///
66/// #[derive(Encode, Decode)]
67/// pub struct TickEvent<'de> {
68/// pub message: &'de str,
69/// pub tick: u32,
70/// }
71///
72/// api::define! {
73/// pub type Hello;
74///
75/// impl Endpoint for Hello {
76/// impl<'de> Request for HelloRequest<'de>;
77/// type Response<'de> = HelloResponse<'de>;
78/// }
79///
80/// pub type Tick;
81///
82/// impl Broadcast for Tick {
83/// impl<'de> Event for TickEvent<'de>;
84/// }
85/// }
86/// }
87///
88/// #[derive(Debug, Clone)]
89/// enum Broadcast {
90/// Tick { tick: u32 },
91/// }
92///
93/// #[derive(Clone)]
94/// struct MyHandler;
95///
96/// impl ws::Handler for MyHandler {
97/// type Id = api::Request;
98/// type Response = Option<()>;
99///
100/// async fn handle(
101/// &self,
102/// id: Self::Id,
103/// incoming: &mut ws::Incoming<'_>,
104/// outgoing: &mut ws::Outgoing<'_>,
105/// ) -> Self::Response {
106/// tracing::info!("Handling: {id:?}");
107///
108/// match id {
109/// api::Request::Hello => {
110/// let request = incoming.read::<api::HelloRequest<'_>>()?;
111///
112/// outgoing.write(api::HelloResponse {
113/// message: request.message,
114/// });
115///
116/// Some(())
117/// }
118/// api::Request::Unknown(id) => {
119/// None
120/// }
121/// }
122/// }
123/// }
124///
125/// async fn handler(ws: WebSocketUpgrade, State(sender): State<Sender<Broadcast>>) -> Response {
126/// ws.on_upgrade(move |socket: WebSocket| async move {
127/// let mut subscribe = sender.subscribe();
128///
129/// // NB: Nothing can be sent until the client has negotiated a format,
130/// // which is what this step waits for.
131/// let mut server = match axum08::server(socket, MyHandler).connect().await {
132/// Ok(server) => server,
133/// Err(error) => {
134/// tracing::error!("Failed to negotiate: {error}");
135/// return;
136/// }
137/// };
138///
139/// loop {
140/// tokio::select! {
141/// m = subscribe.recv() => {
142/// let Ok(message) = m else {
143/// continue;
144/// };
145///
146/// let result = match message {
147/// Broadcast::Tick { tick } => {
148/// server.broadcast(api::TickEvent { message: "tick", tick })
149/// },
150/// };
151///
152/// if let Err(error) = result {
153/// tracing::error!("Broadcast failed: {error}");
154///
155/// let mut error = error.source();
156///
157/// while let Some(e) = error.take() {
158/// tracing::error!("Caused by: {e}");
159/// error = e.source();
160/// }
161/// }
162/// }
163/// result = server.run() => {
164/// if let Err(error) = result {
165/// tracing::error!("Websocket error: {error}");
166///
167/// let mut error = error.source();
168///
169/// while let Some(e) = error.take() {
170/// tracing::error!("Caused by: {e}");
171/// error = e.source();
172/// }
173/// }
174///
175/// break;
176/// }
177/// }
178/// }
179/// })
180/// }
181/// ```
182#[inline]
183pub fn server<H>(socket: WebSocket, handler: H) -> Connect<AxumServer, H>
184where
185 H: Handler,
186{
187 Connect::new(socket, handler)
188}
189
190impl crate::ws::server_sealed::Sealed for AxumServer {}
191
192/// Marker type used in combination with [`Server`] to indicate that the
193/// implementation uses axum.
194///
195/// See [`server()`] for how this is constructed and used.
196///
197/// [`Server`]: crate::ws::Server
198#[non_exhaustive]
199pub enum AxumServer {}
200
201impl ServerImpl for AxumServer {
202 type Error = Error;
203 type Message = Message;
204 type Socket = WebSocket;
205
206 #[inline]
207 fn ping(data: Bytes) -> Self::Message {
208 Message::Ping(data)
209 }
210
211 #[inline]
212 fn pong(data: Bytes) -> Self::Message {
213 Message::Pong(data)
214 }
215
216 #[inline]
217 fn binary(data: &[u8]) -> Self::Message {
218 Message::Binary(Bytes::from(data.to_vec()))
219 }
220
221 #[inline]
222 fn text(data: &[u8]) -> Self::Message {
223 // NB: A text frame is only ever produced in a mode which guarantees
224 // that everything written into it is valid UTF-8, so the lossy path is
225 // never taken.
226 Message::Text(Utf8Bytes::from(&*String::from_utf8_lossy(data)))
227 }
228
229 #[inline]
230 fn close(code: u16, reason: &str) -> Self::Message {
231 Message::Close(Some(CloseFrame {
232 code,
233 reason: reason.into(),
234 }))
235 }
236}
237
238impl crate::ws::socket_sealed::Sealed for WebSocket {}
239
240impl SocketImpl for WebSocket {
241 type Message = Message;
242 type Error = Error;
243
244 #[inline]
245 #[allow(private_interfaces)]
246 fn poll_next(
247 self: Pin<&mut Self>,
248 ctx: &mut Context<'_>,
249 ) -> Poll<Option<Result<ws::Message, Self::Error>>> {
250 let Some(result) = ready!(Stream::poll_next(self, ctx)) else {
251 return Poll::Ready(None);
252 };
253
254 let message = match result {
255 Ok(message) => message,
256 Err(err) => return Poll::Ready(Some(Err(err))),
257 };
258
259 let message = match message {
260 Message::Text(data) => ws::Message::Text(Bytes::from(data)),
261 Message::Binary(data) => ws::Message::Binary(data),
262 Message::Ping(data) => ws::Message::Ping(data),
263 Message::Pong(data) => ws::Message::Pong(data),
264 Message::Close(..) => ws::Message::Close,
265 };
266
267 Poll::Ready(Some(Ok(message)))
268 }
269
270 #[inline]
271 fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
272 Sink::poll_ready(self, cx)
273 }
274
275 #[inline]
276 fn start_send(self: Pin<&mut Self>, message: Self::Message) -> Result<(), Self::Error> {
277 Sink::start_send(self, message)
278 }
279
280 #[inline]
281 fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
282 Sink::poll_flush(self, cx)
283 }
284}