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}