Skip to content

Instantly share code, notes, and snippets.

@bellbind
Created September 28, 2017 09:04
Show Gist options
  • Select an option

  • Save bellbind/5d01d2eecd599654c1e72d835fb377fa to your computer and use it in GitHub Desktop.

Select an option

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
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<script src="direct.js"></script>
</head>
<body></body>
</html>
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);
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/");
});
@bellbind
Copy link
Author

bellbind commented Sep 28, 2017

Demo of the concept on browser also as a web server.
One of the solutions of the concept is via (public) reverse proxy.

Usage

  • start proxyd.js as "http://localhost:8080/"
  • open the "direct.html" on modern browser as any URL ( file:// also work on firefox)
  • open the link in "open proxy page" on other tab/window
  • you can show the "Hello Proxy World!" generated on the "direct.html" tab

This implementation omits the sign verification for peer generated publick keys as an identifier for the proxy network.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment