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}