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#[non_exhaustive]
195pub enum AxumServer {}
196
197impl ServerImpl for AxumServer {
198    type Error = Error;
199    type Message = Message;
200    type Socket = WebSocket;
201
202    #[inline]
203    fn ping(data: Bytes) -> Self::Message {
204        Message::Ping(data)
205    }
206
207    #[inline]
208    fn pong(data: Bytes) -> Self::Message {
209        Message::Pong(data)
210    }
211
212    #[inline]
213    fn binary(data: &[u8]) -> Self::Message {
214        Message::Binary(Bytes::from(data.to_vec()))
215    }
216
217    #[inline]
218    fn close(code: u16, reason: &str) -> Self::Message {
219        Message::Close(Some(CloseFrame {
220            code,
221            reason: reason.into(),
222        }))
223    }
224}
225
226impl crate::ws::socket_sealed::Sealed for WebSocket {}
227
228impl SocketImpl for WebSocket {
229    type Message = Message;
230    type Error = Error;
231
232    #[inline]
233    #[allow(private_interfaces)]
234    fn poll_next(
235        self: Pin<&mut Self>,
236        ctx: &mut Context<'_>,
237    ) -> Poll<Option<Result<ws::Message, Self::Error>>> {
238        let Some(result) = ready!(Stream::poll_next(self, ctx)) else {
239            return Poll::Ready(None);
240        };
241
242        let message = match result {
243            Ok(message) => message,
244            Err(err) => return Poll::Ready(Some(Err(err))),
245        };
246
247        let message = match message {
248            Message::Text(..) => ws::Message::Text,
249            Message::Binary(data) => ws::Message::Binary(data),
250            Message::Ping(data) => ws::Message::Ping(data),
251            Message::Pong(data) => ws::Message::Pong(data),
252            Message::Close(..) => ws::Message::Close,
253        };
254
255        Poll::Ready(Some(Ok(message)))
256    }
257
258    #[inline]
259    fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
260        Sink::poll_ready(self, cx)
261    }
262
263    #[inline]
264    fn start_send(self: Pin<&mut Self>, message: Self::Message) -> Result<(), Self::Error> {
265        Sink::start_send(self, message)
266    }
267
268    #[inline]
269    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
270        Sink::poll_flush(self, cx)
271    }
272}