Created
September 28, 2017 09:04
-
-
Save bellbind/5d01d2eecd599654c1e72d835fb377fa to your computer and use it in GitHub Desktop.
[nodejs][websocket]reverse proxy to browser tab as a web server with WebSocket
This file contains hidden or 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
| <!doctype html> | |
| <html> | |
| <head> | |
| <meta charset="utf-8" /> | |
| <script src="direct.js"></script> | |
| </head> | |
| <body></body> | |
| </html> |
This file contains hidden or 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 connectProxy(hash, proxyUrl) { | |
| const wsurl = proxyUrl.replace(/^https?/, "ws"); | |
| const socket = new WebSocket(wsurl, "proxy"); | |
| socket.addEventListener("open", ev => { | |
| socket.send(JSON.stringify({hash})); | |
| }, false); | |
| return socket; | |
| } | |
| window.addEventListener("load", ev => { | |
| // TBD: use hash of pubkey with its sign | |
| const rand32 = crypto.getRandomValues(new Uint8Array(32)); | |
| const hash = Array.from( | |
| rand32, n => n.toString(16).padStart(2, "0")).join(""); | |
| const proxyUrl = "http://localhost:8080/"; | |
| const port = connectProxy(hash, proxyUrl); | |
| port.addEventListener("message", ev => { | |
| const req = JSON.parse(ev.data); | |
| console.log("request arrived", req); | |
| // result generated on browser tab | |
| const res = { | |
| id: req.id, | |
| status: 200, | |
| headers: { | |
| "content-type": "text/plain;charset=utf-8", | |
| }, | |
| body: "Hello Proxy World!", | |
| }; | |
| port.send(JSON.stringify(res)); | |
| }, false); | |
| // show a proxy link for the generator page | |
| const a = document.createElement("a"); | |
| a.href = `${proxyUrl}${hash}/`; | |
| a.target = "_blank"; | |
| a.innerHTML = "open proxy page"; | |
| document.body.appendChild(a); | |
| }, false); |
This file contains hidden or 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
| const crypto = require("crypto"); | |
| const http = require("http"); | |
| // npm install websocket | |
| const websocket = require("websocket"); | |
| const proxies = new Map(); | |
| //TBD: only handle text type body | |
| class Proxy { | |
| constructor({hash}, conn) { | |
| console.log("[new proxy]", hash); | |
| console.assert(hash.match(/^[a-f\d]{64}$/), "hash must be 64-hex"); | |
| //TBD: check with a sign by pubkey for the `hash` | |
| proxies.set(hash, this); | |
| this.hash = hash; | |
| this.conn = conn; | |
| this.responses = new Map(); | |
| conn.on("message", this.onResponse.bind(this)); | |
| conn.once("close", (reason, desc) => { | |
| console.log("closed", this.hash); | |
| for (const res of this.responses.values()) { | |
| res.writeHead(404, {}); | |
| res.end(); | |
| } | |
| proxies.delete(this.hash); | |
| conn.removeAllListeners(); | |
| }); | |
| } | |
| doRequest(req, res) { | |
| console.log("[forward]", req.url); | |
| const id = crypto.randomBytes(32).toString("hex"); | |
| this.responses.set(id, res); | |
| let body = Buffer.of(); | |
| req.setEncoding("utf8"); | |
| req.on("readable", () => { | |
| const buf = req.read(); | |
| if (buf) body = Buffer.concat([body, buf]); | |
| }); | |
| req.once("end", () => { | |
| const reqJson = { | |
| id: id, | |
| method: req.method, | |
| url: req.url, | |
| headers: req.headers, | |
| body: body.toString("utf8"), | |
| }; | |
| console.log(reqJson); | |
| this.conn.sendUTF(JSON.stringify(reqJson)); | |
| }); | |
| } | |
| onResponse(msg) { | |
| console.log("[response]"); | |
| console.assert(msg.type === "utf8"); | |
| const resJson = JSON.parse(msg.utf8Data); | |
| const res = this.responses.get(resJson.id); | |
| this.responses.delete(resJson.id); | |
| res.writeHead(resJson.status, resJson.headers); | |
| res.end(resJson.body); | |
| } | |
| } | |
| // HTTP server | |
| function handler(req, res) { | |
| console.log("[access]", req.url); | |
| if (req.url.match(/^\/[a-f\d]{64}\//)) { | |
| const hash = req.url.slice(1, 65); | |
| if (!proxies.has(hash)) { | |
| res.writeHead(404); | |
| res.end(); | |
| } else { | |
| proxies.get(hash).doRequest(req, res); | |
| } | |
| } else { | |
| res.writeHead(404); | |
| res.end(); | |
| } | |
| } | |
| const httpServer = http.createServer(handler); | |
| // WebSocket Server | |
| const wsServer = new websocket.server({ | |
| httpServer, autoAcceptConnections: false, | |
| }); | |
| wsServer.on("request", req => { | |
| console.log("accept"); | |
| const conn = req.accept("proxy", req.origin); | |
| conn.once("message", msg => { | |
| console.log("[open socket]"); | |
| if (msg.type === "utf8") { | |
| const json = JSON.parse(msg.utf8Data); | |
| new Proxy(json, conn); | |
| } else { | |
| conn.close(websocket.connection.CLOSE_REASON_INVALID_DATA); | |
| } | |
| }); | |
| }); | |
| httpServer.listen(8080, () => { | |
| console.log("proxy host started on http://localhost:8080/"); | |
| }); |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Demo of the concept on browser also as a web server.
One of the solutions of the concept is via (public) reverse proxy.
Usage
proxyd.jsas "http://localhost:8080/"This implementation omits the sign verification for peer generated publick keys as an identifier for the proxy network.