Created
July 25, 2023 19:30
-
-
Save Ezbob/04b53c2e733c7499fc6ecc287be0cc8f to your computer and use it in GitHub Desktop.
Simple reverse proxy for websockets, created in pure NodeJs
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 http = require("http") | |
const https = require("https") | |
let proxyEndpoint = "http://localhost:8080" | |
let isWebsocketUpgrade = (headers) => { | |
return !headers.upgrade || headers.upgrade.toLowerCase() != "websocket" || | |
!headers.connection || headers.connection.toLowerCase() != "upgrade" | |
} | |
let incomingServer = http.createServer() | |
incomingServer.on('request', (req, res) => { | |
// you can accept regular http requests here | |
res.writeHead(403, "Forbidden") | |
res.end() | |
}) | |
incomingServer.on("upgrade", (req, incomingSocket, head) => { | |
if (head && head.length > 0) { | |
incomingSocket.unshift(head) | |
} | |
let agent = proxyEndpoint.startsWith("https") ? https : http | |
// here we could use http follow redirection | |
let handshakeRequest = agent.request(proxyEndpoint, {headers: req.headers}) | |
handshakeRequest.end() | |
handshakeRequest.on("error", err => { | |
// we just answer with 404 if an error happen on handshake | |
if (err) { | |
incomingSocket.write(`HTTP/${req.httpVersion} 404 Not found\r\n`) | |
incomingSocket.write('\r\n') | |
incomingSocket.end() | |
} | |
}) | |
handshakeRequest.on("response", res => { | |
// the http response from the reverse end on the handshake | |
// we respond with 404 if the response is not a websocket request | |
if (!isWebsocketUpgrade(res.headers) || !res.headers["sec-websocket-accept"]) { | |
incomingSocket.write(`HTTP/${req.httpVersion} 404 Not found\r\n`) | |
incomingSocket.write('\r\n') | |
incomingSocket.end() | |
} | |
}) | |
handshakeRequest.on("upgrade", (handShakeResponse, outGoingSocket, head) => { | |
// we get a upgrade request. This happens after the "response" header | |
if (head && head.length > 0) { | |
outGoingSocket.unshift(head) | |
} | |
// two-way piping sets up a duplex connection. This is possible because TCP sockets are both | |
// writable and readable | |
incomingSocket.pipe(outGoingSocket) | |
outGoingSocket.pipe(incomingSocket) | |
// according to the docs, if an error happens on piping, we will need to close the | |
// sockets | |
incomingSocket.on("error", () => { | |
outGoingSocket.end() | |
}) | |
outGoingSocket.on("error", () => { | |
incomingSocket.end() | |
}) | |
// finally, when piping is setup, we sends the switching protocol confirmation | |
// HTTP status line and headers. This begins the transmission. | |
incomingSocket.write(`HTTP/${req.httpVersion} 101 Switching protocol\r\n`) | |
for (let headerName in handShakeResponse.headers) { | |
incomingSocket.write(`${headerName}: ${handShakeResponse.headers[headerName]}\r\n`) | |
} | |
incomingSocket.write("\r\n") | |
}) | |
}) | |
incomingServer.listen(7777, () => { | |
console.log("listening on localhost:7777") | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment