Skip to main content

musli_web/
client.rs

1//! The generic asynchronous client implementation.
2//!
3//! This implements the client side of the same websocket protocol which is
4//! implemented by [`web`] for browsers, but built for native asynchronous
5//! runtimes instead of the browser event loop.
6//!
7//! This is specialized over the `T` parameter through modules such as:
8//!
9//! * [`tungstenite029`] for `tokio-tungstenite` `0.29.x`.
10//! * [`tungstenite030`] for `tokio-tungstenite` `0.30.x`.
11//!
12//! [`tungstenite029`]: crate::tungstenite029
13//! [`tungstenite030`]: crate::tungstenite030
14//! [`web`]: <https://docs.rs/musli-web/latest/musli_web/web/>
15//!
16//! # Overview
17//!
18//! A [`Service`] is a driver which owns the underlying socket. It has to be
19//! driven by calling [`Service::run`], which is typically done in a dedicated
20//! task.
21//!
22//! A [`Handle`] is a cheap and [`Clone`]-able handle to the service which can be
23//! shared and moved freely between tasks. It is used to perform requests, open
24//! channels, and to listen for broadcasts and state changes.
25
26use core::cell::Cell;
27use core::fmt;
28use core::future::Future;
29use core::marker::PhantomData;
30use core::{any, mem};
31
32use alloc::boxed::Box;
33use alloc::string::{String, ToString};
34use alloc::sync::Arc;
35use alloc::vec::Vec;
36
37use std::collections::HashMap;
38use std::collections::hash_map::Entry;
39use std::sync::Mutex;
40use std::sync::atomic::{AtomicBool, AtomicU8, AtomicU32, Ordering};
41
42use bytes::Bytes;
43use rand::prelude::*;
44use rand::rngs::SmallRng;
45use slab::Slab;
46use tokio::sync::{mpsc, oneshot, watch};
47use tokio::time::{Duration, Instant};
48
49use crate::api::{self, ChannelId, DecodeBody, Event, Format, MessageId, Mode};
50use crate::format;
51
52/// The initial reconnect timeout.
53const INITIAL_TIMEOUT: Duration = Duration::from_millis(250);
54/// The maximum reconnect timeout.
55const MAX_TIMEOUT: Duration = Duration::from_millis(4000);
56/// The maximum amount of fuzz added to a reconnect timeout.
57const MAX_FUZZ: u64 = 50;
58/// The default seed used for reconnect fuzzing.
59const DEFAULT_SEED: u64 = 0xdeadbeef;
60
61/// An empty request body.
62#[non_exhaustive]
63pub struct EmptyBody;
64
65/// An empty callback.
66#[non_exhaustive]
67pub struct EmptyCallback;
68
69/// A message received over the underlying socket.
70///
71/// The variant is what tells the client which [`Mode`] the envelope of the
72/// message is spelled in, see the [wire format].
73///
74/// [wire format]: crate::api#wire-format
75///
76/// NB: The variants are constructed by the transport bindings, such as
77/// [`tungstenite029`], so none are constructed when the generic core is built
78/// on its own.
79///
80/// [`tungstenite029`]: crate::tungstenite029
81#[cfg_attr(
82    not(any(feature = "tungstenite029", feature = "tungstenite030")),
83    allow(dead_code)
84)]
85pub(crate) enum Message {
86    /// A text message was received, which is what [`Mode::Text`] uses.
87    Text(Bytes),
88    /// A binary message was received.
89    Binary(Bytes),
90    /// A ping message was received.
91    ///
92    /// Implementations are expected to respond with a pong on their own.
93    Ping,
94    /// A pong message was received.
95    Pong,
96    /// A close message was received.
97    Close,
98    /// A message the protocol has no meaning for was received, which tears the
99    /// connection down.
100    Unsupported,
101}
102
103pub(crate) mod sealed_socket {
104    pub trait Sealed {}
105}
106
107pub(crate) trait SocketImpl
108where
109    Self: 'static + Send + Sized + self::sealed_socket::Sealed,
110{
111    #[doc(hidden)]
112    type Error;
113
114    /// Receive the next message.
115    ///
116    /// The returned future must be cancel safe, since it is used in a `select!`
117    /// loop together with the command queue of the service.
118    #[doc(hidden)]
119    fn recv(&mut self) -> impl Future<Output = Option<Result<Message, Self::Error>>> + Send + '_;
120
121    /// Send a message in the given [`Mode`] and flush it.
122    ///
123    /// [`Mode::Binary`] writes a binary frame and [`Mode::Text`] a text frame,
124    /// which is what carries the mode between peers.
125    #[doc(hidden)]
126    fn send(
127        &mut self,
128        mode: Mode,
129        data: &[u8],
130    ) -> impl Future<Output = Result<(), Self::Error>> + Send + '_;
131
132    /// Close the socket.
133    #[doc(hidden)]
134    fn close(&mut self) -> impl Future<Output = Result<(), Self::Error>> + Send + '_;
135}
136
137pub(crate) mod sealed_client {
138    pub trait Sealed {}
139}
140
141/// Central trait for asynchronous client integration.
142///
143/// Since websocket clients are provided by many different crates, this trait
144/// abstracts over the details of establishing and driving one.
145///
146/// The corresponding modules provide integrations:
147///
148/// * [`tungstenite029`] for `tokio-tungstenite` `0.29.x`.
149/// * [`tungstenite030`] for `tokio-tungstenite` `0.30.x`.
150///
151/// [`tungstenite029`]: crate::tungstenite029
152/// [`tungstenite030`]: crate::tungstenite030
153pub trait ClientImpl
154where
155    Self: 'static + Copy + Sized + self::sealed_client::Sealed,
156{
157    #[doc(hidden)]
158    type Error: 'static + Send + Sync + core::error::Error;
159
160    #[doc(hidden)]
161    #[allow(private_bounds)]
162    type Socket: SocketImpl<Error = Self::Error>;
163
164    /// A caller-supplied way of establishing the underlying socket, used in
165    /// place of connecting to the url. See constructors such as
166    /// [`tungstenite029::connect_with`].
167    ///
168    /// [`tungstenite029::connect_with`]: crate::tungstenite029::connect_with
169    #[doc(hidden)]
170    type Connector: 'static + Send;
171
172    /// The headers added to the websocket handshake, see
173    /// [`ServiceBuilder::header`].
174    #[doc(hidden)]
175    type Headers: 'static + Send + Sync + Default;
176
177    /// Validate a header and add it to `headers`.
178    #[doc(hidden)]
179    fn header(headers: &mut Self::Headers, name: &str, value: &str) -> Result<(), Self::Error>;
180
181    #[doc(hidden)]
182    fn connect<'a>(
183        url: &'a str,
184        headers: &'a Self::Headers,
185    ) -> impl Future<Output = Result<Self::Socket, Self::Error>> + Send + 'a;
186
187    #[doc(hidden)]
188    fn connect_with<'a>(
189        connector: &'a mut Self::Connector,
190        headers: &'a Self::Headers,
191    ) -> impl Future<Output = Result<Self::Socket, Self::Error>> + Send + 'a;
192}
193
194/// Construct a new [`ServiceBuilder`] which will connect to `url`.
195pub fn connect<T>(url: impl AsRef<str>) -> ServiceBuilder<T, EmptyCallback>
196where
197    T: ClientImpl,
198{
199    ServiceBuilder {
200        url: url.as_ref().to_string(),
201        connector: None,
202        headers: T::Headers::default(),
203        invalid_header: None,
204        on_error: EmptyCallback,
205        reconnect: true,
206        seed: DEFAULT_SEED,
207        format: Format::DEFAULT,
208        mode: Mode::DEFAULT,
209        app: 0,
210        _marker: PhantomData,
211    }
212}
213
214/// Construct a new [`ServiceBuilder`] which establishes its socket through
215/// `connector` instead of connecting to `url`.
216///
217/// What `url` is used for during the handshake is up to the implementation; it
218/// is never connected to directly.
219#[cfg_attr(
220    not(any(feature = "tungstenite029", feature = "tungstenite030")),
221    allow(dead_code)
222)]
223pub(crate) fn connect_with<T>(
224    url: impl AsRef<str>,
225    connector: T::Connector,
226) -> ServiceBuilder<T, EmptyCallback>
227where
228    T: ClientImpl,
229{
230    ServiceBuilder {
231        connector: Some(connector),
232        ..connect(url)
233    }
234}
235
236/// The state of the connection.
237///
238/// A listener for state changes can be set up through [`Handle::state`].
239#[derive(Debug, PartialEq, Eq, Clone, Copy)]
240#[non_exhaustive]
241pub enum State {
242    /// The connection is open.
243    Open,
244    /// The connection is closed.
245    Closed,
246    /// The server states an application version other than the one this
247    /// client was built with, see [`ServiceBuilder::app_version`].
248    ///
249    /// The connection is closed and [`Service::run`] returns an error for
250    /// which [`Error::is_incompatible`] holds, since connecting again reaches
251    /// the same server. This state is kept once the service is gone.
252    Incompatible {
253        /// The application version the server states.
254        server: u32,
255        /// The application version this client states.
256        client: u32,
257    },
258}
259
260impl State {
261    /// Check if the state is open.
262    ///
263    /// # Examples
264    ///
265    /// ```
266    /// use musli_web::client::State;
267    ///
268    /// assert!(State::Open.is_open());
269    /// assert!(!State::Closed.is_open());
270    /// ```
271    #[inline]
272    pub fn is_open(&self) -> bool {
273        matches!(self, Self::Open)
274    }
275
276    /// Check if the connection was refused because the server states another
277    /// application version, see [`State::Incompatible`].
278    ///
279    /// # Examples
280    ///
281    /// ```
282    /// use musli_web::client::State;
283    ///
284    /// assert!(State::Incompatible { server: 2, client: 1 }.is_incompatible());
285    /// assert!(!State::Closed.is_incompatible());
286    /// ```
287    #[inline]
288    pub fn is_incompatible(&self) -> bool {
289        matches!(self, Self::Incompatible { .. })
290    }
291}
292
293/// Trait governing how callbacks are called.
294pub trait Callback<I>
295where
296    Self: 'static + Send + Sync,
297{
298    /// Call the callback.
299    fn call(&self, input: I);
300}
301
302impl<I> Callback<I> for EmptyCallback {
303    #[inline]
304    fn call(&self, _: I) {}
305}
306
307impl<F, I> Callback<I> for F
308where
309    F: 'static + Send + Sync + Fn(I),
310{
311    #[inline]
312    fn call(&self, input: I) {
313        self(input)
314    }
315}
316
317/// Error type for the client.
318#[derive(Debug)]
319pub struct Error {
320    kind: ErrorKind,
321}
322
323impl Error {
324    #[inline]
325    const fn new(kind: ErrorKind) -> Self {
326        Self { kind }
327    }
328
329    #[inline]
330    const fn not_connected() -> Self {
331        Self::new(ErrorKind::NotConnected("Client is not connected"))
332    }
333
334    /// Check if the error is caused by an empty packet.
335    ///
336    /// # Examples
337    ///
338    /// ```
339    /// use musli_web::client::RawPacket;
340    ///
341    /// let packet = RawPacket::empty();
342    /// let e = packet.decode::<u32>().unwrap_err();
343    ///
344    /// assert!(e.is_empty_packet());
345    /// assert!(e.is_decode());
346    /// ```
347    #[inline]
348    pub fn is_empty_packet(&self) -> bool {
349        matches!(self.kind, ErrorKind::EmptyPacket)
350    }
351
352    /// Check if the request was never sent, since there was no open
353    /// connection to send it over.
354    ///
355    /// This is the error which is produced if a request is performed while the
356    /// service is not connected or after it has been dropped. Since the server
357    /// never saw the request, it is safe to send it again once the connection
358    /// is open. See [`Handle::wait_until_open`] for how to wait until the
359    /// connection is available.
360    #[inline]
361    pub fn is_not_connected(&self) -> bool {
362        matches!(self.kind, ErrorKind::NotConnected(..))
363    }
364
365    /// Check if the connection closed before a response to the request
366    /// arrived.
367    ///
368    /// Unlike [`is_not_connected`], the request was sent, or was being sent,
369    /// so the server may or may not have handled it. This is the error every
370    /// pending request gets when the connection closes or when the service is
371    /// dropped or closed.
372    ///
373    /// [`is_not_connected`]: Self::is_not_connected
374    #[inline]
375    pub fn is_connection_lost(&self) -> bool {
376        matches!(self.kind, ErrorKind::ConnectionLost(..))
377    }
378
379    /// Check if the request was cancelled before it was answered, since
380    /// another request took its place.
381    #[inline]
382    pub fn is_cancelled(&self) -> bool {
383        matches!(self.kind, ErrorKind::Cancelled)
384    }
385
386    /// Check if the error is caused by something received which could not be
387    /// decoded.
388    ///
389    /// This covers a response or broadcast whose body does not decode as the
390    /// requested type, an [empty packet](Self::is_empty_packet), a frame from
391    /// the server whose header or error message could not be decoded, and a
392    /// message body in an unknown format. Failing to decode a
393    /// [typed error](Self::server_error) is one too.
394    ///
395    /// # Examples
396    ///
397    /// ```
398    /// use musli_web::client::RawPacket;
399    ///
400    /// let packet = RawPacket::empty();
401    /// let e = packet.decode::<u32>().unwrap_err();
402    ///
403    /// assert!(e.is_decode());
404    /// ```
405    #[inline]
406    pub fn is_decode(&self) -> bool {
407        matches!(
408            self.kind,
409            ErrorKind::EmptyPacket
410                | ErrorKind::UnknownFormat(..)
411                | ErrorKind::DecodeResponseHeader(..)
412                | ErrorKind::DecodeErrorMessage(..)
413                | ErrorKind::DecodePacket(..)
414        )
415    }
416
417    /// Check if the error is caused by a header passed to
418    /// [`ServiceBuilder::header`] which cannot be sent, such as a name or a
419    /// value which is not valid in HTTP.
420    ///
421    /// This is what [`Service::run`] and [`Service::try_connect`] fail with
422    /// right away, without trying to connect, since every attempt would send
423    /// the same header. The underlying error is available through
424    /// [`core::error::Error::source`].
425    ///
426    /// # Examples
427    ///
428    /// ```
429    /// use musli_web::tungstenite030;
430    ///
431    /// # #[tokio::main(flavor = "current_thread")] async fn main() {
432    /// let mut service = tungstenite030::connect("ws://127.0.0.1:8080/ws")
433    ///     .header("Cookie", "session=\nabc")
434    ///     .build();
435    ///
436    /// let error = service.try_connect().await.unwrap_err();
437    /// assert!(error.is_invalid_header());
438    /// # }
439    /// ```
440    #[inline]
441    pub fn is_invalid_header(&self) -> bool {
442        matches!(self.kind, ErrorKind::InvalidHeader(..))
443    }
444
445    /// Check if the server states an application version other than the one
446    /// this client states, see [`ServiceBuilder::app_version`].
447    ///
448    /// This is what [`Service::run`] and [`Handle::wait_until_open`] fail with
449    /// once the service has found out, and the versions on either side are
450    /// available through [`State::Incompatible`].
451    #[inline]
452    pub fn is_incompatible(&self) -> bool {
453        matches!(self.kind, ErrorKind::Incompatible { .. })
454    }
455
456    /// Check if the error is a server error, and if so return the message
457    /// reported by the server.
458    ///
459    /// Server errors are produced by a [`Handler`] which returns an error or
460    /// which indicates that it does not support the request being made.
461    ///
462    /// This is the message of a [typed error](Self::server_error) too.
463    ///
464    /// [`Handler`]: <https://docs.rs/musli-web/latest/musli_web/ws/trait.Handler.html>
465    #[inline]
466    pub fn as_server_error(&self) -> Option<&str> {
467        match &self.kind {
468            ErrorKind::Server(error) => Some(&error.message),
469            _ => None,
470        }
471    }
472
473    /// Decode the typed error the server answered with.
474    ///
475    /// A handler which fails with a [`ws::TypedError<E>`] sends `E` encoded in
476    /// the body format of the connection, see [Errors]. This decodes it back
477    /// as `T`, which should be the same type as `E` or one which is
478    /// compatible with it, typically from an API crate shared with the server.
479    ///
480    /// Returns `Ok(None)` if this is not a server error, or if it is one which
481    /// carries no typed error, such as a handler error which is plain text or
482    /// an error the server produced on its own for a request it does not
483    /// support. Those still have a message, see [`as_server_error`].
484    ///
485    /// # Errors
486    ///
487    /// Errors if the typed error does not decode as `T`.
488    ///
489    /// # Examples
490    ///
491    /// ```
492    /// use musli::{Decode, Encode};
493    /// use musli_web::client::Error;
494    ///
495    /// #[derive(Debug, PartialEq, Encode, Decode)]
496    /// enum ServerError {
497    ///     NotFound,
498    /// }
499    ///
500    /// let error = Error::message("Something else");
501    /// assert_eq!(error.server_error::<ServerError>().unwrap(), None);
502    /// ```
503    ///
504    /// [`ws::TypedError<E>`]: <https://docs.rs/musli-web/latest/musli_web/ws/struct.TypedError.html>
505    /// [Errors]: <https://docs.rs/musli-web/latest/musli_web/ws/index.html#errors>
506    /// [`as_server_error`]: Self::as_server_error
507    pub fn server_error<'de, T>(&'de self) -> Result<Option<T>>
508    where
509        T: DecodeBody<'de>,
510    {
511        let ErrorKind::Server(error) = &self.kind else {
512            return Ok(None);
513        };
514
515        let Some((format, payload)) = &error.payload else {
516            return Ok(None);
517        };
518
519        let value = format
520            .decode(payload, &mut 0)
521            .map_err(Error::decode_error_message)?;
522
523        Ok(Some(value))
524    }
525
526    #[inline]
527    fn incompatible(server: u32, client: u32) -> Self {
528        Self::new(ErrorKind::Incompatible { server, client })
529    }
530
531    /// Format a client error consisting of a message.
532    ///
533    /// None of the accessors on [`Error`] match an error constructed this way.
534    #[inline]
535    pub fn message(message: impl fmt::Display) -> Self {
536        Self::new(ErrorKind::Message(message.to_string()))
537    }
538
539    #[inline]
540    fn server(error: ServerError) -> Self {
541        Self::new(ErrorKind::Server(error))
542    }
543
544    #[inline]
545    fn transport<E>(error: E) -> Self
546    where
547        E: 'static + Send + Sync + core::error::Error,
548    {
549        Self::new(ErrorKind::Transport(Box::new(error)))
550    }
551
552    #[inline]
553    fn decode_response_header(error: format::Error) -> Self {
554        Self::new(ErrorKind::DecodeResponseHeader(error))
555    }
556
557    #[inline]
558    fn decode_error_message(error: format::Error) -> Self {
559        Self::new(ErrorKind::DecodeErrorMessage(error))
560    }
561
562    #[inline]
563    fn decode_packet(error: format::Error) -> Self {
564        Self::new(ErrorKind::DecodePacket(error))
565    }
566
567    #[inline]
568    fn encoding_header(error: format::Error) -> Self {
569        Self::new(ErrorKind::EncodingHeader(error))
570    }
571
572    #[inline]
573    fn encoding_body(error: format::Error) -> Self {
574        Self::new(ErrorKind::EncodingBody(error))
575    }
576}
577
578#[derive(Debug)]
579enum ErrorKind {
580    EmptyPacket,
581    /// The request was never sent. Carries the text to display.
582    NotConnected(&'static str),
583    /// The connection closed while the request was pending. Carries the text
584    /// to display.
585    ConnectionLost(&'static str),
586    Cancelled,
587    InvalidHeader(InvalidHeader),
588    UnknownFormat(u8),
589    Incompatible {
590        server: u32,
591        client: u32,
592    },
593    Message(String),
594    Server(ServerError),
595    Transport(Box<dyn core::error::Error + Send + Sync>),
596    DecodeResponseHeader(format::Error),
597    DecodeErrorMessage(format::Error),
598    DecodePacket(format::Error),
599    EncodingHeader(format::Error),
600    EncodingBody(format::Error),
601}
602
603impl fmt::Display for Error {
604    #[inline]
605    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
606        match &self.kind {
607            ErrorKind::EmptyPacket => write!(f, "Packet is empty"),
608            ErrorKind::NotConnected(message) => write!(f, "{message}"),
609            ErrorKind::ConnectionLost(message) => write!(f, "{message}"),
610            ErrorKind::Cancelled => write!(f, "Request cancelled"),
611            ErrorKind::InvalidHeader(header) => {
612                write!(f, "Invalid handshake header `{}`", header.name)
613            }
614            ErrorKind::UnknownFormat(format) => write!(
615                f,
616                "Server used unknown format id {format} for a message body"
617            ),
618            ErrorKind::Incompatible { server, client } => write!(
619                f,
620                "Server states application version {server}, this client states {client}"
621            ),
622            ErrorKind::Message(message) => write!(f, "{message}"),
623            ErrorKind::Server(error) => write!(f, "Server error: {}", error.message),
624            ErrorKind::Transport(..) => write!(f, "Error in underlying transport"),
625            ErrorKind::DecodeResponseHeader(..) => {
626                write!(f, "Encoding error when decoding response header")
627            }
628            ErrorKind::DecodeErrorMessage(..) => {
629                write!(f, "Encoding error when decoding error response")
630            }
631            ErrorKind::DecodePacket(..) => write!(f, "Encoding error when decoding packet"),
632            ErrorKind::EncodingHeader(..) => write!(f, "Encoding error when encoding header"),
633            ErrorKind::EncodingBody(..) => write!(f, "Encoding error when encoding body"),
634        }
635    }
636}
637
638impl core::error::Error for Error {
639    #[inline]
640    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
641        match &self.kind {
642            ErrorKind::Transport(error) => Some(&**error),
643            ErrorKind::InvalidHeader(header) => Some(&*header.error),
644            ErrorKind::DecodeResponseHeader(error) => Some(error),
645            ErrorKind::DecodeErrorMessage(error) => Some(error),
646            ErrorKind::DecodePacket(error) => Some(error),
647            ErrorKind::EncodingHeader(error) => Some(error),
648            ErrorKind::EncodingBody(error) => Some(error),
649            _ => None,
650        }
651    }
652}
653
654/// A header passed to [`ServiceBuilder::header`] which cannot be sent.
655#[derive(Debug, Clone)]
656struct InvalidHeader {
657    /// The name of the header, as it was given.
658    name: String,
659    /// Why it cannot be sent.
660    error: Arc<dyn core::error::Error + Send + Sync>,
661}
662
663/// An error response from the server, see [error
664/// responses](crate::api#error-responses).
665#[derive(Debug, Clone)]
666struct ServerError {
667    /// The human readable message.
668    message: String,
669    /// The typed payload, and the format it is encoded with.
670    payload: Option<(Format, Bytes)>,
671}
672
673impl ServerError {
674    /// A server error without a payload.
675    #[inline]
676    fn message(message: &str) -> Self {
677        Self {
678            message: String::from(message),
679            payload: None,
680        }
681    }
682}
683
684type Result<T, E = Error> = core::result::Result<T, E>;
685
686/// Slab of broadcast listeners.
687type Broadcasts = HashMap<MessageId, Slab<mpsc::UnboundedSender<Result<RawPacket>>>>;
688
689/// A command sent from a [`Handle`] to the [`Service`] driving the connection.
690enum Command {
691    /// Send an already encoded message and register a pending response.
692    Send {
693        serial: u32,
694        mode: Mode,
695        data: Vec<u8>,
696        pending: Pending,
697    },
698    /// Cleanly disconnect a channel.
699    Disconnect { channel: ChannelId },
700    /// Close the service, causing [`Service::run`] to return.
701    Close,
702}
703
704/// A pending response being waited for.
705enum Pending {
706    /// A format negotiation issued by the service itself.
707    Negotiate { format: Format, mode: Mode },
708    /// A regular request, tagged with the endpoint it belongs to.
709    Request {
710        id: MessageId,
711        reply: oneshot::Sender<Result<RawPacket>>,
712    },
713    /// A channel being opened.
714    Channel {
715        reply: oneshot::Sender<Result<ChannelId>>,
716    },
717}
718
719impl Pending {
720    #[inline]
721    fn error(self, error: Error) {
722        match self {
723            Pending::Negotiate { .. } => {
724                tracing::debug!("Format negotiation failed: {error}");
725            }
726            Pending::Request { reply, .. } => {
727                _ = reply.send(Err(error));
728            }
729            Pending::Channel { reply } => {
730                _ = reply.send(Err(error));
731            }
732        }
733    }
734}
735
736/// State shared between a [`Service`] and every [`Handle`] associated with it.
737struct Shared {
738    tx: mpsc::UnboundedSender<Command>,
739    serial: AtomicU32,
740    state: watch::Sender<State>,
741    broadcasts: Mutex<Broadcasts>,
742    /// Set once the service driving this state is gone, at which point no
743    /// further state changes can be observed.
744    gone: AtomicBool,
745    /// The format which is actually in effect, as agreed by the [negotiation
746    /// protocol]. This can differ from the requested format if the server did
747    /// not support it.
748    ///
749    /// [negotiation protocol]: crate::api#negotiating-the-format
750    format: AtomicU8,
751    /// The mode which is actually in effect. This can differ from the requested
752    /// mode if the server could not carry the format that way.
753    mode: AtomicU8,
754}
755
756impl Shared {
757    #[inline]
758    fn next_serial(&self) -> u32 {
759        self.serial.fetch_add(1, Ordering::Relaxed)
760    }
761
762    #[inline]
763    fn is_open(&self) -> bool {
764        self.state.borrow().is_open()
765    }
766
767    /// The format currently in effect.
768    #[inline]
769    fn format(&self) -> Format {
770        Format::from_u8(self.format.load(Ordering::Acquire)).unwrap_or(Format::DEFAULT)
771    }
772
773    #[inline]
774    fn set_format(&self, format: Format) {
775        self.format.store(format.to_u8(), Ordering::Release);
776    }
777
778    /// The mode currently in effect.
779    #[inline]
780    fn mode(&self) -> Mode {
781        Mode::from_index(self.mode.load(Ordering::Acquire)).unwrap_or(Mode::DEFAULT)
782    }
783
784    #[inline]
785    fn set_mode(&self, mode: Mode) {
786        self.mode.store(mode.to_index(), Ordering::Release);
787    }
788
789    /// Test if the service driving this state is gone.
790    #[inline]
791    fn is_gone(&self) -> bool {
792        self.gone.load(Ordering::Acquire)
793    }
794
795    /// Mark the service as gone and wake up anyone waiting for a state change.
796    fn set_gone(&self) {
797        self.gone.store(true, Ordering::Release);
798        // NB: Unlike `send_if_modified` this always notifies, which is needed
799        // to wake up waiters even if the state itself did not change.
800        self.state.send_modify(|state| {
801            // NB: Kept, since it is what explains why the service is gone.
802            if !state.is_incompatible() {
803                *state = State::Closed;
804            }
805        });
806    }
807
808    #[inline]
809    fn send(&self, command: Command) -> Result<()> {
810        if self.tx.send(command).is_err() {
811            return Err(Error::new(ErrorKind::NotConnected(
812                "Client service is down",
813            )));
814        }
815
816        Ok(())
817    }
818}
819
820/// Builder of a [`Service`].
821///
822/// Constructed through [`connect()`].
823pub struct ServiceBuilder<T, E>
824where
825    T: ClientImpl,
826{
827    url: String,
828    connector: Option<T::Connector>,
829    headers: T::Headers,
830    /// The first header which could not be added, see
831    /// [`ServiceBuilder::header`].
832    invalid_header: Option<InvalidHeader>,
833    on_error: E,
834    reconnect: bool,
835    seed: u64,
836    format: Format,
837    mode: Mode,
838    app: u32,
839    _marker: PhantomData<T>,
840}
841
842impl<T, E> ServiceBuilder<T, E>
843where
844    T: ClientImpl,
845    E: Callback<Error>,
846{
847    /// Set the error handler to use for the service.
848    ///
849    /// Errors which are reported here are errors which cannot be associated
850    /// with a particular request, such as a failure to connect or a message
851    /// which could not be decoded.
852    #[inline]
853    pub fn on_error<U>(self, on_error: U) -> ServiceBuilder<T, U>
854    where
855        U: Callback<Error>,
856    {
857        ServiceBuilder {
858            url: self.url,
859            connector: self.connector,
860            headers: self.headers,
861            invalid_header: self.invalid_header,
862            on_error,
863            reconnect: self.reconnect,
864            seed: self.seed,
865            format: self.format,
866            mode: self.mode,
867            app: self.app,
868            _marker: self._marker,
869        }
870    }
871
872    /// Add a header to the HTTP request which opens the websocket, the
873    /// handshake.
874    ///
875    /// This is how a client authenticates with a server which expects a
876    /// session cookie or a token, such as `Cookie` or `Authorization`. The
877    /// header is sent on every connection attempt, so it is sent again when the
878    /// service [reconnects], and it is sent whether the service connects to the
879    /// url or [over a stream of its own].
880    ///
881    /// Calling this again with the same name adds another value rather than
882    /// replacing the first one. Header names are case insensitive.
883    ///
884    /// # Errors
885    ///
886    /// The headers the handshake itself is made of, `Host`, `Connection`,
887    /// `Upgrade`, `Sec-WebSocket-Key` and `Sec-WebSocket-Version`, are set by
888    /// the client and cannot be added. Neither can a name or a value which is
889    /// not valid in HTTP, such as a value with a line break in it.
890    ///
891    /// Since every attempt would send the same header, such a header is not
892    /// retried. Instead [`Service::run`] and [`Service::try_connect`] return an
893    /// error for which [`Error::is_invalid_header`] holds, without trying to
894    /// connect.
895    ///
896    /// [reconnects]: Self::reconnect
897    /// [over a stream of its own]: crate::tungstenite030::connect_with
898    ///
899    /// # Examples
900    ///
901    /// ```no_run
902    /// use musli_web::tungstenite030;
903    ///
904    /// # async fn example(token: &str) -> anyhow::Result<()> {
905    /// let mut service = tungstenite030::connect("ws://127.0.0.1:8080/ws")
906    ///     .header("Cookie", "session=4b0c2d")
907    ///     .header("Authorization", format!("Bearer {token}"))
908    ///     .build();
909    ///
910    /// service.run().await?;
911    /// # Ok(()) }
912    /// ```
913    pub fn header(mut self, name: impl AsRef<str>, value: impl AsRef<str>) -> Self {
914        let name = name.as_ref();
915
916        if let Err(error) = T::header(&mut self.headers, name, value.as_ref()) {
917            // NB: The first one is reported, since it is the first one which
918            // has to be fixed.
919            if self.invalid_header.is_none() {
920                self.invalid_header = Some(InvalidHeader {
921                    name: name.to_string(),
922                    error: Arc::new(error),
923                });
924            }
925        }
926
927        self
928    }
929
930    /// Set the [`Format`] to use for message bodies.
931    ///
932    /// The format is negotiated with the server once the connection is
933    /// established, see the [negotiation protocol]. If the server does not
934    /// support it the error is reported through [`ServiceBuilder::on_error`]
935    /// and the connection falls back to [`Format::DEFAULT`], which can be
936    /// observed through [`Handle::format`].
937    ///
938    /// Defaults to [`Format::DEFAULT`].
939    ///
940    /// [negotiation protocol]: crate::api#negotiating-the-format
941    #[inline]
942    pub fn format(mut self, format: Format) -> Self {
943        self.format = format;
944        self
945    }
946
947    /// Set the [`Mode`] the socket should run in.
948    ///
949    /// [`Mode::Text`] makes every frame human readable, which is what makes it
950    /// possible to read the traffic in a proxy or an inspector. It requires a
951    /// [`Format`] which is human readable too, so it is normally combined with
952    /// [`Format::Json`]:
953    ///
954    /// ```
955    /// use musli_web::api::{Format, Mode};
956    ///
957    /// assert!(Mode::Text.accepts(Format::Json));
958    /// ```
959    ///
960    /// Like the format, the mode is settled with the server once the connection
961    /// is established, see the [negotiation protocol]. A server which cannot
962    /// carry the requested format in the requested mode reports the error
963    /// through [`ServiceBuilder::on_error`] and the connection falls back to
964    /// [`Mode::DEFAULT`], which can be observed through [`Handle::mode`].
965    ///
966    /// Defaults to [`Mode::DEFAULT`].
967    ///
968    /// [negotiation protocol]: crate::api#negotiating-the-format
969    #[inline]
970    pub fn mode(mut self, mode: Mode) -> Self {
971        self.mode = mode;
972        self
973    }
974
975    /// State the version of the application's API this client was built
976    /// with.
977    ///
978    /// This is a version of *your* protocol, the requests and broadcasts
979    /// defined with [`api::define!`], as opposed to [`api::VERSION`] which is
980    /// the version of the envelope this crate puts around them. Give it the
981    /// same constant the server passes to `ws::Connect::app_version`, and bump
982    /// it whenever the API changes in a way an older client cannot cope with.
983    ///
984    /// When the connection is established the server states its version, and
985    /// if it is not this one the service reports [`State::Incompatible`]
986    /// instead of [`State::Open`], closes the connection, and [`Service::run`]
987    /// returns an error for which [`Error::is_incompatible`] holds. It does so
988    /// whether or not [reconnecting] is enabled, since connecting again would
989    /// reach the same server.
990    ///
991    /// Zero, which is the default, states no version. The check only applies
992    /// when both peers state one, so a client or a server which does not opt
993    /// in behaves exactly as if this did not exist. See [application
994    /// versions].
995    ///
996    /// [`api::define!`]: crate::api::define
997    /// [reconnecting]: Self::reconnect
998    /// [application versions]: crate::api#application-versions
999    ///
1000    /// # Examples
1001    ///
1002    /// ```no_run
1003    /// use musli_web::tungstenite030;
1004    ///
1005    /// /// Shared with the server, bumped whenever the API changes.
1006    /// const APP_VERSION: u32 = 7;
1007    ///
1008    /// # async fn example() -> anyhow::Result<()> {
1009    /// let mut service = tungstenite030::connect("ws://127.0.0.1:8080/ws")
1010    ///     .app_version(APP_VERSION)
1011    ///     .build();
1012    ///
1013    /// if let Err(error) = service.run().await {
1014    ///     if error.is_incompatible() {
1015    ///         println!("{}, please upgrade", error);
1016    ///     }
1017    /// }
1018    /// # Ok(()) }
1019    /// ```
1020    #[inline]
1021    pub fn app_version(mut self, version: u32) -> Self {
1022        self.app = version;
1023        self
1024    }
1025
1026    /// Configure whether the service should try to reconnect when the
1027    /// connection is lost.
1028    ///
1029    /// This defaults to `true`. If this is disabled, [`Service::run`] returns
1030    /// once the connection has been lost or could not be established.
1031    #[inline]
1032    pub fn reconnect(mut self, reconnect: bool) -> Self {
1033        self.reconnect = reconnect;
1034        self
1035    }
1036
1037    /// Associate the specified seed with the service.
1038    ///
1039    /// This affects the random fuzzing which is applied to reconnect timeouts.
1040    ///
1041    /// By default the seed is a constant value.
1042    #[inline]
1043    pub fn seed(mut self, seed: u64) -> Self {
1044        self.seed = seed;
1045        self
1046    }
1047
1048    /// Build the service.
1049    ///
1050    /// Note that no connection is established until [`Service::run`] or
1051    /// [`Service::try_connect`] is called.
1052    pub fn build(self) -> Service<T> {
1053        let (tx, rx) = mpsc::unbounded_channel();
1054        let (state, _) = watch::channel(State::Closed);
1055
1056        let shared = Arc::new(Shared {
1057            tx,
1058            serial: AtomicU32::new(0),
1059            state,
1060            broadcasts: Mutex::new(Broadcasts::new()),
1061            gone: AtomicBool::new(false),
1062            format: AtomicU8::new(self.format.to_u8()),
1063            mode: AtomicU8::new(self.mode.to_index()),
1064        });
1065
1066        Service {
1067            handle: Handle {
1068                shared: shared.clone(),
1069            },
1070            shared,
1071            rx,
1072            url: self.url,
1073            connector: self.connector,
1074            headers: self.headers,
1075            invalid_header: self.invalid_header,
1076            on_error: Box::new(self.on_error),
1077            reconnect: self.reconnect,
1078            socket: None,
1079            pending: HashMap::new(),
1080            timeout: INITIAL_TIMEOUT,
1081            next_attempt: Some(Instant::now()),
1082            rng: SmallRng::seed_from_u64(self.seed),
1083            closed: false,
1084            incompatible: None,
1085            requested: self.format,
1086            requested_mode: self.mode,
1087            app: self.app,
1088        }
1089    }
1090}
1091
1092/// The service which drives a connection.
1093///
1094/// This is constructed through [`connect()`] and has to be driven by calling
1095/// [`Service::run`].
1096pub struct Service<T>
1097where
1098    T: ClientImpl,
1099{
1100    handle: Handle,
1101    shared: Arc<Shared>,
1102    rx: mpsc::UnboundedReceiver<Command>,
1103    url: String,
1104    connector: Option<T::Connector>,
1105    /// The headers added to every handshake.
1106    headers: T::Headers,
1107    /// A header which cannot be sent, which fails the service before it
1108    /// connects.
1109    invalid_header: Option<InvalidHeader>,
1110    on_error: Box<dyn Callback<Error>>,
1111    reconnect: bool,
1112    socket: Option<T::Socket>,
1113    pending: HashMap<u32, Pending>,
1114    timeout: Duration,
1115    next_attempt: Option<Instant>,
1116    rng: SmallRng,
1117    closed: bool,
1118    /// The application versions of the server and of this client, once the
1119    /// server has been found to state another one than this client.
1120    incompatible: Option<(u32, u32)>,
1121    /// The format the user asked for, which is re-negotiated on every
1122    /// reconnect.
1123    requested: Format,
1124    /// The mode which was asked for, which the connection falls back from if
1125    /// the server cannot carry the negotiated format that way.
1126    requested_mode: Mode,
1127    /// The application version this client states, or zero for none.
1128    app: u32,
1129}
1130
1131/// The event produced by one iteration of the [`Service::run`] loop.
1132enum Output<E> {
1133    /// A message was received over the socket.
1134    Message(Option<Result<Message, E>>),
1135    /// A command was received from a handle.
1136    Command(Option<Command>),
1137    /// It is time to try and establish a connection.
1138    Connect,
1139}
1140
1141impl<T> Service<T>
1142where
1143    T: ClientImpl,
1144{
1145    /// Get a handle to the service.
1146    ///
1147    /// The returned handle can be cloned and moved freely between tasks.
1148    #[inline]
1149    pub fn handle(&self) -> &Handle {
1150        &self.handle
1151    }
1152
1153    /// Run the service.
1154    ///
1155    /// This drives the underlying connection and must be called for any
1156    /// requests to be processed. It is typically spawned onto a task of its
1157    /// own.
1158    ///
1159    /// Unless disabled through [`ServiceBuilder::reconnect`], a connection
1160    /// which is lost is re-established with an exponential backoff. Any
1161    /// requests which were in flight at that point are failed.
1162    ///
1163    /// This returns once [`Handle::close`] has been called, or the connection
1164    /// has been lost while reconnecting is disabled.
1165    ///
1166    /// # Errors
1167    ///
1168    /// Errors if the server states another application version, see
1169    /// [`ServiceBuilder::app_version`]. The service has then stopped for good,
1170    /// and calling this again returns immediately.
1171    ///
1172    /// Errors right away, without connecting, if a header was passed to
1173    /// [`ServiceBuilder::header`] which cannot be sent.
1174    pub async fn run(&mut self) -> Result<()> {
1175        if let Some(header) = &self.invalid_header {
1176            return Err(Error::new(ErrorKind::InvalidHeader(header.clone())));
1177        }
1178
1179        if let Some((server, client)) = self.incompatible {
1180            return Err(Error::incompatible(server, client));
1181        }
1182
1183        while !self.closed {
1184            let output = {
1185                let rx = &mut self.rx;
1186
1187                match &mut self.socket {
1188                    Some(socket) => {
1189                        tokio::select! {
1190                            message = socket.recv() => Output::Message(message),
1191                            command = rx.recv() => Output::Command(command),
1192                        }
1193                    }
1194                    None => match self.next_attempt {
1195                        Some(deadline) => {
1196                            tokio::select! {
1197                                _ = tokio::time::sleep_until(deadline) => Output::Connect,
1198                                command = rx.recv() => Output::Command(command),
1199                            }
1200                        }
1201                        None => Output::Command(rx.recv().await),
1202                    },
1203                }
1204            };
1205
1206            match output {
1207                Output::Connect => {
1208                    self.connect().await;
1209                }
1210                Output::Command(command) => {
1211                    let Some(command) = command else {
1212                        // Every handle has been dropped and no more commands
1213                        // can be received.
1214                        break;
1215                    };
1216
1217                    self.command(command).await;
1218                }
1219                Output::Message(message) => {
1220                    let Some(message) = message else {
1221                        tracing::debug!("Connection closed by server");
1222                        self.disconnect().await;
1223                        continue;
1224                    };
1225
1226                    let message = match message {
1227                        Ok(message) => message,
1228                        Err(error) => {
1229                            self.on_error.call(Error::transport(error));
1230                            self.disconnect().await;
1231                            continue;
1232                        }
1233                    };
1234
1235                    match message {
1236                        // NB: The frame type is the mode, so a message can
1237                        // always be read regardless of what has been agreed on.
1238                        Message::Binary(bytes) => {
1239                            let post = self.message(Mode::Binary, bytes);
1240                            self.post(post).await;
1241                        }
1242                        Message::Text(bytes) => {
1243                            let post = self.message(Mode::Text, bytes);
1244                            self.post(post).await;
1245                        }
1246                        Message::Unsupported => {
1247                            self.on_error.call(Error::message("Unsupported message"));
1248                            self.disconnect().await;
1249                        }
1250                        Message::Ping | Message::Pong => {}
1251                        Message::Close => {
1252                            tracing::debug!("Close message received");
1253                            self.disconnect().await;
1254                        }
1255                    }
1256                }
1257            }
1258        }
1259
1260        self.shutdown().await;
1261
1262        match self.incompatible {
1263            Some((server, client)) => Err(Error::incompatible(server, client)),
1264            None => Ok(()),
1265        }
1266    }
1267
1268    /// Carry out what processing a message said has to happen next.
1269    async fn post(&mut self, post: Result<Post>) {
1270        match post {
1271            Ok(Post::Negotiate) => self.send_negotiate().await,
1272            Ok(Post::None) => {}
1273            Ok(Post::Reject) => self.disconnect().await,
1274            Ok(Post::Incompatible { server }) => {
1275                // NB: Negotiated anyway, so that the server hears which
1276                // version this client states and can tell why it goes away.
1277                self.send_negotiate().await;
1278                self.incompatible(server).await;
1279            }
1280            Err(error) => self.on_error.call(error),
1281        }
1282    }
1283
1284    /// Give up on a server which states application version `server`.
1285    async fn incompatible(&mut self, server: u32) {
1286        let client = self.app;
1287        tracing::debug!(server, client, "Incompatible server");
1288
1289        if let Some(mut socket) = self.socket.take() {
1290            _ = socket.close().await;
1291        }
1292
1293        self.close_pending(|| Error::incompatible(server, client));
1294        self.emit_state(State::Incompatible { server, client });
1295        self.next_attempt = None;
1296        self.closed = true;
1297        self.incompatible = Some((server, client));
1298    }
1299
1300    /// Establish the connection now, returning a failure to the caller instead
1301    /// of scheduling a reconnect.
1302    ///
1303    /// This lets a caller fail fast when there is nothing to connect to, such
1304    /// as a refused connection or a missing unix socket, rather than leaving
1305    /// [`Service::run`] to retry with a backoff. On success the established
1306    /// connection is the one [`Service::run`] goes on to drive. If a connection
1307    /// is already established this does nothing.
1308    ///
1309    /// The error is not reported through [`ServiceBuilder::on_error`]. The
1310    /// underlying transport error is available through
1311    /// [`core::error::Error::source`] on the returned error.
1312    ///
1313    /// Errors without connecting if a header was passed to
1314    /// [`ServiceBuilder::header`] which cannot be sent, see
1315    /// [`Error::is_invalid_header`].
1316    pub async fn try_connect(&mut self) -> Result<()> {
1317        if let Some(header) = &self.invalid_header {
1318            return Err(Error::new(ErrorKind::InvalidHeader(header.clone())));
1319        }
1320
1321        if self.socket.is_some() {
1322            return Ok(());
1323        }
1324
1325        let socket = self.open().await.map_err(Error::transport)?;
1326        self.established(socket);
1327        Ok(())
1328    }
1329
1330    /// Open a new socket, either through the caller-supplied connector or by
1331    /// connecting to the url.
1332    async fn open(&mut self) -> Result<T::Socket, T::Error> {
1333        tracing::debug!(url = self.url.as_str(), "Connecting");
1334
1335        match &mut self.connector {
1336            Some(connector) => T::connect_with(connector, &self.headers).await,
1337            None => T::connect(&self.url, &self.headers).await,
1338        }
1339    }
1340
1341    /// Install a newly established socket.
1342    fn established(&mut self, socket: T::Socket) {
1343        tracing::debug!("Connection established");
1344        self.socket = Some(socket);
1345        self.next_attempt = None;
1346        self.timeout = INITIAL_TIMEOUT;
1347    }
1348
1349    /// Try to establish a connection.
1350    async fn connect(&mut self) {
1351        match self.open().await {
1352            Ok(socket) => {
1353                self.established(socket);
1354            }
1355            Err(error) => {
1356                self.on_error.call(Error::transport(error));
1357                self.schedule_reconnect();
1358            }
1359        }
1360    }
1361
1362    /// Tear down the current connection and schedule a reconnect.
1363    async fn disconnect(&mut self) {
1364        if let Some(mut socket) = self.socket.take() {
1365            _ = socket.close().await;
1366        }
1367
1368        self.emit_state(State::Closed);
1369        self.close_pending(|| Error::new(ErrorKind::ConnectionLost("Connection closed")));
1370        self.schedule_reconnect();
1371    }
1372
1373    /// Shut the service down for good.
1374    async fn shutdown(&mut self) {
1375        if let Some(mut socket) = self.socket.take() {
1376            _ = socket.close().await;
1377        }
1378
1379        // NB: Kept, since it is what explains why the service stopped.
1380        if !self.shared.state.borrow().is_incompatible() {
1381            self.emit_state(State::Closed);
1382        }
1383
1384        self.close_pending(|| Error::new(ErrorKind::ConnectionLost("Client service closed")));
1385    }
1386
1387    fn schedule_reconnect(&mut self) {
1388        if !self.reconnect {
1389            tracing::debug!("Reconnecting is disabled, closing service");
1390            self.closed = true;
1391            return;
1392        }
1393
1394        let fuzz = self.rng.random_range(0..=MAX_FUZZ);
1395
1396        let timeout = self
1397            .timeout
1398            .saturating_add(Duration::from_millis(fuzz))
1399            .min(MAX_TIMEOUT);
1400
1401        self.timeout = self.timeout.saturating_mul(2).min(MAX_TIMEOUT);
1402        self.next_attempt = Some(Instant::now() + timeout);
1403        tracing::debug!(?timeout, "Scheduling reconnect");
1404    }
1405
1406    /// Fail every pending request, since there is no chance they will be
1407    /// responded to any more.
1408    fn close_pending(&mut self, error: impl Fn() -> Error) {
1409        for (_, pending) in self.pending.drain() {
1410            pending.error(error());
1411        }
1412    }
1413
1414    fn emit_state(&mut self, state: State) {
1415        self.shared.state.send_if_modified(|current| {
1416            if *current == state {
1417                return false;
1418            }
1419
1420            *current = state;
1421            true
1422        });
1423    }
1424
1425    /// Handle a command received from a handle.
1426    async fn command(&mut self, command: Command) {
1427        match command {
1428            Command::Send {
1429                serial,
1430                mode,
1431                data,
1432                pending,
1433            } => {
1434                let Some(socket) = self.socket.as_mut() else {
1435                    pending.error(Error::not_connected());
1436                    return;
1437                };
1438
1439                if let Err(error) = socket.send(mode, &data).await {
1440                    pending.error(Error::transport(error));
1441                    self.disconnect().await;
1442                    return;
1443                }
1444
1445                if let Some(existing) = self.pending.insert(serial, pending) {
1446                    existing.error(Error::new(ErrorKind::Cancelled));
1447                }
1448            }
1449            Command::Disconnect { channel } => {
1450                if let Err(error) = self.send_disconnect(channel).await {
1451                    self.on_error.call(error);
1452                }
1453            }
1454            Command::Close => {
1455                self.closed = true;
1456            }
1457        }
1458    }
1459
1460    async fn send_disconnect(&mut self, channel: ChannelId) -> Result<()> {
1461        let Some(socket) = self.socket.as_mut() else {
1462            return Ok(());
1463        };
1464
1465        let mut data = Vec::new();
1466        let mode = self.shared.mode();
1467
1468        let header = api::RequestHeader {
1469            version: api::VERSION,
1470            serial: 0,
1471            id: MessageId::DISCONNECT.get(),
1472            // NB: Carries no body.
1473            format: 0,
1474            channel,
1475            app: 0,
1476        };
1477
1478        format::encode_envelope(mode, &mut data, &header).map_err(Error::encoding_header)?;
1479
1480        tracing::debug!(?channel, "Sending disconnect");
1481
1482        if let Err(error) = socket.send(mode, &data).await {
1483            let error = Error::transport(error);
1484            self.disconnect().await;
1485            return Err(error);
1486        }
1487
1488        Ok(())
1489    }
1490
1491    /// Dispatch a value to every listener of the given broadcast.
1492    ///
1493    /// Note that listeners are never removed here, since removal is the
1494    /// exclusive responsibility of the corresponding [`Listener`]. Sending to a
1495    /// listener which has been dropped but not yet cleared is simply ignored.
1496    fn dispatch(&self, id: MessageId, value: impl Fn() -> Result<RawPacket>) {
1497        let broadcasts = self
1498            .shared
1499            .broadcasts
1500            .lock()
1501            .unwrap_or_else(|e| e.into_inner());
1502
1503        let Some(slots) = broadcasts.get(&id) else {
1504            return;
1505        };
1506
1507        for (_, tx) in slots.iter() {
1508            _ = tx.send(value());
1509        }
1510    }
1511
1512    /// Resolve the format a message body is encoded with from its envelope.
1513    fn body_format(header: &api::ResponseHeader) -> Result<Format> {
1514        let Some(format) = Format::from_u8(header.format) else {
1515            return Err(Error::new(ErrorKind::UnknownFormat(header.format)));
1516        };
1517
1518        Ok(format)
1519    }
1520
1521    /// Decode the body of an error response, whose `error` header is `id`.
1522    ///
1523    /// An error id which carries no message is reported with `unsupported`.
1524    fn server_error(
1525        id: MessageId,
1526        header: &api::ResponseHeader,
1527        bytes: &Bytes,
1528        mut at: usize,
1529        unsupported: &str,
1530    ) -> Result<ServerError> {
1531        let typed = match id {
1532            MessageId::ERROR_MESSAGE => false,
1533            MessageId::TYPED_ERROR_MESSAGE => true,
1534            _ => return Ok(ServerError::message(unsupported)),
1535        };
1536
1537        let format = Self::body_format(header)?;
1538
1539        let error: api::ErrorMessage<'_> = format
1540            .decode(bytes, &mut at)
1541            .map_err(Error::decode_error_message)?;
1542
1543        // NB: The payload is only decoded once the caller says what type it
1544        // is, see `Error::server_error`.
1545        let payload = if typed {
1546            Some((format, bytes.slice(at.min(bytes.len())..)))
1547        } else {
1548            None
1549        };
1550
1551        Ok(ServerError {
1552            message: String::from(error.message),
1553            payload,
1554        })
1555    }
1556
1557    /// Process an incoming message which arrived in the given mode.
1558    fn message(&mut self, mode: Mode, bytes: Bytes) -> Result<Post> {
1559        let mut at = 0;
1560
1561        let header: api::ResponseHeader = match format::decode_envelope(mode, &bytes, &mut at) {
1562            Ok(header) => header,
1563            // NB: The server speaks a protocol this build does not have, so
1564            // there is nothing to be gained by carrying on with the session.
1565            // The connection is dropped rather than negotiated.
1566            Err(error) if error.unsupported_version().is_some() => {
1567                self.on_error.call(Error::decode_response_header(error));
1568                return Ok(Post::Reject);
1569            }
1570            Err(error) => return Err(Error::decode_response_header(error)),
1571        };
1572
1573        if let Some(broadcast) = MessageId::new(header.broadcast) {
1574            tracing::debug!(?header, "Got broadcast");
1575
1576            if broadcast == MessageId::SERVER_HELLO {
1577                if self.app != 0 && header.app != 0 && header.app != self.app {
1578                    return Ok(Post::Incompatible { server: header.app });
1579                }
1580
1581                // NB: The connection is not reported as open until the format
1582                // has been negotiated, so that server-initiated messages are
1583                // never encoded with a format this client did not agree to.
1584                tracing::debug!("Server hello, negotiating format");
1585                return Ok(Post::Negotiate);
1586            }
1587
1588            if let Some(id) = MessageId::new(header.error) {
1589                let error = Self::server_error(id, &header, &bytes, at, "Unsupported broadcast")?;
1590                self.dispatch(broadcast, || Err(Error::server(error.clone())));
1591                return Ok(Post::None);
1592            }
1593
1594            let format = Self::body_format(&header)?;
1595
1596            let packet = RawPacket {
1597                id: broadcast,
1598                buf: bytes,
1599                at: Cell::new(at),
1600                format,
1601                channel: header.channel,
1602            };
1603
1604            self.dispatch(broadcast, || Ok(packet.clone()));
1605            return Ok(Post::None);
1606        }
1607
1608        tracing::debug!(?header, "Got response");
1609
1610        let Some(pending) = self.pending.remove(&header.serial) else {
1611            // NB: This is normal, it simply indicates that the request has been
1612            // cancelled.
1613            tracing::trace!(?header.serial, "Got message with unknown serial");
1614            return Ok(Post::None);
1615        };
1616
1617        if let Some(id) = MessageId::new(header.error) {
1618            let error = match Self::server_error(id, &header, &bytes, at, "Unsupported request") {
1619                Ok(error) => error,
1620                Err(error) => match pending {
1621                    Pending::Negotiate { .. } => return Err(error),
1622                    // NB: Fail the request rather than leave it to find out
1623                    // that its reply was dropped.
1624                    pending => {
1625                        pending.error(error);
1626                        return Ok(Post::None);
1627                    }
1628                },
1629            };
1630
1631            match pending {
1632                Pending::Negotiate {
1633                    format,
1634                    mode: requested,
1635                } => {
1636                    // NB: The server cannot speak the requested format, or
1637                    // cannot carry it in the requested mode, so fall back to the
1638                    // defaults rather than leaving the connection unusable. What
1639                    // is actually in effect is observable through
1640                    // `Handle::format` and `Handle::mode`.
1641                    self.on_error.call(Error::message(format_args!(
1642                        "Server rejected format `{format}` in `{requested}` mode ({}), falling back to `{}`",
1643                        error.message,
1644                        Format::DEFAULT
1645                    )));
1646
1647                    self.shared.set_format(Format::DEFAULT);
1648                    // NB: The frame this arrived in is the mode the server
1649                    // settled on, so there is nothing to guess at.
1650                    self.shared.set_mode(mode);
1651                    self.emit_state(State::Open);
1652                }
1653                pending => {
1654                    pending.error(Error::server(error));
1655                }
1656            }
1657
1658            return Ok(Post::None);
1659        }
1660
1661        match pending {
1662            Pending::Negotiate { format, .. } => {
1663                // NB: Trust the format the server echoed back over the one that
1664                // was asked for, so that a server which downgrades is honored.
1665                // The mode is read off the frame for the same reason.
1666                let accepted = Format::from_u8(header.format).unwrap_or(format);
1667                tracing::debug!(?accepted, ?mode, "Format negotiated");
1668                self.shared.set_format(accepted);
1669                self.shared.set_mode(mode);
1670                self.emit_state(State::Open);
1671            }
1672            Pending::Channel { reply } => {
1673                _ = reply.send(Ok(header.channel));
1674            }
1675            Pending::Request { id, reply } => {
1676                let format = Self::body_format(&header)?;
1677
1678                let packet = RawPacket {
1679                    id,
1680                    buf: bytes,
1681                    at: Cell::new(at),
1682                    format,
1683                    channel: header.channel,
1684                };
1685
1686                _ = reply.send(Ok(packet));
1687            }
1688        }
1689
1690        Ok(Post::None)
1691    }
1692
1693    /// Ask the server to use the requested format for the rest of the
1694    /// connection.
1695    async fn send_negotiate(&mut self) {
1696        let format = self.requested;
1697        let mode = self.requested_mode;
1698        let serial = self.shared.next_serial();
1699
1700        let header = api::RequestHeader {
1701            version: api::VERSION,
1702            serial,
1703            id: MessageId::NEGOTIATE.get(),
1704            format: format.to_u8(),
1705            // NB: Carries no body.
1706            channel: ChannelId::NONE,
1707            app: self.app,
1708        };
1709
1710        let mut data = Vec::new();
1711
1712        // NB: The frame this goes out in is what asks for the mode, so the
1713        // envelope has to be spelled the same way.
1714        if let Err(error) = format::encode_envelope(mode, &mut data, &header) {
1715            self.on_error.call(Error::encoding_header(error));
1716            return;
1717        }
1718
1719        let Some(socket) = self.socket.as_mut() else {
1720            return;
1721        };
1722
1723        tracing::debug!(?format, ?mode, "Requesting format");
1724
1725        if let Err(error) = socket.send(mode, &data).await {
1726            self.on_error.call(Error::transport(error));
1727            self.disconnect().await;
1728            return;
1729        }
1730
1731        self.pending
1732            .insert(serial, Pending::Negotiate { format, mode });
1733    }
1734}
1735
1736/// Work which has to happen after a message has been processed, once the
1737/// borrow of the message buffer has been released.
1738enum Post {
1739    /// Nothing to do.
1740    None,
1741    /// The format has to be negotiated with the server.
1742    Negotiate,
1743    /// The server cannot be talked to, so the connection has to be dropped.
1744    Reject,
1745    /// The server states application version `server`, which is not the one
1746    /// this client states.
1747    Incompatible { server: u32 },
1748}
1749
1750impl<T> Drop for Service<T>
1751where
1752    T: ClientImpl,
1753{
1754    fn drop(&mut self) {
1755        self.shared.set_gone();
1756
1757        for (_, pending) in self.pending.drain() {
1758            pending.error(Error::new(ErrorKind::ConnectionLost(
1759                "Client service closed",
1760            )));
1761        }
1762
1763        self.shared
1764            .broadcasts
1765            .lock()
1766            .unwrap_or_else(|e| e.into_inner())
1767            .clear();
1768    }
1769}
1770
1771impl<T> fmt::Debug for Service<T>
1772where
1773    T: ClientImpl,
1774{
1775    #[inline]
1776    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1777        f.debug_struct("Service")
1778            .field("url", &self.url)
1779            .field("state", &*self.shared.state.borrow())
1780            .finish_non_exhaustive()
1781    }
1782}
1783
1784/// A handle to the client service.
1785///
1786/// This is cheap to clone and can be moved freely between tasks.
1787#[derive(Clone)]
1788pub struct Handle {
1789    shared: Arc<Shared>,
1790}
1791
1792impl Handle {
1793    /// Get the current state of the connection.
1794    #[inline]
1795    pub fn state(&self) -> State {
1796        *self.shared.state.borrow()
1797    }
1798
1799    /// Check if the connection is currently open.
1800    #[inline]
1801    pub fn is_open(&self) -> bool {
1802        self.shared.is_open()
1803    }
1804
1805    /// The [`Format`] currently in effect for message bodies.
1806    ///
1807    /// Before the connection has been opened this is the format which was
1808    /// requested through [`ServiceBuilder::format`]. Once the connection is
1809    /// open it is the format the server actually agreed to, which can differ if
1810    /// the server does not support what was asked for.
1811    #[inline]
1812    pub fn format(&self) -> Format {
1813        self.shared.format()
1814    }
1815
1816    /// The [`Mode`] the socket is currently running in.
1817    ///
1818    /// Before the connection is open this is the mode which was requested
1819    /// through [`ServiceBuilder::mode`]. Once the connection is open it is the
1820    /// mode which was settled on, see the [negotiation protocol].
1821    ///
1822    /// [negotiation protocol]: crate::api#negotiating-the-format
1823    #[inline]
1824    pub fn mode(&self) -> Mode {
1825        self.shared.mode()
1826    }
1827
1828    /// Listen for state changes to the underlying connection.
1829    ///
1830    /// This indicates when the connection is open and ready to receive requests
1831    /// through [`State::Open`], or if it's closed and requests will be rejected
1832    /// through [`State::Closed`]. A server which states another application
1833    /// version is reported through [`State::Incompatible`], see
1834    /// [`ServiceBuilder::app_version`].
1835    #[inline]
1836    pub fn on_state_change(&self) -> StateListener {
1837        StateListener {
1838            rx: self.shared.state.subscribe(),
1839            shared: self.shared.clone(),
1840        }
1841    }
1842
1843    /// Wait until the connection is open.
1844    ///
1845    /// Since a connection is established asynchronously, this must be called
1846    /// before performing the first request unless you are prepared to handle
1847    /// the [`Error::is_not_connected`] error.
1848    ///
1849    /// This errors if the [`Service`] driving the connection is gone, since the
1850    /// connection can then never be established, and with an error for which
1851    /// [`Error::is_incompatible`] holds if the server states another
1852    /// application version, see [`ServiceBuilder::app_version`].
1853    pub async fn wait_until_open(&self) -> Result<()> {
1854        let mut rx = self.shared.state.subscribe();
1855
1856        loop {
1857            match *rx.borrow_and_update() {
1858                State::Open => return Ok(()),
1859                State::Incompatible { server, client } => {
1860                    return Err(Error::incompatible(server, client));
1861                }
1862                State::Closed => {}
1863            }
1864
1865            if self.shared.is_gone() || rx.changed().await.is_err() {
1866                return Err(Error::new(ErrorKind::NotConnected(
1867                    "Client service is down",
1868                )));
1869            }
1870        }
1871    }
1872
1873    /// Open a new logical channel to the websocket server.
1874    ///
1875    /// A channel can be uniquely identified on the client and server side over
1876    /// a single connection. This means that if you send a request over a
1877    /// channel using [`Channel::request`], the server can access the
1878    /// [`ChannelId`] to determine which channel sent the request and the client
1879    /// has the ability to correlate any responses sent by the server by
1880    /// inspecting [`Packet::channel`] or [`RawPacket::channel`].
1881    ///
1882    /// The maximum number of channels is implementation defined, but expect it
1883    /// to be relatively low like `65535` (non-zero 16 bits) to reduce payload
1884    /// sizes. Failure to allocate a channel is an error.
1885    ///
1886    /// Note that a channel is scoped to the connection it was opened over. If
1887    /// the connection is lost and re-established, any channel opened over the
1888    /// old connection is stale and has to be opened again.
1889    pub async fn channel(&self) -> Result<Channel> {
1890        if !self.shared.is_open() {
1891            return Err(Error::not_connected());
1892        }
1893
1894        let serial = self.shared.next_serial();
1895
1896        let header = api::RequestHeader {
1897            version: api::VERSION,
1898            serial,
1899            id: MessageId::CONNECT.get(),
1900            // NB: Carries no body.
1901            format: 0,
1902            channel: ChannelId::NONE,
1903            app: 0,
1904        };
1905
1906        let mode = self.shared.mode();
1907        let mut data = Vec::new();
1908        format::encode_envelope(mode, &mut data, &header).map_err(Error::encoding_header)?;
1909
1910        let (reply, rx) = oneshot::channel();
1911
1912        self.shared.send(Command::Send {
1913            serial,
1914            mode,
1915            data,
1916            pending: Pending::Channel { reply },
1917        })?;
1918
1919        let Ok(result) = rx.await else {
1920            return Err(Error::new(ErrorKind::ConnectionLost(
1921                "Client service is down",
1922            )));
1923        };
1924
1925        Ok(Channel {
1926            shared: self.shared.clone(),
1927            id: result?,
1928        })
1929    }
1930
1931    /// Send a request over the default channel.
1932    ///
1933    /// See [`RequestBuilder::send`] for how the request is completed.
1934    #[inline]
1935    pub fn request(&self) -> RequestBuilder<'_, EmptyBody> {
1936        RequestBuilder {
1937            shared: &self.shared,
1938            channel: ChannelId::NONE,
1939            body: EmptyBody,
1940        }
1941    }
1942
1943    /// Send a request over the default channel and decode its response.
1944    ///
1945    /// This sends `body`, waits for the response and decodes it as the
1946    /// response of the endpoint `body` is a request for. It is the same as
1947    /// [`request`] followed by [`RequestBuilder::send`] and
1948    /// [`Packet::decode_response`], except that the response is returned as
1949    /// an owned value which does not borrow from the packet it was decoded
1950    /// from.
1951    ///
1952    /// The response type of the endpoint must therefore be owned, which is
1953    /// what the `for<'de> ...: DecodeBody<'de>` bound says: it decodes from
1954    /// any buffer, so it holds on to none of them. A response declared as
1955    /// `type Response<'de> = User;` qualifies, one which borrows such as
1956    /// `type Response<'de> = UserRef<'de>;` does not compile here. For those
1957    /// keep the [`Packet`] around through [`RequestBuilder::send`] and decode
1958    /// from it instead.
1959    ///
1960    /// See [`Channel::call`] to call over a channel.
1961    ///
1962    /// [`request`]: Self::request
1963    ///
1964    /// # Errors
1965    ///
1966    /// The request fails with an [`Error`] which tells what went wrong:
1967    ///
1968    /// * [`Error::is_not_connected`] if the connection is not open, so the
1969    ///   request was never sent. See [`Handle::wait_until_open`].
1970    /// * [`Error::as_server_error`] if the handler on the server failed, and
1971    ///   [`Error::server_error`] to decode the typed error it failed with.
1972    /// * A decoding error if the response does not decode as the response
1973    ///   type, for instance since the server is built from another version of
1974    ///   the API.
1975    /// * Any other error if the connection is lost or the [`Service`] is
1976    ///   gone before the response arrives.
1977    ///
1978    /// # Examples
1979    ///
1980    /// ```
1981    /// use musli_web::client::{Error, Handle};
1982    ///
1983    /// mod api {
1984    ///     use musli::{Decode, Encode};
1985    ///     use musli_web::api;
1986    ///
1987    ///     #[derive(Encode, Decode)]
1988    ///     pub struct UserRequest<'de> {
1989    ///         pub name: &'de str,
1990    ///     }
1991    ///
1992    ///     #[derive(Debug, Encode, Decode)]
1993    ///     pub struct UserResponse {
1994    ///         pub name: String,
1995    ///         pub age: u32,
1996    ///     }
1997    ///
1998    ///     /// The typed error the server fails with.
1999    ///     #[derive(Debug, Encode, Decode)]
2000    ///     pub enum UserError {
2001    ///         NotFound,
2002    ///     }
2003    ///
2004    ///     api::define! {
2005    ///         pub type User;
2006    ///
2007    ///         impl Endpoint for User {
2008    ///             impl<'de> Request for UserRequest<'de>;
2009    ///             type Response<'de> = UserResponse;
2010    ///         }
2011    ///     }
2012    /// }
2013    ///
2014    /// /// Look up the age of a user, or `None` if there is no such user.
2015    /// async fn age(handle: &Handle, name: &str) -> Result<Option<u32>, Error> {
2016    ///     match handle.call(api::UserRequest { name }).await {
2017    ///         Ok(user) => Ok(Some(user.age)),
2018    ///         Err(error) => match error.server_error::<api::UserError>()? {
2019    ///             Some(api::UserError::NotFound) => Ok(None),
2020    ///             None => Err(error),
2021    ///         },
2022    ///     }
2023    /// }
2024    /// ```
2025    pub async fn call<B>(
2026        &self,
2027        body: B,
2028    ) -> Result<<B::Endpoint as api::Endpoint>::Response<'static>>
2029    where
2030        B: api::Request,
2031        for<'de> <B::Endpoint as api::Endpoint>::Response<'static>: DecodeBody<'de>,
2032    {
2033        self.request()
2034            .body(body)
2035            .send()
2036            .await?
2037            .decode_any_response()
2038    }
2039
2040    /// Listen for broadcasts of type `T`.
2041    ///
2042    /// Broadcasts are buffered in the returned listener until they are received
2043    /// with [`Listener::recv`]. Dropping the listener removes it.
2044    ///
2045    /// Note that the buffer is unbounded, so a listener which is not drained
2046    /// keeps accumulating broadcasts. Drop or [`clear`] a listener which is no
2047    /// longer of interest.
2048    ///
2049    /// [`clear`]: Listener::clear
2050    pub fn on_broadcast<T>(&self) -> Listener<T>
2051    where
2052        T: api::Broadcast,
2053    {
2054        let (tx, rx) = mpsc::unbounded_channel();
2055
2056        let index = {
2057            let mut broadcasts = self
2058                .shared
2059                .broadcasts
2060                .lock()
2061                .unwrap_or_else(|e| e.into_inner());
2062
2063            broadcasts.entry(T::ID).or_default().insert(tx)
2064        };
2065
2066        Listener {
2067            shared: Some(self.shared.clone()),
2068            id: T::ID,
2069            index,
2070            rx,
2071            _marker: PhantomData,
2072        }
2073    }
2074
2075    /// Close the service.
2076    ///
2077    /// This causes [`Service::run`] to return.
2078    #[inline]
2079    pub fn close(&self) {
2080        _ = self.shared.tx.send(Command::Close);
2081    }
2082}
2083
2084impl PartialEq for Handle {
2085    #[inline]
2086    fn eq(&self, other: &Self) -> bool {
2087        Arc::ptr_eq(&self.shared, &other.shared)
2088    }
2089}
2090
2091impl Eq for Handle {}
2092
2093impl fmt::Debug for Handle {
2094    #[inline]
2095    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2096        f.debug_struct("Handle")
2097            .field("state", &*self.shared.state.borrow())
2098            .finish_non_exhaustive()
2099    }
2100}
2101
2102/// A channel to a websocket server.
2103///
2104/// See [`Handle::channel`] for more details.
2105///
2106/// Dropping this cleanly disconnects the channel, which is signalled to the
2107/// server through [`Handler::close_channel`].
2108///
2109/// [`Handler::close_channel`]: <https://docs.rs/musli-web/latest/musli_web/ws/trait.Handler.html#method.close_channel>
2110pub struct Channel {
2111    shared: Arc<Shared>,
2112    id: ChannelId,
2113}
2114
2115impl Channel {
2116    /// Get the channel identifier for this channel.
2117    #[inline]
2118    pub fn id(&self) -> ChannelId {
2119        self.id
2120    }
2121
2122    /// Get a handle associated with this channel.
2123    ///
2124    /// A handle sheds the channel information and allows for setting up things
2125    /// like broadcast listeners.
2126    #[inline]
2127    pub fn handle(&self) -> Handle {
2128        Handle {
2129            shared: self.shared.clone(),
2130        }
2131    }
2132
2133    /// Send a request over the current channel.
2134    ///
2135    /// See [`RequestBuilder::send`] for how the request is completed.
2136    #[inline]
2137    pub fn request(&self) -> RequestBuilder<'_, EmptyBody> {
2138        RequestBuilder {
2139            shared: &self.shared,
2140            channel: self.id,
2141            body: EmptyBody,
2142        }
2143    }
2144
2145    /// Send a request over the current channel and decode its response.
2146    ///
2147    /// This is the same as [`Handle::call`] except that the request is sent
2148    /// over this channel, see it for how the response is decoded and the
2149    /// errors it fails with.
2150    pub async fn call<B>(
2151        &self,
2152        body: B,
2153    ) -> Result<<B::Endpoint as api::Endpoint>::Response<'static>>
2154    where
2155        B: api::Request,
2156        for<'de> <B::Endpoint as api::Endpoint>::Response<'static>: DecodeBody<'de>,
2157    {
2158        self.request()
2159            .body(body)
2160            .send()
2161            .await?
2162            .decode_any_response()
2163    }
2164}
2165
2166impl Drop for Channel {
2167    #[inline]
2168    fn drop(&mut self) {
2169        _ = self.shared.send(Command::Disconnect { channel: self.id });
2170    }
2171}
2172
2173impl fmt::Debug for Channel {
2174    #[inline]
2175    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2176        f.debug_struct("Channel")
2177            .field("id", &self.id)
2178            .field("state", &*self.shared.state.borrow())
2179            .finish_non_exhaustive()
2180    }
2181}
2182
2183/// A request builder.
2184///
2185/// Set the body of the request with [`RequestBuilder::body`] and send it with
2186/// [`RequestBuilder::send`].
2187pub struct RequestBuilder<'a, B> {
2188    shared: &'a Arc<Shared>,
2189    channel: ChannelId,
2190    body: B,
2191}
2192
2193impl<'a, B> RequestBuilder<'a, B> {
2194    /// Set the body of the request.
2195    #[inline]
2196    pub fn body<U>(self, body: U) -> RequestBuilder<'a, U>
2197    where
2198        U: api::Request,
2199    {
2200        RequestBuilder {
2201            shared: self.shared,
2202            channel: self.channel,
2203            body,
2204        }
2205    }
2206}
2207
2208impl<B> RequestBuilder<'_, B>
2209where
2210    B: api::Request,
2211{
2212    /// Send the request and wait for the typed response.
2213    pub async fn send(self) -> Result<Packet<B::Endpoint>> {
2214        Ok(Packet::new(self.send_raw().await?))
2215    }
2216
2217    /// Send the request and wait for the raw response.
2218    pub async fn send_raw(self) -> Result<RawPacket> {
2219        let id = <B::Endpoint as api::Endpoint>::ID;
2220
2221        if !self.shared.is_open() {
2222            return Err(Error::not_connected());
2223        }
2224
2225        let serial = self.shared.next_serial();
2226        let format = self.shared.format();
2227        let mode = self.shared.mode();
2228
2229        let header = api::RequestHeader {
2230            version: api::VERSION,
2231            serial,
2232            id: id.get(),
2233            format: format.to_u8(),
2234            channel: self.channel,
2235            app: 0,
2236        };
2237
2238        let mut data = Vec::new();
2239        format::encode_envelope(mode, &mut data, &header).map_err(Error::encoding_header)?;
2240        format
2241            .encode(&mut data, &self.body)
2242            .map_err(Error::encoding_body)?;
2243
2244        tracing::debug!(
2245            serial,
2246            ?id,
2247            ?format,
2248            ?mode,
2249            len = data.len(),
2250            "Sending request"
2251        );
2252
2253        let (reply, rx) = oneshot::channel();
2254
2255        self.shared.send(Command::Send {
2256            serial,
2257            mode,
2258            data,
2259            pending: Pending::Request { id, reply },
2260        })?;
2261
2262        let Ok(result) = rx.await else {
2263            return Err(Error::new(ErrorKind::ConnectionLost(
2264                "Client service is down",
2265            )));
2266        };
2267
2268        result
2269    }
2270}
2271
2272/// A listener for broadcasts of type `T`.
2273///
2274/// Constructed through [`Handle::on_broadcast`]. Dropping this removes the
2275/// listener.
2276pub struct Listener<T> {
2277    shared: Option<Arc<Shared>>,
2278    id: MessageId,
2279    index: usize,
2280    rx: mpsc::UnboundedReceiver<Result<RawPacket>>,
2281    _marker: PhantomData<T>,
2282}
2283
2284impl<T> Listener<T> {
2285    /// Receive the next raw broadcast.
2286    ///
2287    /// Returns `None` if the service has been shut down.
2288    #[inline]
2289    pub async fn recv_raw(&mut self) -> Option<Result<RawPacket>> {
2290        self.rx.recv().await
2291    }
2292
2293    /// Receive the next broadcast.
2294    ///
2295    /// Returns `None` if the service has been shut down.
2296    #[inline]
2297    pub async fn recv(&mut self) -> Option<Result<Packet<T>>> {
2298        Some(match self.rx.recv().await? {
2299            Ok(packet) => Ok(Packet::new(packet)),
2300            Err(error) => Err(error),
2301        })
2302    }
2303
2304    /// Clear the listener without dropping it.
2305    ///
2306    /// This removes the associated listener from being notified. Any broadcasts
2307    /// which have already been buffered can still be received, after which
2308    /// [`Listener::recv`] returns `None`.
2309    pub fn clear(&mut self) {
2310        let Some(shared) = self.shared.take() else {
2311            return;
2312        };
2313
2314        let index = mem::take(&mut self.index);
2315
2316        let mut broadcasts = shared.broadcasts.lock().unwrap_or_else(|e| e.into_inner());
2317
2318        let Entry::Occupied(mut e) = broadcasts.entry(self.id) else {
2319            return;
2320        };
2321
2322        _ = e.get_mut().try_remove(index);
2323
2324        if e.get().is_empty() {
2325            e.remove();
2326        }
2327    }
2328}
2329
2330impl<T> Drop for Listener<T> {
2331    #[inline]
2332    fn drop(&mut self) {
2333        self.clear();
2334    }
2335}
2336
2337impl<T> fmt::Debug for Listener<T> {
2338    #[inline]
2339    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2340        f.debug_struct("Listener")
2341            .field("type", &any::type_name::<T>())
2342            .field("id", &self.id)
2343            .finish_non_exhaustive()
2344    }
2345}
2346
2347/// A listener for state changes.
2348///
2349/// Constructed through [`Handle::on_state_change`].
2350pub struct StateListener {
2351    rx: watch::Receiver<State>,
2352    shared: Arc<Shared>,
2353}
2354
2355impl StateListener {
2356    /// Get the most recently observed state.
2357    #[inline]
2358    pub fn state(&self) -> State {
2359        *self.rx.borrow()
2360    }
2361
2362    /// Wait for the state to change and return the new state.
2363    ///
2364    /// Returns `None` if the [`Service`] driving the connection is gone.
2365    #[inline]
2366    pub async fn changed(&mut self) -> Option<State> {
2367        if self.shared.is_gone() {
2368            return None;
2369        }
2370
2371        self.rx.changed().await.ok()?;
2372
2373        if self.shared.is_gone() {
2374            return None;
2375        }
2376
2377        Some(*self.rx.borrow_and_update())
2378    }
2379
2380    /// Wait until the observed state is `state`.
2381    ///
2382    /// Returns `false` if the [`Service`] driving the connection is gone before
2383    /// the state could be observed.
2384    pub async fn wait_until(&mut self, state: State) -> bool {
2385        loop {
2386            if *self.rx.borrow_and_update() == state {
2387                return true;
2388            }
2389
2390            if self.shared.is_gone() {
2391                return false;
2392            }
2393
2394            if self.rx.changed().await.is_err() {
2395                return false;
2396            }
2397        }
2398    }
2399}
2400
2401impl Clone for StateListener {
2402    #[inline]
2403    fn clone(&self) -> Self {
2404        Self {
2405            rx: self.rx.clone(),
2406            shared: self.shared.clone(),
2407        }
2408    }
2409}
2410
2411impl fmt::Debug for StateListener {
2412    #[inline]
2413    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2414        f.debug_struct("StateListener")
2415            .field("state", &*self.rx.borrow())
2416            .finish()
2417    }
2418}
2419
2420/// A raw packet of data.
2421#[derive(Clone)]
2422pub struct RawPacket {
2423    id: MessageId,
2424    buf: Bytes,
2425    at: Cell<usize>,
2426    format: Format,
2427    channel: ChannelId,
2428}
2429
2430impl RawPacket {
2431    /// Construct an empty raw packet.
2432    ///
2433    /// # Examples
2434    ///
2435    /// ```
2436    /// use musli_web::api::MessageId;
2437    /// use musli_web::client::RawPacket;
2438    ///
2439    /// let packet = RawPacket::empty();
2440    ///
2441    /// assert!(packet.is_empty());
2442    /// assert_eq!(packet.id(), MessageId::EMPTY);
2443    /// ```
2444    #[inline]
2445    pub const fn empty() -> Self {
2446        Self {
2447            id: MessageId::EMPTY,
2448            buf: Bytes::new(),
2449            at: Cell::new(0),
2450            format: Format::DEFAULT,
2451            channel: ChannelId::NONE,
2452        }
2453    }
2454
2455    /// The [`Format`] the body of this packet is encoded with.
2456    ///
2457    /// # Examples
2458    ///
2459    /// ```
2460    /// use musli_web::api::Format;
2461    /// use musli_web::client::RawPacket;
2462    ///
2463    /// let packet = RawPacket::empty();
2464    /// assert_eq!(packet.format(), Format::DEFAULT);
2465    /// ```
2466    #[inline]
2467    pub fn format(&self) -> Format {
2468        self.format
2469    }
2470
2471    /// Return the channel this packet belongs to.
2472    ///
2473    /// For a response this is the channel the request was sent over, and for
2474    /// a broadcast it is the channel the broadcast originated from, as written
2475    /// by [`ws::Server::broadcast_from`]. A page can compare it to the id of its
2476    /// own channel to recognize the echo of a change it made itself, see
2477    /// [`RawPacket::is_from`].
2478    ///
2479    /// This is [`ChannelId::NONE`] for a request which was not sent over a
2480    /// channel, and for a broadcast which no channel is credited with, such as
2481    /// one written with [`ws::Server::broadcast`].
2482    ///
2483    /// [`ws::Server::broadcast`]: https://docs.rs/musli-web/latest/musli_web/ws/struct.Server.html#method.broadcast
2484    /// [`ws::Server::broadcast_from`]: https://docs.rs/musli-web/latest/musli_web/ws/struct.Server.html#method.broadcast_from
2485    #[inline]
2486    pub fn channel(&self) -> ChannelId {
2487        self.channel
2488    }
2489
2490    /// Test if this packet originated from `channel`.
2491    ///
2492    /// This is `true` if the packet is a broadcast which was written with
2493    /// [`ws::Server::broadcast_from`] and `channel` as its origin, or a
2494    /// response to a request sent over `channel`. It is never `true` for
2495    /// [`ChannelId::NONE`], so a broadcast which no channel is credited with
2496    /// is not mistaken for the echo of a page which has not opened a channel
2497    /// yet.
2498    ///
2499    /// Channel ids are unique across every connection to a server by
2500    /// default, so a broadcast caused by another client never matches.
2501    ///
2502    /// [`ws::Server::broadcast_from`]: https://docs.rs/musli-web/latest/musli_web/ws/struct.Server.html#method.broadcast_from
2503    ///
2504    /// # Examples
2505    ///
2506    /// ```
2507    /// use musli_web::api::ChannelId;
2508    /// use musli_web::client::RawPacket;
2509    ///
2510    /// let packet = RawPacket::empty();
2511    /// assert_eq!(packet.channel(), ChannelId::NONE);
2512    /// assert!(!packet.is_from(ChannelId::NONE));
2513    /// ```
2514    #[inline]
2515    pub fn is_from(&self, channel: ChannelId) -> bool {
2516        channel != ChannelId::NONE && self.channel == channel
2517    }
2518
2519    /// Decode the contents of a raw packet.
2520    ///
2521    /// This can be called multiple times if there are multiple payloads in
2522    /// sequence of the response.
2523    ///
2524    /// You can check if the packet is empty using [`RawPacket::is_empty`].
2525    pub fn decode<'this, T>(&'this self) -> Result<T>
2526    where
2527        T: DecodeBody<'this>,
2528    {
2529        if self.id == MessageId::EMPTY {
2530            return Err(Error::new(ErrorKind::EmptyPacket));
2531        }
2532
2533        let mut at = self.at.get();
2534
2535        match self.format.decode(&self.buf, &mut at) {
2536            Ok(value) => {
2537                self.at.set(at);
2538                Ok(value)
2539            }
2540            Err(error) => {
2541                self.at.set(self.len());
2542                Err(Error::decode_packet(error))
2543            }
2544        }
2545    }
2546
2547    /// Get the underlying byte slice of the packet.
2548    ///
2549    /// # Examples
2550    ///
2551    /// ```
2552    /// use musli_web::client::RawPacket;
2553    ///
2554    /// let packet = RawPacket::empty();
2555    /// assert_eq!(packet.as_slice(), &[] as &[u8]);
2556    /// ```
2557    #[inline]
2558    pub fn as_slice(&self) -> &[u8] {
2559        &self.buf
2560    }
2561
2562    /// Get the number of bytes remaining to be decoded in the packet.
2563    ///
2564    /// # Examples
2565    ///
2566    /// ```
2567    /// use musli_web::client::RawPacket;
2568    ///
2569    /// let packet = RawPacket::empty();
2570    /// assert_eq!(packet.remaining(), 0);
2571    /// ```
2572    #[inline]
2573    pub fn remaining(&self) -> usize {
2574        self.buf.len().saturating_sub(self.at.get())
2575    }
2576
2577    /// Get the length of the packet.
2578    ///
2579    /// # Examples
2580    ///
2581    /// ```
2582    /// use musli_web::client::RawPacket;
2583    ///
2584    /// let packet = RawPacket::empty();
2585    /// assert_eq!(packet.len(), 0);
2586    /// ```
2587    #[inline]
2588    pub fn len(&self) -> usize {
2589        self.buf.len()
2590    }
2591
2592    /// Check if the packet is empty.
2593    ///
2594    /// # Examples
2595    ///
2596    /// ```
2597    /// use musli_web::client::RawPacket;
2598    ///
2599    /// let packet = RawPacket::empty();
2600    /// assert!(packet.is_empty());
2601    /// ```
2602    #[inline]
2603    pub fn is_empty(&self) -> bool {
2604        self.at.get() >= self.len()
2605    }
2606
2607    /// The id of the packet this is a response to as specified by
2608    /// [`Endpoint::ID`] or [`Broadcast::ID`].
2609    ///
2610    /// [`Endpoint::ID`]: crate::api::Endpoint::ID
2611    /// [`Broadcast::ID`]: crate::api::Broadcast::ID
2612    #[inline]
2613    pub fn id(&self) -> MessageId {
2614        self.id
2615    }
2616}
2617
2618impl fmt::Debug for RawPacket {
2619    #[inline]
2620    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2621        f.debug_struct("RawPacket")
2622            .field("id", &self.id)
2623            .field("remaining", &self.remaining())
2624            .finish()
2625    }
2626}
2627
2628/// A typed packet of data.
2629pub struct Packet<T> {
2630    raw: RawPacket,
2631    _marker: PhantomData<T>,
2632}
2633
2634impl<T> Packet<T> {
2635    /// Construct an empty packet.
2636    ///
2637    /// # Examples
2638    ///
2639    /// ```
2640    /// use musli_web::api::MessageId;
2641    /// use musli_web::client::Packet;
2642    ///
2643    /// let packet = Packet::<()>::empty();
2644    ///
2645    /// assert!(packet.is_empty());
2646    /// assert_eq!(packet.id(), MessageId::EMPTY);
2647    /// ```
2648    #[inline]
2649    pub const fn empty() -> Self {
2650        Self {
2651            raw: RawPacket::empty(),
2652            _marker: PhantomData,
2653        }
2654    }
2655
2656    /// Construct a new typed packet from a raw one.
2657    ///
2658    /// Note that this does not guarantee that the typed packet is correct, but
2659    /// the `T` parameter becomes associated with it allowing it to be used
2660    /// automatically with methods such as [`Packet::decode`].
2661    #[inline]
2662    pub fn new(raw: RawPacket) -> Self {
2663        Self {
2664            raw,
2665            _marker: PhantomData,
2666        }
2667    }
2668
2669    /// Return the channel this packet belongs to.
2670    ///
2671    /// For a response this is the channel the request was sent over, and for
2672    /// a broadcast it is the channel the broadcast originated from, as written
2673    /// by [`ws::Server::broadcast_from`]. A page can compare it to the id of its
2674    /// own channel to recognize the echo of a change it made itself, see
2675    /// [`Packet::is_from`].
2676    ///
2677    /// This is [`ChannelId::NONE`] for a request which was not sent over a
2678    /// channel, and for a broadcast which no channel is credited with, such as
2679    /// one written with [`ws::Server::broadcast`].
2680    ///
2681    /// [`ws::Server::broadcast`]: https://docs.rs/musli-web/latest/musli_web/ws/struct.Server.html#method.broadcast
2682    /// [`ws::Server::broadcast_from`]: https://docs.rs/musli-web/latest/musli_web/ws/struct.Server.html#method.broadcast_from
2683    #[inline]
2684    pub fn channel(&self) -> ChannelId {
2685        self.raw.channel()
2686    }
2687
2688    /// Test if this packet originated from `channel`.
2689    ///
2690    /// This is `true` if the packet is a broadcast which was written with
2691    /// [`ws::Server::broadcast_from`] and `channel` as its origin, or a
2692    /// response to a request sent over `channel`. It is never `true` for
2693    /// [`ChannelId::NONE`], so a broadcast which no channel is credited with
2694    /// is not mistaken for the echo of a page which has not opened a channel
2695    /// yet.
2696    ///
2697    /// Channel ids are unique across every connection to a server by
2698    /// default, so a broadcast caused by another client never matches.
2699    ///
2700    /// [`ws::Server::broadcast_from`]: https://docs.rs/musli-web/latest/musli_web/ws/struct.Server.html#method.broadcast_from
2701    ///
2702    /// # Examples
2703    ///
2704    /// ```
2705    /// use musli_web::api::ChannelId;
2706    /// use musli_web::client::Packet;
2707    ///
2708    /// let packet = Packet::<()>::empty();
2709    /// assert_eq!(packet.channel(), ChannelId::NONE);
2710    /// assert!(!packet.is_from(ChannelId::NONE));
2711    /// ```
2712    #[inline]
2713    pub fn is_from(&self, channel: ChannelId) -> bool {
2714        self.raw.is_from(channel)
2715    }
2716
2717    /// The [`Format`] the body of this packet is encoded with.
2718    #[inline]
2719    pub fn format(&self) -> Format {
2720        self.raw.format()
2721    }
2722
2723    /// Convert a packet into a raw packet.
2724    #[inline]
2725    pub fn into_raw(self) -> RawPacket {
2726        self.raw
2727    }
2728
2729    /// Get the number of bytes remaining to be decoded in the packet.
2730    ///
2731    /// # Examples
2732    ///
2733    /// ```
2734    /// use musli_web::client::Packet;
2735    ///
2736    /// let packet = Packet::<()>::empty();
2737    /// assert_eq!(packet.remaining(), 0);
2738    /// ```
2739    #[inline]
2740    pub fn remaining(&self) -> usize {
2741        self.raw.remaining()
2742    }
2743
2744    /// Check if the packet is empty.
2745    ///
2746    /// # Examples
2747    ///
2748    /// ```
2749    /// use musli_web::client::Packet;
2750    ///
2751    /// let packet = Packet::<()>::empty();
2752    /// assert!(packet.is_empty());
2753    /// ```
2754    #[inline]
2755    pub fn is_empty(&self) -> bool {
2756        self.raw.is_empty()
2757    }
2758
2759    /// The id of the packet this is a response to as specified by
2760    /// [`Endpoint::ID`] or [`Broadcast::ID`].
2761    ///
2762    /// [`Endpoint::ID`]: crate::api::Endpoint::ID
2763    /// [`Broadcast::ID`]: crate::api::Broadcast::ID
2764    #[inline]
2765    pub fn id(&self) -> MessageId {
2766        self.raw.id()
2767    }
2768}
2769
2770impl<T> Packet<T>
2771where
2772    T: api::Decodable,
2773{
2774    /// Decode the contents of a packet.
2775    ///
2776    /// This can be called multiple times if there are multiple payloads in
2777    /// sequence of the response.
2778    ///
2779    /// You can check if the packet is empty using [`Packet::is_empty`].
2780    #[inline]
2781    pub fn decode(&self) -> Result<T::Type<'_>> {
2782        self.decode_any()
2783    }
2784
2785    /// Decode any contents of a packet.
2786    ///
2787    /// This can be called multiple times if there are multiple payloads in
2788    /// sequence of the response.
2789    ///
2790    /// You can check if the packet is empty using [`Packet::is_empty`].
2791    #[inline]
2792    pub fn decode_any<'de, R>(&'de self) -> Result<R>
2793    where
2794        R: DecodeBody<'de>,
2795    {
2796        self.raw.decode()
2797    }
2798}
2799
2800impl<T> Packet<T>
2801where
2802    T: api::Endpoint,
2803{
2804    /// Decode the response of a packet.
2805    ///
2806    /// This can be called multiple times if there are multiple payloads in
2807    /// sequence of the response.
2808    ///
2809    /// You can check if the packet is empty using [`Packet::is_empty`].
2810    #[inline]
2811    pub fn decode_response(&self) -> Result<T::Response<'_>> {
2812        self.decode_any_response()
2813    }
2814
2815    /// Decode any response of a packet.
2816    ///
2817    /// This can be called multiple times if there are multiple payloads in
2818    /// sequence of the response.
2819    ///
2820    /// You can check if the packet is empty using [`Packet::is_empty`].
2821    #[inline]
2822    pub fn decode_any_response<'de, R>(&'de self) -> Result<R>
2823    where
2824        R: DecodeBody<'de>,
2825    {
2826        self.raw.decode()
2827    }
2828}
2829
2830impl<T> Packet<T>
2831where
2832    T: api::Broadcast,
2833{
2834    /// Decode the primary event related to a broadcast.
2835    #[inline]
2836    pub fn decode_event<'de>(&'de self) -> Result<T::Event<'de>>
2837    where
2838        T: api::BroadcastWithEvent,
2839    {
2840        self.decode_event_any()
2841    }
2842
2843    /// Decode any event related to a broadcast.
2844    #[inline]
2845    pub fn decode_event_any<'de, E>(&'de self) -> Result<E>
2846    where
2847        E: Event<Broadcast = T> + DecodeBody<'de>,
2848    {
2849        self.raw.decode()
2850    }
2851}
2852
2853impl<T> Clone for Packet<T> {
2854    #[inline]
2855    fn clone(&self) -> Self {
2856        Self {
2857            raw: self.raw.clone(),
2858            _marker: PhantomData,
2859        }
2860    }
2861}
2862
2863impl<T> fmt::Debug for Packet<T> {
2864    #[inline]
2865    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2866        f.debug_struct("Packet")
2867            .field("type", &any::type_name::<T>())
2868            .field("remaining", &self.remaining())
2869            .finish()
2870    }
2871}
2872
2873#[cfg(test)]
2874mod tests;