Skip to main content

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