musli_web/web.rs
1//! The generic web implementation.
2//!
3//! This is specialized over the `H` parameter through modules such as:
4//!
5//! * [`web03`] for `web-sys` `0.3.x`.
6//!
7//! [`web03`]: crate::web03
8//!
9//! # Reconnecting
10//!
11//! The service reconnects on its own whenever the connection is lost, but
12//! nothing sent over the old connection carries over: pending requests fail
13//! with [`Error::is_connection_lost`], and the server forgets every
14//! [`Channel`] which was opened over it. Anything a page fetched has to be
15//! fetched again once the connection is back.
16//!
17//! The one thing it does not reconnect after is a server which states another
18//! application version than the page, which it reports as
19//! [`State::Incompatible`]. The page is out of date and the usual response is
20//! to reload it, see [`ServiceBuilder::app_version`].
21//!
22//! To tell when that is, every handle exposes a connection generation through
23//! [`Handle::generation`]. It counts how many times the connection has opened
24//! so far, so it starts at `0` and goes up by one each time the connection
25//! reaches [`State::Open`]. Since it never goes back, a component can hold on
26//! to the generation it last fetched for and compare it to a single [`Copy`]
27//! value, rather than tracking whether it has seen the connection close.
28//!
29//! There are two ways to learn that the generation has changed:
30//!
31//! * [`Handle::on_open`] calls a listener with the new generation every time
32//! the connection opens. This suits a component which holds a [`Handle`]
33//! and fetches its own data.
34//! * [`Handle::on_state_change`] reports every change of [`State`]. A root
35//! component which already listens for it can read [`Handle::generation`]
36//! when it sees [`State::Open`], and pass the value down as a property, so
37//! that a child refetches when the property changes.
38//!
39//! A [`Channel`] is opened again by itself every time the connection reopens,
40//! and tells about it through the callback set with
41//! [`ChannelBuilder::on_change`], see [`Handle::channel`]. A component which
42//! holds a channel can fetch again when it hears [`ChannelEvent::Opened`]
43//! instead of tracking the generation.
44//!
45//! ## Refetching after a reconnect
46//!
47//! This passes the generation down from the root component:
48//!
49//! ```
50//! # extern crate yew023 as yew;
51//! use yew::prelude::*;
52//! use musli_web::web03::prelude::*;
53//!
54//! #[derive(Properties, PartialEq)]
55//! struct ListProps {
56//! ws: ws::Handle,
57//! /// The connection generation, from `ws::Handle::generation`.
58//! generation: u64,
59//! }
60//!
61//! struct List;
62//!
63//! impl List {
64//! fn fetch(&mut self, ctx: &Context<Self>) {
65//! // Send the requests this component shows the result of. Before the
66//! // connection is open they fail as not connected, and are sent again
67//! // once the generation changes.
68//! }
69//! }
70//!
71//! impl Component for List {
72//! type Message = ();
73//! type Properties = ListProps;
74//!
75//! fn create(ctx: &Context<Self>) -> Self {
76//! let mut this = Self;
77//! this.fetch(ctx);
78//! this
79//! }
80//!
81//! fn changed(&mut self, ctx: &Context<Self>, old: &ListProps) -> bool {
82//! if ctx.props().generation != old.generation {
83//! self.fetch(ctx);
84//! }
85//!
86//! true
87//! }
88//!
89//! fn view(&self, _: &Context<Self>) -> Html {
90//! html! {}
91//! }
92//! }
93//!
94//! enum Msg {
95//! StateChanged(ws::State),
96//! }
97//!
98//! #[derive(Properties, PartialEq)]
99//! struct AppProps {
100//! ws: ws::Handle,
101//! }
102//!
103//! struct App {
104//! generation: u64,
105//! _state: ws::StateListener,
106//! }
107//!
108//! impl Component for App {
109//! type Message = Msg;
110//! type Properties = AppProps;
111//!
112//! fn create(ctx: &Context<Self>) -> Self {
113//! let (_, state) = ctx
114//! .props()
115//! .ws
116//! .on_state_change(ctx.link().callback(Msg::StateChanged));
117//!
118//! Self {
119//! generation: ctx.props().ws.generation(),
120//! _state: state,
121//! }
122//! }
123//!
124//! fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
125//! match msg {
126//! Msg::StateChanged(_) => {
127//! let generation = ctx.props().ws.generation();
128//! let changed = self.generation != generation;
129//! self.generation = generation;
130//! changed
131//! }
132//! }
133//! }
134//!
135//! fn view(&self, ctx: &Context<Self>) -> Html {
136//! html! {
137//! <List ws={ctx.props().ws.clone()} generation={self.generation} />
138//! }
139//! }
140//! }
141//! ```
142
143use core::cell::{Cell, RefCell};
144use core::future::Future;
145use core::hash::Hash;
146use core::marker::PhantomData;
147use core::mem;
148use core::ops::Deref;
149use core::pin::Pin;
150use core::ptr::NonNull;
151use core::task::{Context, Poll, Waker};
152use core::{any, fmt};
153use std::collections::VecDeque;
154
155use alloc::boxed::Box;
156use alloc::format;
157use alloc::rc::Rc;
158use alloc::rc::Weak;
159use alloc::string::{String, ToString};
160use alloc::vec::Vec;
161
162use std::collections::hash_map::{Entry, HashMap};
163
164use slab::Slab;
165
166use crate::api::{self, ChannelId, DecodeBody, Event, Format, MessageId, Mode};
167use crate::format;
168
169#[cfg(test)]
170mod tests;
171
172const MAX_CAPACITY: usize = 1048576;
173
174/// An empty request body.
175#[non_exhaustive]
176pub struct EmptyBody;
177
178/// An empty callback.
179#[non_exhaustive]
180pub struct EmptyCallback;
181
182trait RequestCallback {
183 fn error(&self, error: Error);
184
185 fn as_request(&self) -> Option<&(dyn Callback<Result<RawPacket, Error>> + 'static)> {
186 None
187 }
188
189 fn as_channel(&self) -> Option<&(dyn Fn(Result<ChannelId, Error>) + 'static)> {
190 None
191 }
192
193 /// The format and mode this is a negotiation for, if it is one.
194 fn as_negotiate(&self) -> Option<(Format, Mode)> {
195 None
196 }
197}
198
199/// The pending callback used for a format negotiation issued by the service
200/// itself.
201struct NegotiateCallback(Format, Mode);
202
203impl RequestCallback for NegotiateCallback {
204 #[inline]
205 fn as_negotiate(&self) -> Option<(Format, Mode)> {
206 Some((self.0, self.1))
207 }
208
209 #[inline]
210 fn error(&self, error: Error) {
211 tracing::debug!("Format negotiation failed: {error}");
212 }
213}
214
215/// Slab of state listeners.
216type StateListeners = Slab<Rc<dyn Callback<State>>>;
217/// Slab of broadcast listeners.
218type Broadcasts = HashMap<MessageId, Slab<Rc<dyn Callback<Result<RawPacket>>>>>;
219/// Queue of recycled buffers.
220type Buffers = VecDeque<Box<BufData>>;
221/// Pending requests.
222type PendingRequests = HashMap<u32, Box<Pending<dyn RequestCallback>>>;
223
224/// Location information for WebSocket implementation.
225#[doc(hidden)]
226pub struct Location {
227 pub(crate) protocol: String,
228 pub(crate) host: String,
229 pub(crate) port: String,
230}
231
232pub(crate) mod sealed_socket {
233 pub trait Sealed {}
234}
235
236pub(crate) trait SocketImpl
237where
238 Self: Sized + self::sealed_socket::Sealed,
239{
240 #[doc(hidden)]
241 type Handles;
242
243 #[doc(hidden)]
244 fn new(url: &str, handles: &Self::Handles) -> Result<Self, Error>;
245
246 /// Send a message in the given [`Mode`].
247 ///
248 /// [`Mode::Binary`] writes a binary frame and [`Mode::Text`] a text frame,
249 /// which is what carries the mode between peers.
250 #[doc(hidden)]
251 fn send(&self, mode: Mode, data: &[u8]) -> Result<(), Error>;
252
253 #[doc(hidden)]
254 fn close(self) -> Result<(), Error>;
255}
256
257pub(crate) mod sealed_window {
258 pub trait Sealed {}
259}
260
261pub(crate) trait WindowImpl
262where
263 Self: Sized + self::sealed_window::Sealed,
264{
265 #[doc(hidden)]
266 type Timeout;
267
268 #[doc(hidden)]
269 type OnBeforeUnload;
270
271 #[doc(hidden)]
272 fn new() -> Result<Self, Error>;
273
274 #[doc(hidden)]
275 fn location(&self) -> Result<Location, Error>;
276
277 #[doc(hidden)]
278 fn set_timeout(
279 &self,
280 millis: u32,
281 callback: impl Fn() + 'static,
282 ) -> Result<Self::Timeout, Error>;
283
284 #[doc(hidden)]
285 fn onbeforeunload(&self, callback: impl Fn() + 'static) -> Result<Self::OnBeforeUnload, Error>;
286}
287
288pub(crate) mod sealed_web {
289 pub trait Sealed {}
290}
291
292/// Central trait for web integration.
293///
294/// Since web integration is currently unstable, this requires multiple
295/// different implementations, each time an ecosystem breaking change is
296/// released.
297///
298/// The crate in focus here is `web-sys`, and the corresponding modules provide
299/// integrations:
300///
301/// * [web03] for `web-sys` `0.3.x`.
302///
303/// [web03]: crate::web03
304pub trait WebImpl
305where
306 Self: 'static + Copy + Sized + self::sealed_web::Sealed,
307{
308 #[doc(hidden)]
309 #[allow(private_bounds)]
310 type Window: WindowImpl;
311
312 #[doc(hidden)]
313 type Handles;
314
315 #[doc(hidden)]
316 #[allow(private_bounds)]
317 type Socket: SocketImpl<Handles = Self::Handles>;
318
319 #[doc(hidden)]
320 #[allow(private_interfaces)]
321 fn handles(shared: &Weak<Shared<Self>>) -> Self::Handles;
322
323 #[doc(hidden)]
324 fn random(range: u32) -> u32;
325}
326
327/// Construct a new [`ServiceBuilder`] associated with the given [`Connect`]
328/// strategy.
329pub fn connect<H>(connect: Connect) -> ServiceBuilder<H, EmptyCallback>
330where
331 H: WebImpl,
332{
333 ServiceBuilder {
334 connect,
335 on_error: EmptyCallback,
336 close_before_unload: false,
337 format: Format::DEFAULT,
338 mode: Mode::DEFAULT,
339 app: 0,
340 _marker: PhantomData,
341 }
342}
343
344/// The state of the connection.
345///
346/// A listener for state changes can be set up through for example
347/// [`Handle::on_state_change`].
348#[derive(Debug, PartialEq, Eq, Clone, Copy)]
349#[non_exhaustive]
350pub enum State {
351 /// The connection is open.
352 Open,
353 /// The connection is closed.
354 Closed,
355 /// The server states an application version other than the one this
356 /// client was built with, see [`ServiceBuilder::app_version`].
357 ///
358 /// The connection is closed and the service stops reconnecting, since
359 /// connecting again reaches the same server. The page is out of date, so
360 /// the usual thing to do is to ask the user to reload it, or to reload it
361 /// outright. [`Service::open`] tries again, which is only worth doing if
362 /// the server might have changed back.
363 Incompatible {
364 /// The application version the server states.
365 server: u32,
366 /// The application version this client states.
367 client: u32,
368 },
369}
370
371impl State {
372 /// Check if the state is open.
373 pub fn is_open(&self) -> bool {
374 matches!(self, Self::Open)
375 }
376
377 /// Check if the connection was refused because the server states another
378 /// application version, see [`State::Incompatible`].
379 ///
380 /// # Examples
381 ///
382 /// ```
383 /// use musli_web::web::State;
384 ///
385 /// assert!(State::Incompatible { server: 2, client: 1 }.is_incompatible());
386 /// assert!(!State::Closed.is_incompatible());
387 /// ```
388 pub fn is_incompatible(&self) -> bool {
389 matches!(self, Self::Incompatible { .. })
390 }
391}
392
393/// Error type for the WebSocket service.
394///
395/// Errors are delivered to request, channel and broadcast callbacks, and to
396/// the connection-wide handler set with [`ServiceBuilder::on_error`]. Use the
397/// accessors below to tell what failed instead of matching on the text
398/// produced by [`Display`], which is meant for humans and may change:
399///
400/// * [`is_not_connected`] - the request was never sent, since there was no
401/// open connection or channel to send it over. It is safe to send it again
402/// once the connection is open.
403/// * [`is_connection_lost`] - the request was sent, or about to be, but the
404/// connection closed before a response arrived. The server may or may not
405/// have handled it.
406/// * [`is_cancelled`] - the request was replaced by another one before it was
407/// answered.
408/// * [`as_server_error`] - the server answered with an error, such as one
409/// returned by its handler. If the handler failed with a typed error,
410/// [`server_error`] decodes it.
411/// * [`is_decode`] - something received could not be decoded, such as a
412/// response which does not match the expected type.
413///
414/// Anything else, such as a failure in the browser APIs, only carries a
415/// message.
416///
417/// # Examples
418///
419/// ```
420/// use musli_web::web::Error;
421///
422/// fn describe(error: &Error) -> String {
423/// if error.is_not_connected() {
424/// String::from("Not connected, try again later")
425/// } else if error.is_connection_lost() {
426/// String::from("Connection lost, the request may not have been handled")
427/// } else if let Some(message) = error.as_server_error() {
428/// format!("The server said: {message}")
429/// } else {
430/// error.to_string()
431/// }
432/// }
433///
434/// let error = Error::message("Something else");
435/// assert_eq!(describe(&error), "Something else");
436/// ```
437///
438/// [`Display`]: core::fmt::Display
439/// [`is_not_connected`]: Self::is_not_connected
440/// [`is_connection_lost`]: Self::is_connection_lost
441/// [`is_cancelled`]: Self::is_cancelled
442/// [`as_server_error`]: Self::as_server_error
443/// [`server_error`]: Self::server_error
444/// [`is_decode`]: Self::is_decode
445#[derive(Debug)]
446pub struct Error {
447 kind: ErrorKind,
448}
449
450impl Error {
451 #[inline]
452 fn new(kind: ErrorKind) -> Self {
453 Self { kind }
454 }
455
456 /// Check if the error is caused by an empty packet.
457 ///
458 /// An empty packet is also a [decode error](Self::is_decode).
459 ///
460 /// # Examples
461 ///
462 /// ```
463 /// use musli_web::web::{Error, RawPacket};
464 ///
465 /// let packet = RawPacket::empty();
466 /// let e = packet.decode::<u32>().unwrap_err();
467 ///
468 /// assert!(e.is_empty_packet());
469 /// assert!(e.is_decode());
470 /// ```
471 #[inline]
472 pub fn is_empty_packet(&self) -> bool {
473 matches!(self.kind, ErrorKind::EmptyPacket)
474 }
475
476 /// Check if the request was never sent, since there was no open
477 /// connection or channel to send it over.
478 ///
479 /// This is the error a request or channel gets when it is sent before the
480 /// connection is [open](State::Open), after the [`Service`] has been
481 /// dropped, or over a [`Channel`] which is not open. Since the server never
482 /// saw the request, it is safe to send it again once the connection is
483 /// open, see [`Handle::on_state_change`].
484 ///
485 /// # Examples
486 ///
487 /// ```
488 /// use musli_web::web::Error;
489 ///
490 /// let error = Error::message("Something else");
491 /// assert!(!error.is_not_connected());
492 /// ```
493 #[inline]
494 pub fn is_not_connected(&self) -> bool {
495 matches!(self.kind, ErrorKind::NotConnected(..))
496 }
497
498 /// Check if the connection closed before a response to the request
499 /// arrived.
500 ///
501 /// Unlike [`is_not_connected`], the request was sent, or was being sent,
502 /// so the server may or may not have handled it. This is the error every
503 /// pending request gets when the connection closes or when the
504 /// [`Service`] is dropped.
505 ///
506 /// [`is_not_connected`]: Self::is_not_connected
507 #[inline]
508 pub fn is_connection_lost(&self) -> bool {
509 matches!(self.kind, ErrorKind::ConnectionLost(..))
510 }
511
512 /// Check if the request was cancelled before it was answered, since
513 /// another request took its place.
514 #[inline]
515 pub fn is_cancelled(&self) -> bool {
516 matches!(self.kind, ErrorKind::Cancelled)
517 }
518
519 /// Check if the error is a server error, and if so return the message
520 /// reported by the server.
521 ///
522 /// Server errors are produced by a [`Handler`] which returns an error or
523 /// which does not support the request or broadcast being made. The message
524 /// is returned as the server sent it, without the `Server error: ` prefix
525 /// that [`Display`] adds.
526 ///
527 /// [`Display`]: core::fmt::Display
528 /// [`Handler`]: <https://docs.rs/musli-web/latest/musli_web/ws/trait.Handler.html>
529 ///
530 /// This is the message of a [typed error](Self::server_error) too.
531 #[inline]
532 pub fn as_server_error(&self) -> Option<&str> {
533 match &self.kind {
534 ErrorKind::Server(error) => Some(&error.message),
535 _ => None,
536 }
537 }
538
539 /// Decode the typed error the server answered with.
540 ///
541 /// A handler which fails with a `ws::TypedError<E>` sends `E` encoded in
542 /// the body format of the connection, see [Errors]. This decodes it back
543 /// as `T`, which should be the same type as `E` or one which is
544 /// compatible with it, typically from an API crate shared with the server.
545 ///
546 /// Returns `Ok(None)` if this is not a server error, or if it is one which
547 /// carries no typed error, such as a handler error which is plain text or
548 /// an error the server produced on its own for a request it does not
549 /// support. Those still have a message, see [`as_server_error`].
550 ///
551 /// # Errors
552 ///
553 /// Errors if the typed error does not decode as `T`. The returned error is
554 /// a [decode error](Self::is_decode).
555 ///
556 /// # Examples
557 ///
558 /// ```
559 /// use musli::{Decode, Encode};
560 /// use musli_web::web::Error;
561 ///
562 /// #[derive(Debug, PartialEq, Encode, Decode)]
563 /// enum ServerError {
564 /// NotFound,
565 /// Forbidden,
566 /// }
567 ///
568 /// fn describe(error: &Error) -> String {
569 /// match error.server_error::<ServerError>() {
570 /// Ok(Some(ServerError::NotFound)) => String::from("No such thing"),
571 /// Ok(Some(ServerError::Forbidden)) => String::from("Not allowed"),
572 /// // NB: Not a typed error, or not one we understand.
573 /// Ok(None) | Err(..) => error.to_string(),
574 /// }
575 /// }
576 ///
577 /// let error = Error::message("Something else");
578 /// assert_eq!(error.server_error::<ServerError>().unwrap(), None);
579 /// assert_eq!(describe(&error), "Something else");
580 /// ```
581 ///
582 /// [Errors]: <https://docs.rs/musli-web/latest/musli_web/ws/index.html#errors>
583 /// [`as_server_error`]: Self::as_server_error
584 pub fn server_error<'de, T>(&'de self) -> Result<Option<T>>
585 where
586 T: DecodeBody<'de>,
587 {
588 let ErrorKind::Server(error) = &self.kind else {
589 return Ok(None);
590 };
591
592 let Some((format, payload)) = &error.payload else {
593 return Ok(None);
594 };
595
596 let value = format
597 .decode(payload, &mut 0)
598 .map_err(Error::decode_error_message)?;
599
600 Ok(Some(value))
601 }
602
603 /// Check if the error is caused by something received which could not be
604 /// decoded.
605 ///
606 /// This covers a response or broadcast whose body does not decode as the
607 /// requested type, an [empty packet](Self::is_empty_packet), a frame from
608 /// the server whose header, error message or format could not be decoded,
609 /// and a [typed error](Self::server_error) which does not decode as the
610 /// requested type.
611 ///
612 /// # Examples
613 ///
614 /// ```
615 /// use musli_web::web::RawPacket;
616 ///
617 /// let packet = RawPacket::empty();
618 /// let e = packet.decode::<u32>().unwrap_err();
619 ///
620 /// assert!(e.is_decode());
621 /// ```
622 #[inline]
623 pub fn is_decode(&self) -> bool {
624 matches!(
625 self.kind,
626 ErrorKind::EmptyPacket
627 | ErrorKind::UnknownFormat(..)
628 | ErrorKind::DecodeResponseHeader(..)
629 | ErrorKind::DecodeErrorMessage(..)
630 | ErrorKind::DecodePacket(..)
631 )
632 }
633
634 /// Format a WebSocket error consisting of a message.
635 ///
636 /// None of the accessors on [`Error`] match an error constructed this way.
637 #[inline]
638 pub fn message(message: impl fmt::Display) -> Self {
639 Self::new(ErrorKind::Message(message.to_string()))
640 }
641
642 #[inline]
643 fn server(error: ServerError) -> Self {
644 Self::new(ErrorKind::Server(error))
645 }
646
647 #[inline]
648 pub(crate) fn decode_response_header(error: format::Error) -> Self {
649 Self::new(ErrorKind::DecodeResponseHeader(error))
650 }
651
652 #[inline]
653 pub(crate) fn decode_error_message(error: format::Error) -> Self {
654 Self::new(ErrorKind::DecodeErrorMessage(error))
655 }
656
657 #[inline]
658 pub(crate) fn decode_packet(error: format::Error) -> Self {
659 Self::new(ErrorKind::DecodePacket(error))
660 }
661
662 #[inline]
663 pub(crate) fn encoding_header(error: format::Error) -> Self {
664 Self::new(ErrorKind::EncodingHeader(error))
665 }
666
667 #[inline]
668 pub(crate) fn encoding_body(error: format::Error) -> Self {
669 Self::new(ErrorKind::EncodingBody(error))
670 }
671}
672
673#[derive(Debug)]
674enum ErrorKind {
675 EmptyPacket,
676 /// The request was never sent. Carries the text to display.
677 NotConnected(&'static str),
678 /// The connection closed while the request was pending. Carries the text
679 /// to display.
680 ConnectionLost(&'static str),
681 Cancelled,
682 Message(String),
683 Server(ServerError),
684 UnknownFormat(u8),
685 DecodeResponseHeader(format::Error),
686 DecodeErrorMessage(format::Error),
687 DecodePacket(format::Error),
688 EncodingHeader(format::Error),
689 EncodingBody(format::Error),
690}
691
692impl fmt::Display for Error {
693 #[inline]
694 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
695 match &self.kind {
696 ErrorKind::EmptyPacket => write!(f, "Packet is empty"),
697 ErrorKind::NotConnected(message) => write!(f, "{message}"),
698 ErrorKind::ConnectionLost(message) => write!(f, "{message}"),
699 ErrorKind::Cancelled => write!(f, "Request cancelled"),
700 ErrorKind::Message(message) => write!(f, "{message}"),
701 ErrorKind::Server(error) => write!(f, "Server error: {}", error.message),
702 ErrorKind::UnknownFormat(format) => write!(
703 f,
704 "Server used unknown format id {format} for a message body"
705 ),
706 ErrorKind::DecodeResponseHeader(..) => {
707 write!(f, "Encoding error when decoding response header")
708 }
709 ErrorKind::DecodeErrorMessage(..) => {
710 write!(f, "Encoding error when decoding error response")
711 }
712 ErrorKind::DecodePacket(..) => write!(f, "Encoding error when decoding packet"),
713 ErrorKind::EncodingHeader(..) => write!(f, "Encoding error when encoding header"),
714 ErrorKind::EncodingBody(..) => write!(f, "Encoding error when encoding body"),
715 }
716 }
717}
718
719impl core::error::Error for Error {
720 #[inline]
721 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
722 match &self.kind {
723 ErrorKind::DecodeResponseHeader(error) => Some(error),
724 ErrorKind::DecodeErrorMessage(error) => Some(error),
725 ErrorKind::DecodePacket(error) => Some(error),
726 ErrorKind::EncodingHeader(error) => Some(error),
727 ErrorKind::EncodingBody(error) => Some(error),
728 _ => None,
729 }
730 }
731}
732
733#[cfg(feature = "wasm_bindgen02")]
734impl From<wasm_bindgen02::JsValue> for Error {
735 #[inline]
736 fn from(error: wasm_bindgen02::JsValue) -> Self {
737 Self::new(ErrorKind::Message(format!("{error:?}")))
738 }
739}
740
741/// An error response from the server, see [error
742/// responses](crate::api#error-responses).
743#[derive(Debug, Clone)]
744struct ServerError {
745 /// The human readable message.
746 message: String,
747 /// The typed payload, and the format it is encoded with.
748 payload: Option<(Format, Box<[u8]>)>,
749}
750
751impl ServerError {
752 /// A server error without a payload.
753 #[inline]
754 fn message(message: &str) -> Self {
755 Self {
756 message: String::from(message),
757 payload: None,
758 }
759 }
760}
761
762type Result<T, E = Error> = core::result::Result<T, E>;
763
764const INITIAL_TIMEOUT: u32 = 250;
765const MAX_TIMEOUT: u32 = 4000;
766
767/// How to connect to the WebSocket.
768#[derive(Debug)]
769enum ConnectKind {
770 Location { path: String },
771 Url { url: String },
772}
773
774/// A specification for how to connect a WebSocket.
775pub struct Connect {
776 kind: ConnectKind,
777}
778
779impl Connect {
780 /// Connect to the same location with a custom path.
781 ///
782 /// Note that any number of `/` prefixes are ignored, the canonical
783 /// representation always ignores them and the path is relative to the
784 /// current location.
785 #[inline]
786 pub fn location(path: impl AsRef<str>) -> Self {
787 Self {
788 kind: ConnectKind::Location {
789 path: String::from(path.as_ref()),
790 },
791 }
792 }
793
794 /// Connect to the specified URL.
795 #[inline]
796 pub fn url(url: String) -> Self {
797 Self {
798 kind: ConnectKind::Url { url },
799 }
800 }
801}
802
803impl fmt::Debug for Connect {
804 #[inline]
805 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
806 self.kind.fmt(f)
807 }
808}
809
810/// Generic but shared fields which do not depend on specialization over `H`.
811struct Generic {
812 state_listeners: RefCell<StateListeners>,
813 /// The number of times the connection has opened, see
814 /// [`Handle::generation`].
815 generation: Cell<u64>,
816 requests: RefCell<PendingRequests>,
817 broadcasts: RefCell<Broadcasts>,
818 buffers: RefCell<Buffers>,
819}
820
821/// Shared implementation details for WebSocket implementations.
822pub(crate) struct Shared<H>
823where
824 H: WebImpl,
825{
826 connect: Connect,
827 state: Cell<State>,
828 should_be: Cell<State>,
829 connecting: Cell<bool>,
830 /// The format in effect, as agreed by the [negotiation protocol].
831 ///
832 /// [negotiation protocol]: crate::api#negotiating-the-format
833 format: Cell<Format>,
834 /// The format the user asked for, re-negotiated on every reconnect.
835 requested: Format,
836 /// The mode in effect, as decided by the frame the negotiation was answered
837 /// in.
838 mode: Cell<Mode>,
839 /// The mode the user asked for, re-negotiated on every reconnect.
840 requested_mode: Mode,
841 /// The application version this client states, or zero for none.
842 app: u32,
843 handle: Handle<H>,
844 pub(crate) on_error: Box<dyn Callback<Error>>,
845 window: H::Window,
846 handles: H::Handles,
847 serial: Cell<u32>,
848 defer_broadcasts: RefCell<VecDeque<Weak<dyn Callback<Result<RawPacket>>>>>,
849 defer_state_listeners: RefCell<VecDeque<Weak<dyn Callback<State>>>>,
850 pub(crate) socket: RefCell<Option<H::Socket>>,
851 output: RefCell<Vec<u8>>,
852 current_timeout: Cell<u32>,
853 reconnect_timeout: RefCell<Option<<H::Window as WindowImpl>::Timeout>>,
854 _window_onbeforeunload: Option<<H::Window as WindowImpl>::OnBeforeUnload>,
855 g: Rc<Generic>,
856}
857
858impl<H> Drop for Shared<H>
859where
860 H: WebImpl,
861{
862 fn drop(&mut self) {
863 if let Some(s) = self.socket.take() {
864 tracing::debug!("Closing connection during drop");
865
866 if let Err(error) = s.close() {
867 self.on_error.call(error);
868 }
869 }
870
871 // We don't need to worry about mutable borrows here, since we only have
872 // weak references to Shared and by virtue of this being dropped they
873 // are all invalid.
874 let state_listeners = mem::take(&mut *self.g.state_listeners.borrow_mut());
875
876 // NB: Listeners are called before the requests are taken, since a
877 // listener can cancel a request of its own, such as a channel which
878 // was being opened.
879 for (_, listener) in state_listeners {
880 listener.call(State::Closed);
881 }
882
883 let requests = mem::take(&mut *self.g.requests.borrow_mut());
884
885 for (_, p) in requests {
886 p.callback.error(Error::new(ErrorKind::ConnectionLost(
887 "Websocket service closed",
888 )));
889 }
890 }
891}
892
893/// Builder of a service.
894pub struct ServiceBuilder<H, E>
895where
896 H: WebImpl,
897{
898 connect: Connect,
899 on_error: E,
900 close_before_unload: bool,
901 format: Format,
902 mode: Mode,
903 app: u32,
904 _marker: PhantomData<H>,
905}
906
907impl<H, E> ServiceBuilder<H, E>
908where
909 H: WebImpl,
910 E: Callback<Error>,
911{
912 /// Set the error handler to use for the service.
913 pub fn on_error<U>(self, on_error: U) -> ServiceBuilder<H, U>
914 where
915 U: Callback<Error>,
916 {
917 ServiceBuilder {
918 connect: self.connect,
919 on_error,
920 close_before_unload: self.close_before_unload,
921 format: self.format,
922 mode: self.mode,
923 app: self.app,
924 _marker: self._marker,
925 }
926 }
927
928 /// Set the [`Format`] to use for message bodies.
929 ///
930 /// The format is negotiated with the server once the connection is
931 /// established, see the [negotiation protocol]. If the server does not
932 /// support it the error is reported through [`ServiceBuilder::on_error`]
933 /// and the connection falls back to [`Format::DEFAULT`], which can be
934 /// observed through [`Handle::format`].
935 ///
936 /// Defaults to [`Format::DEFAULT`].
937 ///
938 /// [negotiation protocol]: crate::api#negotiating-the-format
939 #[inline]
940 pub fn format(mut self, format: Format) -> ServiceBuilder<H, E> {
941 self.format = format;
942 self
943 }
944
945 /// Set the [`Mode`] the socket should run in.
946 ///
947 /// [`Mode::Text`] makes every frame human readable, so the traffic can be
948 /// read straight out of the browser's network inspector. It requires a
949 /// [`Format`] which is human readable too, so it is normally combined with
950 /// [`Format::Json`]:
951 ///
952 /// ```
953 /// use musli_web::api::{Format, Mode};
954 ///
955 /// assert!(Mode::Text.accepts(Format::Json));
956 /// ```
957 ///
958 /// Like the format, the mode is settled with the server once the connection
959 /// is established, see the [negotiation protocol]. A server which cannot
960 /// carry the requested format in the requested mode reports the error
961 /// through [`ServiceBuilder::on_error`] and the connection falls back to
962 /// [`Mode::DEFAULT`], which can be observed through [`Handle::mode`].
963 ///
964 /// Defaults to [`Mode::DEFAULT`].
965 ///
966 /// [negotiation protocol]: crate::api#negotiating-the-format
967 #[inline]
968 pub fn mode(mut self, mode: Mode) -> ServiceBuilder<H, E> {
969 self.mode = mode;
970 self
971 }
972
973 /// State the version of the application's API this client was built
974 /// with.
975 ///
976 /// This is a version of *your* protocol, the requests and broadcasts
977 /// defined with [`api::define!`], as opposed to [`api::VERSION`] which is
978 /// the version of the envelope this crate puts around them. Give it the
979 /// same constant the server passes to `ws::Connect::app_version`, from a
980 /// crate both of them depend on, and bump it whenever the API changes in a
981 /// way an older page cannot cope with.
982 ///
983 /// When the connection is established the server states its version, and
984 /// if it is not this one the service reports [`State::Incompatible`]
985 /// instead of [`State::Open`], closes the connection and stops
986 /// reconnecting. That is how a page which was loaded before the server was
987 /// upgraded learns that it has to reload.
988 ///
989 /// Zero, which is the default, states no version. The check only applies
990 /// when both peers state one, so a client or a server which does not opt
991 /// in behaves exactly as if this did not exist. See [application
992 /// versions].
993 ///
994 /// [`api::define!`]: crate::api::define
995 /// [application versions]: crate::api#application-versions
996 ///
997 /// # Examples
998 ///
999 /// Reloading the page once the server says it is out of date:
1000 ///
1001 /// ```no_run
1002 /// use musli_web::web03::prelude::*;
1003 ///
1004 /// /// Shared with the server, bumped whenever the API changes.
1005 /// const APP_VERSION: u32 = 7;
1006 ///
1007 /// let service = ws::connect(ws::Connect::location("ws"))
1008 /// .app_version(APP_VERSION)
1009 /// .build();
1010 ///
1011 /// // Keep the listener for as long as the page should reload itself.
1012 /// let (_, listener) = service.handle().on_state_change(|state: ws::State| {
1013 /// // Carries both versions, as `State::Incompatible { server, client }`.
1014 /// if state.is_incompatible() {
1015 /// reload_page();
1016 /// }
1017 /// });
1018 ///
1019 /// /// With `web-sys`, `window().location().reload()`.
1020 /// fn reload_page() {
1021 /// # todo!()
1022 /// }
1023 /// # drop(listener);
1024 /// ```
1025 #[inline]
1026 pub fn app_version(mut self, version: u32) -> Self {
1027 self.app = version;
1028 self
1029 }
1030
1031 /// Install an event handler which will try to close the WebSocket
1032 /// connection on page unload.
1033 ///
1034 /// This is not strictly necessary, but some browsers like Firefox might be
1035 /// slow to recycle the connection unless it has been explicitly closed.
1036 #[inline]
1037 pub fn close_before_unload(mut self) -> Self {
1038 self.close_before_unload = true;
1039 self
1040 }
1041
1042 /// Build a new service and open it.
1043 ///
1044 /// In order to close or open the service again, see [`Service::close`] and
1045 /// [`Service::open`].
1046 pub fn build(self) -> Service<H> {
1047 let window = match H::Window::new() {
1048 Ok(window) => window,
1049 Err(error) => {
1050 panic!("{error}")
1051 }
1052 };
1053
1054 let shared = Rc::<Shared<H>>::new_cyclic(move |shared| {
1055 let window_onbeforeunload = if self.close_before_unload {
1056 let shared = shared.clone();
1057
1058 let result = window.onbeforeunload(move || {
1059 tracing::debug!("Trying to close WebSocket connection on page unload");
1060
1061 if let Some(shared) = shared.upgrade() {
1062 shared.close();
1063 }
1064 });
1065
1066 match result {
1067 Ok(onbeforeunload) => Some(onbeforeunload),
1068 Err(error) => {
1069 self.on_error.call(Error::message(format_args!(
1070 "Failed to set onbeforeunload: {error}"
1071 )));
1072 None
1073 }
1074 }
1075 } else {
1076 None
1077 };
1078
1079 Shared {
1080 connect: self.connect,
1081 state: Cell::new(State::Closed),
1082 should_be: Cell::new(State::Closed),
1083 connecting: Cell::new(false),
1084 format: Cell::new(self.format),
1085 requested: self.format,
1086 mode: Cell::new(self.mode),
1087 requested_mode: self.mode,
1088 app: self.app,
1089 handle: Handle {
1090 shared: shared.clone(),
1091 },
1092 on_error: Box::new(self.on_error),
1093 window,
1094 handles: H::handles(shared),
1095 serial: Cell::new(0),
1096 defer_broadcasts: RefCell::new(VecDeque::new()),
1097 defer_state_listeners: RefCell::new(VecDeque::new()),
1098 socket: RefCell::new(None),
1099 output: RefCell::new(Vec::new()),
1100 current_timeout: Cell::new(INITIAL_TIMEOUT),
1101 reconnect_timeout: RefCell::new(None),
1102 _window_onbeforeunload: window_onbeforeunload,
1103 g: Rc::new(Generic {
1104 state_listeners: RefCell::new(Slab::new()),
1105 generation: Cell::new(0),
1106 broadcasts: RefCell::new(HashMap::new()),
1107 requests: RefCell::new(PendingRequests::new()),
1108 buffers: RefCell::new(VecDeque::new()),
1109 }),
1110 }
1111 });
1112
1113 let service = Service { shared };
1114 service.open();
1115 service
1116 }
1117}
1118
1119/// The service handle.
1120///
1121/// Once dropped this will cause the service to be disconnected and all requests
1122/// to be cancelled.
1123pub struct Service<H>
1124where
1125 H: WebImpl,
1126{
1127 shared: Rc<Shared<H>>,
1128}
1129
1130impl<H> Service<H>
1131where
1132 H: WebImpl,
1133{
1134 /// Attempt to establish a WebSocket connection.
1135 #[deprecated(since = "0.3.0", note = "Use `open` instead")]
1136 pub fn connect(&self) {
1137 self.shared.open()
1138 }
1139
1140 /// Open the WebSocket connection.
1141 ///
1142 /// Calling this method indicates that the connection should stay open, and
1143 /// the implementation will attempt to reconnect until [`Service::close`] is
1144 /// called.
1145 pub fn open(&self) {
1146 self.shared.open()
1147 }
1148
1149 /// Close the WebSocket connection.
1150 ///
1151 /// This is not strictly necessary, but some browsers like Firefox might be
1152 /// slow to recycle the connection unless it has been explicitly closed.
1153 ///
1154 /// It is generally recommended that you call this when `beforeunload` on
1155 /// window is fired.
1156 pub fn close(&self) {
1157 self.shared.close();
1158 }
1159
1160 /// Return the handle to the service.
1161 ///
1162 /// A [`Handle`] instances does not force the underlying WebSocket to stay
1163 /// connected, and is invalidated when [`Service`] is destructed.
1164 pub fn handle(&self) -> &Handle<H> {
1165 &self.shared.handle
1166 }
1167}
1168
1169impl<H> Clone for Service<H>
1170where
1171 H: WebImpl,
1172{
1173 #[inline]
1174 fn clone(&self) -> Self {
1175 Self {
1176 shared: self.shared.clone(),
1177 }
1178 }
1179}
1180
1181impl<H> Shared<H>
1182where
1183 H: WebImpl,
1184{
1185 /// Send a client message.
1186 fn send_client_request<T>(&self, serial: u32, channel: ChannelId, body: &T) -> Result<()>
1187 where
1188 T: api::Request,
1189 {
1190 let Some(ref socket) = *self.socket.borrow() else {
1191 return Err(Error::new(ErrorKind::NotConnected(
1192 "Socket is not connected",
1193 )));
1194 };
1195
1196 let format = self.format.get();
1197 let mode = self.mode.get();
1198
1199 let header = api::RequestHeader {
1200 version: api::VERSION,
1201 serial,
1202 id: <T::Endpoint as api::Endpoint>::ID.get(),
1203 format: format.to_u8(),
1204 channel,
1205 app: 0,
1206 };
1207
1208 let out = &mut *self.output.borrow_mut();
1209
1210 format::encode_envelope(mode, &mut *out, &header).map_err(Error::encoding_header)?;
1211 format
1212 .encode(&mut *out, body)
1213 .map_err(Error::encoding_body)?;
1214
1215 tracing::debug!(
1216 header.serial,
1217 ?header.id,
1218 len = out.len(),
1219 "Sending request"
1220 );
1221
1222 socket.send(mode, out.as_slice())?;
1223
1224 out.clear();
1225 out.shrink_to(MAX_CAPACITY);
1226 Ok(())
1227 }
1228
1229 /// Send a client message.
1230 fn send_connect(&self, serial: u32) -> Result<()> {
1231 let Some(ref socket) = *self.socket.borrow() else {
1232 return Err(Error::new(ErrorKind::NotConnected(
1233 "Socket is not connected",
1234 )));
1235 };
1236
1237 let mode = self.mode.get();
1238
1239 let header = api::RequestHeader {
1240 version: api::VERSION,
1241 serial,
1242 id: MessageId::CONNECT.get(),
1243 // NB: Carries no body.
1244 format: 0,
1245 channel: ChannelId::NONE,
1246 app: 0,
1247 };
1248
1249 let out = &mut *self.output.borrow_mut();
1250
1251 format::encode_envelope(mode, &mut *out, &header).map_err(Error::encoding_header)?;
1252
1253 tracing::debug!(
1254 header.serial,
1255 ?header.id,
1256 len = out.len(),
1257 "Sending request"
1258 );
1259
1260 socket.send(mode, out.as_slice())?;
1261
1262 out.clear();
1263 out.shrink_to(MAX_CAPACITY);
1264 Ok(())
1265 }
1266
1267 /// Ask the server to use the requested format for the rest of the
1268 /// connection.
1269 fn send_negotiate(self: &Rc<Self>) -> Result<()> {
1270 let format = self.requested;
1271 let mode = self.requested_mode;
1272 let serial = self.serial.get();
1273 self.serial.set(serial.wrapping_add(1));
1274
1275 {
1276 let Some(ref socket) = *self.socket.borrow() else {
1277 return Err(Error::new(ErrorKind::NotConnected(
1278 "Socket is not connected",
1279 )));
1280 };
1281
1282 let header = api::RequestHeader {
1283 version: api::VERSION,
1284 serial,
1285 id: MessageId::NEGOTIATE.get(),
1286 format: format.to_u8(),
1287 // NB: Carries no body.
1288 channel: ChannelId::NONE,
1289 app: self.app,
1290 };
1291
1292 let out = &mut *self.output.borrow_mut();
1293
1294 // NB: The frame this goes out in is what asks for the mode, so the
1295 // envelope has to be spelled the same way.
1296 format::encode_envelope(mode, &mut *out, &header).map_err(Error::encoding_header)?;
1297
1298 tracing::debug!(?format, ?mode, "Requesting format");
1299
1300 socket.send(mode, out.as_slice())?;
1301
1302 out.clear();
1303 out.shrink_to(MAX_CAPACITY);
1304 }
1305
1306 let pending = Pending {
1307 id: MessageId::NEGOTIATE,
1308 serial,
1309 callback: NegotiateCallback(format, mode),
1310 };
1311
1312 let existing = self
1313 .g
1314 .requests
1315 .borrow_mut()
1316 .insert(serial, Box::new(pending));
1317
1318 if let Some(p) = existing {
1319 p.callback.error(Error::new(ErrorKind::Cancelled));
1320 }
1321
1322 Ok(())
1323 }
1324
1325 /// Send a disconnect.
1326 fn remove_channel(&self, channel: ChannelId) {
1327 if let Err(error) = self._send_disconnect(channel) {
1328 self.on_error.call(error);
1329 }
1330 }
1331
1332 fn _send_disconnect(&self, channel: ChannelId) -> Result<()> {
1333 let Some(ref socket) = *self.socket.borrow() else {
1334 return Err(Error::new(ErrorKind::NotConnected(
1335 "Socket is not connected",
1336 )));
1337 };
1338
1339 let mode = self.mode.get();
1340
1341 let header = api::RequestHeader {
1342 version: api::VERSION,
1343 serial: 0,
1344 id: MessageId::DISCONNECT.get(),
1345 // NB: Carries no body.
1346 format: 0,
1347 channel,
1348 app: 0,
1349 };
1350
1351 let out = &mut *self.output.borrow_mut();
1352
1353 format::encode_envelope(mode, &mut *out, &header).map_err(Error::encoding_header)?;
1354
1355 tracing::debug!(
1356 header.serial,
1357 ?header.id,
1358 len = out.len(),
1359 "Sending request"
1360 );
1361
1362 socket.send(mode, out.as_slice())?;
1363
1364 out.clear();
1365 out.shrink_to(MAX_CAPACITY);
1366 Ok(())
1367 }
1368
1369 pub(crate) fn next_buffer(self: &Rc<Self>, needed: usize) -> Box<BufData> {
1370 match self.g.buffers.borrow_mut().pop_front() {
1371 Some(mut buf) => {
1372 // NB: Recycled buffers are cleared when they are returned, so
1373 // every message starts writing from the beginning.
1374 debug_assert!(buf.data.is_empty());
1375 buf.data.reserve(needed);
1376 buf
1377 }
1378 None => Box::new(BufData::with_capacity(Rc::downgrade(&self.g), needed)),
1379 }
1380 }
1381
1382 /// Handle a text frame.
1383 pub(crate) fn text_message(self: &Rc<Self>, text: &str) -> Result<()> {
1384 let bytes = text.as_bytes();
1385 let mut buf = self.next_buffer(bytes.len());
1386 buf.data.extend_from_slice(bytes);
1387 self.message(Mode::Text, buf)
1388 }
1389
1390 /// Resolve the format a message body is encoded with from its envelope.
1391 fn body_format(header: &api::ResponseHeader) -> Result<Format> {
1392 let Some(format) = Format::from_u8(header.format) else {
1393 return Err(Error::new(ErrorKind::UnknownFormat(header.format)));
1394 };
1395
1396 Ok(format)
1397 }
1398
1399 /// Decode the body of an error response, whose `error` header is `id`.
1400 ///
1401 /// An error id which carries no message is reported with `unsupported`.
1402 fn server_error(
1403 id: MessageId,
1404 header: &api::ResponseHeader,
1405 buf: &[u8],
1406 mut at: usize,
1407 unsupported: &str,
1408 ) -> Result<ServerError> {
1409 let typed = match id {
1410 MessageId::ERROR_MESSAGE => false,
1411 MessageId::TYPED_ERROR_MESSAGE => true,
1412 _ => return Ok(ServerError::message(unsupported)),
1413 };
1414
1415 let format = Self::body_format(header)?;
1416
1417 let error: api::ErrorMessage<'_> = format
1418 .decode(buf, &mut at)
1419 .map_err(Error::decode_error_message)?;
1420
1421 // NB: The payload is only decoded once the caller says what type it
1422 // is, see `Error::server_error`.
1423 let payload = if typed {
1424 Some((format, Box::from(buf.get(at..).unwrap_or_default())))
1425 } else {
1426 None
1427 };
1428
1429 Ok(ServerError {
1430 message: String::from(error.message),
1431 payload,
1432 })
1433 }
1434
1435 pub(crate) fn message(self: &Rc<Self>, mode: Mode, buf: Box<BufData>) -> Result<()> {
1436 // Wrap the buffer in a simple shared reference-counted container.
1437 let buf = BufRc::new(buf);
1438 let mut at = 0;
1439
1440 // NB: The frame type is the mode, so a message can always be read
1441 // regardless of what has been agreed on.
1442 let header: api::ResponseHeader = match format::decode_envelope(mode, &buf, &mut at) {
1443 Ok(header) => header,
1444 // NB: The server speaks a protocol this build does not have, so
1445 // there is nothing to be gained by carrying on with the session.
1446 // The connection is dropped rather than negotiated.
1447 Err(error) if error.unsupported_version().is_some() => {
1448 self.on_error.call(Error::decode_response_header(error));
1449 return self.close_and_reconnect();
1450 }
1451 Err(error) => return Err(Error::decode_response_header(error)),
1452 };
1453
1454 if let Some(broadcast) = MessageId::new(header.broadcast) {
1455 tracing::debug!(?header, "Got broadcast");
1456
1457 if broadcast == MessageId::SERVER_HELLO {
1458 // NB: The connection is not reported as open until the format
1459 // has been negotiated, so that server-initiated messages are
1460 // never encoded with a format this client did not agree to.
1461 tracing::debug!("Server hello, negotiating format");
1462 self.send_negotiate()?;
1463
1464 // NB: Checked after negotiating, so that the server hears
1465 // which version this client states and can tell why it goes
1466 // away.
1467 if self.app != 0 && header.app != 0 && header.app != self.app {
1468 self.incompatible(header.app);
1469 }
1470
1471 return Ok(());
1472 }
1473
1474 if !self.defer_broadcasts(broadcast) {
1475 return Ok(());
1476 };
1477
1478 if let Some(id) = MessageId::new(header.error) {
1479 let error = Self::server_error(id, &header, &buf, at, "Unsupported broadcast")?;
1480
1481 while let Some(callback) = self.defer_broadcasts.borrow_mut().pop_front() {
1482 if let Some(callback) = callback.upgrade() {
1483 callback.call(Err(Error::server(error.clone())));
1484 }
1485 }
1486
1487 return Ok(());
1488 }
1489
1490 let format = Self::body_format(&header)?;
1491
1492 let packet = RawPacket {
1493 buf: Some(buf.clone()),
1494 at: Cell::new(at),
1495 id: broadcast,
1496 format,
1497 channel: header.channel,
1498 };
1499
1500 while let Some(callback) = self.defer_broadcasts.borrow_mut().pop_front() {
1501 if let Some(callback) = callback.upgrade() {
1502 callback.call(Ok(packet.clone()));
1503 }
1504 }
1505 } else {
1506 tracing::debug!(?header, "Got response");
1507 let p = self.g.requests.borrow_mut().remove(&header.serial);
1508
1509 if let Some(p) = p {
1510 if let Some(id) = MessageId::new(header.error) {
1511 let error =
1512 match Self::server_error(id, &header, &buf, at, "Unsupported request") {
1513 Ok(error) => error,
1514 // NB: Fail the request rather than leave it waiting
1515 // for a response which has already arrived.
1516 Err(error) if p.callback.as_negotiate().is_none() => {
1517 p.callback.error(error);
1518 return Ok(());
1519 }
1520 Err(error) => return Err(error),
1521 };
1522
1523 if let Some((format, requested)) = p.callback.as_negotiate() {
1524 // NB: The server cannot speak the requested format, or
1525 // cannot carry it in the requested mode, so fall back to
1526 // the defaults rather than leaving the connection
1527 // unusable.
1528 self.on_error.call(Error::message(format_args!(
1529 "Server rejected format `{format}` in `{requested}` mode ({}), falling back to `{}`",
1530 error.message,
1531 Format::DEFAULT
1532 )));
1533
1534 self.format.set(Format::DEFAULT);
1535 // NB: The frame this arrived in is the mode the server
1536 // settled on, so there is nothing to guess at.
1537 self.mode.set(mode);
1538 self.emit_state_change(State::Open);
1539 return Ok(());
1540 }
1541
1542 p.callback.error(Error::server(error));
1543 return Ok(());
1544 }
1545
1546 match p.id {
1547 MessageId::NEGOTIATE => {
1548 let Some((requested, _)) = p.callback.as_negotiate() else {
1549 p.callback
1550 .error(Error::message("Unexpected negotiate response"));
1551 return Ok(());
1552 };
1553
1554 // NB: Trust the format the server echoed back over the
1555 // one that was asked for, so that a server which
1556 // downgrades is honored. The mode is read off the frame
1557 // for the same reason.
1558 let accepted = Format::from_u8(header.format).unwrap_or(requested);
1559 tracing::debug!(?accepted, ?mode, "Format negotiated");
1560 self.format.set(accepted);
1561 self.mode.set(mode);
1562 self.emit_state_change(State::Open);
1563 return Ok(());
1564 }
1565 MessageId::CONNECT => {
1566 let Some(callback) = &p.callback.as_channel() else {
1567 p.callback
1568 .error(Error::message("Unexpected channel response"));
1569 return Ok(());
1570 };
1571
1572 callback(Ok(header.channel));
1573 return Ok(());
1574 }
1575 _ => {
1576 let Some(callback) = p.callback.as_request() else {
1577 p.callback
1578 .error(Error::message("Unexpected channel response"));
1579 return Ok(());
1580 };
1581
1582 let format = Self::body_format(&header)?;
1583
1584 let packet = RawPacket {
1585 id: p.id,
1586 buf: Some(buf),
1587 at: Cell::new(at),
1588 format,
1589 channel: header.channel,
1590 };
1591
1592 callback.call(Ok(packet));
1593 return Ok(());
1594 }
1595 }
1596 }
1597
1598 // NB: This is normal, it simply indicates that the handler has been
1599 // closed.
1600 tracing::trace!(?header.serial, "Got message with unknown serial");
1601 }
1602
1603 Ok(())
1604 }
1605
1606 fn defer_broadcasts(self: &Rc<Self>, kind: MessageId) -> bool {
1607 // Note: We need to defer this, since the outcome of calling
1608 // the broadcast callback might be that the broadcast
1609 // listener is modified, which could require mutable access
1610 // to broadcasts.
1611 let mut defer = self.defer_broadcasts.borrow_mut();
1612
1613 let broadcasts = self.g.broadcasts.borrow();
1614
1615 let Some(broadcasts) = broadcasts.get(&kind) else {
1616 return false;
1617 };
1618
1619 for (_, callback) in broadcasts.iter() {
1620 defer.push_back(Rc::downgrade(callback));
1621 }
1622
1623 !defer.is_empty()
1624 }
1625
1626 pub(crate) fn close_and_reconnect(self: &Rc<Self>) -> Result<(), Error> {
1627 tracing::debug!("Closing and reconnecting");
1628
1629 // We need a weak reference back to shared state to handle the timeout.
1630 let shared = Rc::downgrade(self);
1631
1632 if self.state.get() == State::Closed {
1633 let current_timeout = self.current_timeout.get();
1634
1635 if current_timeout < MAX_TIMEOUT {
1636 let fuzz = H::random(50);
1637
1638 self.current_timeout.set(
1639 current_timeout
1640 .saturating_mul(2)
1641 .saturating_add(fuzz)
1642 .min(MAX_TIMEOUT),
1643 );
1644 }
1645 } else {
1646 self.current_timeout.set(INITIAL_TIMEOUT);
1647 self.close_once();
1648 }
1649
1650 let timeout = self.current_timeout.get();
1651
1652 tracing::debug!(?timeout, "Setting");
1653
1654 let timeout = self.window.set_timeout(timeout, move || {
1655 if let Some(shared) = shared.upgrade() {
1656 Self::try_once(&shared);
1657 }
1658 })?;
1659
1660 drop(self.reconnect_timeout.borrow_mut().replace(timeout));
1661 self.connecting.set(false);
1662 Ok(())
1663 }
1664
1665 /// Give up on a server which states application version `server`.
1666 ///
1667 /// Connecting again would reach the same server, so this stops
1668 /// reconnecting until [`Service::open`] is called again.
1669 fn incompatible(self: &Rc<Self>, server: u32) {
1670 let client = self.app;
1671 tracing::debug!(server, client, "Incompatible server");
1672
1673 self.should_be.set(State::Closed);
1674
1675 let timeout = self.reconnect_timeout.borrow_mut().take();
1676 drop(timeout);
1677
1678 self.current_timeout.set(INITIAL_TIMEOUT);
1679 self.connecting.set(false);
1680
1681 // NB: The socket goes first, so that a listener which looks at the
1682 // service sees it closed.
1683 if let Some(s) = self.socket.take() {
1684 if let Err(error) = s.close() {
1685 self.on_error.call(error);
1686 }
1687 }
1688
1689 self.close_pending();
1690 self.emit_state_change(State::Incompatible { server, client });
1691 }
1692
1693 /// Fail the pending request with the given serial, if it is still pending.
1694 fn fail_request(&self, serial: u32, error: Error) {
1695 let p = self.g.requests.borrow_mut().remove(&serial);
1696
1697 if let Some(p) = p {
1698 p.callback.error(error);
1699 }
1700 }
1701
1702 /// Close an pending requests with an error, since there is no chance they
1703 /// will be responded to any more.
1704 fn close_pending(self: &Rc<Self>) {
1705 loop {
1706 let Some(serial) = self.g.requests.borrow().keys().next().copied() else {
1707 break;
1708 };
1709
1710 let p = {
1711 let mut requests = self.g.requests.borrow_mut();
1712
1713 let Some(p) = requests.remove(&serial) else {
1714 break;
1715 };
1716
1717 p
1718 };
1719
1720 p.callback
1721 .error(Error::new(ErrorKind::ConnectionLost("Connection closed")));
1722 }
1723 }
1724
1725 fn emit_state_change(&self, state: State) {
1726 if self.state.get() == state {
1727 return;
1728 }
1729
1730 self.state.set(state);
1731
1732 if state == State::Open {
1733 // NB: Bumped before any listener runs, so that every listener sees
1734 // the generation of the connection that just opened.
1735 let generation = self.g.generation.get().wrapping_add(1);
1736 self.g.generation.set(generation);
1737 }
1738
1739 {
1740 // We need to collect callbacks to avoid the callback recursively
1741 // borrowing state listeners, which it would if it modifies any
1742 // existing state listeners.
1743 let mut defer = self.defer_state_listeners.borrow_mut();
1744
1745 for (_, callback) in self.g.state_listeners.borrow().iter() {
1746 defer.push_back(Rc::downgrade(callback));
1747 }
1748
1749 if defer.is_empty() {
1750 return;
1751 }
1752 }
1753
1754 while let Some(callback) = self.defer_state_listeners.borrow_mut().pop_front() {
1755 if let Some(callback) = callback.upgrade() {
1756 callback.call(state);
1757 }
1758 }
1759 }
1760
1761 #[inline]
1762 fn open(self: &Rc<Self>) {
1763 self.should_be.set(State::Open);
1764 self.try_once();
1765 }
1766
1767 fn close(self: &Rc<Self>) {
1768 self.should_be.set(State::Closed);
1769
1770 // NB: A pending reconnect would otherwise only be stopped by
1771 // `should_be` once it fires, and would keep the timer alive until then.
1772 let timeout = self.reconnect_timeout.borrow_mut().take();
1773 drop(timeout);
1774
1775 // NB: The next `open` starts over rather than inheriting the back-off
1776 // of the connection which was closed on purpose.
1777 self.current_timeout.set(INITIAL_TIMEOUT);
1778 self.close_once();
1779 }
1780
1781 fn try_once(self: &Rc<Self>) {
1782 if self.should_be.get() == State::Open && !self.connecting.get() {
1783 tracing::debug!("Trying to open connection");
1784 self.connect_once();
1785 }
1786 }
1787
1788 fn close_once(self: &Rc<Self>) {
1789 self.connecting.set(false);
1790 self.emit_state_change(State::Closed);
1791
1792 if let Some(s) = self.socket.take() {
1793 tracing::debug!("Closing old socket once");
1794
1795 if let Err(error) = s.close() {
1796 self.on_error.call(error);
1797 }
1798 }
1799
1800 self.close_pending();
1801 }
1802
1803 fn connect_once(self: &Rc<Self>) {
1804 self.connecting.set(true);
1805
1806 // NB: A failed attempt leaves no socket behind whose events could ever
1807 // reset `connecting`, so it has to be reset here or `open` would never
1808 // try again.
1809 if let Err(error) = self.try_connect() {
1810 self.on_error.call(error);
1811 self.should_be.set(State::Closed);
1812 self.connecting.set(false);
1813 }
1814 }
1815
1816 fn try_connect(self: &Rc<Self>) -> Result<()> {
1817 let url = match &self.connect.kind {
1818 ConnectKind::Location { path } => {
1819 let location = match WindowImpl::location(&self.window) {
1820 Ok(location) => location,
1821 Err(e) => {
1822 return Err(Error::message(format_args!("Could not get location: {e}")));
1823 }
1824 };
1825
1826 let Location {
1827 protocol,
1828 host,
1829 port,
1830 } = location;
1831
1832 let protocol = match protocol.as_str() {
1833 "https:" => "wss:",
1834 "http:" => "ws:",
1835 other => {
1836 return Err(Error::message(format_args!(
1837 "Unsupported protocol `{other}` for same host connection"
1838 )));
1839 }
1840 };
1841
1842 let path = ForcePrefix(path, '/');
1843 format!("{protocol}//{host}:{port}{path}")
1844 }
1845 ConnectKind::Url { url } => url.clone(),
1846 };
1847
1848 // We explicitly want to close and dispose of the old socket first.
1849 if let Some(s) = self.socket.borrow_mut().take() {
1850 tracing::debug!("Closing old socket");
1851
1852 if let Err(error) = s.close() {
1853 self.on_error.call(error);
1854 }
1855 }
1856
1857 let ws = SocketImpl::new(&url, &self.handles)?;
1858 *self.socket.borrow_mut() = Some(ws);
1859 Ok(())
1860 }
1861}
1862
1863/// Trait governing how callbacks are called.
1864pub trait Callback<I>
1865where
1866 Self: 'static,
1867{
1868 /// Call the callback.
1869 fn call(&self, input: I);
1870}
1871
1872impl<I> Callback<I> for EmptyCallback {
1873 #[inline]
1874 fn call(&self, _: I) {}
1875}
1876
1877impl<F, I> Callback<I> for F
1878where
1879 F: 'static + Fn(I),
1880{
1881 #[inline]
1882 fn call(&self, input: I) {
1883 self(input)
1884 }
1885}
1886
1887/// A builder for a [`Channel`], see [`Handle::channel`].
1888///
1889/// Set the callback to tell about the channel opening and closing with
1890/// [`ChannelBuilder::on_change`], and open the channel with
1891/// [`ChannelBuilder::open`].
1892pub struct ChannelBuilder<'a, H, C>
1893where
1894 H: WebImpl,
1895{
1896 shared: &'a Weak<Shared<H>>,
1897 callback: C,
1898}
1899
1900impl<'a, H, C> ChannelBuilder<'a, H, C>
1901where
1902 H: WebImpl,
1903{
1904 /// Set the callback which is told about the channel opening and closing.
1905 ///
1906 /// The callback is called with:
1907 ///
1908 /// * [`ChannelEvent::Opened`] every time the channel has been opened,
1909 /// whether for the first time or again after a reconnect. This is the
1910 /// point at which to send the requests which need the channel.
1911 /// * [`ChannelEvent::Closed`] when an open channel has been lost with the
1912 /// connection.
1913 /// * [`ChannelEvent::Failed`] when the server could not open the channel.
1914 ///
1915 /// # Examples
1916 ///
1917 /// ```
1918 /// # extern crate yew023 as yew;
1919 /// use yew::prelude::*;
1920 /// use musli_web::web03::prelude::*;
1921 ///
1922 /// mod api {
1923 /// use musli::{Decode, Encode};
1924 /// use musli_web::api;
1925 ///
1926 /// #[derive(Encode, Decode)]
1927 /// pub struct HelloRequest<'de> {
1928 /// pub message: &'de str,
1929 /// }
1930 ///
1931 /// #[derive(Encode, Decode)]
1932 /// pub struct HelloResponse<'de> {
1933 /// pub message: &'de str,
1934 /// }
1935 ///
1936 /// api::define! {
1937 /// pub type Hello;
1938 ///
1939 /// impl Endpoint for Hello {
1940 /// impl<'de> Request for HelloRequest<'de>;
1941 /// type Response<'de> = HelloResponse<'de>;
1942 /// }
1943 /// }
1944 /// }
1945 ///
1946 /// enum Msg {
1947 /// OnHello(Result<ws::Packet<api::Hello>, ws::Error>),
1948 /// Channel(ws::ChannelEvent),
1949 /// }
1950 ///
1951 /// #[derive(Properties, PartialEq)]
1952 /// struct Props {
1953 /// ws: ws::Handle,
1954 /// }
1955 ///
1956 /// struct App {
1957 /// message: String,
1958 /// _hello: ws::Request,
1959 /// channel: ws::Channel,
1960 /// }
1961 ///
1962 /// impl Component for App {
1963 /// type Message = Msg;
1964 /// type Properties = Props;
1965 ///
1966 /// fn create(ctx: &Context<Self>) -> Self {
1967 /// let channel = ctx.props().ws
1968 /// .channel()
1969 /// .on_change(ctx.link().callback(Msg::Channel))
1970 /// .open();
1971 ///
1972 /// Self {
1973 /// message: String::from("No Message :("),
1974 /// _hello: ws::Request::new(),
1975 /// channel,
1976 /// }
1977 /// }
1978 ///
1979 /// fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
1980 /// match msg {
1981 /// Msg::OnHello(Err(error)) => {
1982 /// tracing::error!("Request error: {:?}", error);
1983 /// false
1984 /// }
1985 /// Msg::OnHello(Ok(packet)) => {
1986 /// if let Ok(response) = packet.decode() {
1987 /// self.message = response.message.to_owned();
1988 /// }
1989 ///
1990 /// true
1991 /// }
1992 /// Msg::Channel(ws::ChannelEvent::Opened(..)) => {
1993 /// self._hello = self.channel
1994 /// .request()
1995 /// .body(api::HelloRequest { message: "Hello!"})
1996 /// .on_packet(ctx.link().callback(Msg::OnHello))
1997 /// .send();
1998 ///
1999 /// true
2000 /// }
2001 /// Msg::Channel(ws::ChannelEvent::Failed(error)) => {
2002 /// tracing::error!("Channel error: {error}");
2003 /// true
2004 /// }
2005 /// Msg::Channel(..) => true,
2006 /// }
2007 /// }
2008 ///
2009 /// fn view(&self, ctx: &Context<Self>) -> Html {
2010 /// html! {
2011 /// <div>
2012 /// <h1>{"WebSocket Example"}</h1>
2013 /// <p>{format!("Message: {}", self.message)}</p>
2014 /// </div>
2015 /// }
2016 /// }
2017 /// }
2018 /// ```
2019 pub fn on_change<U>(self, callback: U) -> ChannelBuilder<'a, H, U>
2020 where
2021 U: Callback<ChannelEvent>,
2022 {
2023 ChannelBuilder {
2024 shared: self.shared,
2025 callback,
2026 }
2027 }
2028}
2029
2030impl<'a, H, C> ChannelBuilder<'a, H, C>
2031where
2032 C: Callback<ChannelEvent>,
2033 H: WebImpl,
2034{
2035 /// Open the channel.
2036 ///
2037 /// The channel is opened straight away if the connection is open, and
2038 /// otherwise as soon as it opens. After that it is opened again every time
2039 /// the connection reopens, until the returned [`Channel`] and every clone
2040 /// of it has been dropped.
2041 pub fn open(self) -> Channel<H> {
2042 let inner = Rc::new(ChannelInner {
2043 handle: Handle {
2044 shared: self.shared.clone(),
2045 },
2046 id: Cell::new(ChannelId::NONE),
2047 generation: Cell::new(0),
2048 on_change: Box::new(self.callback),
2049 queue: RefCell::new(VecDeque::new()),
2050 connect: RefCell::new(Request::new()),
2051 state: RefCell::new(StateListener::new()),
2052 });
2053
2054 let (state, listener) = inner.handle.on_state_change({
2055 // NB: Weak, since the listener is owned by the channel.
2056 let inner = Rc::downgrade(&inner);
2057
2058 move |state: State| {
2059 if let Some(inner) = inner.upgrade() {
2060 inner.on_state(state);
2061 }
2062 }
2063 });
2064
2065 *inner.state.borrow_mut() = listener;
2066
2067 if state.is_open() {
2068 inner.connect();
2069 }
2070
2071 Channel { inner }
2072 }
2073}
2074
2075/// Ask the server for a new channel over the current connection.
2076///
2077/// The callback is called with the id the server allocated, or with the error
2078/// which prevented it.
2079fn connect_channel<H>(
2080 shared: &Weak<Shared<H>>,
2081 callback: impl Fn(Result<ChannelId, Error>) + 'static,
2082) -> Request
2083where
2084 H: WebImpl,
2085{
2086 struct RequestCallbackImpl<C>(C);
2087
2088 impl<C> RequestCallback for RequestCallbackImpl<C>
2089 where
2090 C: Fn(Result<ChannelId, Error>) + 'static,
2091 {
2092 #[inline]
2093 fn as_channel(&self) -> Option<&(dyn Fn(Result<ChannelId, Error>) + 'static)> {
2094 Some(&self.0)
2095 }
2096
2097 #[inline]
2098 fn error(&self, error: Error) {
2099 (self.0)(Err(error));
2100 }
2101 }
2102
2103 let Some(shared) = shared.upgrade() else {
2104 callback(Err(Error::new(ErrorKind::NotConnected(
2105 "WebSocket service is down",
2106 ))));
2107 return Request::new();
2108 };
2109
2110 if shared.state.get() != State::Open {
2111 callback(Err(Error::new(ErrorKind::NotConnected(
2112 "WebSocket is not connected",
2113 ))));
2114 return Request::new();
2115 }
2116
2117 let serial = shared.serial.get();
2118 shared.serial.set(serial.wrapping_add(1));
2119
2120 let pending = Pending {
2121 id: MessageId::CONNECT,
2122 serial,
2123 callback: RequestCallbackImpl(callback),
2124 };
2125
2126 let existing = shared
2127 .g
2128 .requests
2129 .borrow_mut()
2130 .insert(serial, Box::new(pending));
2131
2132 if let Some(p) = existing {
2133 p.callback.error(Error::new(ErrorKind::Cancelled));
2134 }
2135
2136 if let Err(error) = shared.send_connect(serial) {
2137 // NB: The request is registered before it is sent, since sending
2138 // can observe synchronously that the peer has gone away and fail
2139 // every pending request before it returns. If that did not already
2140 // fail this request, it is failed here.
2141 shared.fail_request(serial, error);
2142 return Request::new();
2143 }
2144
2145 Request {
2146 serial,
2147 g: Rc::downgrade(&shared.g),
2148 }
2149}
2150
2151/// A request builder .
2152///
2153/// Associate the callback to be used by using either
2154/// [`RequestBuilder::on_packet`] or [`RequestBuilder::on_raw_packet`] depending
2155/// on your needs.
2156///
2157/// Send the request with [`RequestBuilder::send`].
2158pub struct RequestBuilder<'a, H, B, C>
2159where
2160 H: WebImpl,
2161{
2162 shared: &'a Weak<Shared<H>>,
2163 channel: Option<ChannelId>,
2164 body: B,
2165 callback: C,
2166}
2167
2168impl<'a, H, B, C> RequestBuilder<'a, H, B, C>
2169where
2170 H: WebImpl,
2171{
2172 /// Set the body of the request.
2173 #[inline]
2174 pub fn body<U>(self, body: U) -> RequestBuilder<'a, H, U, C>
2175 where
2176 U: api::Request,
2177 {
2178 RequestBuilder {
2179 shared: self.shared,
2180 channel: self.channel,
2181 body,
2182 callback: self.callback,
2183 }
2184 }
2185
2186 /// Handle the response using the specified callback.
2187 ///
2188 /// # Examples
2189 ///
2190 /// ```
2191 /// # extern crate yew023 as yew;
2192 /// use yew::prelude::*;
2193 /// use musli_web::web03::prelude::*;
2194 ///
2195 /// mod api {
2196 /// use musli::{Decode, Encode};
2197 /// use musli_web::api;
2198 ///
2199 /// #[derive(Encode, Decode)]
2200 /// pub struct HelloRequest<'de> {
2201 /// pub message: &'de str,
2202 /// }
2203 ///
2204 /// #[derive(Encode, Decode)]
2205 /// pub struct HelloResponse<'de> {
2206 /// pub message: &'de str,
2207 /// }
2208 ///
2209 /// api::define! {
2210 /// pub type Hello;
2211 ///
2212 /// impl Endpoint for Hello {
2213 /// impl<'de> Request for HelloRequest<'de>;
2214 /// type Response<'de> = HelloResponse<'de>;
2215 /// }
2216 /// }
2217 /// }
2218 ///
2219 /// enum Msg {
2220 /// OnHello(Result<ws::Packet<api::Hello>, ws::Error>),
2221 /// }
2222 ///
2223 /// #[derive(Properties, PartialEq)]
2224 /// struct Props {
2225 /// ws: ws::Handle,
2226 /// }
2227 ///
2228 /// struct App {
2229 /// message: String,
2230 /// _hello: ws::Request,
2231 /// }
2232 ///
2233 /// impl Component for App {
2234 /// type Message = Msg;
2235 /// type Properties = Props;
2236 ///
2237 /// fn create(ctx: &Context<Self>) -> Self {
2238 /// let hello = ctx.props().ws
2239 /// .request()
2240 /// .body(api::HelloRequest { message: "Hello!"})
2241 /// .on_packet(ctx.link().callback(Msg::OnHello))
2242 /// .send();
2243 ///
2244 /// Self {
2245 /// message: String::from("No Message :("),
2246 /// _hello: hello,
2247 /// }
2248 /// }
2249 ///
2250 /// fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
2251 /// match msg {
2252 /// Msg::OnHello(Err(error)) => {
2253 /// tracing::error!("Request error: {:?}", error);
2254 /// false
2255 /// }
2256 /// Msg::OnHello(Ok(packet)) => {
2257 /// if let Ok(response) = packet.decode() {
2258 /// self.message = response.message.to_owned();
2259 /// }
2260 ///
2261 /// true
2262 /// }
2263 /// }
2264 /// }
2265 ///
2266 /// fn view(&self, ctx: &Context<Self>) -> Html {
2267 /// html! {
2268 /// <div>
2269 /// <h1>{"WebSocket Example"}</h1>
2270 /// <p>{format!("Message: {}", self.message)}</p>
2271 /// </div>
2272 /// }
2273 /// }
2274 /// }
2275 /// ```
2276 pub fn on_packet<E>(
2277 self,
2278 callback: impl Callback<Result<Packet<E>>>,
2279 ) -> RequestBuilder<'a, H, B, impl Callback<Result<RawPacket>>>
2280 where
2281 E: api::Endpoint,
2282 {
2283 self.on_raw_packet(move |result: Result<RawPacket>| match result {
2284 Ok(ok) => callback.call(Ok(Packet::new(ok))),
2285 Err(err) => callback.call(Err(err)),
2286 })
2287 }
2288
2289 /// Handle the response using the specified callback.
2290 ///
2291 /// # Examples
2292 ///
2293 /// ```
2294 /// # extern crate yew023 as yew;
2295 /// use yew::prelude::*;
2296 /// use musli_web::web03::prelude::*;
2297 ///
2298 /// mod api {
2299 /// use musli::{Decode, Encode};
2300 /// use musli_web::api;
2301 ///
2302 /// #[derive(Encode, Decode)]
2303 /// pub struct HelloRequest<'de> {
2304 /// pub message: &'de str,
2305 /// }
2306 ///
2307 /// #[derive(Encode, Decode)]
2308 /// pub struct HelloResponse<'de> {
2309 /// pub message: &'de str,
2310 /// }
2311 ///
2312 /// api::define! {
2313 /// pub type Hello;
2314 ///
2315 /// impl Endpoint for Hello {
2316 /// impl<'de> Request for HelloRequest<'de>;
2317 /// type Response<'de> = HelloResponse<'de>;
2318 /// }
2319 /// }
2320 /// }
2321 ///
2322 /// enum Msg {
2323 /// OnHello(Result<ws::RawPacket, ws::Error>),
2324 /// }
2325 ///
2326 /// #[derive(Properties, PartialEq)]
2327 /// struct Props {
2328 /// ws: ws::Handle,
2329 /// }
2330 ///
2331 /// struct App {
2332 /// message: String,
2333 /// _hello: ws::Request,
2334 /// }
2335 ///
2336 /// impl Component for App {
2337 /// type Message = Msg;
2338 /// type Properties = Props;
2339 ///
2340 /// fn create(ctx: &Context<Self>) -> Self {
2341 /// let link = ctx.link().clone();
2342 ///
2343 /// let hello = ctx.props().ws
2344 /// .request()
2345 /// .body(api::HelloRequest { message: "Hello!"})
2346 /// .on_raw_packet(move |packet| link.send_message(Msg::OnHello(packet)))
2347 /// .send();
2348 ///
2349 /// Self {
2350 /// message: String::from("No Message :("),
2351 /// _hello: hello,
2352 /// }
2353 /// }
2354 ///
2355 /// fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
2356 /// match msg {
2357 /// Msg::OnHello(Err(error)) => {
2358 /// tracing::error!("Request error: {:?}", error);
2359 /// false
2360 /// }
2361 /// Msg::OnHello(Ok(packet)) => {
2362 /// if let Ok(response) = packet.decode::<api::HelloResponse>() {
2363 /// self.message = response.message.to_owned();
2364 /// }
2365 ///
2366 /// true
2367 /// }
2368 /// }
2369 /// }
2370 ///
2371 /// fn view(&self, ctx: &Context<Self>) -> Html {
2372 /// html! {
2373 /// <div>
2374 /// <h1>{"WebSocket Example"}</h1>
2375 /// <p>{format!("Message: {}", self.message)}</p>
2376 /// </div>
2377 /// }
2378 /// }
2379 /// }
2380 /// ```
2381 pub fn on_raw_packet<U>(self, callback: U) -> RequestBuilder<'a, H, B, U>
2382 where
2383 U: Callback<Result<RawPacket, Error>>,
2384 {
2385 RequestBuilder {
2386 shared: self.shared,
2387 channel: self.channel,
2388 body: self.body,
2389 callback,
2390 }
2391 }
2392}
2393
2394impl<'a, H, B, C> RequestBuilder<'a, H, B, C>
2395where
2396 B: api::Request,
2397 C: Callback<Result<RawPacket>>,
2398 H: WebImpl,
2399{
2400 /// Send the request.
2401 ///
2402 /// This requires that a body has been set using [`RequestBuilder::body`].
2403 pub fn send(self) -> Request {
2404 struct RequestCallbackImpl<C>(C);
2405
2406 impl<C> RequestCallback for RequestCallbackImpl<C>
2407 where
2408 C: Callback<Result<RawPacket>>,
2409 {
2410 #[inline]
2411 fn as_request(&self) -> Option<&(dyn Callback<Result<RawPacket>> + 'static)> {
2412 Some(&self.0)
2413 }
2414
2415 #[inline]
2416 fn error(&self, error: Error) {
2417 self.0.call(Err(error));
2418 }
2419 }
2420
2421 let Some(shared) = self.shared.upgrade() else {
2422 self.callback.call(Err(Error::new(ErrorKind::NotConnected(
2423 "WebSocket service is down",
2424 ))));
2425 return Request::new();
2426 };
2427
2428 if shared.state.get() != State::Open {
2429 self.callback.call(Err(Error::new(ErrorKind::NotConnected(
2430 "WebSocket is not connected",
2431 ))));
2432 return Request::new();
2433 }
2434
2435 let Some(channel) = self.channel else {
2436 self.callback.call(Err(Error::new(ErrorKind::NotConnected(
2437 "WebSocket request over closed channel",
2438 ))));
2439 return Request::new();
2440 };
2441
2442 let serial = shared.serial.get();
2443 shared.serial.set(serial.wrapping_add(1));
2444
2445 let pending = Pending {
2446 id: <B::Endpoint as api::Endpoint>::ID,
2447 serial,
2448 callback: RequestCallbackImpl(self.callback),
2449 };
2450
2451 let existing = shared
2452 .g
2453 .requests
2454 .borrow_mut()
2455 .insert(serial, Box::new(pending));
2456
2457 if let Some(p) = existing {
2458 p.callback.error(Error::new(ErrorKind::Cancelled));
2459 }
2460
2461 if let Err(error) = shared.send_client_request(serial, channel, &self.body) {
2462 // NB: The request is registered before it is sent, since sending
2463 // can observe synchronously that the peer has gone away and fail
2464 // every pending request before it returns. If that did not already
2465 // fail this request, it is failed here.
2466 shared.fail_request(serial, error);
2467 return Request::new();
2468 }
2469
2470 Request {
2471 serial,
2472 g: Rc::downgrade(&shared.g),
2473 }
2474 }
2475}
2476
2477/// The handle for a pending request.
2478///
2479/// Dropping or [`clear()`] this handle will cancel the request.
2480///
2481/// [`clear()`]: Self::clear
2482pub struct Request {
2483 serial: u32,
2484 g: Weak<Generic>,
2485}
2486
2487impl Request {
2488 /// An empty request handler.
2489 ///
2490 /// # Examples
2491 ///
2492 /// ```
2493 /// use musli_web::web03::prelude::*;
2494 ///
2495 /// let mut request = ws::Request::new();
2496 /// assert_eq!(request.is_pending(), false);
2497 /// ```
2498 #[inline]
2499 pub const fn new() -> Self {
2500 Self {
2501 serial: 0,
2502 g: Weak::new(),
2503 }
2504 }
2505
2506 /// Clear the request handle without dropping it, cancelling any pending
2507 /// requests.
2508 pub fn clear(&mut self) {
2509 let removed = {
2510 let serial = mem::take(&mut self.serial);
2511
2512 let Some(g) = self.g.upgrade() else {
2513 return;
2514 };
2515
2516 self.g = Weak::new();
2517
2518 let Some(p) = g.requests.borrow_mut().remove(&serial) else {
2519 return;
2520 };
2521
2522 p
2523 };
2524
2525 drop(removed);
2526 }
2527
2528 /// Indicates if a request is pending.
2529 ///
2530 /// # Examples
2531 ///
2532 /// ```
2533 /// use musli_web::web03::prelude::*;
2534 ///
2535 /// let mut request = ws::Request::new();
2536 /// assert_eq!(request.is_pending(), false);
2537 /// ```
2538 #[inline]
2539 pub fn is_pending(&self) -> bool {
2540 let Some(g) = self.g.upgrade() else {
2541 return false;
2542 };
2543
2544 g.requests.borrow().contains_key(&self.serial)
2545 }
2546}
2547
2548impl Default for Request {
2549 #[inline]
2550 fn default() -> Self {
2551 Self::new()
2552 }
2553}
2554
2555impl Drop for Request {
2556 #[inline]
2557 fn drop(&mut self) {
2558 self.clear();
2559 }
2560}
2561
2562/// A request in flight which resolves to its decoded response, see
2563/// [`Handle::call`].
2564///
2565/// Dropping it cancels the request, the same way as dropping a [`Request`]
2566/// does.
2567#[must_use = "Futures do nothing unless polled, and dropping this cancels the request"]
2568pub struct Call<T> {
2569 slot: Rc<CallSlot>,
2570 request: Request,
2571 _marker: PhantomData<fn() -> T>,
2572}
2573
2574/// Where the callback of a [`Call`] leaves the outcome of the request.
2575#[derive(Default)]
2576struct CallSlot {
2577 result: Cell<Option<Result<RawPacket>>>,
2578 waker: Cell<Option<Waker>>,
2579}
2580
2581impl<T> Call<T> {
2582 /// Send a request through `builder`, keeping the outcome for the
2583 /// returned future.
2584 fn send<H, B>(builder: RequestBuilder<'_, H, B, EmptyCallback>) -> Self
2585 where
2586 H: WebImpl,
2587 B: api::Request,
2588 {
2589 let slot = Rc::new(CallSlot::default());
2590
2591 // NB: The callback can be called before `send` returns, such as when
2592 // the request is not connected. The slot then already has the outcome
2593 // when the future is first polled.
2594 let request = builder
2595 .on_raw_packet({
2596 let slot = slot.clone();
2597
2598 move |result: Result<RawPacket>| {
2599 slot.result.set(Some(result));
2600
2601 if let Some(waker) = slot.waker.take() {
2602 waker.wake();
2603 }
2604 }
2605 })
2606 .send();
2607
2608 Self {
2609 slot,
2610 request,
2611 _marker: PhantomData,
2612 }
2613 }
2614
2615 /// Indicates if the request is still waiting for its response.
2616 ///
2617 /// Once this is `false`, polling the future no longer has to wait.
2618 #[inline]
2619 pub fn is_pending(&self) -> bool {
2620 self.request.is_pending()
2621 }
2622}
2623
2624impl<T> Future for Call<T>
2625where
2626 for<'de> T: DecodeBody<'de>,
2627{
2628 type Output = Result<T>;
2629
2630 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
2631 let this = self.get_mut();
2632
2633 let Some(result) = this.slot.result.take() else {
2634 this.slot.waker.set(Some(cx.waker().clone()));
2635 return Poll::Pending;
2636 };
2637
2638 Poll::Ready(result.and_then(|packet| packet.decode()))
2639 }
2640}
2641
2642impl<T> fmt::Debug for Call<T> {
2643 #[inline]
2644 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2645 f.debug_struct("Call")
2646 .field("type", &any::type_name::<T>())
2647 .field("is_pending", &self.is_pending())
2648 .finish()
2649 }
2650}
2651
2652/// A set of pending requests, kept alive until each completes.
2653///
2654/// Dropping a [`Request`] cancels it, so a component with several requests
2655/// in flight at once has to keep every one of them somewhere. Push each one
2656/// sent into a `Requests` with [`Requests::push`]. A request stays pending
2657/// until it completes in any way, by its response, an error, or the
2658/// connection being lost, and the set forgets it once it has.
2659///
2660/// Every request still pending is cancelled when the set is dropped or
2661/// [`cleared`], so a component which keeps a set cancels what it sent once
2662/// it goes away.
2663///
2664/// To keep at most one request for each of a number of things, such as one
2665/// fetch per row, use [`KeyedRequests`] instead.
2666///
2667/// [`cleared`]: Requests::clear
2668///
2669/// # Examples
2670///
2671/// ```
2672/// # extern crate yew023 as yew;
2673/// use yew::prelude::*;
2674/// use musli_web::web03::prelude::*;
2675///
2676/// mod api {
2677/// use musli::{Decode, Encode};
2678/// use musli_web::api;
2679///
2680/// #[derive(Encode, Decode)]
2681/// pub struct SaveRequest<'de> {
2682/// pub text: &'de str,
2683/// }
2684///
2685/// api::define! {
2686/// pub type Save;
2687///
2688/// impl Endpoint for Save {
2689/// impl<'de> Request for SaveRequest<'de>;
2690/// type Response<'de> = ();
2691/// }
2692/// }
2693/// }
2694///
2695/// enum Msg {
2696/// Save(String),
2697/// Saved(Result<ws::Packet<api::Save>, ws::Error>),
2698/// }
2699///
2700/// #[derive(Properties, PartialEq)]
2701/// struct Props {
2702/// ws: ws::Handle,
2703/// }
2704///
2705/// struct Notes {
2706/// // Every save sent which has not completed yet.
2707/// saves: ws::Requests,
2708/// }
2709///
2710/// impl Component for Notes {
2711/// type Message = Msg;
2712/// type Properties = Props;
2713///
2714/// fn create(_: &Context<Self>) -> Self {
2715/// Self {
2716/// saves: ws::Requests::new(),
2717/// }
2718/// }
2719///
2720/// fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
2721/// match msg {
2722/// Msg::Save(text) => {
2723/// // Saving again does not cancel an earlier save.
2724/// self.saves.push(
2725/// ctx.props()
2726/// .ws
2727/// .request()
2728/// .body(api::SaveRequest { text: &text })
2729/// .on_packet(ctx.link().callback(Msg::Saved))
2730/// .send(),
2731/// );
2732///
2733/// true
2734/// }
2735/// Msg::Saved(result) => {
2736/// if let Err(error) = result {
2737/// tracing::error!("Save failed: {error}");
2738/// }
2739///
2740/// true
2741/// }
2742/// }
2743/// }
2744///
2745/// fn view(&self, ctx: &Context<Self>) -> Html {
2746/// let onclick = ctx.link().callback(|_: MouseEvent| Msg::Save(String::from("Hello")));
2747///
2748/// html! {
2749/// <>
2750/// <button {onclick}>{"Save"}</button>
2751/// if !self.saves.is_empty() {
2752/// <p>{format!("Saving {}...", self.saves.len())}</p>
2753/// }
2754/// </>
2755/// }
2756/// }
2757/// }
2758/// ```
2759#[derive(Default)]
2760pub struct Requests {
2761 requests: Vec<Request>,
2762}
2763
2764impl Requests {
2765 /// Construct an empty set of requests.
2766 ///
2767 /// # Examples
2768 ///
2769 /// ```
2770 /// use musli_web::web03::prelude::*;
2771 ///
2772 /// let requests = ws::Requests::new();
2773 /// assert!(requests.is_empty());
2774 /// ```
2775 #[inline]
2776 pub const fn new() -> Self {
2777 Self {
2778 requests: Vec::new(),
2779 }
2780 }
2781
2782 /// Keep `request` until it completes.
2783 ///
2784 /// A request which is not pending, such as one which could not be sent,
2785 /// is dropped straight away.
2786 pub fn push(&mut self, request: Request) {
2787 if !request.is_pending() {
2788 return;
2789 }
2790
2791 // NB: Completed requests are only forgotten when the set would
2792 // otherwise have to grow, which keeps pushing amortized constant time
2793 // while holding at most twice as many requests as were ever pending
2794 // at once.
2795 if self.requests.len() == self.requests.capacity() {
2796 self.requests.retain(Request::is_pending);
2797 }
2798
2799 self.requests.push(request);
2800 }
2801
2802 /// The number of requests which are still pending.
2803 pub fn len(&self) -> usize {
2804 self.requests.iter().filter(|r| r.is_pending()).count()
2805 }
2806
2807 /// Whether no request is pending.
2808 pub fn is_empty(&self) -> bool {
2809 !self.requests.iter().any(Request::is_pending)
2810 }
2811
2812 /// Cancel every pending request.
2813 pub fn clear(&mut self) {
2814 let requests = mem::take(&mut self.requests);
2815 // NB: Dropped once the set is empty, since cancelling a request drops
2816 // its callback.
2817 drop(requests);
2818 }
2819}
2820
2821impl fmt::Debug for Requests {
2822 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2823 f.debug_struct("Requests")
2824 .field("len", &self.len())
2825 .finish()
2826 }
2827}
2828
2829impl Extend<Request> for Requests {
2830 fn extend<I>(&mut self, iter: I)
2831 where
2832 I: IntoIterator<Item = Request>,
2833 {
2834 for request in iter {
2835 self.push(request);
2836 }
2837 }
2838}
2839
2840/// A set of pending requests with at most one for each key, kept alive until
2841/// each completes.
2842///
2843/// This is like [`Requests`], except that each request is stored under a key
2844/// with [`KeyedRequests::insert`]. Storing a request under a key which already
2845/// has one cancels the request stored before it, so that what is fetched for
2846/// a key is always the latest. Any number of keys can have a request in
2847/// flight at once, and a key is forgotten once its request completes.
2848///
2849/// Every request still pending is cancelled when the set is dropped or
2850/// [`cleared`].
2851///
2852/// [`cleared`]: KeyedRequests::clear
2853///
2854/// # Examples
2855///
2856/// ```
2857/// # extern crate yew023 as yew;
2858/// use yew::prelude::*;
2859/// use musli_web::web03::prelude::*;
2860///
2861/// mod api {
2862/// use musli::{Decode, Encode};
2863/// use musli_web::api;
2864///
2865/// #[derive(Encode, Decode)]
2866/// pub struct RenameRequest<'de> {
2867/// pub id: u32,
2868/// pub name: &'de str,
2869/// }
2870///
2871/// api::define! {
2872/// pub type Rename;
2873///
2874/// impl Endpoint for Rename {
2875/// impl<'de> Request for RenameRequest<'de>;
2876/// type Response<'de> = ();
2877/// }
2878/// }
2879/// }
2880///
2881/// enum Msg {
2882/// Rename(u32, String),
2883/// Renamed(Result<ws::Packet<api::Rename>, ws::Error>),
2884/// }
2885///
2886/// #[derive(Properties, PartialEq)]
2887/// struct Props {
2888/// ws: ws::Handle,
2889/// rows: Vec<u32>,
2890/// }
2891///
2892/// struct Rows {
2893/// // At most one rename in flight for each row.
2894/// renames: ws::KeyedRequests<u32>,
2895/// }
2896///
2897/// impl Component for Rows {
2898/// type Message = Msg;
2899/// type Properties = Props;
2900///
2901/// fn create(_: &Context<Self>) -> Self {
2902/// Self {
2903/// renames: ws::KeyedRequests::new(),
2904/// }
2905/// }
2906///
2907/// fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
2908/// match msg {
2909/// Msg::Rename(id, name) => {
2910/// // Renaming a row again cancels its earlier rename, but
2911/// // leaves the renames of other rows alone.
2912/// self.renames.insert(
2913/// id,
2914/// ctx.props()
2915/// .ws
2916/// .request()
2917/// .body(api::RenameRequest { id, name: &name })
2918/// .on_packet(ctx.link().callback(Msg::Renamed))
2919/// .send(),
2920/// );
2921///
2922/// true
2923/// }
2924/// Msg::Renamed(_) => true,
2925/// }
2926/// }
2927///
2928/// fn view(&self, ctx: &Context<Self>) -> Html {
2929/// html! {
2930/// {for ctx.props().rows.iter().copied().map(|id| {
2931/// let onclick = ctx.link().callback(move |_: MouseEvent| {
2932/// Msg::Rename(id, format!("Row {id}"))
2933/// });
2934///
2935/// let label = if self.renames.is_pending(&id) {
2936/// String::from("Renaming...")
2937/// } else {
2938/// format!("Rename #{id}")
2939/// };
2940///
2941/// html!(<button {onclick}>{label}</button>)
2942/// })}
2943/// }
2944/// }
2945/// }
2946/// ```
2947pub struct KeyedRequests<K> {
2948 requests: HashMap<K, Request>,
2949}
2950
2951impl<K> KeyedRequests<K> {
2952 /// Construct an empty set of requests.
2953 ///
2954 /// # Examples
2955 ///
2956 /// ```
2957 /// use musli_web::web03::prelude::*;
2958 ///
2959 /// let requests = ws::KeyedRequests::<u32>::new();
2960 /// assert!(requests.is_empty());
2961 /// ```
2962 #[inline]
2963 pub fn new() -> Self {
2964 Self {
2965 requests: HashMap::new(),
2966 }
2967 }
2968
2969 /// The number of keys whose request is still pending.
2970 pub fn len(&self) -> usize {
2971 self.requests.values().filter(|r| r.is_pending()).count()
2972 }
2973
2974 /// Whether no request is pending.
2975 pub fn is_empty(&self) -> bool {
2976 !self.requests.values().any(Request::is_pending)
2977 }
2978
2979 /// Iterate over the keys whose request is still pending, in no
2980 /// particular order.
2981 pub fn keys(&self) -> impl Iterator<Item = &K> {
2982 self.requests
2983 .iter()
2984 .filter(|(_, r)| r.is_pending())
2985 .map(|(k, _)| k)
2986 }
2987
2988 /// Cancel every pending request.
2989 pub fn clear(&mut self) {
2990 let requests = mem::take(&mut self.requests);
2991 // NB: Dropped once the set is empty, since cancelling a request drops
2992 // its callback.
2993 drop(requests);
2994 }
2995}
2996
2997impl<K> KeyedRequests<K>
2998where
2999 K: Eq + Hash,
3000{
3001 /// Keep `request` under `key` until it completes, cancelling the request
3002 /// stored under `key` before it.
3003 ///
3004 /// A request which is not pending, such as one which could not be sent,
3005 /// is dropped straight away, but still cancels the one before it.
3006 pub fn insert(&mut self, key: K, request: Request) {
3007 if !request.is_pending() {
3008 let old = self.requests.remove(&key);
3009 drop(old);
3010 return;
3011 }
3012
3013 // NB: Completed requests are only forgotten when the set would
3014 // otherwise have to grow, see `Requests::push`.
3015 if self.requests.len() == self.requests.capacity() {
3016 self.requests.retain(|_, r| r.is_pending());
3017 }
3018
3019 let old = self.requests.insert(key, request);
3020 drop(old);
3021 }
3022
3023 /// Cancel the request stored under `key`, returning whether it was still
3024 /// pending.
3025 pub fn remove<Q>(&mut self, key: &Q) -> bool
3026 where
3027 K: core::borrow::Borrow<Q>,
3028 Q: ?Sized + Eq + Hash,
3029 {
3030 let Some(request) = self.requests.remove(key) else {
3031 return false;
3032 };
3033
3034 let pending = request.is_pending();
3035 drop(request);
3036 pending
3037 }
3038
3039 /// Whether the request stored under `key` is still pending.
3040 pub fn is_pending<Q>(&self, key: &Q) -> bool
3041 where
3042 K: core::borrow::Borrow<Q>,
3043 Q: ?Sized + Eq + Hash,
3044 {
3045 self.requests.get(key).is_some_and(Request::is_pending)
3046 }
3047}
3048
3049impl<K> Default for KeyedRequests<K> {
3050 #[inline]
3051 fn default() -> Self {
3052 Self::new()
3053 }
3054}
3055
3056impl<K> fmt::Debug for KeyedRequests<K>
3057where
3058 K: fmt::Debug,
3059{
3060 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3061 f.debug_set().entries(self.keys()).finish()
3062 }
3063}
3064
3065/// The handle for a pending request.
3066///
3067/// Dropping or calling [`clear()`] on this handle remove the listener.
3068///
3069/// [`clear()`]: Self::clear
3070pub struct Listener {
3071 kind: Option<MessageId>,
3072 index: usize,
3073 g: Weak<Generic>,
3074}
3075
3076impl Listener {
3077 /// Construct an empty listener.
3078 #[inline]
3079 pub const fn new() -> Self {
3080 Self {
3081 kind: None,
3082 index: 0,
3083 g: Weak::new(),
3084 }
3085 }
3086
3087 /// Build up an empty listener with the specified kind.
3088 #[inline]
3089 pub(crate) const fn empty_with_kind(kind: MessageId) -> Self {
3090 Self {
3091 kind: Some(kind),
3092 index: 0,
3093 g: Weak::new(),
3094 }
3095 }
3096
3097 /// Clear the listener without dropping it.
3098 ///
3099 /// This will remove the associated broadcast listener from being notified.
3100 pub fn clear(&mut self) {
3101 // Gather values here to drop them outside of the upgrade block.
3102 let removed;
3103 let removed_value;
3104
3105 {
3106 let Some(g) = self.g.upgrade() else {
3107 return;
3108 };
3109
3110 self.g = Weak::new();
3111 let index = mem::take(&mut self.index);
3112
3113 let Some(kind) = self.kind.take() else {
3114 return;
3115 };
3116
3117 let mut broadcasts = g.broadcasts.borrow_mut();
3118
3119 let Entry::Occupied(mut e) = broadcasts.entry(kind) else {
3120 return;
3121 };
3122
3123 removed = e.get_mut().try_remove(index);
3124
3125 if e.get().is_empty() {
3126 removed_value = Some(e.remove());
3127 } else {
3128 removed_value = None;
3129 }
3130 }
3131
3132 // Drop here, to avoid invoking any destructors which might borrow
3133 // shared mutably earlier.
3134 drop(removed);
3135 drop(removed_value);
3136 }
3137}
3138
3139impl Default for Listener {
3140 #[inline]
3141 fn default() -> Self {
3142 Self::new()
3143 }
3144}
3145
3146impl Drop for Listener {
3147 #[inline]
3148 fn drop(&mut self) {
3149 self.clear();
3150 }
3151}
3152
3153/// The handle for state change listening.
3154///
3155/// Dropping or calling [`clear()`] on this handle will remove the associated
3156/// callback from being notified.
3157///
3158/// [`clear()`]: Self::clear
3159pub struct StateListener {
3160 index: usize,
3161 g: Weak<Generic>,
3162}
3163
3164impl StateListener {
3165 /// Construct an empty state listener.
3166 #[inline]
3167 pub const fn new() -> Self {
3168 Self {
3169 index: 0,
3170 g: Weak::new(),
3171 }
3172 }
3173
3174 /// Clear the state listener without dropping it.
3175 ///
3176 /// This will remove the associated callback from being notified.
3177 pub fn clear(&mut self) {
3178 let removed = {
3179 let Some(g) = self.g.upgrade() else {
3180 return;
3181 };
3182
3183 self.g = Weak::new();
3184
3185 g.state_listeners.borrow_mut().try_remove(self.index)
3186 };
3187
3188 drop(removed);
3189 }
3190}
3191
3192impl Default for StateListener {
3193 #[inline]
3194 fn default() -> Self {
3195 Self::new()
3196 }
3197}
3198
3199impl Drop for StateListener {
3200 #[inline]
3201 fn drop(&mut self) {
3202 self.clear();
3203 }
3204}
3205
3206pub(crate) struct BufData {
3207 /// Buffer being used.
3208 pub(crate) data: Vec<u8>,
3209 /// Number of strong references to this buffer.
3210 strong: Cell<usize>,
3211 /// Reference to shared state where the buffer will be recycled to.
3212 g: Weak<Generic>,
3213}
3214
3215impl BufData {
3216 fn with_capacity(g: Weak<Generic>, capacity: usize) -> Self {
3217 Self {
3218 data: Vec::with_capacity(capacity),
3219 strong: Cell::new(0),
3220 g,
3221 }
3222 }
3223
3224 unsafe fn dec(ptr: NonNull<BufData>) {
3225 unsafe {
3226 let count = ptr.as_ref().strong.get().wrapping_sub(1);
3227 ptr.as_ref().strong.set(count);
3228
3229 if count > 0 {
3230 return;
3231 }
3232
3233 let mut buf = Box::from_raw(ptr.as_ptr());
3234
3235 // Try to recycle the buffer if shared is available, else let it be
3236 // dropped and free here.
3237 let Some(g) = buf.as_ref().g.upgrade() else {
3238 return;
3239 };
3240
3241 let mut buffers = g.buffers.borrow_mut();
3242
3243 // NB: The next message is written from the start of the buffer, so
3244 // the contents of this one must not linger.
3245 buf.data.clear();
3246
3247 // We size our buffers to some max capacity to avod overuse in case
3248 // we infrequently need to handle some massive message. If we don't
3249 // shrink the allocation, then memory use can run away over time.
3250 buf.data.shrink_to(MAX_CAPACITY);
3251
3252 buffers.push_back(buf);
3253 }
3254 }
3255
3256 unsafe fn inc(ptr: NonNull<BufData>) {
3257 unsafe {
3258 let count = ptr.as_ref().strong.get().wrapping_add(1);
3259
3260 if count == 0 {
3261 std::process::abort();
3262 }
3263
3264 ptr.as_ref().strong.set(count);
3265 }
3266 }
3267}
3268
3269/// A shared buffer of data that is recycled when dropped.
3270struct BufRc {
3271 data: NonNull<BufData>,
3272}
3273
3274impl BufRc {
3275 fn new(data: Box<BufData>) -> Self {
3276 let data = NonNull::from(Box::leak(data));
3277
3278 unsafe {
3279 BufData::inc(data);
3280 }
3281
3282 Self { data }
3283 }
3284}
3285
3286impl Deref for BufRc {
3287 type Target = [u8];
3288
3289 fn deref(&self) -> &Self::Target {
3290 unsafe { &(*self.data.as_ptr()).data }
3291 }
3292}
3293
3294impl Clone for BufRc {
3295 fn clone(&self) -> Self {
3296 unsafe {
3297 BufData::inc(self.data);
3298 }
3299
3300 Self { data: self.data }
3301 }
3302}
3303
3304impl Drop for BufRc {
3305 fn drop(&mut self) {
3306 unsafe {
3307 BufData::dec(self.data);
3308 }
3309 }
3310}
3311
3312/// A raw packet of data.
3313#[derive(Clone)]
3314pub struct RawPacket {
3315 id: MessageId,
3316 buf: Option<BufRc>,
3317 at: Cell<usize>,
3318 format: Format,
3319 channel: ChannelId,
3320}
3321
3322impl RawPacket {
3323 /// Construct an empty raw packet.
3324 ///
3325 /// # Examples
3326 ///
3327 /// ```
3328 /// use musli_web::api::MessageId;
3329 /// use musli_web::web::RawPacket;
3330 ///
3331 /// let packet = RawPacket::empty();
3332 ///
3333 /// assert!(packet.is_empty());
3334 /// assert_eq!(packet.id(), MessageId::EMPTY);
3335 /// ```
3336 pub const fn empty() -> Self {
3337 Self {
3338 id: MessageId::EMPTY,
3339 buf: None,
3340 at: Cell::new(0),
3341 format: Format::DEFAULT,
3342 channel: ChannelId::NONE,
3343 }
3344 }
3345
3346 /// The [`Format`] the body of this packet is encoded with.
3347 ///
3348 /// # Examples
3349 ///
3350 /// ```
3351 /// use musli_web::api::Format;
3352 /// use musli_web::web::RawPacket;
3353 ///
3354 /// let packet = RawPacket::empty();
3355 /// assert_eq!(packet.format(), Format::DEFAULT);
3356 /// ```
3357 #[inline]
3358 pub fn format(&self) -> Format {
3359 self.format
3360 }
3361
3362 /// Return the channel this packet belongs to.
3363 ///
3364 /// For a response this is the channel the request was sent over, and for
3365 /// a broadcast it is the channel the broadcast originated from, as written
3366 /// by [`ws::Server::broadcast_from`]. A page can compare it to the id of its
3367 /// own channel to recognize the echo of a change it made itself, see
3368 /// [`RawPacket::is_from`].
3369 ///
3370 /// This is [`ChannelId::NONE`] for a request which was not sent over a
3371 /// channel, and for a broadcast which no channel is credited with, such as
3372 /// one written with [`ws::Server::broadcast`].
3373 ///
3374 /// [`ws::Server::broadcast`]: https://docs.rs/musli-web/latest/musli_web/ws/struct.Server.html#method.broadcast
3375 /// [`ws::Server::broadcast_from`]: https://docs.rs/musli-web/latest/musli_web/ws/struct.Server.html#method.broadcast_from
3376 #[inline]
3377 pub fn channel(&self) -> ChannelId {
3378 self.channel
3379 }
3380
3381 /// Test if this packet originated from `channel`.
3382 ///
3383 /// This is `true` if the packet is a broadcast which was written with
3384 /// [`ws::Server::broadcast_from`] and `channel` as its origin, or a
3385 /// response to a request sent over `channel`. It is never `true` for
3386 /// [`ChannelId::NONE`], so a broadcast which no channel is credited with
3387 /// is not mistaken for the echo of a page which has not opened a channel
3388 /// yet.
3389 ///
3390 /// Channel ids are unique across every connection to a server by
3391 /// default, so a broadcast caused by another client never matches.
3392 ///
3393 /// [`ws::Server::broadcast_from`]: https://docs.rs/musli-web/latest/musli_web/ws/struct.Server.html#method.broadcast_from
3394 ///
3395 /// # Examples
3396 ///
3397 /// ```
3398 /// use musli_web::api::ChannelId;
3399 /// use musli_web::web::RawPacket;
3400 ///
3401 /// let packet = RawPacket::empty();
3402 /// assert_eq!(packet.channel(), ChannelId::NONE);
3403 /// assert!(!packet.is_from(ChannelId::NONE));
3404 /// ```
3405 #[inline]
3406 pub fn is_from(&self, channel: ChannelId) -> bool {
3407 channel != ChannelId::NONE && self.channel == channel
3408 }
3409
3410 /// Decode the contents of a raw packet.
3411 ///
3412 /// This can be called multiple times if there are multiple payloads in
3413 /// sequence of the response.
3414 ///
3415 /// You can check if the packet is empty using [`RawPacket::is_empty`].
3416 pub fn decode<'this, T>(&'this self) -> Result<T>
3417 where
3418 T: DecodeBody<'this>,
3419 {
3420 if self.id == MessageId::EMPTY {
3421 return Err(Error::new(ErrorKind::EmptyPacket));
3422 }
3423
3424 let mut at = self.at.get();
3425
3426 match self.format.decode(self.as_slice(), &mut at) {
3427 Ok(value) => {
3428 self.at.set(at);
3429 Ok(value)
3430 }
3431 Err(error) => {
3432 self.at.set(self.len());
3433 Err(Error::decode_packet(error))
3434 }
3435 }
3436 }
3437
3438 /// Get the underlying byte slice of the packet.
3439 ///
3440 /// # Examples
3441 ///
3442 /// ```
3443 /// use musli_web::web::RawPacket;
3444 ///
3445 /// let packet = RawPacket::empty();
3446 /// assert_eq!(packet.as_slice(), &[0u8; 0]);
3447 /// ```
3448 pub fn as_slice(&self) -> &[u8] {
3449 match &self.buf {
3450 Some(buf) => buf.as_ref(),
3451 None => &[],
3452 }
3453 }
3454
3455 /// Get the number of bytes remaining to be decoded in the packet.
3456 ///
3457 /// # Examples
3458 ///
3459 /// ```
3460 /// use musli_web::web::RawPacket;
3461 ///
3462 /// let packet = RawPacket::empty();
3463 /// assert_eq!(packet.remaining(), 0);
3464 /// ```
3465 pub fn remaining(&self) -> usize {
3466 self.as_slice().len().saturating_sub(self.at.get())
3467 }
3468
3469 /// Get the length of the packet.
3470 ///
3471 /// # Examples
3472 ///
3473 /// ```
3474 /// use musli_web::web::RawPacket;
3475 ///
3476 /// let packet = RawPacket::empty();
3477 /// assert_eq!(packet.len(), 0);
3478 /// ```
3479 pub fn len(&self) -> usize {
3480 match &self.buf {
3481 Some(buf) => buf.len(),
3482 None => 0,
3483 }
3484 }
3485
3486 /// Check if the packet is empty.
3487 ///
3488 /// # Examples
3489 ///
3490 /// ```
3491 /// use musli_web::web::RawPacket;
3492 ///
3493 /// let packet = RawPacket::empty();
3494 /// assert!(packet.is_empty());
3495 /// ```
3496 pub fn is_empty(&self) -> bool {
3497 self.at.get() >= self.len()
3498 }
3499
3500 /// The id of the packet this is a response to as specified by
3501 /// [`Endpoint::ID`] or [`Broadcast::ID`].
3502 ///
3503 /// [`Endpoint::ID`]: crate::api::Endpoint::ID
3504 /// [`Broadcast::ID`]: crate::api::Broadcast::ID
3505 pub fn id(&self) -> MessageId {
3506 self.id
3507 }
3508}
3509
3510impl fmt::Debug for RawPacket {
3511 #[inline]
3512 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3513 f.debug_struct("RawPacket")
3514 .field("remaining", &self.remaining())
3515 .finish()
3516 }
3517}
3518
3519/// A typed packet of data.
3520pub struct Packet<T> {
3521 raw: RawPacket,
3522 _marker: PhantomData<T>,
3523}
3524
3525impl<T> Packet<T> {
3526 /// Construct an empty package.
3527 ///
3528 /// # Examples
3529 ///
3530 /// ```
3531 /// use musli_web::api::MessageId;
3532 /// use musli_web::web::Packet;
3533 ///
3534 /// let packet = Packet::<()>::empty();
3535 ///
3536 /// assert!(packet.is_empty());
3537 /// assert_eq!(packet.id(), MessageId::EMPTY);
3538 /// ```
3539 pub const fn empty() -> Self {
3540 Self {
3541 raw: RawPacket::empty(),
3542 _marker: PhantomData,
3543 }
3544 }
3545
3546 /// Return the channel this packet belongs to.
3547 ///
3548 /// For a response this is the channel the request was sent over, and for
3549 /// a broadcast it is the channel the broadcast originated from, as written
3550 /// by [`ws::Server::broadcast_from`]. A page can compare it to the id of its
3551 /// own channel to recognize the echo of a change it made itself, see
3552 /// [`Packet::is_from`].
3553 ///
3554 /// This is [`ChannelId::NONE`] for a request which was not sent over a
3555 /// channel, and for a broadcast which no channel is credited with, such as
3556 /// one written with [`ws::Server::broadcast`].
3557 ///
3558 /// [`ws::Server::broadcast`]: https://docs.rs/musli-web/latest/musli_web/ws/struct.Server.html#method.broadcast
3559 /// [`ws::Server::broadcast_from`]: https://docs.rs/musli-web/latest/musli_web/ws/struct.Server.html#method.broadcast_from
3560 #[inline]
3561 pub fn channel(&self) -> ChannelId {
3562 self.raw.channel()
3563 }
3564
3565 /// Test if this packet originated from `channel`.
3566 ///
3567 /// This is `true` if the packet is a broadcast which was written with
3568 /// [`ws::Server::broadcast_from`] and `channel` as its origin, or a
3569 /// response to a request sent over `channel`. It is never `true` for
3570 /// [`ChannelId::NONE`], so a broadcast which no channel is credited with
3571 /// is not mistaken for the echo of a page which has not opened a channel
3572 /// yet.
3573 ///
3574 /// Channel ids are unique across every connection to a server by
3575 /// default, so a broadcast caused by another client never matches.
3576 ///
3577 /// [`ws::Server::broadcast_from`]: https://docs.rs/musli-web/latest/musli_web/ws/struct.Server.html#method.broadcast_from
3578 ///
3579 /// # Examples
3580 ///
3581 /// ```
3582 /// use musli_web::api::ChannelId;
3583 /// use musli_web::web::Packet;
3584 ///
3585 /// let packet = Packet::<()>::empty();
3586 /// assert_eq!(packet.channel(), ChannelId::NONE);
3587 /// assert!(!packet.is_from(ChannelId::NONE));
3588 /// ```
3589 #[inline]
3590 pub fn is_from(&self, channel: ChannelId) -> bool {
3591 self.raw.is_from(channel)
3592 }
3593
3594 /// The [`Format`] the body of this packet is encoded with.
3595 #[inline]
3596 pub fn format(&self) -> Format {
3597 self.raw.format()
3598 }
3599
3600 /// Construct a new typed package from a raw one.
3601 ///
3602 /// Note that this does not guarantee that the typed package is correct, but
3603 /// the `T` parameter becomes associated with it allowing it to be used
3604 /// automatically with methods such as [`Packet::decode`].
3605 #[inline]
3606 pub fn new(raw: RawPacket) -> Self {
3607 Self {
3608 raw,
3609 _marker: PhantomData,
3610 }
3611 }
3612
3613 /// Convert a packet into a raw packet.
3614 ///
3615 /// To determine which endpoint or broadcast it belongs to the
3616 /// [`RawPacket::id`] method can be used.
3617 pub fn into_raw(self) -> RawPacket {
3618 self.raw
3619 }
3620
3621 /// Get the number of bytes remaining to be decoded in the packet.
3622 ///
3623 /// # Examples
3624 ///
3625 /// ```
3626 /// use musli_web::web::Packet;
3627 ///
3628 /// let packet = Packet::<()>::empty();
3629 /// assert_eq!(packet.remaining(), 0);
3630 /// ```
3631 pub fn remaining(&self) -> usize {
3632 self.raw.remaining()
3633 }
3634
3635 /// Check if the packet is empty.
3636 ///
3637 /// # Examples
3638 ///
3639 /// ```
3640 /// use musli_web::web::Packet;
3641 ///
3642 /// let packet = Packet::<()>::empty();
3643 /// assert!(packet.is_empty());
3644 /// ```
3645 pub fn is_empty(&self) -> bool {
3646 self.raw.is_empty()
3647 }
3648
3649 /// The id of the packet this is a response to as specified by
3650 /// [`Endpoint::ID`] or [`Broadcast::ID`].
3651 ///
3652 /// [`Endpoint::ID`]: crate::api::Endpoint::ID
3653 /// [`Broadcast::ID`]: crate::api::Broadcast::ID
3654 pub fn id(&self) -> MessageId {
3655 self.raw.id()
3656 }
3657}
3658
3659impl<T> Packet<T>
3660where
3661 T: api::Decodable,
3662{
3663 /// Decode the contents of a packet.
3664 ///
3665 /// This can be called multiple times if there are multiple payloads in
3666 /// sequence of the response.
3667 ///
3668 /// You can check if the packet is empty using [`Packet::is_empty`].
3669 pub fn decode(&self) -> Result<T::Type<'_>> {
3670 self.decode_any()
3671 }
3672
3673 /// Decode any contents of a packet.
3674 ///
3675 /// This can be called multiple times if there are multiple payloads in
3676 /// sequence of the response.
3677 ///
3678 /// You can check if the packet is empty using [`Packet::is_empty`].
3679 pub fn decode_any<'de, R>(&'de self) -> Result<R>
3680 where
3681 R: DecodeBody<'de>,
3682 {
3683 self.raw.decode()
3684 }
3685}
3686
3687impl<T> Packet<T>
3688where
3689 T: api::Endpoint,
3690{
3691 /// Decode the contents of a packet.
3692 ///
3693 /// This can be called multiple times if there are multiple payloads in
3694 /// sequence of the response.
3695 ///
3696 /// You can check if the packet is empty using [`Packet::is_empty`].
3697 pub fn decode_response(&self) -> Result<T::Response<'_>> {
3698 self.decode_any_response()
3699 }
3700
3701 /// Decode any contents of a packet.
3702 ///
3703 /// This can be called multiple times if there are multiple payloads in
3704 /// sequence of the response.
3705 ///
3706 /// You can check if the packet is empty using [`Packet::is_empty`].
3707 pub fn decode_any_response<'de, R>(&'de self) -> Result<R>
3708 where
3709 R: DecodeBody<'de>,
3710 {
3711 self.raw.decode()
3712 }
3713}
3714
3715impl<T> Packet<T>
3716where
3717 T: api::Broadcast,
3718{
3719 /// Decode the primary event related to a broadcast.
3720 pub fn decode_event<'de>(&'de self) -> Result<T::Event<'de>>
3721 where
3722 T: api::BroadcastWithEvent,
3723 {
3724 self.decode_event_any()
3725 }
3726
3727 /// Decode any event related to a broadcast.
3728 pub fn decode_event_any<'de, E>(&'de self) -> Result<E>
3729 where
3730 E: Event<Broadcast = T> + DecodeBody<'de>,
3731 {
3732 self.raw.decode()
3733 }
3734}
3735
3736impl<T> Clone for Packet<T> {
3737 #[inline]
3738 fn clone(&self) -> Self {
3739 Self {
3740 raw: self.raw.clone(),
3741 _marker: PhantomData,
3742 }
3743 }
3744}
3745
3746impl<T> fmt::Debug for Packet<T> {
3747 #[inline]
3748 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3749 f.debug_struct("Packet")
3750 .field("type", &any::type_name::<T>())
3751 .field("remaining", &self.remaining())
3752 .finish()
3753 }
3754}
3755
3756/// A handle to the WebSocket service.
3757pub struct Handle<H>
3758where
3759 H: WebImpl,
3760{
3761 shared: Weak<Shared<H>>,
3762}
3763
3764impl<H> Handle<H>
3765where
3766 H: WebImpl,
3767{
3768 /// The [`Format`] currently in effect for message bodies.
3769 ///
3770 /// Before the connection has been opened this is the format which was
3771 /// requested through [`ServiceBuilder::format`]. Once the connection is
3772 /// open it is the format the server actually agreed to, which can differ if
3773 /// the server does not support what was asked for.
3774 pub fn format(&self) -> Format {
3775 let Some(shared) = self.shared.upgrade() else {
3776 return Format::DEFAULT;
3777 };
3778
3779 shared.format.get()
3780 }
3781
3782 /// The [`Mode`] the socket is currently running in.
3783 ///
3784 /// Before the connection has been opened this is the mode which was
3785 /// requested through [`ServiceBuilder::mode`]. Once the connection is open
3786 /// it is the mode which was settled on, which can differ if the server
3787 /// could not carry the negotiated format that way.
3788 pub fn mode(&self) -> Mode {
3789 let Some(shared) = self.shared.upgrade() else {
3790 return Mode::DEFAULT;
3791 };
3792
3793 shared.mode.get()
3794 }
3795
3796 /// The generation of the connection.
3797 ///
3798 /// This counts the number of times the connection has opened, that is
3799 /// reached [`State::Open`]. It is `0` until the connection first opens,
3800 /// and goes up by one every time it opens again after having been closed.
3801 /// A handle which is not connected to a service, such as
3802 /// [`Handle::default`], always reports `0`.
3803 ///
3804 /// The generation is updated before any listener hears of the connection
3805 /// opening, so it can be read from inside [`Handle::on_state_change`] and
3806 /// [`Handle::on_open`] callbacks.
3807 ///
3808 /// Comparing the generation to the one data was last fetched in tells
3809 /// whether it has to be fetched again, see [refetching after a
3810 /// reconnect](crate::web#refetching-after-a-reconnect).
3811 ///
3812 /// # Examples
3813 ///
3814 /// ```
3815 /// use musli_web::web03::prelude::*;
3816 ///
3817 /// let handle = ws::Handle::default();
3818 /// assert_eq!(handle.generation(), 0);
3819 /// ```
3820 pub fn generation(&self) -> u64 {
3821 let Some(shared) = self.shared.upgrade() else {
3822 return 0;
3823 };
3824
3825 shared.g.generation.get()
3826 }
3827
3828 /// Set up a new logical channel to the WebSocket server.
3829 ///
3830 /// A channel can be uniquely identified on the client and server side over
3831 /// a single connection. This means that if you send a request over a
3832 /// channel using [`Channel::request`], the server can access the
3833 /// [`ChannelId`] to determine which channel sent the request and the client
3834 /// has the ability to correlate any responses sent by the server by
3835 /// inspecting [`Packet::channel`] or [`RawPacket::channel`].
3836 ///
3837 /// A broadcast caused by a request over a channel can name that channel as
3838 /// its origin with [`ws::Server::broadcast_from`], which every listener
3839 /// sees as [`Packet::channel`]. This lets a page skip the echo of its own
3840 /// changes with [`Packet::is_from`], without carrying the [`ChannelId`] in
3841 /// the body of the event.
3842 ///
3843 /// The maximum number of channels is implementation defined, but expect it
3844 /// to be relatively low like `65535` (non-zero 16 bits) to reduce payload
3845 /// sizes. Failure to allocate a channel is reported as
3846 /// [`ChannelEvent::Failed`].
3847 ///
3848 /// [`ws::Server::broadcast_from`]: https://docs.rs/musli-web/latest/musli_web/ws/struct.Server.html#method.broadcast_from
3849 ///
3850 /// # Reconnecting
3851 ///
3852 /// The server forgets a channel when the connection it was opened over
3853 /// closes. The [`Channel`] returned by [`ChannelBuilder::open`] takes care
3854 /// of this: it opens itself again every time the connection reopens, so a
3855 /// component holds the same [`Channel`] for its whole life. Every time it
3856 /// opens the callback set with [`ChannelBuilder::on_change`] is told with
3857 /// [`ChannelEvent::Opened`], which is when to send the requests the
3858 /// component needs the channel for. It gets a new id each time, and
3859 /// [`ChannelEvent::Closed`] is reported when it is lost.
3860 ///
3861 /// While the channel is not open [`Channel::is_open`] returns `false`, and
3862 /// requests over it fail with [`Error::is_not_connected`] without being
3863 /// sent, so there is no need to check before sending. A request which
3864 /// should go out as soon as the channel is back can be queued with
3865 /// [`Channel::when_open`].
3866 ///
3867 /// # Examples
3868 ///
3869 /// ```
3870 /// # extern crate yew023 as yew;
3871 /// use yew::prelude::*;
3872 /// use musli_web::web03::prelude::*;
3873 ///
3874 /// enum Msg {
3875 /// Channel(ws::ChannelEvent),
3876 /// }
3877 ///
3878 /// #[derive(Properties, PartialEq)]
3879 /// struct Props {
3880 /// ws: ws::Handle,
3881 /// }
3882 ///
3883 /// struct App {
3884 /// channel: ws::Channel,
3885 /// }
3886 ///
3887 /// impl App {
3888 /// fn fetch(&mut self, ctx: &Context<Self>) {
3889 /// // Send the requests this component shows the result of over
3890 /// // `self.channel`.
3891 /// }
3892 /// }
3893 ///
3894 /// impl Component for App {
3895 /// type Message = Msg;
3896 /// type Properties = Props;
3897 ///
3898 /// fn create(ctx: &Context<Self>) -> Self {
3899 /// // The channel is opened once the connection is, and opened
3900 /// // again every time the connection reopens.
3901 /// let channel = ctx.props().ws
3902 /// .channel()
3903 /// .on_change(ctx.link().callback(Msg::Channel))
3904 /// .open();
3905 ///
3906 /// Self { channel }
3907 /// }
3908 ///
3909 /// fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
3910 /// match msg {
3911 /// Msg::Channel(ws::ChannelEvent::Opened(..)) => {
3912 /// // Opened for the first time or after a reconnect, so
3913 /// // fetch everything again.
3914 /// self.fetch(ctx);
3915 /// true
3916 /// }
3917 /// Msg::Channel(ws::ChannelEvent::Failed(error)) => {
3918 /// tracing::error!("Could not open channel: {error}");
3919 /// true
3920 /// }
3921 /// Msg::Channel(..) => true,
3922 /// }
3923 /// }
3924 ///
3925 /// fn view(&self, ctx: &Context<Self>) -> Html {
3926 /// html! {
3927 /// <div>
3928 /// <h1>{"WebSocket Example"}</h1>
3929 /// <p>{format!("Channel open: {}", self.channel.is_open())}</p>
3930 /// </div>
3931 /// }
3932 /// }
3933 /// }
3934 /// ```
3935 pub fn channel(&self) -> ChannelBuilder<'_, H, EmptyCallback> {
3936 ChannelBuilder {
3937 shared: &self.shared,
3938 callback: EmptyCallback,
3939 }
3940 }
3941
3942 /// Send a request of type `T`.
3943 ///
3944 /// Returns a handle for the request.
3945 ///
3946 /// If the handle is dropped, the request is cancelled.
3947 ///
3948 /// # Examples
3949 ///
3950 /// ```
3951 /// # extern crate yew023 as yew;
3952 /// use yew::prelude::*;
3953 /// use musli_web::web03::prelude::*;
3954 ///
3955 /// mod api {
3956 /// use musli::{Decode, Encode};
3957 /// use musli_web::api;
3958 ///
3959 /// #[derive(Encode, Decode)]
3960 /// pub struct HelloRequest<'de> {
3961 /// pub message: &'de str,
3962 /// }
3963 ///
3964 /// #[derive(Encode, Decode)]
3965 /// pub struct HelloResponse<'de> {
3966 /// pub message: &'de str,
3967 /// }
3968 ///
3969 /// api::define! {
3970 /// pub type Hello;
3971 ///
3972 /// impl Endpoint for Hello {
3973 /// impl<'de> Request for HelloRequest<'de>;
3974 /// type Response<'de> = HelloResponse<'de>;
3975 /// }
3976 /// }
3977 /// }
3978 ///
3979 /// enum Msg {
3980 /// OnHello(Result<ws::Packet<api::Hello>, ws::Error>),
3981 /// }
3982 ///
3983 /// #[derive(Properties, PartialEq)]
3984 /// struct Props {
3985 /// ws: ws::Handle,
3986 /// }
3987 ///
3988 /// struct App {
3989 /// message: String,
3990 /// _hello: ws::Request,
3991 /// }
3992 ///
3993 /// impl Component for App {
3994 /// type Message = Msg;
3995 /// type Properties = Props;
3996 ///
3997 /// fn create(ctx: &Context<Self>) -> Self {
3998 /// let hello = ctx.props().ws
3999 /// .request()
4000 /// .body(api::HelloRequest { message: "Hello!"})
4001 /// .on_packet(ctx.link().callback(Msg::OnHello))
4002 /// .send();
4003 ///
4004 /// Self {
4005 /// message: String::from("No Message :("),
4006 /// _hello: hello,
4007 /// }
4008 /// }
4009 ///
4010 /// fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
4011 /// match msg {
4012 /// Msg::OnHello(Err(error)) => {
4013 /// tracing::error!("Request error: {:?}", error);
4014 /// false
4015 /// }
4016 /// Msg::OnHello(Ok(packet)) => {
4017 /// if let Ok(response) = packet.decode() {
4018 /// self.message = response.message.to_owned();
4019 /// }
4020 ///
4021 /// true
4022 /// }
4023 /// }
4024 /// }
4025 ///
4026 /// fn view(&self, ctx: &Context<Self>) -> Html {
4027 /// html! {
4028 /// <div>
4029 /// <h1>{"WebSocket Example"}</h1>
4030 /// <p>{format!("Message: {}", self.message)}</p>
4031 /// </div>
4032 /// }
4033 /// }
4034 /// }
4035 /// ```
4036 pub fn request(&self) -> RequestBuilder<'_, H, EmptyBody, EmptyCallback> {
4037 RequestBuilder {
4038 shared: &self.shared,
4039 channel: Some(ChannelId::NONE),
4040 body: EmptyBody,
4041 callback: EmptyCallback,
4042 }
4043 }
4044
4045 /// Send a request over the default channel and get a future which
4046 /// resolves to its decoded response.
4047 ///
4048 /// This is an asynchronous alternative to building the request with
4049 /// [`request`] and handling the response with a callback. The returned
4050 /// [`Call`] resolves to the response of the endpoint `body` is a request
4051 /// for, decoded as an owned value. Dropping it before it resolves cancels
4052 /// the request.
4053 ///
4054 /// The request is sent straight away, the future only waits for the
4055 /// response. It has to be driven by something which polls futures, such as
4056 /// [`Scope::send_future`] in yew or `wasm_bindgen_futures::spawn_local`.
4057 /// Note that a future handed to either of them is not dropped when the
4058 /// component goes away, so the request is not cancelled with it.
4059 ///
4060 /// The response type of the endpoint must be owned, which is what the
4061 /// `for<'de> ...: DecodeBody<'de>` bound says: it decodes from any buffer,
4062 /// so it holds on to none of them. A response declared as `type
4063 /// Response<'de> = User;` qualifies, one which borrows such as `type
4064 /// Response<'de> = UserRef<'de>;` does not compile here. For those use
4065 /// [`RequestBuilder::on_packet`] and decode from the [`Packet`] instead.
4066 ///
4067 /// See [`Channel::call`] to call over a channel.
4068 ///
4069 /// [`request`]: Self::request
4070 /// [`Scope::send_future`]: <https://docs.rs/yew/latest/yew/html/struct.Scope.html#method.send_future>
4071 ///
4072 /// # Errors
4073 ///
4074 /// The future resolves to an [`Error`] which tells what went wrong, the
4075 /// same as the one a callback would get:
4076 ///
4077 /// * [`Error::is_not_connected`] if the request was never sent since the
4078 /// connection is not open.
4079 /// * [`Error::is_connection_lost`] if the connection closed before the
4080 /// response arrived.
4081 /// * [`Error::is_cancelled`] if another request took its place.
4082 /// * [`Error::as_server_error`] if the handler on the server failed, and
4083 /// [`Error::server_error`] to decode the typed error it failed with.
4084 /// * [`Error::is_decode`] if the response does not decode as the response
4085 /// type, for instance since the server is built from another version of
4086 /// the API.
4087 ///
4088 /// # Examples
4089 ///
4090 /// ```
4091 /// # extern crate yew023 as yew;
4092 /// use yew::prelude::*;
4093 /// use musli_web::web03::prelude::*;
4094 ///
4095 /// mod api {
4096 /// use musli::{Decode, Encode};
4097 /// use musli_web::api;
4098 ///
4099 /// #[derive(Encode, Decode)]
4100 /// pub struct UserRequest<'de> {
4101 /// pub name: &'de str,
4102 /// }
4103 ///
4104 /// #[derive(Encode, Decode)]
4105 /// pub struct UserResponse {
4106 /// pub name: String,
4107 /// pub age: u32,
4108 /// }
4109 ///
4110 /// api::define! {
4111 /// pub type User;
4112 ///
4113 /// impl Endpoint for User {
4114 /// impl<'de> Request for UserRequest<'de>;
4115 /// type Response<'de> = UserResponse;
4116 /// }
4117 /// }
4118 /// }
4119 ///
4120 /// enum Msg {
4121 /// Age(Result<u32, ws::Error>),
4122 /// }
4123 ///
4124 /// #[derive(Properties, PartialEq)]
4125 /// struct Props {
4126 /// ws: ws::Handle,
4127 /// }
4128 ///
4129 /// struct Age {
4130 /// age: Option<u32>,
4131 /// }
4132 ///
4133 /// impl Component for Age {
4134 /// type Message = Msg;
4135 /// type Properties = Props;
4136 ///
4137 /// fn create(ctx: &Context<Self>) -> Self {
4138 /// let call = ctx.props().ws.call(api::UserRequest { name: "Alice" });
4139 ///
4140 /// ctx.link().send_future(async move {
4141 /// Msg::Age(call.await.map(|user| user.age))
4142 /// });
4143 ///
4144 /// Self { age: None }
4145 /// }
4146 ///
4147 /// fn update(&mut self, _: &Context<Self>, msg: Self::Message) -> bool {
4148 /// match msg {
4149 /// Msg::Age(Ok(age)) => {
4150 /// self.age = Some(age);
4151 /// true
4152 /// }
4153 /// Msg::Age(Err(error)) => {
4154 /// tracing::error!("Failed to get age: {error}");
4155 /// false
4156 /// }
4157 /// }
4158 /// }
4159 ///
4160 /// fn view(&self, _: &Context<Self>) -> Html {
4161 /// html! {
4162 /// if let Some(age) = self.age {
4163 /// <p>{format!("Alice is {age}")}</p>
4164 /// }
4165 /// }
4166 /// }
4167 /// }
4168 /// ```
4169 pub fn call<B>(&self, body: B) -> Call<<B::Endpoint as api::Endpoint>::Response<'static>>
4170 where
4171 B: api::Request,
4172 for<'de> <B::Endpoint as api::Endpoint>::Response<'static>: DecodeBody<'de>,
4173 {
4174 Call::send(self.request().body(body))
4175 }
4176
4177 /// Listen for broadcasts of type `T`.
4178 ///
4179 /// Returns a handle for the listener that will cancel the listener if
4180 /// dropped.
4181 ///
4182 /// # Examples
4183 ///
4184 /// ```
4185 /// # extern crate yew023 as yew;
4186 /// use yew::prelude::*;
4187 /// use musli_web::web03::prelude::*;
4188 ///
4189 /// mod api {
4190 /// use musli::{Decode, Encode};
4191 /// use musli_web::api;
4192 ///
4193 /// #[derive(Encode, Decode)]
4194 /// pub struct TickEvent<'de> {
4195 /// pub message: &'de str,
4196 /// pub tick: u32,
4197 /// }
4198 ///
4199 /// api::define! {
4200 /// pub type Tick;
4201 ///
4202 /// impl Broadcast for Tick {
4203 /// impl<'de> Event for TickEvent<'de>;
4204 /// }
4205 /// }
4206 /// }
4207 ///
4208 /// enum Msg {
4209 /// Tick(Result<ws::Packet<api::Tick>, ws::Error>),
4210 /// }
4211 ///
4212 /// #[derive(Properties, PartialEq)]
4213 /// struct Props {
4214 /// ws: ws::Handle,
4215 /// }
4216 ///
4217 /// struct App {
4218 /// tick: u32,
4219 /// _listen: ws::Listener,
4220 /// }
4221 ///
4222 /// impl Component for App {
4223 /// type Message = Msg;
4224 /// type Properties = Props;
4225 ///
4226 /// fn create(ctx: &Context<Self>) -> Self {
4227 /// let listen = ctx.props().ws.on_broadcast(ctx.link().callback(Msg::Tick));
4228 ///
4229 /// Self {
4230 /// tick: 0,
4231 /// _listen: listen,
4232 /// }
4233 /// }
4234 ///
4235 /// fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
4236 /// match msg {
4237 /// Msg::Tick(Err(error)) => {
4238 /// tracing::error!("Tick error: {error}");
4239 /// false
4240 /// }
4241 /// Msg::Tick(Ok(packet)) => {
4242 /// if let Ok(tick) = packet.decode_event() {
4243 /// self.tick = tick.tick;
4244 /// }
4245 ///
4246 /// true
4247 /// }
4248 /// }
4249 /// }
4250 ///
4251 /// fn view(&self, ctx: &Context<Self>) -> Html {
4252 /// html! {
4253 /// <div>
4254 /// <h1>{"WebSocket Example"}</h1>
4255 /// <p>{format!("Tick: {}", self.tick)}</p>
4256 /// </div>
4257 /// }
4258 /// }
4259 /// }
4260 /// ```
4261 pub fn on_broadcast<T>(&self, callback: impl Callback<Result<Packet<T>>>) -> Listener
4262 where
4263 T: api::Broadcast,
4264 {
4265 self.on_raw_broadcast::<T>(move |result| match result {
4266 Ok(packet) => callback.call(Ok(Packet::new(packet))),
4267 Err(error) => callback.call(Err(error)),
4268 })
4269 }
4270
4271 /// Listen for broadcasts of type `T`.
4272 ///
4273 /// Returns a handle for the listener that will cancel the listener if
4274 /// dropped.
4275 ///
4276 /// # Examples
4277 ///
4278 /// ```
4279 /// # extern crate yew023 as yew;
4280 /// use yew::prelude::*;
4281 /// use musli_web::web03::prelude::*;
4282 ///
4283 /// mod api {
4284 /// use musli::{Decode, Encode};
4285 /// use musli_web::api;
4286 ///
4287 /// #[derive(Encode, Decode)]
4288 /// pub struct TickEvent<'de> {
4289 /// pub message: &'de str,
4290 /// pub tick: u32,
4291 /// }
4292 ///
4293 /// api::define! {
4294 /// pub type Tick;
4295 ///
4296 /// impl Broadcast for Tick {
4297 /// impl<'de> Event for TickEvent<'de>;
4298 /// }
4299 /// }
4300 /// }
4301 ///
4302 /// enum Msg {
4303 /// Tick(Result<ws::RawPacket, ws::Error>),
4304 /// }
4305 ///
4306 /// #[derive(Properties, PartialEq)]
4307 /// struct Props {
4308 /// ws: ws::Handle,
4309 /// }
4310 ///
4311 /// struct App {
4312 /// tick: u32,
4313 /// _listen: ws::Listener,
4314 /// }
4315 ///
4316 /// impl Component for App {
4317 /// type Message = Msg;
4318 /// type Properties = Props;
4319 ///
4320 /// fn create(ctx: &Context<Self>) -> Self {
4321 /// let link = ctx.link().clone();
4322 /// let listen = ctx.props().ws.on_raw_broadcast::<api::Tick>(ctx.link().callback(Msg::Tick));
4323 ///
4324 /// Self {
4325 /// tick: 0,
4326 /// _listen: listen,
4327 /// }
4328 /// }
4329 ///
4330 /// fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
4331 /// match msg {
4332 /// Msg::Tick(Err(error)) => {
4333 /// tracing::error!("Tick error: {error}");
4334 /// false
4335 /// }
4336 /// Msg::Tick(Ok(packet)) => {
4337 /// if let Ok(tick) = packet.decode::<api::TickEvent>() {
4338 /// self.tick = tick.tick;
4339 /// }
4340 ///
4341 /// true
4342 /// }
4343 /// }
4344 /// }
4345 ///
4346 /// fn view(&self, ctx: &Context<Self>) -> Html {
4347 /// html! {
4348 /// <div>
4349 /// <h1>{"WebSocket Example"}</h1>
4350 /// <p>{format!("Tick: {}", self.tick)}</p>
4351 /// </div>
4352 /// }
4353 /// }
4354 /// }
4355 /// ```
4356 pub fn on_raw_broadcast<T>(&self, callback: impl Callback<Result<RawPacket>>) -> Listener
4357 where
4358 T: api::Broadcast,
4359 {
4360 let Some(shared) = self.shared.upgrade() else {
4361 return Listener::empty_with_kind(T::ID);
4362 };
4363
4364 let index = {
4365 let mut broadcasts = shared.g.broadcasts.borrow_mut();
4366 let slots = broadcasts.entry(T::ID).or_default();
4367 slots.insert(Rc::new(callback))
4368 };
4369
4370 Listener {
4371 kind: Some(T::ID),
4372 index,
4373 g: Rc::downgrade(&shared.g),
4374 }
4375 }
4376
4377 /// Listen for state changes to the underlying connection.
4378 ///
4379 /// This indicates when the connection is open and ready to receive requests
4380 /// through [`State::Open`], or if it's closed and requests will be queued
4381 /// through [`State::Closed`]. A server which states another application
4382 /// version is reported through [`State::Incompatible`], see
4383 /// [`ServiceBuilder::app_version`].
4384 ///
4385 /// Note that if you are connecting through a proxy the reported updates
4386 /// might be volatile. It is always best to send a message over the
4387 /// connection on the server side that once received allows the client to
4388 /// know that it is connected.
4389 ///
4390 /// Dropping the returned handle will cancel the listener.
4391 ///
4392 /// # Examples
4393 ///
4394 /// ```
4395 /// # extern crate yew023 as yew;
4396 /// use yew::prelude::*;
4397 /// use musli_web::web03::prelude::*;
4398 ///
4399 /// enum Msg {
4400 /// StateChange(ws::State),
4401 /// }
4402 ///
4403 /// #[derive(Properties, PartialEq)]
4404 /// struct Props {
4405 /// ws: ws::Handle,
4406 /// }
4407 ///
4408 /// struct App {
4409 /// state: ws::State,
4410 /// _listen: ws::StateListener,
4411 /// }
4412 ///
4413 /// impl Component for App {
4414 /// type Message = Msg;
4415 /// type Properties = Props;
4416 ///
4417 /// fn create(ctx: &Context<Self>) -> Self {
4418 /// let link = ctx.link().clone();
4419 ///
4420 /// let (state, listen) = ctx.props().ws.on_state_change(move |state| {
4421 /// link.send_message(Msg::StateChange(state));
4422 /// });
4423 ///
4424 /// Self {
4425 /// state,
4426 /// _listen: listen,
4427 /// }
4428 /// }
4429 ///
4430 /// fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
4431 /// match msg {
4432 /// Msg::StateChange(state) => {
4433 /// self.state = state;
4434 /// true
4435 /// }
4436 /// }
4437 /// }
4438 ///
4439 /// fn view(&self, ctx: &Context<Self>) -> Html {
4440 /// html! {
4441 /// <div>
4442 /// <h1>{"WebSocket Example"}</h1>
4443 /// <p>{format!("State: {:?}", self.state)}</p>
4444 /// </div>
4445 /// }
4446 /// }
4447 /// }
4448 pub fn on_state_change(&self, callback: impl Callback<State>) -> (State, StateListener) {
4449 let Some(shared) = self.shared.upgrade() else {
4450 return (
4451 State::Closed,
4452 StateListener {
4453 index: 0,
4454 g: Weak::new(),
4455 },
4456 );
4457 };
4458
4459 let (state, index) = {
4460 let index = shared
4461 .g
4462 .state_listeners
4463 .borrow_mut()
4464 .insert(Rc::new(callback));
4465 (shared.state.get(), index)
4466 };
4467
4468 let listener = StateListener {
4469 index,
4470 g: Rc::downgrade(&shared.g),
4471 };
4472
4473 (state, listener)
4474 }
4475
4476 /// Listen for the connection opening.
4477 ///
4478 /// The callback is called with the new [generation] every time the
4479 /// connection reaches [`State::Open`], which is the point at which a
4480 /// component should fetch what it shows again. It is not called when the
4481 /// connection closes.
4482 ///
4483 /// The returned generation is `Some` with the current generation if the
4484 /// connection is open already, in which case the callback is not called
4485 /// for it, and `None` if it is not. A component can fetch straight away
4486 /// if it is `Some`, and otherwise wait for the callback.
4487 ///
4488 /// Dropping the returned handle will cancel the listener.
4489 ///
4490 /// [generation]: Self::generation
4491 ///
4492 /// # Examples
4493 ///
4494 /// ```
4495 /// # extern crate yew023 as yew;
4496 /// use yew::prelude::*;
4497 /// use musli_web::web03::prelude::*;
4498 ///
4499 /// enum Msg {
4500 /// Opened(u64),
4501 /// }
4502 ///
4503 /// #[derive(Properties, PartialEq)]
4504 /// struct Props {
4505 /// ws: ws::Handle,
4506 /// }
4507 ///
4508 /// struct App {
4509 /// /// The generation the data was last fetched in.
4510 /// fetched: Option<u64>,
4511 /// _open: ws::StateListener,
4512 /// }
4513 ///
4514 /// impl App {
4515 /// fn fetch(&mut self, ctx: &Context<Self>, generation: u64) {
4516 /// if self.fetched == Some(generation) {
4517 /// return;
4518 /// }
4519 ///
4520 /// self.fetched = Some(generation);
4521 /// // Send the requests this component shows the result of.
4522 /// }
4523 /// }
4524 ///
4525 /// impl Component for App {
4526 /// type Message = Msg;
4527 /// type Properties = Props;
4528 ///
4529 /// fn create(ctx: &Context<Self>) -> Self {
4530 /// let (generation, open) = ctx.props().ws.on_open(ctx.link().callback(Msg::Opened));
4531 ///
4532 /// let mut this = Self {
4533 /// fetched: None,
4534 /// _open: open,
4535 /// };
4536 ///
4537 /// if let Some(generation) = generation {
4538 /// this.fetch(ctx, generation);
4539 /// }
4540 ///
4541 /// this
4542 /// }
4543 ///
4544 /// fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
4545 /// match msg {
4546 /// Msg::Opened(generation) => {
4547 /// self.fetch(ctx, generation);
4548 /// true
4549 /// }
4550 /// }
4551 /// }
4552 ///
4553 /// fn view(&self, _: &Context<Self>) -> Html {
4554 /// html! {}
4555 /// }
4556 /// }
4557 /// ```
4558 pub fn on_open(&self, callback: impl Callback<u64>) -> (Option<u64>, StateListener) {
4559 let Some(shared) = self.shared.upgrade() else {
4560 return (None, StateListener::new());
4561 };
4562
4563 let callback = {
4564 // NB: Weak, since the listener is owned by the generic state.
4565 let g = Rc::downgrade(&shared.g);
4566
4567 move |state: State| {
4568 if state != State::Open {
4569 return;
4570 }
4571
4572 if let Some(g) = g.upgrade() {
4573 callback.call(g.generation.get());
4574 }
4575 }
4576 };
4577
4578 let index = shared
4579 .g
4580 .state_listeners
4581 .borrow_mut()
4582 .insert(Rc::new(callback));
4583
4584 let generation = shared
4585 .state
4586 .get()
4587 .is_open()
4588 .then(|| shared.g.generation.get());
4589
4590 let listener = StateListener {
4591 index,
4592 g: Rc::downgrade(&shared.g),
4593 };
4594
4595 (generation, listener)
4596 }
4597}
4598
4599impl<H> Clone for Handle<H>
4600where
4601 H: WebImpl,
4602{
4603 #[inline]
4604 fn clone(&self) -> Self {
4605 Self {
4606 shared: self.shared.clone(),
4607 }
4608 }
4609}
4610
4611/// Construct a default handle that is not connected to a backend.
4612///
4613/// # Examples
4614///
4615/// ```
4616/// use musli_web::web03::prelude::*;
4617///
4618/// let handle = ws::Handle::default();
4619/// ```
4620impl<H> Default for Handle<H>
4621where
4622 H: WebImpl,
4623{
4624 #[inline]
4625 fn default() -> Self {
4626 Self {
4627 shared: Weak::new(),
4628 }
4629 }
4630}
4631
4632impl<H> PartialEq for Handle<H>
4633where
4634 H: WebImpl,
4635{
4636 #[inline]
4637 fn eq(&self, other: &Self) -> bool {
4638 Weak::ptr_eq(&self.shared, &other.shared)
4639 }
4640}
4641
4642impl<H> fmt::Debug for Handle<H>
4643where
4644 H: WebImpl,
4645{
4646 #[inline]
4647 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4648 let mut f = f.debug_struct("Handle");
4649
4650 if let Some(shared) = self.shared.upgrade() {
4651 f.field("connect", &shared.connect);
4652 f.field("state", &shared.state.get());
4653 }
4654
4655 f.finish()
4656 }
4657}
4658
4659/// What happened to a [`Channel`], as told to the callback set with
4660/// [`ChannelBuilder::on_change`].
4661#[derive(Debug)]
4662#[non_exhaustive]
4663pub enum ChannelEvent {
4664 /// The channel was opened over the current connection with the given
4665 /// [`ChannelId`], and requests can be sent over it.
4666 ///
4667 /// This is reported every time the channel opens, including after a
4668 /// reconnect, at which point the server has forgotten everything which
4669 /// was sent over the channel before and the id is likely different.
4670 Opened(ChannelId),
4671 /// The channel was lost along with the connection it was opened over.
4672 ///
4673 /// It opens again by itself once the connection is back.
4674 Closed,
4675 /// The server could not open the channel, for example since it has run
4676 /// out of channel ids or its handler refused it.
4677 ///
4678 /// The channel stays closed until the connection reopens, at which point
4679 /// it is tried again.
4680 Failed(Error),
4681}
4682
4683/// A logical channel to a WebSocket server, which opens itself again every
4684/// time the connection does.
4685///
4686/// A channel is set up with [`Handle::channel`], see it for more details.
4687///
4688/// Cloning a channel is cheap and every clone refers to the same channel. The
4689/// channel is closed and stops reopening once the last clone has been dropped.
4690///
4691/// While the channel is not open, such as before the server has answered or
4692/// while the connection is down, [`Channel::is_open`] returns `false`,
4693/// [`Channel::id`] returns [`ChannelId::NONE`], and requests sent over it fail
4694/// with [`Error::is_not_connected`] without being sent.
4695pub struct Channel<H>
4696where
4697 H: WebImpl,
4698{
4699 inner: Rc<ChannelInner<H>>,
4700}
4701
4702/// The state of a channel shared by its clones.
4703struct ChannelInner<H>
4704where
4705 H: WebImpl,
4706{
4707 handle: Handle<H>,
4708 /// The id of the channel while it is open, or [`ChannelId::NONE`].
4709 id: Cell<ChannelId>,
4710 /// The generation of the connection the channel was last opened over.
4711 generation: Cell<u64>,
4712 on_change: Box<dyn Callback<ChannelEvent>>,
4713 /// Waiting to be called once the channel is open, see
4714 /// [`Channel::when_open`].
4715 queue: RefCell<VecDeque<Box<dyn FnOnce(&Channel<H>)>>>,
4716 /// The pending request to open the channel.
4717 connect: RefCell<Request>,
4718 state: RefCell<StateListener>,
4719}
4720
4721impl<H> ChannelInner<H>
4722where
4723 H: WebImpl,
4724{
4725 fn is_open(&self) -> bool {
4726 let id = self.id.get();
4727
4728 if id == ChannelId::NONE {
4729 return false;
4730 }
4731
4732 let Some(shared) = self.handle.shared.upgrade() else {
4733 return false;
4734 };
4735
4736 shared.state.get().is_open() && shared.g.generation.get() == self.generation.get()
4737 }
4738
4739 fn on_state(self: &Rc<Self>, state: State) {
4740 match state {
4741 State::Open => {
4742 self.connect();
4743 }
4744 State::Closed | State::Incompatible { .. } => {
4745 // NB: Dropped outside of the borrow, since dropping a request
4746 // touches the shared request table.
4747 let connect = mem::take(&mut *self.connect.borrow_mut());
4748 drop(connect);
4749
4750 if self.id.replace(ChannelId::NONE) != ChannelId::NONE {
4751 self.on_change.call(ChannelEvent::Closed);
4752 }
4753 }
4754 }
4755 }
4756
4757 fn connect(self: &Rc<Self>) {
4758 let request = connect_channel(&self.handle.shared, {
4759 let inner = Rc::downgrade(self);
4760
4761 move |result| {
4762 if let Some(inner) = inner.upgrade() {
4763 inner.connected(result);
4764 }
4765 }
4766 });
4767
4768 let old = mem::replace(&mut *self.connect.borrow_mut(), request);
4769 drop(old);
4770 }
4771
4772 fn connected(self: &Rc<Self>, result: Result<ChannelId>) {
4773 let id = match result {
4774 Ok(id) => id,
4775 Err(error) => {
4776 // NB: These mean that the connection went away, in which case
4777 // the channel is opened again once it is back.
4778 if !(error.is_connection_lost() || error.is_not_connected() || error.is_cancelled())
4779 {
4780 self.on_change.call(ChannelEvent::Failed(error));
4781 }
4782
4783 return;
4784 }
4785 };
4786
4787 self.id.set(id);
4788 self.generation.set(self.handle.generation());
4789
4790 let channel = Channel {
4791 inner: self.clone(),
4792 };
4793
4794 // NB: Each call can close the channel, in which case what remains in
4795 // the queue waits for it to open again.
4796 while channel.is_open() {
4797 let Some(f) = self.queue.borrow_mut().pop_front() else {
4798 break;
4799 };
4800
4801 f(&channel);
4802 }
4803
4804 if channel.is_open() && self.id.get() == id {
4805 self.on_change.call(ChannelEvent::Opened(id));
4806 }
4807 }
4808}
4809
4810impl<H> Drop for ChannelInner<H>
4811where
4812 H: WebImpl,
4813{
4814 #[inline]
4815 fn drop(&mut self) {
4816 // NB: A channel from an earlier connection was dropped by the server
4817 // along with that connection, and its id may since have been handed
4818 // to another channel over the current one.
4819 if self.is_open() {
4820 if let Some(shared) = self.handle.shared.upgrade() {
4821 shared.remove_channel(self.id.get());
4822 }
4823 }
4824 }
4825}
4826
4827impl<H> Channel<H>
4828where
4829 H: WebImpl,
4830{
4831 /// The id the server gave the channel.
4832 ///
4833 /// This is [`ChannelId::NONE`] while the channel is not [open], and
4834 /// usually changes each time the channel opens again after a reconnect.
4835 ///
4836 /// [open]: Self::is_open
4837 #[inline]
4838 pub fn id(&self) -> ChannelId {
4839 if self.inner.is_open() {
4840 self.inner.id.get()
4841 } else {
4842 ChannelId::NONE
4843 }
4844 }
4845
4846 /// The [generation] of the connection the channel was last opened over.
4847 ///
4848 /// This is `0` until the channel first opens.
4849 ///
4850 /// [generation]: Handle::generation
4851 #[inline]
4852 pub fn generation(&self) -> u64 {
4853 self.inner.generation.get()
4854 }
4855
4856 /// Whether requests can be sent over this channel.
4857 ///
4858 /// This is `true` from when the server has opened the channel until the
4859 /// connection it was opened over closes. It is `false` for a [default]
4860 /// channel, and while the channel is being opened again after a
4861 /// reconnect. A request over a channel which is not open fails with
4862 /// [`Error::is_not_connected`] without being sent.
4863 ///
4864 /// [default]: Channel::default
4865 ///
4866 /// # Examples
4867 ///
4868 /// ```
4869 /// use musli_web::web03::prelude::*;
4870 ///
4871 /// let channel = ws::Channel::default();
4872 /// assert!(!channel.is_open());
4873 /// ```
4874 #[inline]
4875 pub fn is_open(&self) -> bool {
4876 self.inner.is_open()
4877 }
4878
4879 /// Get the handle this channel was opened through.
4880 ///
4881 /// A handle sheds the channel information and allows for setting up things
4882 /// like broadcast listener.
4883 #[inline]
4884 pub fn handle(&self) -> &Handle<H> {
4885 &self.inner.handle
4886 }
4887
4888 /// Send a request of type `T` over the channel.
4889 ///
4890 /// Returns a handle for the request. If the handle is dropped, the request
4891 /// is cancelled.
4892 ///
4893 /// If the channel is not [open], the request fails with
4894 /// [`Error::is_not_connected`] without being sent. Send it once the
4895 /// channel has opened instead, as reported by [`ChannelEvent::Opened`], or
4896 /// use [`Channel::when_open`]. See [`ChannelBuilder::on_change`] for an
4897 /// example.
4898 ///
4899 /// [open]: Self::is_open
4900 pub fn request(&self) -> RequestBuilder<'_, H, EmptyBody, EmptyCallback> {
4901 RequestBuilder {
4902 shared: &self.inner.handle.shared,
4903 channel: self.inner.is_open().then(|| self.inner.id.get()),
4904 body: EmptyBody,
4905 callback: EmptyCallback,
4906 }
4907 }
4908
4909 /// Send a request over the current channel and get a future which
4910 /// resolves to its decoded response.
4911 ///
4912 /// This is the same as [`Handle::call`] except that the request is sent
4913 /// over this channel, see it for how the response is decoded and the
4914 /// errors it resolves to. Like [`request`], it fails with
4915 /// [`Error::is_not_connected`] without being sent if the channel is not
4916 /// open.
4917 ///
4918 /// [`request`]: Self::request
4919 pub fn call<B>(&self, body: B) -> Call<<B::Endpoint as api::Endpoint>::Response<'static>>
4920 where
4921 B: api::Request,
4922 for<'de> <B::Endpoint as api::Endpoint>::Response<'static>: DecodeBody<'de>,
4923 {
4924 Call::send(self.request().body(body))
4925 }
4926
4927 /// Call `f` with the channel once it is open.
4928 ///
4929 /// If the channel is open, `f` is called straight away. Otherwise it is
4930 /// queued and called as soon as the channel opens, before
4931 /// [`ChannelEvent::Opened`] is reported. Queued calls are made in the order
4932 /// they were queued, and are dropped without being called if the channel
4933 /// is dropped first.
4934 ///
4935 /// This suits a request made in response to the user, which should be
4936 /// sent even if the connection happens to be down at the time.
4937 ///
4938 /// # Examples
4939 ///
4940 /// ```
4941 /// use std::cell::Cell;
4942 /// use std::rc::Rc;
4943 ///
4944 /// use musli_web::web03::prelude::*;
4945 ///
4946 /// let channel = ws::Channel::default();
4947 /// let called = Rc::new(Cell::new(false));
4948 ///
4949 /// channel.when_open({
4950 /// let called = called.clone();
4951 /// move |_: &ws::Channel| called.set(true)
4952 /// });
4953 ///
4954 /// // A default channel never opens.
4955 /// assert!(!called.get());
4956 /// ```
4957 pub fn when_open(&self, f: impl FnOnce(&Channel<H>) + 'static) {
4958 if self.inner.is_open() {
4959 f(self);
4960 return;
4961 }
4962
4963 self.inner.queue.borrow_mut().push_back(Box::new(f));
4964 }
4965}
4966
4967impl<H> Clone for Channel<H>
4968where
4969 H: WebImpl,
4970{
4971 #[inline]
4972 fn clone(&self) -> Self {
4973 Self {
4974 inner: self.inner.clone(),
4975 }
4976 }
4977}
4978
4979/// Construct a channel which is not connected to a service and never opens.
4980///
4981/// This is a placeholder for a component which does not have a channel yet.
4982///
4983/// # Examples
4984///
4985/// ```
4986/// use musli_web::web03::prelude::*;
4987///
4988/// let channel = ws::Channel::default();
4989/// assert!(!channel.is_open());
4990/// assert_eq!(channel.id(), ws::ChannelId::NONE);
4991/// ```
4992impl<H> Default for Channel<H>
4993where
4994 H: WebImpl,
4995{
4996 #[inline]
4997 fn default() -> Self {
4998 Self {
4999 inner: Rc::new(ChannelInner {
5000 handle: Handle::default(),
5001 id: Cell::new(ChannelId::NONE),
5002 generation: Cell::new(0),
5003 on_change: Box::new(EmptyCallback),
5004 queue: RefCell::new(VecDeque::new()),
5005 connect: RefCell::new(Request::new()),
5006 state: RefCell::new(StateListener::new()),
5007 }),
5008 }
5009 }
5010}
5011
5012/// Channels are equal if they are opened through the same service and are
5013/// currently open with the same id, or are both closed.
5014///
5015/// So a property holding a channel changes when the channel opens, closes or
5016/// opens again with a new id.
5017impl<H> PartialEq for Channel<H>
5018where
5019 H: WebImpl,
5020{
5021 #[inline]
5022 fn eq(&self, other: &Self) -> bool {
5023 Weak::ptr_eq(&self.inner.handle.shared, &other.inner.handle.shared)
5024 && self.id() == other.id()
5025 }
5026}
5027
5028impl<H> fmt::Debug for Channel<H>
5029where
5030 H: WebImpl,
5031{
5032 #[inline]
5033 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5034 let mut f = f.debug_struct("Channel");
5035
5036 if let Some(shared) = self.inner.handle.shared.upgrade() {
5037 f.field("connect", &shared.connect);
5038 f.field("state", &shared.state.get());
5039 }
5040
5041 f.field("id", &self.id());
5042 f.field("generation", &self.generation());
5043 f.finish()
5044 }
5045}
5046
5047struct Pending<C>
5048where
5049 C: ?Sized,
5050{
5051 id: MessageId,
5052 serial: u32,
5053 callback: C,
5054}
5055
5056impl<C> fmt::Debug for Pending<C> {
5057 #[inline]
5058 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5059 f.debug_struct("Pending")
5060 .field("serial", &self.serial)
5061 .field("id", &self.id)
5062 .finish_non_exhaustive()
5063 }
5064}
5065
5066struct ForcePrefix<'a>(&'a str, char);
5067
5068impl fmt::Display for ForcePrefix<'_> {
5069 #[inline]
5070 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5071 let Self(string, prefix) = *self;
5072 prefix.fmt(f)?;
5073 string.trim_start_matches(prefix).fmt(f)?;
5074 Ok(())
5075 }
5076}