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;