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
9use core::cell::{Cell, RefCell};
10use core::marker::PhantomData;
11use core::mem;
12use core::ops::Deref;
13use core::ptr::NonNull;
14use core::{any, fmt};
15use std::collections::VecDeque;
16
17use alloc::boxed::Box;
18use alloc::format;
19use alloc::rc::Rc;
20use alloc::rc::Weak;
21use alloc::string::{String, ToString};
22use alloc::vec::Vec;
23
24use std::collections::hash_map::{Entry, HashMap};
25
26use slab::Slab;
27
28use crate::api::{self, ChannelId, DecodeBody, Event, Format, MessageId};
29use crate::format;
30
31const MAX_CAPACITY: usize = 1048576;
32
33/// An empty request body.
34#[non_exhaustive]
35pub struct EmptyBody;
36
37/// An empty callback.
38#[non_exhaustive]
39pub struct EmptyCallback;
40
41trait RequestCallback {
42 fn error(&self, error: Error);
43
44 fn as_request(&self) -> Option<&(dyn Callback<Result<RawPacket, Error>> + 'static)> {
45 None
46 }
47
48 fn as_channel(&self) -> Option<&(dyn Fn(Result<ChannelId, Error>) + 'static)> {
49 None
50 }
51
52 /// The format this is a negotiation for, if it is one.
53 fn as_negotiate(&self) -> Option<Format> {
54 None
55 }
56}
57
58/// The pending callback used for a format negotiation issued by the service
59/// itself.
60struct NegotiateCallback(Format);
61
62impl RequestCallback for NegotiateCallback {
63 #[inline]
64 fn as_negotiate(&self) -> Option<Format> {
65 Some(self.0)
66 }
67
68 #[inline]
69 fn error(&self, error: Error) {
70 tracing::debug!("Format negotiation failed: {error}");
71 }
72}
73
74/// Slab of state listeners.
75type StateListeners = Slab<Rc<dyn Callback<State>>>;
76/// Slab of broadcast listeners.
77type Broadcasts = HashMap<MessageId, Slab<Rc<dyn Callback<Result<RawPacket>>>>>;
78/// Queue of recycled buffers.
79type Buffers = VecDeque<Box<BufData>>;
80/// Pending requests.
81type Requests = HashMap<u32, Box<Pending<dyn RequestCallback>>>;
82
83/// Location information for WebSocket implementation.
84#[doc(hidden)]
85pub struct Location {
86 pub(crate) protocol: String,
87 pub(crate) host: String,
88 pub(crate) port: String,
89}
90
91pub(crate) mod sealed_socket {
92 pub trait Sealed {}
93}
94
95pub(crate) trait SocketImpl
96where
97 Self: Sized + self::sealed_socket::Sealed,
98{
99 #[doc(hidden)]
100 type Handles;
101
102 #[doc(hidden)]
103 fn new(url: &str, handles: &Self::Handles) -> Result<Self, Error>;
104
105 #[doc(hidden)]
106 fn send(&self, data: &[u8]) -> Result<(), Error>;
107
108 #[doc(hidden)]
109 fn close(self) -> Result<(), Error>;
110}
111
112pub(crate) mod sealed_window {
113 pub trait Sealed {}
114}
115
116pub(crate) trait WindowImpl
117where
118 Self: Sized + self::sealed_window::Sealed,
119{
120 #[doc(hidden)]
121 type Timeout;
122
123 #[doc(hidden)]
124 type OnBeforeUnload;
125
126 #[doc(hidden)]
127 fn new() -> Result<Self, Error>;
128
129 #[doc(hidden)]
130 fn location(&self) -> Result<Location, Error>;
131
132 #[doc(hidden)]
133 fn set_timeout(
134 &self,
135 millis: u32,
136 callback: impl Fn() + 'static,
137 ) -> Result<Self::Timeout, Error>;
138
139 #[doc(hidden)]
140 fn onbeforeunload(&self, callback: impl Fn() + 'static) -> Result<Self::OnBeforeUnload, Error>;
141}
142
143pub(crate) mod sealed_web {
144 pub trait Sealed {}
145}
146
147/// Central trait for web integration.
148///
149/// Since web integration is currently unstable, this requires multiple
150/// different implementations, each time an ecosystem breaking change is
151/// released.
152///
153/// The crate in focus here is `web-sys`, and the corresponding modules provide
154/// integrations:
155///
156/// * [web03] for `web-sys` `0.3.x`.
157///
158/// [web03]: crate::web03
159pub trait WebImpl
160where
161 Self: 'static + Copy + Sized + self::sealed_web::Sealed,
162{
163 #[doc(hidden)]
164 #[allow(private_bounds)]
165 type Window: WindowImpl;
166
167 #[doc(hidden)]
168 type Handles;
169
170 #[doc(hidden)]
171 #[allow(private_bounds)]
172 type Socket: SocketImpl<Handles = Self::Handles>;
173
174 #[doc(hidden)]
175 #[allow(private_interfaces)]
176 fn handles(shared: &Weak<Shared<Self>>) -> Self::Handles;
177
178 #[doc(hidden)]
179 fn random(range: u32) -> u32;
180}
181
182/// Construct a new [`ServiceBuilder`] associated with the given [`Connect`]
183/// strategy.
184pub fn connect<H>(connect: Connect) -> ServiceBuilder<H, EmptyCallback>
185where
186 H: WebImpl,
187{
188 ServiceBuilder {
189 connect,
190 on_error: EmptyCallback,
191 close_before_unload: false,
192 format: Format::DEFAULT,
193 _marker: PhantomData,
194 }
195}
196
197/// The state of the connection.
198///
199/// A listener for state changes can be set up through for example
200/// [`Handle::on_state_change`].
201#[derive(Debug, PartialEq, Eq, Clone, Copy)]
202#[non_exhaustive]
203pub enum State {
204 /// The connection is open.
205 Open,
206 /// The connection is closed.
207 Closed,
208}
209
210impl State {
211 /// Check if the state is open.
212 pub fn is_open(&self) -> bool {
213 matches!(self, Self::Open)
214 }
215}
216
217/// Error type for the WebSocket service.
218#[derive(Debug)]
219pub struct Error {
220 kind: ErrorKind,
221}
222
223impl Error {
224 #[inline]
225 fn new(kind: ErrorKind) -> Self {
226 Self { kind }
227 }
228
229 /// Check if the error is caused by an empty packet.
230 ///
231 /// # Examples
232 ///
233 /// ```
234 /// use musli_web::web::{Error, RawPacket};
235 ///
236 /// let packet = RawPacket::empty();
237 /// let e = packet.decode::<u32>().unwrap_err();
238 ///
239 /// assert!(e.is_empty_packet());
240 /// ```
241 #[inline]
242 pub fn is_empty_packet(&self) -> bool {
243 matches!(self.kind, ErrorKind::EmptyPacket)
244 }
245
246 /// Format a WebSocket error consisting of a message.
247 #[inline]
248 pub fn message(message: impl fmt::Display) -> Self {
249 Self::new(ErrorKind::Message(message.to_string()))
250 }
251
252 #[inline]
253 pub(crate) fn decode_response_header(error: format::Error) -> Self {
254 Self::new(ErrorKind::DecodeResponseHeader(error))
255 }
256
257 #[inline]
258 pub(crate) fn decode_error_message(error: format::Error) -> Self {
259 Self::new(ErrorKind::DecodeErrorMessage(error))
260 }
261
262 #[inline]
263 pub(crate) fn decode_packet(error: format::Error) -> Self {
264 Self::new(ErrorKind::DecodePacket(error))
265 }
266
267 #[inline]
268 pub(crate) fn encoding_header(error: format::Error) -> Self {
269 Self::new(ErrorKind::EncodingHeader(error))
270 }
271
272 #[inline]
273 pub(crate) fn encoding_body(error: format::Error) -> Self {
274 Self::new(ErrorKind::EncodingBody(error))
275 }
276}
277
278#[derive(Debug)]
279enum ErrorKind {
280 EmptyPacket,
281 Message(String),
282 DecodeResponseHeader(format::Error),
283 DecodeErrorMessage(format::Error),
284 DecodePacket(format::Error),
285 EncodingHeader(format::Error),
286 EncodingBody(format::Error),
287}
288
289impl fmt::Display for Error {
290 #[inline]
291 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
292 match &self.kind {
293 ErrorKind::EmptyPacket => write!(f, "Packet is empty"),
294 ErrorKind::Message(message) => write!(f, "{message}"),
295 ErrorKind::DecodeResponseHeader(..) => {
296 write!(f, "Encoding error when decoding response header")
297 }
298 ErrorKind::DecodeErrorMessage(..) => {
299 write!(f, "Encoding error when decoding error response")
300 }
301 ErrorKind::DecodePacket(..) => write!(f, "Encoding error when decoding packet"),
302 ErrorKind::EncodingHeader(..) => write!(f, "Encoding error when encoding header"),
303 ErrorKind::EncodingBody(..) => write!(f, "Encoding error when encoding body"),
304 }
305 }
306}
307
308impl core::error::Error for Error {
309 #[inline]
310 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
311 match &self.kind {
312 ErrorKind::DecodeResponseHeader(error) => Some(error),
313 ErrorKind::DecodeErrorMessage(error) => Some(error),
314 ErrorKind::DecodePacket(error) => Some(error),
315 ErrorKind::EncodingHeader(error) => Some(error),
316 ErrorKind::EncodingBody(error) => Some(error),
317 _ => None,
318 }
319 }
320}
321
322#[cfg(feature = "wasm_bindgen02")]
323impl From<wasm_bindgen02::JsValue> for Error {
324 #[inline]
325 fn from(error: wasm_bindgen02::JsValue) -> Self {
326 Self::new(ErrorKind::Message(format!("{error:?}")))
327 }
328}
329
330type Result<T, E = Error> = core::result::Result<T, E>;
331
332const INITIAL_TIMEOUT: u32 = 250;
333const MAX_TIMEOUT: u32 = 4000;
334
335/// How to connect to the WebSocket.
336#[derive(Debug)]
337enum ConnectKind {
338 Location { path: String },
339 Url { url: String },
340}
341
342/// A specification for how to connect a WebSocket.
343pub struct Connect {
344 kind: ConnectKind,
345}
346
347impl Connect {
348 /// Connect to the same location with a custom path.
349 ///
350 /// Note that any number of `/` prefixes are ignored, the canonical
351 /// representation always ignores them and the path is relative to the
352 /// current location.
353 #[inline]
354 pub fn location(path: impl AsRef<str>) -> Self {
355 Self {
356 kind: ConnectKind::Location {
357 path: String::from(path.as_ref()),
358 },
359 }
360 }
361
362 /// Connect to the specified URL.
363 #[inline]
364 pub fn url(url: String) -> Self {
365 Self {
366 kind: ConnectKind::Url { url },
367 }
368 }
369}
370
371impl fmt::Debug for Connect {
372 #[inline]
373 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
374 self.kind.fmt(f)
375 }
376}
377
378/// Generic but shared fields which do not depend on specialization over `H`.
379struct Generic {
380 state_listeners: RefCell<StateListeners>,
381 requests: RefCell<Requests>,
382 broadcasts: RefCell<Broadcasts>,
383 buffers: RefCell<Buffers>,
384}
385
386/// Shared implementation details for WebSocket implementations.
387pub(crate) struct Shared<H>
388where
389 H: WebImpl,
390{
391 connect: Connect,
392 state: Cell<State>,
393 should_be: Cell<State>,
394 connecting: Cell<bool>,
395 /// The format in effect, as agreed by the [negotiation protocol].
396 ///
397 /// [negotiation protocol]: crate::api#negotiating-the-format
398 format: Cell<Format>,
399 /// The format the user asked for, re-negotiated on every reconnect.
400 requested: Format,
401 handle: Handle<H>,
402 pub(crate) on_error: Box<dyn Callback<Error>>,
403 window: H::Window,
404 handles: H::Handles,
405 serial: Cell<u32>,
406 defer_broadcasts: RefCell<VecDeque<Weak<dyn Callback<Result<RawPacket>>>>>,
407 defer_state_listeners: RefCell<VecDeque<Weak<dyn Callback<State>>>>,
408 pub(crate) socket: RefCell<Option<H::Socket>>,
409 output: RefCell<Vec<u8>>,
410 current_timeout: Cell<u32>,
411 reconnect_timeout: RefCell<Option<<H::Window as WindowImpl>::Timeout>>,
412 _window_onbeforeunload: Option<<H::Window as WindowImpl>::OnBeforeUnload>,
413 g: Rc<Generic>,
414}
415
416impl<H> Drop for Shared<H>
417where
418 H: WebImpl,
419{
420 fn drop(&mut self) {
421 if let Some(s) = self.socket.take() {
422 tracing::debug!("Closing connection during drop");
423
424 if let Err(error) = s.close() {
425 self.on_error.call(error);
426 }
427 }
428
429 // We don't need to worry about mutable borrows here, since we only have
430 // weak references to Shared and by virtue of this being dropped they
431 // are all invalid.
432 let state_listeners = mem::take(&mut *self.g.state_listeners.borrow_mut());
433 let mut requests = self.g.requests.borrow_mut();
434
435 for (_, listener) in state_listeners {
436 listener.call(State::Closed);
437 }
438
439 for (_, p) in requests.drain() {
440 p.callback.error(Error::message("Websocket service closed"));
441 }
442 }
443}
444
445/// Builder of a service.
446pub struct ServiceBuilder<H, E>
447where
448 H: WebImpl,
449{
450 connect: Connect,
451 on_error: E,
452 close_before_unload: bool,
453 format: Format,
454 _marker: PhantomData<H>,
455}
456
457impl<H, E> ServiceBuilder<H, E>
458where
459 H: WebImpl,
460 E: Callback<Error>,
461{
462 /// Set the error handler to use for the service.
463 pub fn on_error<U>(self, on_error: U) -> ServiceBuilder<H, U>
464 where
465 U: Callback<Error>,
466 {
467 ServiceBuilder {
468 connect: self.connect,
469 on_error,
470 close_before_unload: self.close_before_unload,
471 format: self.format,
472 _marker: self._marker,
473 }
474 }
475
476 /// Set the [`Format`] to use for message bodies.
477 ///
478 /// The format is negotiated with the server once the connection is
479 /// established, see the [negotiation protocol]. If the server does not
480 /// support it the error is reported through [`ServiceBuilder::on_error`]
481 /// and the connection falls back to [`Format::DEFAULT`], which can be
482 /// observed through [`Handle::format`].
483 ///
484 /// Defaults to [`Format::DEFAULT`].
485 ///
486 /// [negotiation protocol]: crate::api#negotiating-the-format
487 #[inline]
488 pub fn format(mut self, format: Format) -> ServiceBuilder<H, E> {
489 self.format = format;
490 self
491 }
492
493 /// Install an event handler which will try to close the WebSocket
494 /// connection on page unload.
495 ///
496 /// This is not strictly necessary, but some browsers like Firefox might be
497 /// slow to recycle the connection unless it has been explicitly closed.
498 #[inline]
499 pub fn close_before_unload(mut self) -> Self {
500 self.close_before_unload = true;
501 self
502 }
503
504 /// Build a new service and open it.
505 ///
506 /// In order to close or open the service again, see [`Service::close`] and
507 /// [`Service::open`].
508 pub fn build(self) -> Service<H> {
509 let window = match H::Window::new() {
510 Ok(window) => window,
511 Err(error) => {
512 panic!("{error}")
513 }
514 };
515
516 let shared = Rc::<Shared<H>>::new_cyclic(move |shared| {
517 let window_onbeforeunload = if self.close_before_unload {
518 let shared = shared.clone();
519
520 let result = window.onbeforeunload(move || {
521 tracing::debug!("Trying to close WebSocket connection on page unload");
522
523 if let Some(shared) = shared.upgrade() {
524 shared.close();
525 }
526 });
527
528 match result {
529 Ok(onbeforeunload) => Some(onbeforeunload),
530 Err(error) => {
531 self.on_error.call(Error::message(format_args!(
532 "Failed to set onbeforeunload: {error}"
533 )));
534 None
535 }
536 }
537 } else {
538 None
539 };
540
541 Shared {
542 connect: self.connect,
543 state: Cell::new(State::Closed),
544 should_be: Cell::new(State::Closed),
545 connecting: Cell::new(false),
546 format: Cell::new(self.format),
547 requested: self.format,
548 handle: Handle {
549 shared: shared.clone(),
550 },
551 on_error: Box::new(self.on_error),
552 window,
553 handles: H::handles(shared),
554 serial: Cell::new(0),
555 defer_broadcasts: RefCell::new(VecDeque::new()),
556 defer_state_listeners: RefCell::new(VecDeque::new()),
557 socket: RefCell::new(None),
558 output: RefCell::new(Vec::new()),
559 current_timeout: Cell::new(INITIAL_TIMEOUT),
560 reconnect_timeout: RefCell::new(None),
561 _window_onbeforeunload: window_onbeforeunload,
562 g: Rc::new(Generic {
563 state_listeners: RefCell::new(Slab::new()),
564 broadcasts: RefCell::new(HashMap::new()),
565 requests: RefCell::new(Requests::new()),
566 buffers: RefCell::new(VecDeque::new()),
567 }),
568 }
569 });
570
571 let service = Service { shared };
572 service.open();
573 service
574 }
575}
576
577/// The service handle.
578///
579/// Once dropped this will cause the service to be disconnected and all requests
580/// to be cancelled.
581pub struct Service<H>
582where
583 H: WebImpl,
584{
585 shared: Rc<Shared<H>>,
586}
587
588impl<H> Service<H>
589where
590 H: WebImpl,
591{
592 /// Attempt to establish a WebSocket connection.
593 #[deprecated(since = "0.3.0", note = "Use `open` instead")]
594 pub fn connect(&self) {
595 self.shared.open()
596 }
597
598 /// Open the WebSocket connection.
599 ///
600 /// Calling this method indicates that the connection should stay open, and
601 /// the implementation will attempt to reconnect until [`Service::close`] is
602 /// called.
603 pub fn open(&self) {
604 self.shared.open()
605 }
606
607 /// Close the WebSocket connection.
608 ///
609 /// This is not strictly necessary, but some browsers like Firefox might be
610 /// slow to recycle the connection unless it has been explicitly closed.
611 ///
612 /// It is generally recommended that you call this when `beforeunload` on
613 /// window is fired.
614 pub fn close(&self) {
615 self.shared.close();
616 }
617
618 /// Return the handle to the service.
619 ///
620 /// A [`Handle`] instances does not force the underlying WebSocket to stay
621 /// connected, and is invalidated when [`Service`] is destructed.
622 pub fn handle(&self) -> &Handle<H> {
623 &self.shared.handle
624 }
625}
626
627impl<H> Clone for Service<H>
628where
629 H: WebImpl,
630{
631 #[inline]
632 fn clone(&self) -> Self {
633 Self {
634 shared: self.shared.clone(),
635 }
636 }
637}
638
639impl<H> Shared<H>
640where
641 H: WebImpl,
642{
643 /// Send a client message.
644 fn send_client_request<T>(&self, serial: u32, channel: ChannelId, body: &T) -> Result<()>
645 where
646 T: api::Request,
647 {
648 let Some(ref socket) = *self.socket.borrow() else {
649 return Err(Error::message("Socket is not connected"));
650 };
651
652 let format = self.format.get();
653
654 let header = api::RequestHeader {
655 serial,
656 id: <T::Endpoint as api::Endpoint>::ID.get(),
657 format: format.to_u8(),
658 channel,
659 };
660
661 let out = &mut *self.output.borrow_mut();
662
663 format::encode_envelope(&mut *out, &header).map_err(Error::encoding_header)?;
664 format
665 .encode(&mut *out, body)
666 .map_err(Error::encoding_body)?;
667
668 tracing::debug!(
669 header.serial,
670 ?header.id,
671 len = out.len(),
672 "Sending request"
673 );
674
675 socket.send(out.as_slice())?;
676
677 out.clear();
678 out.shrink_to(MAX_CAPACITY);
679 Ok(())
680 }
681
682 /// Send a client message.
683 fn send_connect(&self, serial: u32) -> Result<()> {
684 let Some(ref socket) = *self.socket.borrow() else {
685 return Err(Error::message("Socket is not connected"));
686 };
687
688 let header = api::RequestHeader {
689 serial,
690 id: MessageId::CONNECT.get(),
691 // NB: Carries no body.
692 format: 0,
693 channel: ChannelId::NONE,
694 };
695
696 let out = &mut *self.output.borrow_mut();
697
698 format::encode_envelope(&mut *out, &header).map_err(Error::encoding_header)?;
699
700 tracing::debug!(
701 header.serial,
702 ?header.id,
703 len = out.len(),
704 "Sending request"
705 );
706
707 socket.send(out.as_slice())?;
708
709 out.clear();
710 out.shrink_to(MAX_CAPACITY);
711 Ok(())
712 }
713
714 /// Ask the server to use the requested format for the rest of the
715 /// connection.
716 fn send_negotiate(self: &Rc<Self>) -> Result<()> {
717 let format = self.requested;
718 let serial = self.serial.get();
719 self.serial.set(serial.wrapping_add(1));
720
721 {
722 let Some(ref socket) = *self.socket.borrow() else {
723 return Err(Error::message("Socket is not connected"));
724 };
725
726 let header = api::RequestHeader {
727 serial,
728 id: MessageId::NEGOTIATE.get(),
729 format: format.to_u8(),
730 // NB: Carries no body.
731 channel: ChannelId::NONE,
732 };
733
734 let out = &mut *self.output.borrow_mut();
735
736 format::encode_envelope(&mut *out, &header).map_err(Error::encoding_header)?;
737
738 tracing::debug!(?format, "Requesting format");
739
740 socket.send(out.as_slice())?;
741
742 out.clear();
743 out.shrink_to(MAX_CAPACITY);
744 }
745
746 let pending = Pending {
747 id: MessageId::NEGOTIATE,
748 serial,
749 callback: NegotiateCallback(format),
750 };
751
752 let existing = self
753 .g
754 .requests
755 .borrow_mut()
756 .insert(serial, Box::new(pending));
757
758 if let Some(p) = existing {
759 p.callback.error(Error::message("Request cancelled"));
760 }
761
762 Ok(())
763 }
764
765 /// Send a disconnect.
766 fn remove_channel(&self, channel: ChannelId) {
767 if let Err(error) = self._send_disconnect(channel) {
768 self.on_error.call(error);
769 }
770 }
771
772 fn _send_disconnect(&self, channel: ChannelId) -> Result<()> {
773 let Some(ref socket) = *self.socket.borrow() else {
774 return Err(Error::message("Socket is not connected"));
775 };
776
777 let header = api::RequestHeader {
778 serial: 0,
779 id: MessageId::DISCONNECT.get(),
780 // NB: Carries no body.
781 format: 0,
782 channel,
783 };
784
785 let out = &mut *self.output.borrow_mut();
786
787 format::encode_envelope(&mut *out, &header).map_err(Error::encoding_header)?;
788
789 tracing::debug!(
790 header.serial,
791 ?header.id,
792 len = out.len(),
793 "Sending request"
794 );
795
796 socket.send(out.as_slice())?;
797
798 out.clear();
799 out.shrink_to(MAX_CAPACITY);
800 Ok(())
801 }
802
803 pub(crate) fn next_buffer(self: &Rc<Self>, needed: usize) -> Box<BufData> {
804 match self.g.buffers.borrow_mut().pop_front() {
805 Some(mut buf) => {
806 if buf.data.capacity() < needed {
807 buf.data.reserve(needed - buf.data.len());
808 }
809
810 buf
811 }
812 None => Box::new(BufData::with_capacity(Rc::downgrade(&self.g), needed)),
813 }
814 }
815
816 /// Resolve the format a message body is encoded with from its envelope.
817 fn body_format(header: &api::ResponseHeader) -> Result<Format> {
818 let Some(format) = Format::from_u8(header.format) else {
819 return Err(Error::message(format_args!(
820 "Server used unknown format id {} for a message body",
821 header.format
822 )));
823 };
824
825 Ok(format)
826 }
827
828 pub(crate) fn message(self: &Rc<Self>, buf: Box<BufData>) -> Result<()> {
829 // Wrap the buffer in a simple shared reference-counted container.
830 let buf = BufRc::new(buf);
831 let mut at = 0;
832
833 let header: api::ResponseHeader =
834 format::decode_envelope(&buf, &mut at).map_err(Error::decode_response_header)?;
835
836 if let Some(broadcast) = MessageId::new(header.broadcast) {
837 tracing::debug!(?header, "Got broadcast");
838
839 if broadcast == MessageId::SERVER_HELLO {
840 // NB: The connection is not reported as open until the format
841 // has been negotiated, so that server-initiated messages are
842 // never encoded with a format this client did not agree to.
843 tracing::debug!("Server hello, negotiating format");
844 self.send_negotiate()?;
845 return Ok(());
846 }
847
848 if !self.defer_broadcasts(broadcast) {
849 return Ok(());
850 };
851
852 if let Some(id) = MessageId::new(header.error) {
853 let error = match id {
854 MessageId::ERROR_MESSAGE => Self::body_format(&header)?
855 .decode(&buf, &mut at)
856 .map_err(Error::decode_error_message)?,
857 _ => api::ErrorMessage {
858 message: "Unsupported broadcast",
859 },
860 };
861
862 while let Some(callback) = self.defer_broadcasts.borrow_mut().pop_front() {
863 if let Some(callback) = callback.upgrade() {
864 callback.call(Err(Error::message(format_args!(
865 "Server error: {}",
866 error.message
867 ))));
868 }
869 }
870
871 return Ok(());
872 }
873
874 let format = Self::body_format(&header)?;
875
876 let packet = RawPacket {
877 buf: Some(buf.clone()),
878 at: Cell::new(at),
879 id: broadcast,
880 format,
881 channel: header.channel,
882 };
883
884 while let Some(callback) = self.defer_broadcasts.borrow_mut().pop_front() {
885 if let Some(callback) = callback.upgrade() {
886 callback.call(Ok(packet.clone()));
887 }
888 }
889 } else {
890 tracing::debug!(?header, "Got response");
891 let p = self.g.requests.borrow_mut().remove(&header.serial);
892
893 if let Some(p) = p {
894 if let Some(id) = MessageId::new(header.error) {
895 let error = match id {
896 MessageId::ERROR_MESSAGE => Self::body_format(&header)?
897 .decode(&buf, &mut at)
898 .map_err(Error::decode_error_message)?,
899 _ => api::ErrorMessage {
900 message: "Unsupported request",
901 },
902 };
903
904 if let Some(format) = p.callback.as_negotiate() {
905 // NB: The server cannot speak the requested format, so
906 // fall back to the default rather than leaving the
907 // connection unusable.
908 self.on_error.call(Error::message(format_args!(
909 "Server rejected format `{format}` ({}), falling back to `{}`",
910 error.message,
911 Format::DEFAULT
912 )));
913
914 self.format.set(Format::DEFAULT);
915 self.emit_state_change(State::Open);
916 return Ok(());
917 }
918
919 p.callback.error(Error::message(format_args!(
920 "Server error: {}",
921 error.message
922 )));
923 return Ok(());
924 }
925
926 match p.id {
927 MessageId::NEGOTIATE => {
928 let Some(requested) = p.callback.as_negotiate() else {
929 p.callback
930 .error(Error::message("Unexpected negotiate response"));
931 return Ok(());
932 };
933
934 // NB: Trust the format the server echoed back over the
935 // one that was asked for, so that a server which
936 // downgrades is honored.
937 let accepted = Format::from_u8(header.format).unwrap_or(requested);
938 tracing::debug!(?accepted, "Format negotiated");
939 self.format.set(accepted);
940 self.emit_state_change(State::Open);
941 return Ok(());
942 }
943 MessageId::CONNECT => {
944 let Some(callback) = &p.callback.as_channel() else {
945 p.callback
946 .error(Error::message("Unexpected channel response"));
947 return Ok(());
948 };
949
950 callback(Ok(header.channel));
951 return Ok(());
952 }
953 _ => {
954 let Some(callback) = p.callback.as_request() else {
955 p.callback
956 .error(Error::message("Unexpected channel response"));
957 return Ok(());
958 };
959
960 let format = Self::body_format(&header)?;
961
962 let packet = RawPacket {
963 id: p.id,
964 buf: Some(buf),
965 at: Cell::new(at),
966 format,
967 channel: header.channel,
968 };
969
970 callback.call(Ok(packet));
971 return Ok(());
972 }
973 }
974 }
975
976 // NB: This is normal, it simply indicates that the handler has been
977 // closed.
978 tracing::trace!(?header.serial, "Got message with unknown serial");
979 }
980
981 Ok(())
982 }
983
984 fn defer_broadcasts(self: &Rc<Self>, kind: MessageId) -> bool {
985 // Note: We need to defer this, since the outcome of calling
986 // the broadcast callback might be that the broadcast
987 // listener is modified, which could require mutable access
988 // to broadcasts.
989 let mut defer = self.defer_broadcasts.borrow_mut();
990
991 let broadcasts = self.g.broadcasts.borrow();
992
993 let Some(broadcasts) = broadcasts.get(&kind) else {
994 return false;
995 };
996
997 for (_, callback) in broadcasts.iter() {
998 defer.push_back(Rc::downgrade(callback));
999 }
1000
1001 !defer.is_empty()
1002 }
1003
1004 pub(crate) fn close_and_reconnect(self: &Rc<Self>) -> Result<(), Error> {
1005 tracing::debug!("Closing and reconnecting");
1006
1007 // We need a weak reference back to shared state to handle the timeout.
1008 let shared = Rc::downgrade(self);
1009
1010 if self.state.get() == State::Closed {
1011 let current_timeout = self.current_timeout.get();
1012
1013 if current_timeout < MAX_TIMEOUT {
1014 let fuzz = H::random(50);
1015
1016 self.current_timeout.set(
1017 current_timeout
1018 .saturating_mul(2)
1019 .saturating_add(fuzz)
1020 .min(MAX_TIMEOUT),
1021 );
1022 }
1023 } else {
1024 self.current_timeout.set(INITIAL_TIMEOUT);
1025 self.close_once();
1026 }
1027
1028 let timeout = self.current_timeout.get();
1029
1030 tracing::debug!(?timeout, "Setting");
1031
1032 let timeout = self.window.set_timeout(timeout, move || {
1033 if let Some(shared) = shared.upgrade() {
1034 Self::try_once(&shared);
1035 }
1036 })?;
1037
1038 drop(self.reconnect_timeout.borrow_mut().replace(timeout));
1039 self.connecting.set(false);
1040 Ok(())
1041 }
1042
1043 /// Close an pending requests with an error, since there is no chance they
1044 /// will be responded to any more.
1045 fn close_pending(self: &Rc<Self>) {
1046 loop {
1047 let Some(serial) = self.g.requests.borrow().keys().next().copied() else {
1048 break;
1049 };
1050
1051 let p = {
1052 let mut requests = self.g.requests.borrow_mut();
1053
1054 let Some(p) = requests.remove(&serial) else {
1055 break;
1056 };
1057
1058 p
1059 };
1060
1061 p.callback.error(Error::message("Connection closed"));
1062 }
1063 }
1064
1065 fn emit_state_change(&self, state: State) {
1066 if self.state.get() == state {
1067 return;
1068 }
1069
1070 self.state.set(state);
1071
1072 {
1073 // We need to collect callbacks to avoid the callback recursively
1074 // borrowing state listeners, which it would if it modifies any
1075 // existing state listeners.
1076 let mut defer = self.defer_state_listeners.borrow_mut();
1077
1078 for (_, callback) in self.g.state_listeners.borrow().iter() {
1079 defer.push_back(Rc::downgrade(callback));
1080 }
1081
1082 if defer.is_empty() {
1083 return;
1084 }
1085 }
1086
1087 while let Some(callback) = self.defer_state_listeners.borrow_mut().pop_front() {
1088 if let Some(callback) = callback.upgrade() {
1089 callback.call(state);
1090 }
1091 }
1092 }
1093
1094 #[inline]
1095 fn open(self: &Rc<Self>) {
1096 self.should_be.set(State::Open);
1097 self.try_once();
1098 }
1099
1100 #[inline]
1101 fn close(self: &Rc<Self>) {
1102 self.should_be.set(State::Closed);
1103 self.try_once();
1104 }
1105
1106 fn try_once(self: &Rc<Self>) {
1107 if self.should_be.get() == State::Open && !self.connecting.get() {
1108 tracing::debug!("Trying to open connection");
1109 self.connect_once();
1110 }
1111 }
1112
1113 fn close_once(self: &Rc<Self>) {
1114 self.connecting.set(false);
1115 self.emit_state_change(State::Closed);
1116
1117 if let Some(s) = self.socket.take() {
1118 tracing::debug!("Closing old socket once");
1119
1120 if let Err(error) = s.close() {
1121 self.on_error.call(error);
1122 }
1123 }
1124
1125 self.close_pending();
1126 }
1127
1128 fn connect_once(self: &Rc<Self>) {
1129 self.connecting.set(true);
1130
1131 let url = match &self.connect.kind {
1132 ConnectKind::Location { path } => {
1133 let location = match WindowImpl::location(&self.window) {
1134 Ok(location) => location,
1135 Err(e) => {
1136 self.on_error
1137 .call(Error::message(format_args!("Could not get location: {e}")));
1138 self.should_be.set(State::Closed);
1139 return;
1140 }
1141 };
1142
1143 let Location {
1144 protocol,
1145 host,
1146 port,
1147 } = location;
1148
1149 let protocol = match protocol.as_str() {
1150 "https:" => "wss:",
1151 "http:" => "ws:",
1152 other => {
1153 self.on_error.call(Error::message(format_args!(
1154 "Unsupported protocol `{other}` for same host connection"
1155 )));
1156
1157 self.should_be.set(State::Closed);
1158 return;
1159 }
1160 };
1161
1162 let path = ForcePrefix(path, '/');
1163 format!("{protocol}//{host}:{port}{path}")
1164 }
1165 ConnectKind::Url { url } => url.clone(),
1166 };
1167
1168 // We explicitly want to close and dispose of the old socket first.
1169 if let Some(s) = self.socket.borrow_mut().take() {
1170 tracing::debug!("Closing old socket");
1171
1172 if let Err(error) = s.close() {
1173 self.on_error.call(error);
1174 }
1175 }
1176
1177 let ws = match SocketImpl::new(&url, &self.handles) {
1178 Ok(ws) => ws,
1179 Err(error) => {
1180 self.on_error.call(error);
1181 self.should_be.set(State::Closed);
1182 return;
1183 }
1184 };
1185
1186 *self.socket.borrow_mut() = Some(ws);
1187 }
1188}
1189
1190/// Trait governing how callbacks are called.
1191pub trait Callback<I>
1192where
1193 Self: 'static,
1194{
1195 /// Call the callback.
1196 fn call(&self, input: I);
1197}
1198
1199impl<I> Callback<I> for EmptyCallback {
1200 #[inline]
1201 fn call(&self, _: I) {}
1202}
1203
1204impl<F, I> Callback<I> for F
1205where
1206 F: 'static + Fn(I),
1207{
1208 #[inline]
1209 fn call(&self, input: I) {
1210 self(input)
1211 }
1212}
1213
1214/// A request builder .
1215///
1216/// Associate the callback to be used by using either
1217/// [`RequestBuilder::on_packet`] or [`RequestBuilder::on_raw_packet`] depending
1218/// on your needs.
1219///
1220/// Send the request with [`RequestBuilder::send`].
1221pub struct ChannelBuilder<'a, H, C>
1222where
1223 H: WebImpl,
1224{
1225 shared: &'a Weak<Shared<H>>,
1226 callback: C,
1227}
1228
1229impl<'a, H, C> ChannelBuilder<'a, H, C>
1230where
1231 H: WebImpl,
1232{
1233 /// Define the handler to be called when a connection is established.
1234 ///
1235 /// # Examples
1236 ///
1237 /// ```
1238 /// # extern crate yew023 as yew;
1239 /// use yew::prelude::*;
1240 /// use musli_web::web03::prelude::*;
1241 ///
1242 /// mod api {
1243 /// use musli::{Decode, Encode};
1244 /// use musli_web::api;
1245 ///
1246 /// #[derive(Encode, Decode)]
1247 /// pub struct HelloRequest<'de> {
1248 /// pub message: &'de str,
1249 /// }
1250 ///
1251 /// #[derive(Encode, Decode)]
1252 /// pub struct HelloResponse<'de> {
1253 /// pub message: &'de str,
1254 /// }
1255 ///
1256 /// api::define! {
1257 /// pub type Hello;
1258 ///
1259 /// impl Endpoint for Hello {
1260 /// impl<'de> Request for HelloRequest<'de>;
1261 /// type Response<'de> = HelloResponse<'de>;
1262 /// }
1263 /// }
1264 /// }
1265 ///
1266 /// enum Msg {
1267 /// OnHello(Result<ws::Packet<api::Hello>, ws::Error>),
1268 /// OnChannel(Result<ws::Channel, ws::Error>),
1269 /// }
1270 ///
1271 /// #[derive(Properties, PartialEq)]
1272 /// struct Props {
1273 /// ws: ws::Handle,
1274 /// }
1275 ///
1276 /// struct App {
1277 /// message: String,
1278 /// _hello: ws::Request,
1279 /// _channel_open: ws::Request,
1280 /// channel: ws::Channel,
1281 /// }
1282 ///
1283 /// impl Component for App {
1284 /// type Message = Msg;
1285 /// type Properties = Props;
1286 ///
1287 /// fn create(ctx: &Context<Self>) -> Self {
1288 /// let link = ctx.link().clone();
1289 ///
1290 /// let _channel_open = ctx.props().ws
1291 /// .channel()
1292 /// .on_open(ctx.link().callback(Msg::OnChannel))
1293 /// .send();
1294 ///
1295 /// Self {
1296 /// message: String::from("No Message :("),
1297 /// _hello: ws::Request::default(),
1298 /// _channel_open: _channel_open,
1299 /// channel: ws::Channel::default(),
1300 /// }
1301 /// }
1302 ///
1303 /// fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
1304 /// match msg {
1305 /// Msg::OnHello(Err(error)) => {
1306 /// tracing::error!("Request error: {:?}", error);
1307 /// false
1308 /// }
1309 /// Msg::OnHello(Ok(packet)) => {
1310 /// if let Ok(response) = packet.decode() {
1311 /// self.message = response.message.to_owned();
1312 /// }
1313 ///
1314 /// true
1315 /// }
1316 /// Msg::OnChannel(result) => {
1317 /// if let Ok(channel) = result {
1318 /// self.channel = channel;
1319 /// }
1320 ///
1321 /// self._hello = self.channel
1322 /// .request()
1323 /// .body(api::HelloRequest { message: "Hello!"})
1324 /// .on_packet(ctx.link().callback(Msg::OnHello))
1325 /// .send();
1326 ///
1327 /// true
1328 /// }
1329 /// }
1330 /// }
1331 ///
1332 /// fn view(&self, ctx: &Context<Self>) -> Html {
1333 /// html! {
1334 /// <div>
1335 /// <h1>{"WebSocket Example"}</h1>
1336 /// <p>{format!("Message: {}", self.message)}</p>
1337 /// </div>
1338 /// }
1339 /// }
1340 /// }
1341 /// ```
1342 pub fn on_open<U>(self, callback: U) -> ChannelBuilder<'a, H, U>
1343 where
1344 U: Callback<Result<Channel<H>, Error>>,
1345 {
1346 ChannelBuilder {
1347 shared: self.shared,
1348 callback,
1349 }
1350 }
1351}
1352
1353impl<'a, H, C> ChannelBuilder<'a, H, C>
1354where
1355 C: Callback<Result<Channel<H>, Error>>,
1356 H: WebImpl,
1357{
1358 /// Send the connection request.
1359 pub fn send(self) -> Request {
1360 struct RequestCallbackImpl<C>(C);
1361
1362 impl<C> RequestCallback for RequestCallbackImpl<C>
1363 where
1364 C: Fn(Result<ChannelId, Error>) + 'static,
1365 {
1366 #[inline]
1367 fn as_channel(&self) -> Option<&(dyn Fn(Result<ChannelId, Error>) + 'static)> {
1368 Some(&self.0)
1369 }
1370
1371 #[inline]
1372 fn error(&self, error: Error) {
1373 (self.0)(Err(error));
1374 }
1375 }
1376
1377 let Some(shared) = self.shared.upgrade() else {
1378 self.callback
1379 .call(Err(Error::message("WebSocket service is down")));
1380 return Request::new();
1381 };
1382
1383 if shared.state.get() != State::Open {
1384 self.callback
1385 .call(Err(Error::message("WebSocket is not connected")));
1386 return Request::new();
1387 }
1388
1389 let serial = shared.serial.get();
1390
1391 if let Err(error) = shared.send_connect(serial) {
1392 shared.on_error.call(error);
1393 return Request::new();
1394 }
1395
1396 shared.serial.set(serial.wrapping_add(1));
1397
1398 let callback = {
1399 let shared = Rc::downgrade(&shared);
1400
1401 move |result| {
1402 let result = match result {
1403 Err(error) => Err(error),
1404 Ok(id) => Ok(Channel {
1405 handle: Handle {
1406 shared: shared.clone(),
1407 },
1408 id,
1409 }),
1410 };
1411
1412 self.callback.call(result)
1413 }
1414 };
1415
1416 let pending = Pending {
1417 id: MessageId::CONNECT,
1418 serial,
1419 callback: RequestCallbackImpl(callback),
1420 };
1421
1422 let existing = shared
1423 .g
1424 .requests
1425 .borrow_mut()
1426 .insert(serial, Box::new(pending));
1427
1428 if let Some(p) = existing {
1429 p.callback.error(Error::message("Request cancelled"));
1430 }
1431
1432 Request {
1433 serial,
1434 g: Rc::downgrade(&shared.g),
1435 }
1436 }
1437}
1438
1439/// A request builder .
1440///
1441/// Associate the callback to be used by using either
1442/// [`RequestBuilder::on_packet`] or [`RequestBuilder::on_raw_packet`] depending
1443/// on your needs.
1444///
1445/// Send the request with [`RequestBuilder::send`].
1446pub struct RequestBuilder<'a, H, B, C>
1447where
1448 H: WebImpl,
1449{
1450 shared: &'a Weak<Shared<H>>,
1451 channel: Option<ChannelId>,
1452 body: B,
1453 callback: C,
1454}
1455
1456impl<'a, H, B, C> RequestBuilder<'a, H, B, C>
1457where
1458 H: WebImpl,
1459{
1460 /// Set the body of the request.
1461 #[inline]
1462 pub fn body<U>(self, body: U) -> RequestBuilder<'a, H, U, C>
1463 where
1464 U: api::Request,
1465 {
1466 RequestBuilder {
1467 shared: self.shared,
1468 channel: self.channel,
1469 body,
1470 callback: self.callback,
1471 }
1472 }
1473
1474 /// Handle the response using the specified callback.
1475 ///
1476 /// # Examples
1477 ///
1478 /// ```
1479 /// # extern crate yew023 as yew;
1480 /// use yew::prelude::*;
1481 /// use musli_web::web03::prelude::*;
1482 ///
1483 /// mod api {
1484 /// use musli::{Decode, Encode};
1485 /// use musli_web::api;
1486 ///
1487 /// #[derive(Encode, Decode)]
1488 /// pub struct HelloRequest<'de> {
1489 /// pub message: &'de str,
1490 /// }
1491 ///
1492 /// #[derive(Encode, Decode)]
1493 /// pub struct HelloResponse<'de> {
1494 /// pub message: &'de str,
1495 /// }
1496 ///
1497 /// api::define! {
1498 /// pub type Hello;
1499 ///
1500 /// impl Endpoint for Hello {
1501 /// impl<'de> Request for HelloRequest<'de>;
1502 /// type Response<'de> = HelloResponse<'de>;
1503 /// }
1504 /// }
1505 /// }
1506 ///
1507 /// enum Msg {
1508 /// OnHello(Result<ws::Packet<api::Hello>, ws::Error>),
1509 /// }
1510 ///
1511 /// #[derive(Properties, PartialEq)]
1512 /// struct Props {
1513 /// ws: ws::Handle,
1514 /// }
1515 ///
1516 /// struct App {
1517 /// message: String,
1518 /// _hello: ws::Request,
1519 /// }
1520 ///
1521 /// impl Component for App {
1522 /// type Message = Msg;
1523 /// type Properties = Props;
1524 ///
1525 /// fn create(ctx: &Context<Self>) -> Self {
1526 /// let hello = ctx.props().ws
1527 /// .request()
1528 /// .body(api::HelloRequest { message: "Hello!"})
1529 /// .on_packet(ctx.link().callback(Msg::OnHello))
1530 /// .send();
1531 ///
1532 /// Self {
1533 /// message: String::from("No Message :("),
1534 /// _hello: hello,
1535 /// }
1536 /// }
1537 ///
1538 /// fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
1539 /// match msg {
1540 /// Msg::OnHello(Err(error)) => {
1541 /// tracing::error!("Request error: {:?}", error);
1542 /// false
1543 /// }
1544 /// Msg::OnHello(Ok(packet)) => {
1545 /// if let Ok(response) = packet.decode() {
1546 /// self.message = response.message.to_owned();
1547 /// }
1548 ///
1549 /// true
1550 /// }
1551 /// }
1552 /// }
1553 ///
1554 /// fn view(&self, ctx: &Context<Self>) -> Html {
1555 /// html! {
1556 /// <div>
1557 /// <h1>{"WebSocket Example"}</h1>
1558 /// <p>{format!("Message: {}", self.message)}</p>
1559 /// </div>
1560 /// }
1561 /// }
1562 /// }
1563 /// ```
1564 pub fn on_packet<E>(
1565 self,
1566 callback: impl Callback<Result<Packet<E>>>,
1567 ) -> RequestBuilder<'a, H, B, impl Callback<Result<RawPacket>>>
1568 where
1569 E: api::Endpoint,
1570 {
1571 self.on_raw_packet(move |result: Result<RawPacket>| match result {
1572 Ok(ok) => callback.call(Ok(Packet::new(ok))),
1573 Err(err) => callback.call(Err(err)),
1574 })
1575 }
1576
1577 /// Handle the response using the specified callback.
1578 ///
1579 /// # Examples
1580 ///
1581 /// ```
1582 /// # extern crate yew023 as yew;
1583 /// use yew::prelude::*;
1584 /// use musli_web::web03::prelude::*;
1585 ///
1586 /// mod api {
1587 /// use musli::{Decode, Encode};
1588 /// use musli_web::api;
1589 ///
1590 /// #[derive(Encode, Decode)]
1591 /// pub struct HelloRequest<'de> {
1592 /// pub message: &'de str,
1593 /// }
1594 ///
1595 /// #[derive(Encode, Decode)]
1596 /// pub struct HelloResponse<'de> {
1597 /// pub message: &'de str,
1598 /// }
1599 ///
1600 /// api::define! {
1601 /// pub type Hello;
1602 ///
1603 /// impl Endpoint for Hello {
1604 /// impl<'de> Request for HelloRequest<'de>;
1605 /// type Response<'de> = HelloResponse<'de>;
1606 /// }
1607 /// }
1608 /// }
1609 ///
1610 /// enum Msg {
1611 /// OnHello(Result<ws::RawPacket, ws::Error>),
1612 /// }
1613 ///
1614 /// #[derive(Properties, PartialEq)]
1615 /// struct Props {
1616 /// ws: ws::Handle,
1617 /// }
1618 ///
1619 /// struct App {
1620 /// message: String,
1621 /// _hello: ws::Request,
1622 /// }
1623 ///
1624 /// impl Component for App {
1625 /// type Message = Msg;
1626 /// type Properties = Props;
1627 ///
1628 /// fn create(ctx: &Context<Self>) -> Self {
1629 /// let link = ctx.link().clone();
1630 ///
1631 /// let hello = ctx.props().ws
1632 /// .request()
1633 /// .body(api::HelloRequest { message: "Hello!"})
1634 /// .on_raw_packet(move |packet| link.send_message(Msg::OnHello(packet)))
1635 /// .send();
1636 ///
1637 /// Self {
1638 /// message: String::from("No Message :("),
1639 /// _hello: hello,
1640 /// }
1641 /// }
1642 ///
1643 /// fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
1644 /// match msg {
1645 /// Msg::OnHello(Err(error)) => {
1646 /// tracing::error!("Request error: {:?}", error);
1647 /// false
1648 /// }
1649 /// Msg::OnHello(Ok(packet)) => {
1650 /// if let Ok(response) = packet.decode::<api::HelloResponse>() {
1651 /// self.message = response.message.to_owned();
1652 /// }
1653 ///
1654 /// true
1655 /// }
1656 /// }
1657 /// }
1658 ///
1659 /// fn view(&self, ctx: &Context<Self>) -> Html {
1660 /// html! {
1661 /// <div>
1662 /// <h1>{"WebSocket Example"}</h1>
1663 /// <p>{format!("Message: {}", self.message)}</p>
1664 /// </div>
1665 /// }
1666 /// }
1667 /// }
1668 /// ```
1669 pub fn on_raw_packet<U>(self, callback: U) -> RequestBuilder<'a, H, B, U>
1670 where
1671 U: Callback<Result<RawPacket, Error>>,
1672 {
1673 RequestBuilder {
1674 shared: self.shared,
1675 channel: self.channel,
1676 body: self.body,
1677 callback,
1678 }
1679 }
1680}
1681
1682impl<'a, H, B, C> RequestBuilder<'a, H, B, C>
1683where
1684 B: api::Request,
1685 C: Callback<Result<RawPacket>>,
1686 H: WebImpl,
1687{
1688 /// Send the request.
1689 ///
1690 /// This requires that a body has been set using [`RequestBuilder::body`].
1691 pub fn send(self) -> Request {
1692 struct RequestCallbackImpl<C>(C);
1693
1694 impl<C> RequestCallback for RequestCallbackImpl<C>
1695 where
1696 C: Callback<Result<RawPacket>>,
1697 {
1698 #[inline]
1699 fn as_request(&self) -> Option<&(dyn Callback<Result<RawPacket>> + 'static)> {
1700 Some(&self.0)
1701 }
1702
1703 #[inline]
1704 fn error(&self, error: Error) {
1705 self.0.call(Err(error));
1706 }
1707 }
1708
1709 let Some(shared) = self.shared.upgrade() else {
1710 self.callback
1711 .call(Err(Error::message("WebSocket service is down")));
1712 return Request::new();
1713 };
1714
1715 if shared.state.get() != State::Open {
1716 self.callback
1717 .call(Err(Error::message("WebSocket is not connected")));
1718 return Request::new();
1719 }
1720
1721 let Some(channel) = self.channel else {
1722 self.callback
1723 .call(Err(Error::message("WebSocket request over closed channel")));
1724 return Request::new();
1725 };
1726
1727 let serial = shared.serial.get();
1728
1729 if let Err(error) = shared.send_client_request(serial, channel, &self.body) {
1730 shared.on_error.call(error);
1731 return Request::new();
1732 }
1733
1734 shared.serial.set(serial.wrapping_add(1));
1735
1736 let pending = Pending {
1737 id: <B::Endpoint as api::Endpoint>::ID,
1738 serial,
1739 callback: RequestCallbackImpl(self.callback),
1740 };
1741
1742 let existing = shared
1743 .g
1744 .requests
1745 .borrow_mut()
1746 .insert(serial, Box::new(pending));
1747
1748 if let Some(p) = existing {
1749 p.callback.error(Error::message("Request cancelled"));
1750 }
1751
1752 Request {
1753 serial,
1754 g: Rc::downgrade(&shared.g),
1755 }
1756 }
1757}
1758
1759/// The handle for a pending request.
1760///
1761/// Dropping or [`clear()`] this handle will cancel the request.
1762///
1763/// [`clear()`]: Self::clear
1764pub struct Request {
1765 serial: u32,
1766 g: Weak<Generic>,
1767}
1768
1769impl Request {
1770 /// An empty request handler.
1771 ///
1772 /// # Examples
1773 ///
1774 /// ```
1775 /// use musli_web::web03::prelude::*;
1776 ///
1777 /// let mut request = ws::Request::new();
1778 /// assert_eq!(request.is_pending(), false);
1779 /// ```
1780 #[inline]
1781 pub const fn new() -> Self {
1782 Self {
1783 serial: 0,
1784 g: Weak::new(),
1785 }
1786 }
1787
1788 /// Clear the request handle without dropping it, cancelling any pending
1789 /// requests.
1790 pub fn clear(&mut self) {
1791 let removed = {
1792 let serial = mem::take(&mut self.serial);
1793
1794 let Some(g) = self.g.upgrade() else {
1795 return;
1796 };
1797
1798 self.g = Weak::new();
1799
1800 let Some(p) = g.requests.borrow_mut().remove(&serial) else {
1801 return;
1802 };
1803
1804 p
1805 };
1806
1807 drop(removed);
1808 }
1809
1810 /// Indicates if a request is pending.
1811 ///
1812 /// # Examples
1813 ///
1814 /// ```
1815 /// use musli_web::web03::prelude::*;
1816 ///
1817 /// let mut request = ws::Request::new();
1818 /// assert_eq!(request.is_pending(), false);
1819 /// ```
1820 #[inline]
1821 pub fn is_pending(&self) -> bool {
1822 let Some(g) = self.g.upgrade() else {
1823 return false;
1824 };
1825
1826 g.requests.borrow().contains_key(&self.serial)
1827 }
1828}
1829
1830impl Default for Request {
1831 #[inline]
1832 fn default() -> Self {
1833 Self::new()
1834 }
1835}
1836
1837impl Drop for Request {
1838 #[inline]
1839 fn drop(&mut self) {
1840 self.clear();
1841 }
1842}
1843
1844/// The handle for a pending request.
1845///
1846/// Dropping or calling [`clear()`] on this handle remove the listener.
1847///
1848/// [`clear()`]: Self::clear
1849pub struct Listener {
1850 kind: Option<MessageId>,
1851 index: usize,
1852 g: Weak<Generic>,
1853}
1854
1855impl Listener {
1856 /// Construct an empty listener.
1857 #[inline]
1858 pub const fn new() -> Self {
1859 Self {
1860 kind: None,
1861 index: 0,
1862 g: Weak::new(),
1863 }
1864 }
1865
1866 /// Build up an empty listener with the specified kind.
1867 #[inline]
1868 pub(crate) const fn empty_with_kind(kind: MessageId) -> Self {
1869 Self {
1870 kind: Some(kind),
1871 index: 0,
1872 g: Weak::new(),
1873 }
1874 }
1875
1876 /// Clear the listener without dropping it.
1877 ///
1878 /// This will remove the associated broadcast listener from being notified.
1879 pub fn clear(&mut self) {
1880 // Gather values here to drop them outside of the upgrade block.
1881 let removed;
1882 let removed_value;
1883
1884 {
1885 let Some(g) = self.g.upgrade() else {
1886 return;
1887 };
1888
1889 self.g = Weak::new();
1890 let index = mem::take(&mut self.index);
1891
1892 let Some(kind) = self.kind.take() else {
1893 return;
1894 };
1895
1896 let mut broadcasts = g.broadcasts.borrow_mut();
1897
1898 let Entry::Occupied(mut e) = broadcasts.entry(kind) else {
1899 return;
1900 };
1901
1902 removed = e.get_mut().try_remove(index);
1903
1904 if e.get().is_empty() {
1905 removed_value = Some(e.remove());
1906 } else {
1907 removed_value = None;
1908 }
1909 }
1910
1911 // Drop here, to avoid invoking any destructors which might borrow
1912 // shared mutably earlier.
1913 drop(removed);
1914 drop(removed_value);
1915 }
1916}
1917
1918impl Default for Listener {
1919 #[inline]
1920 fn default() -> Self {
1921 Self::new()
1922 }
1923}
1924
1925impl Drop for Listener {
1926 #[inline]
1927 fn drop(&mut self) {
1928 self.clear();
1929 }
1930}
1931
1932/// The handle for state change listening.
1933///
1934/// Dropping or calling [`clear()`] on this handle will remove the associated
1935/// callback from being notified.
1936///
1937/// [`clear()`]: Self::clear
1938pub struct StateListener {
1939 index: usize,
1940 g: Weak<Generic>,
1941}
1942
1943impl StateListener {
1944 /// Construct an empty state listener.
1945 #[inline]
1946 pub const fn new() -> Self {
1947 Self {
1948 index: 0,
1949 g: Weak::new(),
1950 }
1951 }
1952
1953 /// Clear the state listener without dropping it.
1954 ///
1955 /// This will remove the associated callback from being notified.
1956 pub fn clear(&mut self) {
1957 let removed = {
1958 let Some(g) = self.g.upgrade() else {
1959 return;
1960 };
1961
1962 self.g = Weak::new();
1963
1964 g.state_listeners.borrow_mut().try_remove(self.index)
1965 };
1966
1967 drop(removed);
1968 }
1969}
1970
1971impl Default for StateListener {
1972 #[inline]
1973 fn default() -> Self {
1974 Self::new()
1975 }
1976}
1977
1978impl Drop for StateListener {
1979 #[inline]
1980 fn drop(&mut self) {
1981 self.clear();
1982 }
1983}
1984
1985pub(crate) struct BufData {
1986 /// Buffer being used.
1987 pub(crate) data: Vec<u8>,
1988 /// Number of strong references to this buffer.
1989 strong: Cell<usize>,
1990 /// Reference to shared state where the buffer will be recycled to.
1991 g: Weak<Generic>,
1992}
1993
1994impl BufData {
1995 fn with_capacity(g: Weak<Generic>, capacity: usize) -> Self {
1996 Self {
1997 data: Vec::with_capacity(capacity),
1998 strong: Cell::new(0),
1999 g,
2000 }
2001 }
2002
2003 unsafe fn dec(ptr: NonNull<BufData>) {
2004 unsafe {
2005 let count = ptr.as_ref().strong.get().wrapping_sub(1);
2006 ptr.as_ref().strong.set(count);
2007
2008 if count > 0 {
2009 return;
2010 }
2011
2012 let mut buf = Box::from_raw(ptr.as_ptr());
2013
2014 // Try to recycle the buffer if shared is available, else let it be
2015 // dropped and free here.
2016 let Some(g) = buf.as_ref().g.upgrade() else {
2017 return;
2018 };
2019
2020 let mut buffers = g.buffers.borrow_mut();
2021
2022 // Set the length of the recycled buffer.
2023 buf.data.set_len(buf.data.len().min(MAX_CAPACITY));
2024
2025 // We size our buffers to some max capacity to avod overuse in case
2026 // we infrequently need to handle some massive message. If we don't
2027 // shrink the allocation, then memory use can run away over time.
2028 buf.data.shrink_to(MAX_CAPACITY);
2029
2030 buffers.push_back(buf);
2031 }
2032 }
2033
2034 unsafe fn inc(ptr: NonNull<BufData>) {
2035 unsafe {
2036 let count = ptr.as_ref().strong.get().wrapping_add(1);
2037
2038 if count == 0 {
2039 std::process::abort();
2040 }
2041
2042 ptr.as_ref().strong.set(count);
2043 }
2044 }
2045}
2046
2047/// A shared buffer of data that is recycled when dropped.
2048struct BufRc {
2049 data: NonNull<BufData>,
2050}
2051
2052impl BufRc {
2053 fn new(data: Box<BufData>) -> Self {
2054 let data = NonNull::from(Box::leak(data));
2055
2056 unsafe {
2057 BufData::inc(data);
2058 }
2059
2060 Self { data }
2061 }
2062}
2063
2064impl Deref for BufRc {
2065 type Target = [u8];
2066
2067 fn deref(&self) -> &Self::Target {
2068 unsafe { &(*self.data.as_ptr()).data }
2069 }
2070}
2071
2072impl Clone for BufRc {
2073 fn clone(&self) -> Self {
2074 unsafe {
2075 BufData::inc(self.data);
2076 }
2077
2078 Self { data: self.data }
2079 }
2080}
2081
2082impl Drop for BufRc {
2083 fn drop(&mut self) {
2084 unsafe {
2085 BufData::dec(self.data);
2086 }
2087 }
2088}
2089
2090/// A raw packet of data.
2091#[derive(Clone)]
2092pub struct RawPacket {
2093 id: MessageId,
2094 buf: Option<BufRc>,
2095 at: Cell<usize>,
2096 format: Format,
2097 channel: ChannelId,
2098}
2099
2100impl RawPacket {
2101 /// Construct an empty raw packet.
2102 ///
2103 /// # Examples
2104 ///
2105 /// ```
2106 /// use musli_web::api::MessageId;
2107 /// use musli_web::web::RawPacket;
2108 ///
2109 /// let packet = RawPacket::empty();
2110 ///
2111 /// assert!(packet.is_empty());
2112 /// assert_eq!(packet.id(), MessageId::EMPTY);
2113 /// ```
2114 pub const fn empty() -> Self {
2115 Self {
2116 id: MessageId::EMPTY,
2117 buf: None,
2118 at: Cell::new(0),
2119 format: Format::DEFAULT,
2120 channel: ChannelId::NONE,
2121 }
2122 }
2123
2124 /// The [`Format`] the body of this packet is encoded with.
2125 ///
2126 /// # Examples
2127 ///
2128 /// ```
2129 /// use musli_web::api::Format;
2130 /// use musli_web::web::RawPacket;
2131 ///
2132 /// let packet = RawPacket::empty();
2133 /// assert_eq!(packet.format(), Format::DEFAULT);
2134 /// ```
2135 #[inline]
2136 pub fn format(&self) -> Format {
2137 self.format
2138 }
2139
2140 /// Return the connection this packet belongs to.
2141 ///
2142 /// This is [`ChannelId::NONE`] unless the packet belongs to a response to a
2143 /// handle constructed with [`Handle::channel`].
2144 #[inline]
2145 pub fn channel(&self) -> ChannelId {
2146 self.channel
2147 }
2148
2149 /// Decode the contents of a raw packet.
2150 ///
2151 /// This can be called multiple times if there are multiple payloads in
2152 /// sequence of the response.
2153 ///
2154 /// You can check if the packet is empty using [`RawPacket::is_empty`].
2155 pub fn decode<'this, T>(&'this self) -> Result<T>
2156 where
2157 T: DecodeBody<'this>,
2158 {
2159 if self.id == MessageId::EMPTY {
2160 return Err(Error::new(ErrorKind::EmptyPacket));
2161 }
2162
2163 let mut at = self.at.get();
2164
2165 match self.format.decode(self.as_slice(), &mut at) {
2166 Ok(value) => {
2167 self.at.set(at);
2168 Ok(value)
2169 }
2170 Err(error) => {
2171 self.at.set(self.len());
2172 Err(Error::decode_packet(error))
2173 }
2174 }
2175 }
2176
2177 /// Get the underlying byte slice of the packet.
2178 ///
2179 /// # Examples
2180 ///
2181 /// ```
2182 /// use musli_web::web::RawPacket;
2183 ///
2184 /// let packet = RawPacket::empty();
2185 /// assert_eq!(packet.as_slice(), &[0u8; 0]);
2186 /// ```
2187 pub fn as_slice(&self) -> &[u8] {
2188 match &self.buf {
2189 Some(buf) => buf.as_ref(),
2190 None => &[],
2191 }
2192 }
2193
2194 /// Get the number of bytes remaining to be decoded in the packet.
2195 ///
2196 /// # Examples
2197 ///
2198 /// ```
2199 /// use musli_web::web::RawPacket;
2200 ///
2201 /// let packet = RawPacket::empty();
2202 /// assert_eq!(packet.remaining(), 0);
2203 /// ```
2204 pub fn remaining(&self) -> usize {
2205 self.as_slice().len().saturating_sub(self.at.get())
2206 }
2207
2208 /// Get the length of the packet.
2209 ///
2210 /// # Examples
2211 ///
2212 /// ```
2213 /// use musli_web::web::RawPacket;
2214 ///
2215 /// let packet = RawPacket::empty();
2216 /// assert_eq!(packet.len(), 0);
2217 /// ```
2218 pub fn len(&self) -> usize {
2219 match &self.buf {
2220 Some(buf) => buf.len(),
2221 None => 0,
2222 }
2223 }
2224
2225 /// Check if the packet is empty.
2226 ///
2227 /// # Examples
2228 ///
2229 /// ```
2230 /// use musli_web::web::RawPacket;
2231 ///
2232 /// let packet = RawPacket::empty();
2233 /// assert!(packet.is_empty());
2234 /// ```
2235 pub fn is_empty(&self) -> bool {
2236 self.at.get() >= self.len()
2237 }
2238
2239 /// The id of the packet this is a response to as specified by
2240 /// [`Endpoint::ID`] or [`Broadcast::ID`].
2241 ///
2242 /// [`Endpoint::ID`]: crate::api::Endpoint::ID
2243 /// [`Broadcast::ID`]: crate::api::Broadcast::ID
2244 pub fn id(&self) -> MessageId {
2245 self.id
2246 }
2247}
2248
2249impl fmt::Debug for RawPacket {
2250 #[inline]
2251 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2252 f.debug_struct("RawPacket")
2253 .field("remaining", &self.remaining())
2254 .finish()
2255 }
2256}
2257
2258/// A typed packet of data.
2259pub struct Packet<T> {
2260 raw: RawPacket,
2261 _marker: PhantomData<T>,
2262}
2263
2264impl<T> Packet<T> {
2265 /// Construct an empty package.
2266 ///
2267 /// # Examples
2268 ///
2269 /// ```
2270 /// use musli_web::api::MessageId;
2271 /// use musli_web::web::Packet;
2272 ///
2273 /// let packet = Packet::<()>::empty();
2274 ///
2275 /// assert!(packet.is_empty());
2276 /// assert_eq!(packet.id(), MessageId::EMPTY);
2277 /// ```
2278 pub const fn empty() -> Self {
2279 Self {
2280 raw: RawPacket::empty(),
2281 _marker: PhantomData,
2282 }
2283 }
2284
2285 /// Return the connection this packet belongs to.
2286 ///
2287 /// This is [`ChannelId::NONE`] unless the packet belongs to a response to a
2288 /// handle constructed with [`Handle::channel`].
2289 #[inline]
2290 pub fn channel(&self) -> ChannelId {
2291 self.raw.channel()
2292 }
2293
2294 /// The [`Format`] the body of this packet is encoded with.
2295 #[inline]
2296 pub fn format(&self) -> Format {
2297 self.raw.format()
2298 }
2299
2300 /// Construct a new typed package from a raw one.
2301 ///
2302 /// Note that this does not guarantee that the typed package is correct, but
2303 /// the `T` parameter becomes associated with it allowing it to be used
2304 /// automatically with methods such as [`Packet::decode`].
2305 #[inline]
2306 pub fn new(raw: RawPacket) -> Self {
2307 Self {
2308 raw,
2309 _marker: PhantomData,
2310 }
2311 }
2312
2313 /// Convert a packet into a raw packet.
2314 ///
2315 /// To determine which endpoint or broadcast it belongs to the
2316 /// [`RawPacket::id`] method can be used.
2317 pub fn into_raw(self) -> RawPacket {
2318 self.raw
2319 }
2320
2321 /// Get the number of bytes remaining to be decoded in the packet.
2322 ///
2323 /// # Examples
2324 ///
2325 /// ```
2326 /// use musli_web::web::Packet;
2327 ///
2328 /// let packet = Packet::<()>::empty();
2329 /// assert_eq!(packet.remaining(), 0);
2330 /// ```
2331 pub fn remaining(&self) -> usize {
2332 self.raw.remaining()
2333 }
2334
2335 /// Check if the packet is empty.
2336 ///
2337 /// # Examples
2338 ///
2339 /// ```
2340 /// use musli_web::web::Packet;
2341 ///
2342 /// let packet = Packet::<()>::empty();
2343 /// assert!(packet.is_empty());
2344 /// ```
2345 pub fn is_empty(&self) -> bool {
2346 self.raw.is_empty()
2347 }
2348
2349 /// The id of the packet this is a response to as specified by
2350 /// [`Endpoint::ID`] or [`Broadcast::ID`].
2351 ///
2352 /// [`Endpoint::ID`]: crate::api::Endpoint::ID
2353 /// [`Broadcast::ID`]: crate::api::Broadcast::ID
2354 pub fn id(&self) -> MessageId {
2355 self.raw.id()
2356 }
2357}
2358
2359impl<T> Packet<T>
2360where
2361 T: api::Decodable,
2362{
2363 /// Decode the contents of a packet.
2364 ///
2365 /// This can be called multiple times if there are multiple payloads in
2366 /// sequence of the response.
2367 ///
2368 /// You can check if the packet is empty using [`Packet::is_empty`].
2369 pub fn decode(&self) -> Result<T::Type<'_>> {
2370 self.decode_any()
2371 }
2372
2373 /// Decode any contents of a packet.
2374 ///
2375 /// This can be called multiple times if there are multiple payloads in
2376 /// sequence of the response.
2377 ///
2378 /// You can check if the packet is empty using [`Packet::is_empty`].
2379 pub fn decode_any<'de, R>(&'de self) -> Result<R>
2380 where
2381 R: DecodeBody<'de>,
2382 {
2383 self.raw.decode()
2384 }
2385}
2386
2387impl<T> Packet<T>
2388where
2389 T: api::Endpoint,
2390{
2391 /// Decode the contents of a packet.
2392 ///
2393 /// This can be called multiple times if there are multiple payloads in
2394 /// sequence of the response.
2395 ///
2396 /// You can check if the packet is empty using [`Packet::is_empty`].
2397 pub fn decode_response(&self) -> Result<T::Response<'_>> {
2398 self.decode_any_response()
2399 }
2400
2401 /// Decode any contents of a packet.
2402 ///
2403 /// This can be called multiple times if there are multiple payloads in
2404 /// sequence of the response.
2405 ///
2406 /// You can check if the packet is empty using [`Packet::is_empty`].
2407 pub fn decode_any_response<'de, R>(&'de self) -> Result<R>
2408 where
2409 R: DecodeBody<'de>,
2410 {
2411 self.raw.decode()
2412 }
2413}
2414
2415impl<T> Packet<T>
2416where
2417 T: api::Broadcast,
2418{
2419 /// Decode the primary event related to a broadcast.
2420 pub fn decode_event<'de>(&'de self) -> Result<T::Event<'de>>
2421 where
2422 T: api::BroadcastWithEvent,
2423 {
2424 self.decode_event_any()
2425 }
2426
2427 /// Decode any event related to a broadcast.
2428 pub fn decode_event_any<'de, E>(&'de self) -> Result<E>
2429 where
2430 E: Event<Broadcast = T> + DecodeBody<'de>,
2431 {
2432 self.raw.decode()
2433 }
2434}
2435
2436impl<T> Clone for Packet<T> {
2437 #[inline]
2438 fn clone(&self) -> Self {
2439 Self {
2440 raw: self.raw.clone(),
2441 _marker: PhantomData,
2442 }
2443 }
2444}
2445
2446impl<T> fmt::Debug for Packet<T> {
2447 #[inline]
2448 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2449 f.debug_struct("Packet")
2450 .field("type", &any::type_name::<T>())
2451 .field("remaining", &self.remaining())
2452 .finish()
2453 }
2454}
2455
2456/// A handle to the WebSocket service.
2457pub struct Handle<H>
2458where
2459 H: WebImpl,
2460{
2461 shared: Weak<Shared<H>>,
2462}
2463
2464impl<H> Handle<H>
2465where
2466 H: WebImpl,
2467{
2468 /// Open a new logical channel to the WebSocket server.
2469 ///
2470 /// A channel can be uniquely identified on the client and server side over
2471 /// a single connection. This means that if you send a request over a
2472 /// channel using [`Channel::request`], the server can access the
2473 /// [`ChannelId`] to determine which channel sent the request and the client
2474 /// has the ability to correlate any responses sent by the server by
2475 /// inspecting [`Packet::channel`] or [`RawPacket::channel`].
2476 ///
2477 /// The [`ChannelId`] can also be sent out-of-bounds, for example as part of
2478 /// the body of a [`ws::Server::broadcast`] allowing the client to filter
2479 /// broadcasts that originated from itself to avoid bouncing updates.
2480 ///
2481 /// The maximum number of channels is implementation defined, but expect it
2482 /// to be relatively low like `65535` (non-zero 16 bits) to reduce payload
2483 /// sizes. Failure to allocate a channel is an error.
2484 ///
2485 /// [`ws::Server::broadcast`]: crate::ws::Server::broadcast
2486 ///
2487 /// # Examples
2488 ///
2489 /// ```
2490 /// # extern crate yew023 as yew;
2491 /// use yew::prelude::*;
2492 /// use musli_web::web03::prelude::*;
2493 ///
2494 /// enum Msg {
2495 /// OnChannel(Result<ws::Channel, ws::Error>),
2496 /// }
2497 ///
2498 /// #[derive(Properties, PartialEq)]
2499 /// struct Props {
2500 /// ws: ws::Handle,
2501 /// }
2502 ///
2503 /// struct App {
2504 /// _channel_open: ws::Request,
2505 /// channel: ws::Channel,
2506 /// }
2507 ///
2508 /// impl Component for App {
2509 /// type Message = Msg;
2510 /// type Properties = Props;
2511 ///
2512 /// fn create(ctx: &Context<Self>) -> Self {
2513 /// let link = ctx.link().clone();
2514 ///
2515 /// let _channel_open = ctx.props().ws
2516 /// .channel()
2517 /// .on_open(ctx.link().callback(Msg::OnChannel))
2518 /// .send();
2519 ///
2520 /// Self {
2521 /// _channel_open: _channel_open,
2522 /// channel: ws::Channel::default(),
2523 /// }
2524 /// }
2525 ///
2526 /// fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
2527 /// match msg {
2528 /// Msg::OnChannel(result) => {
2529 /// if let Ok(channel) = result {
2530 /// self.channel = channel;
2531 /// }
2532 ///
2533 /// true
2534 /// }
2535 /// }
2536 /// }
2537 ///
2538 /// fn view(&self, ctx: &Context<Self>) -> Html {
2539 /// html! {
2540 /// <div>
2541 /// <h1>{"WebSocket Example"}</h1>
2542 /// </div>
2543 /// }
2544 /// }
2545 /// }
2546 /// ```
2547 /// The [`Format`] currently in effect for message bodies.
2548 ///
2549 /// Before the connection has been opened this is the format which was
2550 /// requested through [`ServiceBuilder::format`]. Once the connection is
2551 /// open it is the format the server actually agreed to, which can differ if
2552 /// the server does not support what was asked for.
2553 pub fn format(&self) -> Format {
2554 let Some(shared) = self.shared.upgrade() else {
2555 return Format::DEFAULT;
2556 };
2557
2558 shared.format.get()
2559 }
2560
2561 pub fn channel(&self) -> ChannelBuilder<'_, H, EmptyCallback> {
2562 ChannelBuilder {
2563 shared: &self.shared,
2564 callback: EmptyCallback,
2565 }
2566 }
2567
2568 /// Send a request of type `T`.
2569 ///
2570 /// Returns a handle for the request.
2571 ///
2572 /// If the handle is dropped, the request is cancelled.
2573 ///
2574 /// # Examples
2575 ///
2576 /// ```
2577 /// # extern crate yew023 as yew;
2578 /// use yew::prelude::*;
2579 /// use musli_web::web03::prelude::*;
2580 ///
2581 /// mod api {
2582 /// use musli::{Decode, Encode};
2583 /// use musli_web::api;
2584 ///
2585 /// #[derive(Encode, Decode)]
2586 /// pub struct HelloRequest<'de> {
2587 /// pub message: &'de str,
2588 /// }
2589 ///
2590 /// #[derive(Encode, Decode)]
2591 /// pub struct HelloResponse<'de> {
2592 /// pub message: &'de str,
2593 /// }
2594 ///
2595 /// api::define! {
2596 /// pub type Hello;
2597 ///
2598 /// impl Endpoint for Hello {
2599 /// impl<'de> Request for HelloRequest<'de>;
2600 /// type Response<'de> = HelloResponse<'de>;
2601 /// }
2602 /// }
2603 /// }
2604 ///
2605 /// enum Msg {
2606 /// OnHello(Result<ws::Packet<api::Hello>, ws::Error>),
2607 /// }
2608 ///
2609 /// #[derive(Properties, PartialEq)]
2610 /// struct Props {
2611 /// ws: ws::Handle,
2612 /// }
2613 ///
2614 /// struct App {
2615 /// message: String,
2616 /// _hello: ws::Request,
2617 /// }
2618 ///
2619 /// impl Component for App {
2620 /// type Message = Msg;
2621 /// type Properties = Props;
2622 ///
2623 /// fn create(ctx: &Context<Self>) -> Self {
2624 /// let hello = ctx.props().ws
2625 /// .request()
2626 /// .body(api::HelloRequest { message: "Hello!"})
2627 /// .on_packet(ctx.link().callback(Msg::OnHello))
2628 /// .send();
2629 ///
2630 /// Self {
2631 /// message: String::from("No Message :("),
2632 /// _hello: hello,
2633 /// }
2634 /// }
2635 ///
2636 /// fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
2637 /// match msg {
2638 /// Msg::OnHello(Err(error)) => {
2639 /// tracing::error!("Request error: {:?}", error);
2640 /// false
2641 /// }
2642 /// Msg::OnHello(Ok(packet)) => {
2643 /// if let Ok(response) = packet.decode() {
2644 /// self.message = response.message.to_owned();
2645 /// }
2646 ///
2647 /// true
2648 /// }
2649 /// }
2650 /// }
2651 ///
2652 /// fn view(&self, ctx: &Context<Self>) -> Html {
2653 /// html! {
2654 /// <div>
2655 /// <h1>{"WebSocket Example"}</h1>
2656 /// <p>{format!("Message: {}", self.message)}</p>
2657 /// </div>
2658 /// }
2659 /// }
2660 /// }
2661 /// ```
2662 pub fn request(&self) -> RequestBuilder<'_, H, EmptyBody, EmptyCallback> {
2663 RequestBuilder {
2664 shared: &self.shared,
2665 channel: Some(ChannelId::NONE),
2666 body: EmptyBody,
2667 callback: EmptyCallback,
2668 }
2669 }
2670
2671 /// Listen for broadcasts of type `T`.
2672 ///
2673 /// Returns a handle for the listener that will cancel the listener if
2674 /// dropped.
2675 ///
2676 /// # Examples
2677 ///
2678 /// ```
2679 /// # extern crate yew023 as yew;
2680 /// use yew::prelude::*;
2681 /// use musli_web::web03::prelude::*;
2682 ///
2683 /// mod api {
2684 /// use musli::{Decode, Encode};
2685 /// use musli_web::api;
2686 ///
2687 /// #[derive(Encode, Decode)]
2688 /// pub struct TickEvent<'de> {
2689 /// pub message: &'de str,
2690 /// pub tick: u32,
2691 /// }
2692 ///
2693 /// api::define! {
2694 /// pub type Tick;
2695 ///
2696 /// impl Broadcast for Tick {
2697 /// impl<'de> Event for TickEvent<'de>;
2698 /// }
2699 /// }
2700 /// }
2701 ///
2702 /// enum Msg {
2703 /// Tick(Result<ws::Packet<api::Tick>, ws::Error>),
2704 /// }
2705 ///
2706 /// #[derive(Properties, PartialEq)]
2707 /// struct Props {
2708 /// ws: ws::Handle,
2709 /// }
2710 ///
2711 /// struct App {
2712 /// tick: u32,
2713 /// _listen: ws::Listener,
2714 /// }
2715 ///
2716 /// impl Component for App {
2717 /// type Message = Msg;
2718 /// type Properties = Props;
2719 ///
2720 /// fn create(ctx: &Context<Self>) -> Self {
2721 /// let listen = ctx.props().ws.on_broadcast(ctx.link().callback(Msg::Tick));
2722 ///
2723 /// Self {
2724 /// tick: 0,
2725 /// _listen: listen,
2726 /// }
2727 /// }
2728 ///
2729 /// fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
2730 /// match msg {
2731 /// Msg::Tick(Err(error)) => {
2732 /// tracing::error!("Tick error: {error}");
2733 /// false
2734 /// }
2735 /// Msg::Tick(Ok(packet)) => {
2736 /// if let Ok(tick) = packet.decode_event() {
2737 /// self.tick = tick.tick;
2738 /// }
2739 ///
2740 /// true
2741 /// }
2742 /// }
2743 /// }
2744 ///
2745 /// fn view(&self, ctx: &Context<Self>) -> Html {
2746 /// html! {
2747 /// <div>
2748 /// <h1>{"WebSocket Example"}</h1>
2749 /// <p>{format!("Tick: {}", self.tick)}</p>
2750 /// </div>
2751 /// }
2752 /// }
2753 /// }
2754 /// ```
2755 pub fn on_broadcast<T>(&self, callback: impl Callback<Result<Packet<T>>>) -> Listener
2756 where
2757 T: api::Broadcast,
2758 {
2759 self.on_raw_broadcast::<T>(move |result| match result {
2760 Ok(packet) => callback.call(Ok(Packet::new(packet))),
2761 Err(error) => callback.call(Err(error)),
2762 })
2763 }
2764
2765 /// Listen for broadcasts of type `T`.
2766 ///
2767 /// Returns a handle for the listener that will cancel the listener if
2768 /// dropped.
2769 ///
2770 /// # Examples
2771 ///
2772 /// ```
2773 /// # extern crate yew023 as yew;
2774 /// use yew::prelude::*;
2775 /// use musli_web::web03::prelude::*;
2776 ///
2777 /// mod api {
2778 /// use musli::{Decode, Encode};
2779 /// use musli_web::api;
2780 ///
2781 /// #[derive(Encode, Decode)]
2782 /// pub struct TickEvent<'de> {
2783 /// pub message: &'de str,
2784 /// pub tick: u32,
2785 /// }
2786 ///
2787 /// api::define! {
2788 /// pub type Tick;
2789 ///
2790 /// impl Broadcast for Tick {
2791 /// impl<'de> Event for TickEvent<'de>;
2792 /// }
2793 /// }
2794 /// }
2795 ///
2796 /// enum Msg {
2797 /// Tick(Result<ws::RawPacket, ws::Error>),
2798 /// }
2799 ///
2800 /// #[derive(Properties, PartialEq)]
2801 /// struct Props {
2802 /// ws: ws::Handle,
2803 /// }
2804 ///
2805 /// struct App {
2806 /// tick: u32,
2807 /// _listen: ws::Listener,
2808 /// }
2809 ///
2810 /// impl Component for App {
2811 /// type Message = Msg;
2812 /// type Properties = Props;
2813 ///
2814 /// fn create(ctx: &Context<Self>) -> Self {
2815 /// let link = ctx.link().clone();
2816 /// let listen = ctx.props().ws.on_raw_broadcast::<api::Tick>(ctx.link().callback(Msg::Tick));
2817 ///
2818 /// Self {
2819 /// tick: 0,
2820 /// _listen: listen,
2821 /// }
2822 /// }
2823 ///
2824 /// fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
2825 /// match msg {
2826 /// Msg::Tick(Err(error)) => {
2827 /// tracing::error!("Tick error: {error}");
2828 /// false
2829 /// }
2830 /// Msg::Tick(Ok(packet)) => {
2831 /// if let Ok(tick) = packet.decode::<api::TickEvent>() {
2832 /// self.tick = tick.tick;
2833 /// }
2834 ///
2835 /// true
2836 /// }
2837 /// }
2838 /// }
2839 ///
2840 /// fn view(&self, ctx: &Context<Self>) -> Html {
2841 /// html! {
2842 /// <div>
2843 /// <h1>{"WebSocket Example"}</h1>
2844 /// <p>{format!("Tick: {}", self.tick)}</p>
2845 /// </div>
2846 /// }
2847 /// }
2848 /// }
2849 /// ```
2850 pub fn on_raw_broadcast<T>(&self, callback: impl Callback<Result<RawPacket>>) -> Listener
2851 where
2852 T: api::Broadcast,
2853 {
2854 let Some(shared) = self.shared.upgrade() else {
2855 return Listener::empty_with_kind(T::ID);
2856 };
2857
2858 let index = {
2859 let mut broadcasts = shared.g.broadcasts.borrow_mut();
2860 let slots = broadcasts.entry(T::ID).or_default();
2861 slots.insert(Rc::new(callback))
2862 };
2863
2864 Listener {
2865 kind: Some(T::ID),
2866 index,
2867 g: Rc::downgrade(&shared.g),
2868 }
2869 }
2870
2871 /// Listen for state changes to the underlying connection.
2872 ///
2873 /// This indicates when the connection is open and ready to receive requests
2874 /// through [`State::Open`], or if it's closed and requests will be queued
2875 /// through [`State::Closed`].
2876 ///
2877 /// Note that if you are connecting through a proxy the reported updates
2878 /// might be volatile. It is always best to send a message over the
2879 /// connection on the server side that once received allows the client to
2880 /// know that it is connected.
2881 ///
2882 /// Dropping the returned handle will cancel the listener.
2883 ///
2884 /// # Examples
2885 ///
2886 /// ```
2887 /// # extern crate yew023 as yew;
2888 /// use yew::prelude::*;
2889 /// use musli_web::web03::prelude::*;
2890 ///
2891 /// enum Msg {
2892 /// StateChange(ws::State),
2893 /// }
2894 ///
2895 /// #[derive(Properties, PartialEq)]
2896 /// struct Props {
2897 /// ws: ws::Handle,
2898 /// }
2899 ///
2900 /// struct App {
2901 /// state: ws::State,
2902 /// _listen: ws::StateListener,
2903 /// }
2904 ///
2905 /// impl Component for App {
2906 /// type Message = Msg;
2907 /// type Properties = Props;
2908 ///
2909 /// fn create(ctx: &Context<Self>) -> Self {
2910 /// let link = ctx.link().clone();
2911 ///
2912 /// let (state, listen) = ctx.props().ws.on_state_change(move |state| {
2913 /// link.send_message(Msg::StateChange(state));
2914 /// });
2915 ///
2916 /// Self {
2917 /// state,
2918 /// _listen: listen,
2919 /// }
2920 /// }
2921 ///
2922 /// fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
2923 /// match msg {
2924 /// Msg::StateChange(state) => {
2925 /// self.state = state;
2926 /// true
2927 /// }
2928 /// }
2929 /// }
2930 ///
2931 /// fn view(&self, ctx: &Context<Self>) -> Html {
2932 /// html! {
2933 /// <div>
2934 /// <h1>{"WebSocket Example"}</h1>
2935 /// <p>{format!("State: {:?}", self.state)}</p>
2936 /// </div>
2937 /// }
2938 /// }
2939 /// }
2940 pub fn on_state_change(&self, callback: impl Callback<State>) -> (State, StateListener) {
2941 let Some(shared) = self.shared.upgrade() else {
2942 return (
2943 State::Closed,
2944 StateListener {
2945 index: 0,
2946 g: Weak::new(),
2947 },
2948 );
2949 };
2950
2951 let (state, index) = {
2952 let index = shared
2953 .g
2954 .state_listeners
2955 .borrow_mut()
2956 .insert(Rc::new(callback));
2957 (shared.state.get(), index)
2958 };
2959
2960 let listener = StateListener {
2961 index,
2962 g: Rc::downgrade(&shared.g),
2963 };
2964
2965 (state, listener)
2966 }
2967}
2968
2969impl<H> Clone for Handle<H>
2970where
2971 H: WebImpl,
2972{
2973 #[inline]
2974 fn clone(&self) -> Self {
2975 Self {
2976 shared: self.shared.clone(),
2977 }
2978 }
2979}
2980
2981/// Construct a default handle that is not connected to a backend.
2982///
2983/// # Examples
2984///
2985/// ```
2986/// use musli_web::web03::prelude::*;
2987///
2988/// let handle = ws::Handle::default();
2989/// ```
2990impl<H> Default for Handle<H>
2991where
2992 H: WebImpl,
2993{
2994 #[inline]
2995 fn default() -> Self {
2996 Self {
2997 shared: Weak::new(),
2998 }
2999 }
3000}
3001
3002impl<H> PartialEq for Handle<H>
3003where
3004 H: WebImpl,
3005{
3006 #[inline]
3007 fn eq(&self, other: &Self) -> bool {
3008 Weak::ptr_eq(&self.shared, &other.shared)
3009 }
3010}
3011
3012impl<H> fmt::Debug for Handle<H>
3013where
3014 H: WebImpl,
3015{
3016 #[inline]
3017 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3018 let mut f = f.debug_struct("Handle");
3019
3020 if let Some(shared) = self.shared.upgrade() {
3021 f.field("connect", &shared.connect);
3022 f.field("state", &shared.state.get());
3023 }
3024
3025 f.finish()
3026 }
3027}
3028
3029/// A channel to a WebSocket server.
3030///
3031/// See [`Handle::channel`] for more details.
3032pub struct Channel<H>
3033where
3034 H: WebImpl,
3035{
3036 handle: Handle<H>,
3037 id: ChannelId,
3038}
3039
3040impl<H> Channel<H>
3041where
3042 H: WebImpl,
3043{
3044 /// Get the connection identifier for this handle.
3045 #[inline]
3046 pub fn id(&self) -> ChannelId {
3047 self.id
3048 }
3049
3050 /// Get a handle for this channel.
3051 ///
3052 /// A handle sheds the channel information and allows for setting up things
3053 /// like broadcast listener.
3054 pub fn handle(&self) -> &Handle<H> {
3055 &self.handle
3056 }
3057
3058 /// Send a request of type `T` over the current channel.
3059 ///
3060 /// Returns a handle for the request.
3061 ///
3062 /// If the handle is dropped, the request is cancelled.
3063 ///
3064 /// # Examples
3065 ///
3066 /// ```
3067 /// # extern crate yew023 as yew;
3068 /// use yew::prelude::*;
3069 /// use musli_web::web03::prelude::*;
3070 ///
3071 /// mod api {
3072 /// use musli::{Decode, Encode};
3073 /// use musli_web::api;
3074 ///
3075 /// #[derive(Encode, Decode)]
3076 /// pub struct HelloRequest<'de> {
3077 /// pub message: &'de str,
3078 /// }
3079 ///
3080 /// #[derive(Encode, Decode)]
3081 /// pub struct HelloResponse<'de> {
3082 /// pub message: &'de str,
3083 /// }
3084 ///
3085 /// api::define! {
3086 /// pub type Hello;
3087 ///
3088 /// impl Endpoint for Hello {
3089 /// impl<'de> Request for HelloRequest<'de>;
3090 /// type Response<'de> = HelloResponse<'de>;
3091 /// }
3092 /// }
3093 /// }
3094 ///
3095 /// enum Msg {
3096 /// OnHello(Result<ws::Packet<api::Hello>, ws::Error>),
3097 /// OnChannel(Result<ws::Channel, ws::Error>),
3098 /// }
3099 ///
3100 /// #[derive(Properties, PartialEq)]
3101 /// struct Props {
3102 /// ws: ws::Handle,
3103 /// }
3104 ///
3105 /// struct App {
3106 /// message: String,
3107 /// _hello: ws::Request,
3108 /// _channel_open: ws::Request,
3109 /// channel: ws::Channel,
3110 /// }
3111 ///
3112 /// impl Component for App {
3113 /// type Message = Msg;
3114 /// type Properties = Props;
3115 ///
3116 /// fn create(ctx: &Context<Self>) -> Self {
3117 /// let link = ctx.link().clone();
3118 ///
3119 /// let _channel_open = ctx.props().ws
3120 /// .channel()
3121 /// .on_open(ctx.link().callback(Msg::OnChannel))
3122 /// .send();
3123 ///
3124 /// Self {
3125 /// message: String::from("No Message :("),
3126 /// _hello: ws::Request::default(),
3127 /// _channel_open: _channel_open,
3128 /// channel: ws::Channel::default(),
3129 /// }
3130 /// }
3131 ///
3132 /// fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
3133 /// match msg {
3134 /// Msg::OnHello(Err(error)) => {
3135 /// tracing::error!("Request error: {:?}", error);
3136 /// false
3137 /// }
3138 /// Msg::OnHello(Ok(packet)) => {
3139 /// if let Ok(response) = packet.decode() {
3140 /// self.message = response.message.to_owned();
3141 /// }
3142 ///
3143 /// true
3144 /// }
3145 /// Msg::OnChannel(result) => {
3146 /// if let Ok(channel) = result {
3147 /// self.channel = channel;
3148 /// }
3149 ///
3150 /// self._hello = self.channel
3151 /// .request()
3152 /// .body(api::HelloRequest { message: "Hello!"})
3153 /// .on_packet(ctx.link().callback(Msg::OnHello))
3154 /// .send();
3155 ///
3156 /// true
3157 /// }
3158 /// }
3159 /// }
3160 ///
3161 /// fn view(&self, ctx: &Context<Self>) -> Html {
3162 /// html! {
3163 /// <div>
3164 /// <h1>{"WebSocket Example"}</h1>
3165 /// <p>{format!("Message: {}", self.message)}</p>
3166 /// </div>
3167 /// }
3168 /// }
3169 /// }
3170 /// ```
3171 pub fn request(&self) -> RequestBuilder<'_, H, EmptyBody, EmptyCallback> {
3172 RequestBuilder {
3173 shared: &self.handle.shared,
3174 channel: (self.id != ChannelId::NONE).then_some(self.id),
3175 body: EmptyBody,
3176 callback: EmptyCallback,
3177 }
3178 }
3179}
3180
3181/// Construct a default connected handle.
3182///
3183/// # Examples
3184///
3185/// ```
3186/// use musli_web::web03::prelude::*;
3187///
3188/// let channel = ws::Channel::default();
3189/// assert_eq!(channel.id(), ws::ChannelId::NONE);
3190/// ```
3191impl<H> Default for Channel<H>
3192where
3193 H: WebImpl,
3194{
3195 #[inline]
3196 fn default() -> Self {
3197 Self {
3198 handle: Handle::default(),
3199 id: ChannelId::NONE,
3200 }
3201 }
3202}
3203
3204impl<H> PartialEq for Channel<H>
3205where
3206 H: WebImpl,
3207{
3208 #[inline]
3209 fn eq(&self, other: &Self) -> bool {
3210 Weak::ptr_eq(&self.handle.shared, &other.handle.shared) && self.id == other.id
3211 }
3212}
3213
3214impl<H> Drop for Channel<H>
3215where
3216 H: WebImpl,
3217{
3218 #[inline]
3219 fn drop(&mut self) {
3220 if let Some(shared) = self.handle.shared.upgrade() {
3221 shared.remove_channel(self.id);
3222 }
3223 }
3224}
3225
3226impl<H> fmt::Debug for Channel<H>
3227where
3228 H: WebImpl,
3229{
3230 #[inline]
3231 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3232 let mut f = f.debug_struct("Channel");
3233
3234 if let Some(shared) = self.handle.shared.upgrade() {
3235 f.field("connect", &shared.connect);
3236 f.field("state", &shared.state.get());
3237 }
3238
3239 f.field("id", &self.id);
3240 f.finish()
3241 }
3242}
3243
3244struct Pending<C>
3245where
3246 C: ?Sized,
3247{
3248 id: MessageId,
3249 serial: u32,
3250 callback: C,
3251}
3252
3253impl<C> fmt::Debug for Pending<C> {
3254 #[inline]
3255 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3256 f.debug_struct("Pending")
3257 .field("serial", &self.serial)
3258 .field("id", &self.id)
3259 .finish_non_exhaustive()
3260 }
3261}
3262
3263struct ForcePrefix<'a>(&'a str, char);
3264
3265impl fmt::Display for ForcePrefix<'_> {
3266 #[inline]
3267 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3268 let Self(string, prefix) = *self;
3269 prefix.fmt(f)?;
3270 string.trim_start_matches(prefix).fmt(f)?;
3271 Ok(())
3272 }
3273}