Created
January 2, 2022 19:14
-
-
Save aubreyrjones/b3b429e5e853a430236d04640d6e8afb to your computer and use it in GitHub Desktop.
Websocket event client
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
function createEnum(values) { | |
const enumObject = {}; | |
for (const val of values) { | |
enumObject[val] = val; | |
} | |
return Object.freeze(enumObject); | |
} | |
const ChannelState = createEnum(['Created', 'Connecting', 'Authorizing', 'Subscribed', 'Error', 'Closed']); | |
class WSChannel { | |
constructor(url, channel) { | |
this.socket = null; | |
this.channel = channel; | |
this.url = url; | |
this.connectCallback = null; | |
this.handlers = {}; | |
this.state = ChannelState.Created; | |
this.preConnectQueue = []; | |
this.handle("_challenge", data => { | |
this.onChallenge(data.nonce); | |
}); | |
this.handle("_subbed", () => { this.onSubbed(); }); | |
} | |
connect(callback = null) { | |
this.connectCallback = callback; | |
this._reconnect(); | |
} | |
_reconnect() { | |
this.state = ChannelState.Connecting; | |
console.info("Connect initiated."); | |
this.socket = new WebSocket(this.url); | |
this.socket.binaryType = "arraybuffer"; | |
this.socket.addEventListener('open', (event) => { this.onConnect(event); }); | |
this.socket.addEventListener('message', (event) => { this.onMessage(event); }); | |
this.socket.addEventListener('error', (event) => { this.onError(event); }); | |
this.socket.addEventListener('close', (event) => { this.onClose(event); }); | |
} | |
onClose(event) { | |
if (this.state == ChannelState.Subscribed) { | |
console.error("Subscribed channel closed by server.", event); | |
} | |
this.state = ChannelState.Closed; | |
} | |
onError(event) { | |
console.log("Websocket error", event); | |
this.state = ChannelState.Error; | |
} | |
onConnect(event) { | |
console.info("Socket open."); | |
} | |
onChallenge(nonce) { | |
this.state = ChannelState.Authorizing; | |
let url = "/ws/auth?char_id=" + this.channel + "&nonce=" + nonce; | |
fetch(url).then(async response => { | |
let hash = await response.json(); | |
this.send('_response', {"value": hash}); | |
}); | |
} | |
onSubbed() { | |
console.info("Connected to peers."); | |
this.state = ChannelState.Subscribed; | |
if (this.connectCallback) this.connectCallback(); | |
if (this.preConnectQueue.length > 0) { | |
for (let m of this.preConnectQueue) { | |
this.socket.send(m); | |
} | |
this.preConnectQueue.length = 0; // lordy, js is weird. | |
} | |
} | |
onMessage(event) { | |
try { | |
let msg = JSON.parse(event.data); | |
if ('ev' in msg && msg['ev'] in this.handlers) { | |
this.handlers[msg['ev']](msg['data']); | |
} | |
} | |
catch (err) { | |
console.error("Can't decode websocket event: ", err); | |
} | |
} | |
send(eventName, payload) { | |
let msg = { | |
ev: eventName, | |
data: payload || {} | |
}; | |
let msgString = JSON.stringify(msg); | |
if (this.state == ChannelState.Subscribed && this.socket.readyState == 1) { // we're subscribed and the socket's good | |
this.socket.send(msgString); | |
} | |
else { // if we're not connected, push this into a queue to send after connection. | |
this.preConnectQueue.push(msgString); | |
// if we're trying to send events in one of these states, then let's try to reconnect. | |
if (this.state == ChannelState.Subscribed || this.state == ChannelState.Closed || this.state == ChannelState.Error) { | |
this.state = ChannelState.Error; | |
this._reconnect(); | |
} | |
} | |
} | |
handle(eventName, callback) { | |
this.handlers[eventName] = callback; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment