aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app.js23
-rw-r--r--lib/socket.js70
-rw-r--r--public/index.html1
-rw-r--r--public/main.js12
-rw-r--r--public/sock.js43
5 files changed, 139 insertions, 10 deletions
diff --git a/app.js b/app.js
index e026555..d6c8d7e 100644
--- a/app.js
+++ b/app.js
@@ -1,11 +1,11 @@
"use strict"
-const WebSocket = require("ws");
-const http = require("http");
-const fs = require("fs");
+const http = require("http");
+const fs = require("fs");
const Router = require("./lib/router");
const Static = require("./lib/static");
+const Socket = require("./lib/socket");
if (!fs.existsSync("./logs")) fs.mkdirSync("./logs");
@@ -14,8 +14,8 @@ const PORT = Math.clamp(+process.env.PORT||8080, 1, 65535);
const HOST = "0.0.0.0";
const server = http.createServer();
-const wss = new WebSocket.Server({server});
const stat = new Static("./public");
+const wss = new Socket(server);
const app = new Router();
function sj(res, data) { // For convenience
@@ -38,9 +38,18 @@ app.get("/hist.json", (req, res) => sj(res, []));
// req.pipe(fs.createWriteStream("./static/"+path[1]));
//});
-wss.on("connection", ws => {
- ws.on("message", msg => console.log("msg", msg));
- ws.send("hello");
+wss.on("open", client => {
+ console.log(`${Date.now()} SOCK open`);
+ client.send("hello", {foo:"bar"});
+});
+
+wss.on("close", client => {
+ console.log(`${Date.now()} SOCK close`);
+});
+
+wss.on("world", (client, data) => {
+ console.log(`${Date.now()} SOCK world`, data);
+ wss.sendAll("yay", {meh:"woot"});
});
server.on("request", (req, res) => {
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));
+ }
+};
diff --git a/public/index.html b/public/index.html
index c3828a5..a5892c1 100644
--- a/public/index.html
+++ b/public/index.html
@@ -11,6 +11,7 @@
<body>
<h1>nomicvote</h1>
+ <script src="sock.js"></script>
<script src="main.js"></script>
</body>
</html>
diff --git a/public/main.js b/public/main.js
index b3c7986..46d5432 100644
--- a/public/main.js
+++ b/public/main.js
@@ -2,9 +2,15 @@ document.addEventListener("DOMContentLoaded", async () => { "use strict"
const $ = s => document.querySelector(s);
const secure = location.protocol === "https:";
-const sock = new WebSocket(`ws${secure?"s":""}://${location.host}/ws`);
+sock.init(`ws${secure?"s":""}://${location.host}/ws`);
-sock.onmessage = e => console.log("sock", e);
-sock.onopen = () => sock.send("yay");
+sock.on("hello", e => {
+ console.log("hello", e);
+ sock.send("world", {foo:"bar"});
+});
+
+sock.on("yay", e => {
+ console.log("yay", e);
+});
});
diff --git a/public/sock.js b/public/sock.js
new file mode 100644
index 0000000..8cc73f0
--- /dev/null
+++ b/public/sock.js
@@ -0,0 +1,43 @@
+const sock = (() => { "use strict"
+const refresh = () => setTimeout(() => location.reload(), 1e3);
+
+let ws, pingTimeout;
+const prequeue = [];
+const handlers = {
+ "reset": [refresh],
+ "ping": [() => {
+ clearTimeout(pingTimeout);
+ pingTimeout = setTimeout(refresh, 3e4);
+ sock.send("pong", {});
+ }],
+};
+
+const sock = {
+ init: url => {
+ ws = new WebSocket(url);
+ ws.addEventListener("close", refresh);
+ ws.addEventListener("error", refresh);
+ ws.addEventListener("message", e => {
+ const d = JSON.parse(e.data);
+ (handlers[d.type]||[]).forEach(cb => cb(d.data));
+ });
+ ws.addEventListener("open", e => {
+ while (prequeue.length) sock.send(...prequeue.shift());
+ (handlers["open"]||[]).forEach(cb => cb());
+ delete handlers["open"];
+ });
+ },
+
+ on: (type, cb) => {
+ if (type === "open" && ws && ws.readyState === WebSocket.OPEN) cb();
+ else (handlers[type]||(handlers[type]=[])).push(cb);
+ },
+
+ send: (type, data) => {
+ if (ws && ws.readyState === WebSocket.OPEN)
+ ws.send(JSON.stringify({type, data}));
+ else prequeue.push([type, data]);
+ },
+};
+
+return sock })();