123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- 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['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 && !proxyReq.getHeader('expect')) {
- 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 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);
- }
- });
- }
- };
|