Last active
February 17, 2018 01:22
-
-
Save ayanamist/9461784 to your computer and use it in GitHub Desktop.
HTTPS Proxy (Polipo + stunnel)
This file contains 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
#!/usr/bin/env node | |
'use strict'; | |
var fs = require('fs'); | |
var http = require('http'); | |
var https = require('https'); | |
var net = require('net'); | |
var url = require('url'); | |
var util = require('util'); | |
http.globalAgent.maxSockets = 128; | |
var prototype = http.IncomingMessage.prototype; | |
var _addHeaderLine = prototype._addHeaderLine; | |
//Patch ServerRequest to save unmodified copy of headers | |
prototype._addHeaderLine = function (field, value) { | |
var list = this.complete | |
? | |
(this.allTrailers || (this.allTrailers = [])) | |
: | |
(this.allHeaders || (this.allHeaders = [])); | |
list.push(field + ': ' + value); | |
_addHeaderLine.apply(this, arguments); | |
}; | |
var log = function () { | |
util.log(util.format.apply(null, arguments)); | |
}; | |
var proxyServer = https.createServer({ | |
'requestCert': true, | |
'rejectUnauthorized': true, | |
'ca': fs.readFileSync('/etc/stunnel/ca.crt'), | |
'cert': fs.readFileSync('/etc/stunnel/vps-server.pem'), | |
'key': fs.readFileSync('/etc/stunnel/vps-server.pem'), | |
}); | |
proxyServer.on('request', function (request, response) { | |
var reqParsed = url.parse(request.url); | |
var reqHeaders = request.headers; | |
Object.keys(reqHeaders).forEach(function (key) { | |
if (key.toLowerCase().slice(0, 6) === 'proxy-') { | |
delete reqHeaders[key]; | |
} | |
}); | |
var srvRequest = http.request({ | |
hostname: reqParsed.host, | |
host: reqParsed.host, | |
port: reqParsed.port || 80, | |
path: reqParsed.path, | |
method: request.method, | |
headers: reqHeaders, | |
}); | |
srvRequest.setNoDelay(true); | |
request.pipe(srvRequest); | |
srvRequest.on('error', function (err) { | |
srvRequest.abort(); | |
response.writeHead(504); | |
response.end(err.toString()); | |
}); | |
response.on('error', function () { | |
response.abort(); | |
srvRequest.abort(); | |
}); | |
srvRequest.on('response', function (srvResponse) { | |
srvResponse.on('error', function () { | |
srvResponse.abort(); | |
response.abort(); | |
}); | |
// nodejs will make all names of http headers lower case, which breaks many old clients. | |
// Should not directly manipulate socket, because cltResponse.socket will sometimes become null. | |
var rawHeader = Object.create(null); | |
srvResponse.allHeaders.map(function (header) { | |
// We don't need to validate split result, since nodejs has guaranteed by valid srvResponse.headers. | |
var key = header.split(":")[0].trim(); | |
rawHeader[key] = srvResponse.headers[key.toLowerCase()]; | |
}); | |
response.writeHead(srvResponse.statusCode, rawHeader); | |
srvResponse.pipe(response); | |
}); | |
}); | |
proxyServer.on('connect', function (request, socket) { | |
var hostParsed = request.url.split(':'); | |
var srvConn = net.createConnection(Number(hostParsed[1]), hostParsed[0]); | |
srvConn.on('connect', function () { | |
socket.write('HTTP/1.1 200 Connection Established\r\n\r\n'); | |
srvConn.pipe(socket); | |
}); | |
socket.pipe(srvConn); | |
srvConn.on('error', function () { | |
srvConn.end(); | |
socket.end(); | |
}); | |
socket.on('error', function () { | |
socket.end(); | |
srvConn.end(); | |
}); | |
}); | |
if (!module.parent) { | |
proxyServer.listen(443); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment