mirror of
https://github.com/http-party/node-http-proxy.git
synced 2025-12-08 20:59:18 +00:00
* Emit disconnected event instead of error when ECONNRESET ECONNRESET means the other side of the TCP conversation abruptly closed its end of the connection. If we get an ECONNRESET error from the proxy request and the socket is destroyed this means that the client has closed his connection, and emit this as an error can lead to confusion and hacks to filter that kind of message. I think that the best we can do is abort and emit another event, so if any caller wants to handle with that kind of error, he can by listen the disconnected event. https://github.com/nodejitsu/node-http-proxy/issues/813 * code standards, move econnreset check, change ev name
175 lines
4.8 KiB
JavaScript
175 lines
4.8 KiB
JavaScript
var http = require('http'),
|
||
https = require('https'),
|
||
web_o = require('./web-outgoing'),
|
||
common = require('../common'),
|
||
passes = exports;
|
||
|
||
web_o = Object.keys(web_o).map(function(pass) {
|
||
return web_o[pass];
|
||
});
|
||
|
||
/*!
|
||
* 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.
|
||
*/
|
||
|
||
[ // <--
|
||
|
||
/**
|
||
* 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
|
||
*/
|
||
|
||
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
|
||
*/
|
||
|
||
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
|
||
*/
|
||
|
||
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
|
||
*/
|
||
|
||
function stream(req, res, options, _, server, clb) {
|
||
|
||
// And we begin!
|
||
server.emit('start', req, res, options.target)
|
||
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')
|
||
);
|
||
(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); }
|
||
});
|
||
|
||
// 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 on incoming request as well as it makes sense to
|
||
req.on('error', proxyError);
|
||
|
||
// Error Handler
|
||
proxyReq.on('error', proxyError);
|
||
|
||
function proxyError (err){
|
||
if (req.socket.destroyed && err.code === 'ECONNRESET') {
|
||
server.emit('econnreset', err, req, res, options.target);
|
||
return proxyReq.abort();
|
||
}
|
||
|
||
if (clb) {
|
||
clb(err, req, res, options.target);
|
||
} else {
|
||
server.emit('error', err, req, res, options.target);
|
||
}
|
||
}
|
||
|
||
(options.buffer || req).pipe(proxyReq);
|
||
|
||
proxyReq.on('response', function(proxyRes) {
|
||
if(server) { server.emit('proxyRes', proxyRes, req, res); }
|
||
for(var i=0; i < web_o.length; i++) {
|
||
if(web_o[i](req, res, proxyRes, options)) { break; }
|
||
}
|
||
|
||
// Allow us to listen when the proxy has completed
|
||
proxyRes.on('end', function () {
|
||
server.emit('end', req, res, proxyRes);
|
||
});
|
||
|
||
proxyRes.pipe(res);
|
||
});
|
||
|
||
//proxyReq.end();
|
||
}
|
||
|
||
] // <--
|
||
.forEach(function(func) {
|
||
passes[func.name] = func;
|
||
});
|