Created
November 24, 2019 19:13
-
-
Save tmbb/a723471ce7d1d666289edea058bfd652 to your computer and use it in GitHub Desktop.
Websocket over phoenix channels
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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