Created
April 27, 2018 19:00
-
-
Save zbycz/18a5edafbf48aaca750a4cfd3745aba5 to your computer and use it in GitHub Desktop.
http-proxy with ssl dump
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
var httpNative = require('http'), | |
httpsNative = require('https'), | |
web_o = require('./web-outgoing'), | |
common = require('../common'), | |
followRedirects = require('follow-redirects'); | |
web_o = Object.keys(web_o).map(function(pass) { | |
return web_o[pass]; | |
}); | |
var nativeAgents = { http: httpNative, https: httpsNative }; | |
/*! | |
* Array of passes. | |
* | |
* A `pass` is just a function that is executed on `req, res, options` | |
* so that you can easily add new checks while still keeping the base | |
* flexible. | |
*/ | |
module.exports = { | |
/** | |
* Sets `content-length` to '0' if request is of DELETE type. | |
* | |
* @param {ClientRequest} Req Request object | |
* @param {IncomingMessage} Res Response object | |
* @param {Object} Options Config object passed to the proxy | |
* | |
* @api private | |
*/ | |
deleteLength: function deleteLength(req, res, options) { | |
if((req.method === 'DELETE' || req.method === 'OPTIONS') | |
&& !req.headers['content-length']) { | |
req.headers['content-length'] = '0'; | |
delete req.headers['transfer-encoding']; | |
} | |
}, | |
/** | |
* Sets timeout in request socket if it was specified in options. | |
* | |
* @param {ClientRequest} Req Request object | |
* @param {IncomingMessage} Res Response object | |
* @param {Object} Options Config object passed to the proxy | |
* | |
* @api private | |
*/ | |
timeout: function timeout(req, res, options) { | |
if(options.timeout) { | |
req.socket.setTimeout(options.timeout); | |
} | |
}, | |
/** | |
* Sets `x-forwarded-*` headers if specified in config. | |
* | |
* @param {ClientRequest} Req Request object | |
* @param {IncomingMessage} Res Response object | |
* @param {Object} Options Config object passed to the proxy | |
* | |
* @api private | |
*/ | |
XHeaders: function XHeaders(req, res, options) { | |
if(!options.xfwd) return; | |
var encrypted = req.isSpdy || common.hasEncryptedConnection(req); | |
var values = { | |
for : req.connection.remoteAddress || req.socket.remoteAddress, | |
port : common.getPort(req), | |
proto: encrypted ? 'https' : 'http' | |
}; | |
['for', 'port', 'proto'].forEach(function(header) { | |
req.headers['x-forwarded-' + header] = | |
(req.headers['x-forwarded-' + header] || '') + | |
(req.headers['x-forwarded-' + header] ? ',' : '') + | |
values[header]; | |
}); | |
req.headers['x-forwarded-host'] = req.headers['host'] || ''; | |
}, | |
/** | |
* Does the actual proxying. If `forward` is enabled fires up | |
* a ForwardStream, same happens for ProxyStream. The request | |
* just dies otherwise. | |
* | |
* @param {ClientRequest} Req Request object | |
* @param {IncomingMessage} Res Response object | |
* @param {Object} Options Config object passed to the proxy | |
* | |
* @api private | |
*/ | |
stream: function stream(req, res, options, _, server, clb) { | |
// And we begin! | |
server.emit('start', req, res, options.target || options.forward); | |
var agents = options.followRedirects ? followRedirects : nativeAgents; | |
var http = agents.http; | |
var https = agents.https; | |
if(options.forward) { | |
// If forward enable, so just pipe the request | |
var forwardReq = (options.forward.protocol === 'https:' ? https : http).request( | |
common.setupOutgoing(options.ssl || {}, options, req, 'forward') | |
); | |
// error handler (e.g. ECONNRESET, ECONNREFUSED) | |
// Handle errors on incoming request as well as it makes sense to | |
var forwardError = createErrorHandler(forwardReq, options.forward); | |
req.on('error', forwardError); | |
forwardReq.on('error', forwardError); | |
(options.buffer || req).pipe(forwardReq); | |
if(!options.target) { return res.end(); } | |
} | |
// Request initalization | |
var proxyReq = (options.target.protocol === 'https:' ? https : http).request( | |
common.setupOutgoing(options.ssl || {}, options, req) | |
); | |
// Enable developers to modify the proxyReq before headers are sent | |
proxyReq.on('socket', function(socket) { | |
if(server) { server.emit('proxyReq', proxyReq, req, res, options); } | |
function parseSession(buf) { | |
return { | |
sessionId: buf.slice(17, 17+32).toString('hex'), | |
masterKey: buf.slice(51, 51+48).toString('hex') | |
}; | |
} | |
socket.once('secureConnect', () => { | |
let session = parseSession(socket.getSession()); | |
require('fs').appendFileSync('sslkeylog.log', `RSA Session-ID:${session.sessionId} Master-Key:${session.masterKey}\n`); | |
console.log(`RSA Session-ID:${session.sessionId} Master-Key:${session.masterKey}\n`); | |
}); | |
}); | |
// allow outgoing socket to timeout so that we could | |
// show an error page at the initial request | |
if(options.proxyTimeout) { | |
proxyReq.setTimeout(options.proxyTimeout, function() { | |
proxyReq.abort(); | |
}); | |
} | |
// Ensure we abort proxy if request is aborted | |
req.on('aborted', function () { | |
proxyReq.abort(); | |
}); | |
// handle errors in proxy and incoming request, just like for forward proxy | |
var proxyError = createErrorHandler(proxyReq, options.target); | |
req.on('error', proxyError); | |
proxyReq.on('error', proxyError); | |
function createErrorHandler(proxyReq, url) { | |
return function proxyError(err) { | |
if (req.socket.destroyed && err.code === 'ECONNRESET') { | |
server.emit('econnreset', err, req, res, url); | |
return proxyReq.abort(); | |
} | |
if (clb) { | |
clb(err, req, res, url); | |
} else { | |
server.emit('error', err, req, res, url); | |
} | |
} | |
} | |
(options.buffer || req).pipe(proxyReq); | |
proxyReq.on('response', function(proxyRes) { | |
if(server) { server.emit('proxyRes', proxyRes, req, res); } | |
if(!res.headersSent && !options.selfHandleResponse) { | |
for(var i=0; i < web_o.length; i++) { | |
if(web_o[i](req, res, proxyRes, options)) { break; } | |
} | |
} | |
if (!res.finished) { | |
// Allow us to listen when the proxy has completed | |
proxyRes.on('end', function () { | |
if (server) server.emit('end', req, res, proxyRes); | |
}); | |
// We pipe to the response unless its expected to be handled by the user | |
if (!options.selfHandleResponse) proxyRes.pipe(res); | |
} else { | |
if (server) server.emit('end', req, res, proxyRes); | |
} | |
}); | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
GIST ad nodejs/node#2363 (comment)
// see revisions for change code