reconnecting_websocket/
lib.rs

1//! A wrapper around [`WebSocket`] that reconnects when the socket
2//! drops. Uses [`Backoff`] to determine the delay between reconnects
3//!
4//! # Features
5//!
6//! * `tracing` - enables the [`tracing`] crate and logs everything it's doing
7//! * `state-events` - changes the Item type of the stream to be an enum that is either a message or
8//!   a
9//! status change Both are enabled by default
10//!
11//! # Usage
12//!
13//! Input means stuff you want to send from this client to the server
14//!
15//! Outut means stuff you want to receive back from the server
16//!
17//! 1. Implement [`TryFrom`] for [`Message`] for your input type
18//!     * The [`TryFrom::Error`] type must implement [`Debug`]
19//! 1. Implement [`TryFrom<Message>`] for your output type
20//!     * The [`TryFrom::Error`] type must implement [`Debug`]
21//! 1. Both input and output need to implement [`Unpin`] and, if using tracing feature, [`Debug`]
22//! 1. Use [`SocketBuilder`] to set the URL and configure backoff. [`get_proto_and_host`] can help
23//!    constructing the URL relative to the current `window.location`
24//! 1. Call [`SocketBuilder::open`] to connect the socket. The errors `open` returns are likely
25//!    fatal (invalid URL, blocked port), see [`WebSocket::open`] for details. The first connect is
26//!    done in the builder so it fails fast if these fatal errors occur but the same kind of error
27//!    can also occur on any reconnect and be returned by the [`Socket`] [`Stream`] implementation
28//! 1. The returned [`Socket`] can then be polled to get incoming messages. [`Socket::send`] can be
29//!    called to send messages or [`Socket::get_sender`] can be used to get an [`UnboundedSender`].
30//!    [`Socket::close`] or dropping it will drop the inner [`WebSocket`] which sends a close frame
31//!    and cleans up the event handlers
32//!
33//! # Example
34//!
35//! `tests/reconnect.rs`
36//! ```rust
37#![doc = include_str!("../tests/reconnect.rs")]
38//! ```
39//! 
40//! [`WebSocket`]: gloo::net::websocket::futures::WebSocket
41//! [`Backoff`]: exponential_backoff::Backoff
42//! [`WebSocket::open`]: gloo::net::websocket::futures::WebSocket::open
43//! [`Stream`]: futures::Stream
44//! [`UnboundedSender`]: futures::channel::mpsc::UnboundedSender
45
46// TODO: Replace unbounded with a reasonable bounded channel
47
48#![warn(missing_docs)]
49
50use std::fmt::Debug;
51
52use cfg_if::cfg_if;
53#[doc(inline)]
54/// Re-export of [`gloo::net::websocket::Message`].
55pub use gloo::net::websocket::Message;
56
57mod error;
58pub use error::Error;
59
60mod location;
61pub use location::{get_proto_and_host, HttpProtocol, WebSocketProtocol};
62
63mod event;
64pub use event::Event;
65
66mod constants;
67pub use constants::{DEFAULT_BACKOFF_MAX, DEFAULT_BACKOFF_MIN, DEFAULT_MAX_RETRIES};
68
69mod builder;
70pub use builder::SocketBuilder;
71
72mod state;
73pub use state::State;
74
75mod socket;
76pub use socket::{Socket, SocketSink};
77
78mod dummy_tracing;
79
80// Plumbing for making it work with and without tracing
81cfg_if! {
82    if #[cfg(feature = "tracing")] {
83        #[allow(unused_imports)]
84        use tracing::{trace, debug, info, warn, error};
85
86        /// Trait expressing the requirements for a socket input type
87        /// You don't need to implement it directly, there is a blanked implementation for types that implement
88        /// [`Unpin`], [`Debug`], <[`Message`] as [`TryFrom<Self>`]>
89        pub trait SocketInput: Unpin + Debug + Sized
90        where
91            Message: TryFrom<Self>,
92            <Message as TryFrom<Self>>::Error: Debug
93        {}
94
95        /// Trait expressing the requirements for a socket output type
96        /// You don't need to implement it directly, there is a blanked implementation for types that implement
97        /// [`Unpin`], [`Debug`], <`Self` as [`TryFrom<Message>`]>
98        pub trait SocketOutput: Unpin + TryFrom<Message> + Debug
99        where <Self as TryFrom<Message>>::Error: Debug {}
100
101        impl<T: Unpin + Debug + Sized> SocketInput for T
102        where
103            Message: TryFrom<T>,
104            <Message as TryFrom<T>>::Error: Debug
105        {}
106
107        impl<T: Unpin + TryFrom<Message> + Debug> SocketOutput for T
108        where <T as TryFrom<Message>>::Error: Debug {}
109    } else {
110        /// Trait expressing the requirements for a socket input type
111        /// You don't need to implement it directly, there is a blanked implementation for types that implement
112        /// [`Unpin`], [`Debug`], <[`Message`] as [`TryFrom<Self>`]>
113        pub trait SocketInput: Unpin + Sized
114        where
115            Message: TryFrom<Self>,
116            <Message as TryFrom<Self>>::Error: Debug
117        {}
118
119        /// Trait expressing the requirements for a socket output type
120        /// You don't need to implement it directly, there is a blanked implementation for types that implement
121        /// [`Unpin`], [`Debug`], <`Self` as [`TryFrom<Message>`]>
122        pub trait SocketOutput: Unpin + TryFrom<Message>
123        where <Self as TryFrom<Message>>::Error: Debug {}
124
125        impl<T: Unpin + Sized> SocketInput for T
126        where
127            Message: TryFrom<T>,
128            <Message as TryFrom<T>>::Error: Debug
129        {}
130
131        impl<T: Unpin + TryFrom<Message>> SocketOutput for T
132        where <T as TryFrom<Message>>::Error: Debug {}
133    }
134}