Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gloo WebSocket mid-level & high-level APIs. #1

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ version = "0.1.0"
gloo-timers = { version = "0.1.0", path = "crates/timers" }
gloo-console-timer = { version = "0.1.0", path = "crates/console-timer" }
gloo-events = { version = "0.1.0", path = "crates/events" }
gloo-websocket = { version = "0.1.0", path = "crates/websocket" }

[features]
default = []
Expand Down
17 changes: 17 additions & 0 deletions crates/websocket/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "gloo-websocket"
version = "0.1.0"
authors = ["Rust and WebAssembly Working Group"]
edition = "2018"

[dependencies]
# TODO: use upstream crate once <> lands.
backoff = { git="https://github.com/thedodd/backoff", rev="95676e51637b91c3e3ef4abfb8d4df3c1ceb1b55", features=["wasm-bindgen"] }
futures = "0.1"
gloo-events = { version = "0.1.0", path = "../events" }
js-sys = "0.3.17"
wasm-bindgen = "0.2.40"

[dependencies.web-sys]
version = "0.3.17"
features=["BinaryType", "Event", "Location", "MessageEvent", "WebSocket", "Window"]
3 changes: 3 additions & 0 deletions crates/websocket/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
gloo websocket
==============
The Gloo project's mid-level & high-level WebSocket APIs.
147 changes: 147 additions & 0 deletions crates/websocket/src/cb/builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
use std::{
borrow::Cow,
cell::{RefCell},
rc::Rc,
};

use wasm_bindgen::{
JsValue,
closure::Closure,
};
use web_sys::{self, Event};

use crate::{
cb::{
WebSocket,
core::WebSocketCore,
},
common::{
ReconnectConfig,
WsMessage,
},
};

/// A type used for building a new WebSocket instance.
pub struct WebSocketBuilder {
/// User supplied URL for the WebSocket connection.
pub(crate) url: Rc<Cow<'static, str>>,

/// User supplied subprotocols for the WebSocket to use.
pub(crate) protocols: Option<Rc<Vec<Cow<'static, str>>>>,

/// User supplied `message` handler.
pub(crate) onmessage: Option<Rc<RefCell<dyn FnMut(WsMessage)>>>,

/// User supplied `open` handler.
pub(crate) onopen: Option<Rc<RefCell<dyn FnMut(Event)>>>,

/// User supplied `error` handler.
pub(crate) onerror: Option<Rc<RefCell<dyn FnMut(Event)>>>,

/// User supplied `close` handler.
pub(crate) onclose: Option<Rc<RefCell<dyn FnMut(Event)>>>,

/// Reconnection config used for driving the exponential backoff reconnect system.
pub(crate) reconnect: Option<Rc<RefCell<ReconnectConfig>>>,

/// A storage location for EventHandlers.
pub(crate) cb_store: Rc<RefCell<Vec<Option<Closure<dyn FnMut(Event) + 'static>>>>>,

/// A switch to supppress reconnect behavior for when the WebSocket is closing.
pub(crate) is_closing: Rc<RefCell<bool>>,
}

impl WebSocketBuilder {
/// Create a new instance.
pub(crate) fn new(url: Cow<'static, str>) -> Self {
Self{
url: Rc::new(url),
protocols: None,
onmessage: None,
onopen: None,
onerror: None,
onclose: None,
reconnect: Some(Rc::new(RefCell::new(ReconnectConfig::default()))),
cb_store: Rc::new(RefCell::new(vec![])),
is_closing: Rc::new(RefCell::new(false)),
}
}

/// Finalize the build process and return the new WebSocket instance.
///
/// ### Errors
/// According to the [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/WebSocket#Exceptions_thrown)
/// an error will only be returned if "The port to which the connection is being attempted is
/// being blocked".
pub fn build(self) -> Result<WebSocket, JsValue> {
// Build the initial WebSocket instance.
let ws = Rc::new(RefCell::new(
WebSocketCore::build_new_websocket(&self.url, &self.protocols)?
));
let core = WebSocketCore::new(self, ws);
Ok(WebSocket::new(core))
}

/// Set a handler for the WebSocket's `message` event.
///
/// The given closure will be called with the payload of the received WebSocket message frame.
/// The contents of the frame will be placed in a `WsMessage` enum variant matching the
/// `opcode` of the received frame. `WsMessage::Text(_)` for text frames and
/// `WsMessage::Binary(_)` for binary frames.
///
/// See [RFC 6455 1.2](https://tools.ietf.org/html/rfc6455#section-1.2) for more details on
/// the WebSocket framing protocol.
pub fn onmessage(mut self, f: impl FnMut(WsMessage) + 'static) -> Self {
self.onmessage = Some(Rc::new(RefCell::new(f)));
self
}

/// Set a handler for the WebSocket's `open` event.
pub fn onopen(mut self, f: impl FnMut(Event) + 'static) -> Self {
self.onopen = Some(Rc::new(RefCell::new(f)));
self
}

/// Set a handler for the WebSocket's `error` event.
pub fn onerror(mut self, f: impl FnMut(Event) + 'static) -> Self {
self.onerror = Some(Rc::new(RefCell::new(f)));
self
}

/// Set a handler for the WebSocket's `close` event.
pub fn onclose(mut self, f: impl FnMut(Event) + 'static) -> Self {
self.onclose = Some(Rc::new(RefCell::new(f)));
self
}

/// The set of subprotocols to use for this connection.
///
/// See [RFC 6455 1.9](https://tools.ietf.org/html/rfc6455#section-1.9) for more
/// details on subprotocols.
pub fn protocols<I>(mut self, protos: I) -> Self
where
I: Iterator,
I::Item: Into<Cow<'static, str>>,
{
self.protocols = Some(Rc::new(protos.map(|s| s.into()).collect()));
self
}

/// Overwrite the default reconnect config with some custom config.
///
/// Gloo WebSockets are configured to reconnect by default, and will use the default value
/// from a call to `ReconnectConfig::default()`. Overwrite the default with this method.
///
/// If you want to supress reconnect behavior altogether, you should call this instance's
/// `no_reconnect()` method.
pub fn reconnect(mut self, cfg: ReconnectConfig) -> Self {
self.reconnect = Some(Rc::new(RefCell::new(cfg)));
self
}

/// Supress reconnect behavior on the created WebSocket.
pub fn no_reconnect(mut self) -> Self {
self.reconnect = None;
self
}
}
Loading