Skip to content

Instantly share code, notes, and snippets.

@tmbb
Created November 24, 2019 19:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tmbb/a723471ce7d1d666289edea058bfd652 to your computer and use it in GitHub Desktop.
Save tmbb/a723471ce7d1d666289edea058bfd652 to your computer and use it in GitHub Desktop.
Websocket over phoenix channels
/**
* This function generates an event that is compatible with standard
* compliant browsers and IE9 - IE11
*
* This will prevent the error:
* Object doesn't support this action
*
* http://stackoverflow.com/questions/19345392/why-arent-my-parameters-getting-passed-through-to-a-dispatched-event/19345563#19345563
* @param s String The name that the event should use
* @param args Object an optional object that the event will use
*/
function generateEvent(s, args) {
var evt = document.createEvent("CustomEvent");
evt.initCustomEvent(s, false, false, args);
return evt;
}
// Channel join actions
function channelJoinSuccess(socket, { messages }) {
socket.readyState = WebSocket.OPEN
console.log("eventTarget", socket.eventTarget)
let e = generateEvent('open')
socket.eventTarget.dispatchEvent(e)
if (socket.debug || XareDBWebSocket.debugAll) {
console.log("catching up", messages);
}
}
/**
* This behaves like a WebSocket in every way, except if it fails to connect,
* or it gets disconnected, it will repeatedly poll until it successfully connects
* again.
*
* It is API compatible, so when you have:
* ws = new WebSocket('ws://....');
* you can replace with:
* ws = new ReconnectingWebSocket('ws://....');
*
* The event stream will typically look like:
* onconnecting
* onopen
* onmessage
* onmessage
* onclose // lost connection
* onconnecting
* onopen // sometime later...
* onmessage
* onmessage
* etc...
*
* It is API compatible with the standard WebSocket API, apart from the following members:
*
* - `bufferedAmount`
* - `extensions`
* - `binaryType`
*
* Latest version: https://github.com/joewalnes/reconnecting-websocket/
* - Joe Walnes
*
* Syntax
* ======
* var socket = new ReconnectingWebSocket(url, protocols, options);
*
* Parameters
* ==========
* url - The url you are connecting to.
* protocols - Optional string or array of protocols.
* options - See below
*
* Options
* =======
* Options can either be passed upon instantiation or set after instantiation:
*
* var socket = new ReconnectingWebSocket(url, null, { debug: true, reconnectInterval: 4000 });
*
* or
*
* var socket = new ReconnectingWebSocket(url);
* socket.debug = true;
* socket.reconnectInterval = 4000;
*
* debug
* - Whether this instance should log debug messages. Accepts true or false. Default: false.
*
*/
class XareDBWebSocket {
constructor(socket, options) {
// Default settings
var settings = {
/** Whether this instance should log debug messages. */
debug: true,
/** Whether or not the websocket should attempt to connect immediately upon instantiation. */
automaticOpen: true
}
if (!options) { options = {}; }
// Overwrite and define settings with options if they exist.
for (var key in settings) {
if (typeof options[key] !== 'undefined') {
this[key] = options[key];
} else {
this[key] = settings[key];
}
}
// These should be treated as read-only properties
/**
* The current state of the connection.
* Can be one of: WebSocket.CONNECTING, WebSocket.OPEN, WebSocket.CLOSING, WebSocket.CLOSED
* Read only.
*/
this.readyState = WebSocket.CONNECTING;
/**
* A string indicating the name of the sub-protocol the server selected; this will be one of
* the strings specified in the protocols parameter when creating the WebSocket object.
* Read only.
*/
this.protocol = null;
// Private state variables
var eventTarget = document.createElement('div');
// Wire up "on*" properties as event handlers
eventTarget.addEventListener('open', function (event) { self.onopen(event); });
eventTarget.addEventListener('close', function (event) { self.onclose(event); });
eventTarget.addEventListener('connecting', function (event) { self.onconnecting(event); });
eventTarget.addEventListener('message', function (event) { self.onmessage(event); });
eventTarget.addEventListener('error', function (event) { self.onerror(event); });
this.eventTarget = eventTarget;
// Expose the API required by EventTarget
this.addEventListener = eventTarget.addEventListener.bind(eventTarget);
this.removeEventListener = eventTarget.removeEventListener.bind(eventTarget);
this.dispatchEvent = eventTarget.dispatchEvent.bind(eventTarget);
// Create channel (but don't connect it right away)
let self = this
let channel = socket.channel("__xaredb__", {})
this.channel = channel
this.socket = socket
channel.on("m", ({ d: data }) => {
if (self.debug || XareDBWebSocket.debugAll) {
console.log("Got message", data)
}
// dispatch the event
let e = generateEvent('message')
e.data = data
self.eventTarget.dispatchEvent(e)
})
channel.onClose(() => {
self.readyState = WebSocket.CLOSED;
let e = generateEvent('close')
self.eventTarget.dispatchEvent(e)
})
channel.onError(() => {
self.readyState = WebSocket.CLOSED
// dispatch the event
let e = generateEvent('close')
self.eventTarget.dispatchEvent(e)
})
this.open()
}
open() {
this.channel.join()
.receive("ok", (data) => channelJoinSuccess(this, data))
.receive("error", this.channelJoinError)
.receive("timeout", this.channelJoinTimeout)
}
channelJoinError({ reason }) {
if (self.debug || XareDBWebSocket.debugAll) {
console.log("failed join", reason)
}
}
channelJoinTimeout() {
if (self.debug || XareDBWebSocket.debugAll) {
console.log("Networking issue. Still waiting...")
}
}
/**
* Transmits data to the server over the WebSocket connection.
*
* @param data a text string
*/
send(data) {
this.channel.push("m", { d: data }, 10000)
.receive("ok", (msg) => console.log("created message", msg))
.receive("error", (reasons) => console.log("create failed", reasons))
.receive("timeout", () => console.log("Networking issue..."));
}
/**
* Closes the WebSocket connection or connection attempt, if any.
* If the connection is already CLOSED, this method does nothing.
*/
close(_code, _reason) {
this.channel.leave()
}
/**
* An event listener to be called when the WebSocket connection's readyState changes to OPEN;
* this indicates that the connection is ready to send and receive data.
*/
onopen(_event) { }
/** An event listener to be called when the WebSocket connection's readyState changes to CLOSED. */
onclose(_event) { }
/** An event listener to be called when a connection begins being attempted. */
onconnecting(_event) { }
/** An event listener to be called when a message is received from the server. */
onmessage(_event) { }
/** An event listener to be called when an error occurs. */
onerror(_event) { }
}
export { XareDBWebSocket }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment