Skip to content

Instantly share code, notes, and snippets.

@regevbr
Last active January 17, 2023 01:59
Show Gist options
  • Save regevbr/de3f5e0475aedd9081608663241bee10 to your computer and use it in GitHub Desktop.
Save regevbr/de3f5e0475aedd9081608663241bee10 to your computer and use it in GitHub Desktop.
node-http-proxy proxy chain with connect method implemented (+ bonus auth handling)
"use strict";
const http = require('http');
const request = http.request;
const net = require('net');
const httpProxy = require('http-proxy');
let auth = "secret-string";
auth = new Buffer(auth).toString('base64');
auth = "Basic " + auth;
module.exports = function (proxyHost, proxyPort, listenPort) {
const settings = {target: `http://${proxyHost}:${proxyPort}`, toProxy: true, prependPath: false};
const proxy = httpProxy.createProxyServer(settings);
const noProxyChain = !proxyHost && !proxyPort;
proxy.on('error', function(err) {
console.log('ERR:',err);
});
const server = http.createServer(function (req, res) {
if (!req.headers['proxy-authorization'] || req.headers['proxy-authorization'] !== auth) {
res.writeHead(401);
res.end();
return;
}
delete req.headers['proxy-authorization'];
proxy.web(req, res, settings);
});
server.on('connect', function (req, socket) {
if (!req.headers['proxy-authorization'] || req.headers['proxy-authorization'] !== auth) {
socket.write("HTTP/1.1 401 UNAUTHORIZED\r\n\r\n");
socket.end();
socket.destroy();
return;
}
delete req.headers['proxy-authorization'];
if (noProxyChain) {
const parts = req.url.split(':', 2);
const conn = net.connect(parts[1], parts[0], function () {
socket.write("HTTP/1.1 200 OK\r\n\r\n");
socket.pipe(conn);
conn.pipe(socket);
});
conn.on('error', function () {
socket.write("HTTP/1.1 500 SERVER ERROR\r\n\r\n");
socket.end();
socket.destroy();
});
return;
}
const connectOptions = {
"host": proxyHost,
"port": proxyPort,
"headers": req.headers,
"method": "CONNECT",
"path": req.url,
"agent": false
};
// from TunnelingAgent.prototype.createSocket
const connectReq = request(connectOptions);
connectReq.useChunkedEncodingByDefault = false;
connectReq.once('response', onResponse);
connectReq.once('upgrade', onUpgrade);
connectReq.once('connect', onConnect);
connectReq.once('error', onError);
connectReq.end();
function onResponse(res) {
res.upgrade = true;
}
function onUpgrade(res, proxySocket, head) {
process.nextTick(function () {
onConnect(res, proxySocket, head);
});
}
function onConnect(res, proxySocket) {
proxySocket.removeAllListeners();
if (res.statusCode === 200) {
socket.write("HTTP/1.1 200 OK\r\n\r\n");
socket.pipe(proxySocket);
proxySocket.pipe(socket);
} else {
socket.write("HTTP/1.1 500 SERVER ERROR\r\n\r\n");
socket.end();
socket.destroy();
}
}
function onError() {
socket.write("HTTP/1.1 500 SERVER ERROR\r\n\r\n");
socket.end();
socket.destroy();
}
});
server.listen(listenPort);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment