aboutsummaryrefslogtreecommitdiff
path: root/lib/socket.js
diff options
context:
space:
mode:
Diffstat (limited to 'lib/socket.js')
-rw-r--r--lib/socket.js70
1 files changed, 70 insertions, 0 deletions
diff --git a/lib/socket.js b/lib/socket.js
new file mode 100644
index 0000000..971bacd
--- /dev/null
+++ b/lib/socket.js
@@ -0,0 +1,70 @@
+"use strict"
+
+const WebSocket = require("ws");
+
+class Client {
+ constructor(ws) {
+ this.ws = ws;
+ this.handlers = {};
+
+ ws.on("message", msg => { const d = JSON.parse(msg);
+ (this.handlers[d.type]||[]).forEach(cb => cb(d.data));
+ });
+
+ const closecb = () =>
+ (this.handlers["close"]||[]).forEach(cb => cb());
+
+ ws.on("close", closecb);
+ ws.on("error", closecb);
+ }
+
+ on(type, cb) {
+ (this.handlers[type]||(this.handlers[type]=[])).push(cb);
+ }
+
+ send(type, data) {
+ this.ws.send(JSON.stringify({type, data}));
+ }
+}
+
+module.exports = class Socket {
+ constructor(server) {
+ this.wss = new WebSocket.Server({server});
+ this.handlers = {};
+ this.clients = [];
+
+ this.wss.on("connection", ws => {
+ const client = new Client(ws);
+ this.clients.push(client);
+
+ ws.on("message", msg => { const d = JSON.parse(msg);
+ (this.handlers[d.type]||[]).forEach(cb => cb(client, d.data));
+ });
+
+ let pingTimeout;
+ const ping = () => { client.send("ping", {});
+ pingTimeout = setTimeout(() => ws.terminate(), 3e4); };
+ client.on("pong", () => { clearTimeout(pingTimeout);
+ setTimeout(ping, 1e3); }); ping();
+
+ const closecb = () => {
+ const i = this.clients.indexOf(client); if (i < 0) return;
+ (this.handlers["close"]||[]).forEach(cb => cb(client));
+ this.clients.splice(i, 1);
+ }
+
+ ws.on("close", closecb);
+ ws.on("error", closecb);
+
+ (this.handlers["open"]||[]).forEach(cb => cb(client));
+ });
+ }
+
+ on(type, cb) {
+ (this.handlers[type]||(this.handlers[type]=[])).push(cb);
+ }
+
+ sendAll(type, data) {
+ this.clients.forEach(c => c.send(type, data));
+ }
+};