let http = require("http"); let fs = require("fs"); let crypto = require("crypto"); let urllib = require("url"); let ws = require("ws"); let mime = require('mime-types') let sessions = {}; class Session { constructor(id) { this.id = id; this.masterPoll = null; this.timeout = null; this.resetTimeout(); this.master = null; this.target = null; } resetTimeout() { if (this.timeout) clearTimeout(this.timeout); this.timeout = setTimeout(() => { console.log(`${this.id}: No activity for 5m, deleting session.`); sessions[this.id] = null; }, 5 * 60 * 1000); } onMaster(conn) { this.master = conn; this.sendTargetMessage({ type: "master-connect" }); if (this.target) this.sendMasterMessage({ type: "target-connect" }); conn.on("message", msg => { console.log(`${this.id}: Message from master: ${msg}`); this.resetTimeout(); this.onMasterMessage(JSON.parse(msg)); }); conn.on("close", () => { this.master = null; this.sendTargetMessage({ type: "master-disconnect" }); }); } onTarget(conn) { this.target = conn; this.sendMasterMessage({ type: "target-connect" }); if (this.master) this.sendTargetMessage({ type: "master-connect" }); conn.on("message", msg => { console.log(`${this.id}: Message from target: ${msg}`); this.resetTimeout(); this.onTargetMessage(JSON.parse(msg)); }); conn.on("close", () => { this.target = null; this.sendMasterMessage({ type: "target-disconnect" }); }); } onMasterMessage(msg) { console.log(`${this.id}: Forwarding from master:`, msg); this.sendTargetMessage(msg); } onTargetMessage(msg) { console.log(`${this.id}: Forwarding from target:`, msg); this.sendMasterMessage(msg); } sendMasterMessage(msg) { if (this.master) this.master.send(JSON.stringify(msg)); } sendTargetMessage(msg) { if (this.target) this.target.send(JSON.stringify(msg)); } } function createSession() { let id = crypto.randomBytes(16).toString("hex"); sessions[id] = new Session(id); return id; } function sendFile(path, res) { let url = urllib.parse(path); let rs = fs.createReadStream(`web${url.pathname}`); rs.once("error", err => { if (err.code == "EISDIR") { if (url.pathname[url.pathname.length - 1] == "/") { return sendFile(`${url.pathname}index.html`, res); } else { res.writeHead(302, { location: `${url.pathname}/` }); res.end(); return; } } res.writeHead(400); res.end(err.toString()); }); rs.on("open", () => { res.writeHead(200, { "Content-Type": mime.lookup(url.pathname), }); }); rs.pipe(res); } let server = http.createServer((req, res) => { if (req.method == "GET" || req.method == "HEAD") { sendFile(req.url, res); } else if (req.method == "POST" && req.url == "/create") { let id = createSession(res); res.writeHead(302, { location: `/console/?${id}`, }); res.end("Redirecting..."); } }); server.listen(8080); let wss = new ws.Server({ server }); wss.on("connection", conn => { conn.on("message", function onMessage(msg) { let obj; try { obj = JSON.parse(msg); } catch (err) { console.log("Received invalid message"); conn.send(JSON.stringify({ type: "identify-response", err: "Invalid JSON", })); return; } if (obj.type != "identify-target" && obj.type != "identify-master") { conn.send(JSON.stringify({ type: "identify-response", err: "Not identified", })); return; } if (!obj.id) { conn.send(JSON.stringify({ type: "identify-response", err: "Empty session ID", })); return; } let sess = sessions[obj.id]; if (sess == null) { conn.send(JSON.stringify({ type: "identify-response", err: "Invalid session ID", })); return; } conn.send(JSON.stringify({ type: "identify-response", err: null })); conn.off("message", onMessage); if (obj.type == "identify-master") sess.onMaster(conn); else sess.onTarget(conn); }); });