function ProxyServer(options) { EE3.call(this); options = options || {}; options.prependPath = options.prependPath === false ? false : true; this.web = this.proxyRequest = createRightProxy('web')(options); this.ws = this.proxyWebsocketRequest = createRightProxy('ws')(options); this.options = options; this.webPasses = Object.keys(web).map(function(pass) { return web[pass]; }); this.wsPasses = Object.keys(ws).map(function(pass) { return ws[pass]; }); this.on('error', this.onError, this); }
n/a
function createProxyServer(options) {
/*
* `options` is needed and it must have the following layout:
*
* {
* target : <url string to be parsed with the url module>
* forward: <url string to be parsed with the url module>
* agent : <object to be passed to http(s).request>
* ssl : <object to be passed to https.createServer()>
* ws : <true/false, if you want to proxy websockets>
* xfwd : <true/false, adds x-forward headers>
* secure : <true/false, verify SSL certificate>
* toProxy: <true/false, explicitly specify if we are proxying to another proxy>
* prependPath: <true/false, Default: true - specify whether you want to prepend the target's path to the proxy path>
* ignorePath: <true/false, Default: false - specify whether you want to ignore the proxy path of the incoming request>
* localAddress : <Local interface string to bind for outgoing connections>
* changeOrigin: <true/false, Default: false - changes the origin of the host header to the target URL>
* preserveHeaderKeyCase: <true/false, Default: false - specify whether you want to keep letter case of response header key
>
* auth : Basic authentication i.e. 'user:password' to compute an Authorization header.
* hostRewrite: rewrites the location hostname on (301/302/307/308) redirects, Default: null.
* autoRewrite: rewrites the location host/port on (301/302/307/308) redirects based on requested host/port. Default: false
.
* protocolRewrite: rewrites the location protocol on (301/302/307/308) redirects to 'http' or 'https'. Default: null.
* }
*
* NOTE: `options.ws` and `options.ssl` are optional.
* `options.target and `options.forward` cannot be
* both missing
* }
*/
return new ProxyServer(options);
}
n/a
function createProxyServer(options) {
/*
* `options` is needed and it must have the following layout:
*
* {
* target : <url string to be parsed with the url module>
* forward: <url string to be parsed with the url module>
* agent : <object to be passed to http(s).request>
* ssl : <object to be passed to https.createServer()>
* ws : <true/false, if you want to proxy websockets>
* xfwd : <true/false, adds x-forward headers>
* secure : <true/false, verify SSL certificate>
* toProxy: <true/false, explicitly specify if we are proxying to another proxy>
* prependPath: <true/false, Default: true - specify whether you want to prepend the target's path to the proxy path>
* ignorePath: <true/false, Default: false - specify whether you want to ignore the proxy path of the incoming request>
* localAddress : <Local interface string to bind for outgoing connections>
* changeOrigin: <true/false, Default: false - changes the origin of the host header to the target URL>
* preserveHeaderKeyCase: <true/false, Default: false - specify whether you want to keep letter case of response header key
>
* auth : Basic authentication i.e. 'user:password' to compute an Authorization header.
* hostRewrite: rewrites the location hostname on (301/302/307/308) redirects, Default: null.
* autoRewrite: rewrites the location host/port on (301/302/307/308) redirects based on requested host/port. Default: false
.
* protocolRewrite: rewrites the location protocol on (301/302/307/308) redirects to 'http' or 'https'. Default: null.
* }
*
* NOTE: `options.ws` and `options.ssl` are optional.
* `options.target and `options.forward` cannot be
* both missing
* }
*/
return new ProxyServer(options);
}
...
A new proxy is created by calling `createProxyServer` and passing
an `options` object as argument ([valid properties are available here](lib/http-proxy.js#L33-L50))
```javascript
var httpProxy = require('http-proxy');
var proxy = httpProxy.createProxyServer(options); // See (†)
```
†Unless listen(..) is invoked on the object, this does not create a webserver. See below.
An object will be returned with four methods:
* web `req, res, [options]` (used for proxying regular HTTP(S) requests)
* ws `req, socket, head, [options]` (used for proxying WS(S) requests)
...
function createProxyServer(options) {
/*
* `options` is needed and it must have the following layout:
*
* {
* target : <url string to be parsed with the url module>
* forward: <url string to be parsed with the url module>
* agent : <object to be passed to http(s).request>
* ssl : <object to be passed to https.createServer()>
* ws : <true/false, if you want to proxy websockets>
* xfwd : <true/false, adds x-forward headers>
* secure : <true/false, verify SSL certificate>
* toProxy: <true/false, explicitly specify if we are proxying to another proxy>
* prependPath: <true/false, Default: true - specify whether you want to prepend the target's path to the proxy path>
* ignorePath: <true/false, Default: false - specify whether you want to ignore the proxy path of the incoming request>
* localAddress : <Local interface string to bind for outgoing connections>
* changeOrigin: <true/false, Default: false - changes the origin of the host header to the target URL>
* preserveHeaderKeyCase: <true/false, Default: false - specify whether you want to keep letter case of response header key
>
* auth : Basic authentication i.e. 'user:password' to compute an Authorization header.
* hostRewrite: rewrites the location hostname on (301/302/307/308) redirects, Default: null.
* autoRewrite: rewrites the location host/port on (301/302/307/308) redirects based on requested host/port. Default: false
.
* protocolRewrite: rewrites the location protocol on (301/302/307/308) redirects to 'http' or 'https'. Default: null.
* }
*
* NOTE: `options.ws` and `options.ssl` are optional.
* `options.target and `options.forward` cannot be
* both missing
* }
*/
return new ProxyServer(options);
}
...
* ws `req, socket, head, [options]` (used for proxying WS(S) requests)
* listen `port` (a function that wraps the object in a webserver, for your convenience)
* close `[callback]` (a function that closes the inner webserver and stops listening on given port)
It is then possible to proxy requests by calling these functions
```javascript
http.createServer(function(req, res) {
proxy.web(req, res, { target: 'http://mytarget.com:8080' });
});
```
Errors can be listened on either using the Event Emitter API
```javascript
...
function ProxyServer(options) { EE3.call(this); options = options || {}; options.prependPath = options.prependPath === false ? false : true; this.web = this.proxyRequest = createRightProxy('web')(options); this.ws = this.proxyWebsocketRequest = createRightProxy('ws')(options); this.options = options; this.webPasses = Object.keys(web).map(function(pass) { return web[pass]; }); this.wsPasses = Object.keys(ws).map(function(pass) { return ws[pass]; }); this.on('error', this.onError, this); }
n/a
function EventEmitter() { /* Nothing to set */ }
n/a
getPort = function (req) { var res = req.headers.host ? req.headers.host.match(/:(\d+)/) : ''; return res ? res[1] : common.hasEncryptedConnection(req) ? '443' : '80'; }
...
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] ? ',' : '') +
...
hasEncryptedConnection = function (req) { return Boolean(req.connection.encrypted || req.connection.pair); }
...
* @api private
*/
common.getPort = function(req) {
var res = req.headers.host ? req.headers.host.match(/:(\d+)/) : '';
return res ?
res[1] :
common.hasEncryptedConnection(req) ? '443' : '80';
};
/**
* Check if the request has an encrypted connection.
*
* @param {Request} req Incoming HTTP request.
*
...
function rewriteCookieDomain(header, config) { if (Array.isArray(header)) { return header.map(function (headerElement) { return rewriteCookieDomain(headerElement, config); }); } return header.replace(cookieDomainRegex, function(match, prefix, previousDomain) { var newDomain; if (previousDomain in config) { newDomain = config[previousDomain]; } else if ('*' in config) { newDomain = config['*']; } else { //no match, return previous domain return match; } if (newDomain) { //replace domain return prefix + newDomain; } else { //remove domain return ''; } }); }
...
writeHeaders: function writeHeaders(req, res, proxyRes, options) {
var rewriteCookieDomainConfig = options.cookieDomainRewrite,
preserveHeaderKeyCase = options.preserveHeaderKeyCase,
rawHeaderKeyMap,
setHeader = function(key, header) {
if (header == undefined) return;
if (rewriteCookieDomainConfig && key.toLowerCase() === 'set-cookie') {
header = common.rewriteCookieDomain(header, rewriteCookieDomainConfig);
}
res.setHeader(String(key).trim(), header);
};
if (typeof rewriteCookieDomainConfig === 'string') { //also test for ''
rewriteCookieDomainConfig = { '*': rewriteCookieDomainConfig };
}
...
setupOutgoing = function (outgoing, options, req, forward) { outgoing.port = options[forward || 'target'].port || (isSSL.test(options[forward || 'target'].protocol) ? 443 : 80); ['host', 'hostname', 'socketPath', 'pfx', 'key', 'passphrase', 'cert', 'ca', 'ciphers', 'secureProtocol'].forEach( function(e) { outgoing[e] = options[forward || 'target'][e]; } ); outgoing.method = req.method; outgoing.headers = extend({}, req.headers); if (options.headers){ extend(outgoing.headers, options.headers); } if (options.auth) { outgoing.auth = options.auth; } if (options.ca) { outgoing.ca = options.ca; } if (isSSL.test(options[forward || 'target'].protocol)) { outgoing.rejectUnauthorized = (typeof options.secure === "undefined") ? true : options.secure; } outgoing.agent = options.agent || false; outgoing.localAddress = options.localAddress; // // Remark: If we are false and not upgrading, set the connection: close. This is the right thing to do // as node core doesn't handle this COMPLETELY properly yet. // if (!outgoing.agent) { outgoing.headers = outgoing.headers || {}; if (typeof outgoing.headers.connection !== 'string' || !upgradeHeader.test(outgoing.headers.connection) ) { outgoing.headers.connection = 'close'; } } // the final path is target path + relative path requested by user: var target = options[forward || 'target']; var targetPath = target && options.prependPath !== false ? (target.path || '') : ''; // // Remark: Can we somehow not use url.parse as a perf optimization? // var outgoingPath = !options.toProxy ? (url.parse(req.url).path || '') : req.url; // // Remark: ignorePath will just straight up ignore whatever the request's // path is. This can be labeled as FOOT-GUN material if you do not know what // you are doing and are using conflicting options. // outgoingPath = !options.ignorePath ? outgoingPath : ''; outgoing.path = common.urlJoin(targetPath, outgoingPath); if (options.changeOrigin) { outgoing.headers.host = required(outgoing.port, options[forward || 'target'].protocol) && !hasPort(outgoing.host) ? outgoing.host + ':' + outgoing.port : outgoing.host; } return outgoing; }
...
/**
* Copies the right headers from `options` and `req` to
* `outgoing` which is then used to fire the proxied
* request.
*
* Examples:
*
* common.setupOutgoing(outgoing, options, req)
* // => { host: ..., hostname: ...}
*
* @param {Object} Outgoing Base object to be filled with required properties
* @param {Object} Options Config object passed to the proxy
* @param {ClientRequest} Req Request Object
* @param {String} Forward String to select forward or target
*
...
setupSocket = function (socket) { socket.setTimeout(0); socket.setNoDelay(true); socket.setKeepAlive(true, 0); return socket; }
...
/**
* Set the proper configuration for sockets,
* set no delay and set keep alive, also set
* the timeout to 0.
*
* Examples:
*
* common.setupSocket(socket)
* // => Socket
*
* @param {Socket} Socket instance to setup
*
* @return {Socket} Return the configured socket.
*
* @api private
...
urlJoin = function () { // // We do not want to mess with the query string. All we want to touch is the path. // var args = Array.prototype.slice.call(arguments), lastIndex = args.length - 1, last = args[lastIndex], lastSegs = last.split('?'), retSegs; args[lastIndex] = lastSegs.shift(); // // Join all strings, but remove empty strings so we don't get extra slashes from // joining e.g. ['', 'am'] // retSegs = [ args.filter(Boolean).join('/') .replace(/\/+/g, '/') .replace('http:/', 'http://') .replace('https:/', 'https://') ]; // Only join the query string if it exists so we don't have trailing a '?' // on every request // Handle case where there could be multiple ? in the URL. retSegs.push.apply(retSegs, lastSegs); return retSegs.join('?') }
...
//
// Remark: ignorePath will just straight up ignore whatever the request's
// path is. This can be labeled as FOOT-GUN material if you do not know what
// you are doing and are using conflicting options.
//
outgoingPath = !options.ignorePath ? outgoingPath : '';
outgoing.path = common.urlJoin(targetPath, outgoingPath);
if (options.changeOrigin) {
outgoing.headers.host =
required(outgoing.port, options[forward || 'target'].protocol) && !hasPort(outgoing.host)
? outgoing.host + ':' + outgoing.port
: outgoing.host;
}
...
function ProxyServer(options) { EE3.call(this); options = options || {}; options.prependPath = options.prependPath === false ? false : true; this.web = this.proxyRequest = createRightProxy('web')(options); this.ws = this.proxyWebsocketRequest = createRightProxy('ws')(options); this.options = options; this.webPasses = Object.keys(web).map(function(pass) { return web[pass]; }); this.wsPasses = Object.keys(ws).map(function(pass) { return ws[pass]; }); this.on('error', this.onError, this); }
n/a
function createProxyServer(options) {
/*
* `options` is needed and it must have the following layout:
*
* {
* target : <url string to be parsed with the url module>
* forward: <url string to be parsed with the url module>
* agent : <object to be passed to http(s).request>
* ssl : <object to be passed to https.createServer()>
* ws : <true/false, if you want to proxy websockets>
* xfwd : <true/false, adds x-forward headers>
* secure : <true/false, verify SSL certificate>
* toProxy: <true/false, explicitly specify if we are proxying to another proxy>
* prependPath: <true/false, Default: true - specify whether you want to prepend the target's path to the proxy path>
* ignorePath: <true/false, Default: false - specify whether you want to ignore the proxy path of the incoming request>
* localAddress : <Local interface string to bind for outgoing connections>
* changeOrigin: <true/false, Default: false - changes the origin of the host header to the target URL>
* preserveHeaderKeyCase: <true/false, Default: false - specify whether you want to keep letter case of response header key
>
* auth : Basic authentication i.e. 'user:password' to compute an Authorization header.
* hostRewrite: rewrites the location hostname on (301/302/307/308) redirects, Default: null.
* autoRewrite: rewrites the location host/port on (301/302/307/308) redirects based on requested host/port. Default: false
.
* protocolRewrite: rewrites the location protocol on (301/302/307/308) redirects to 'http' or 'https'. Default: null.
* }
*
* NOTE: `options.ws` and `options.ssl` are optional.
* `options.target and `options.forward` cannot be
* both missing
* }
*/
return new ProxyServer(options);
}
n/a
function createProxyServer(options) {
/*
* `options` is needed and it must have the following layout:
*
* {
* target : <url string to be parsed with the url module>
* forward: <url string to be parsed with the url module>
* agent : <object to be passed to http(s).request>
* ssl : <object to be passed to https.createServer()>
* ws : <true/false, if you want to proxy websockets>
* xfwd : <true/false, adds x-forward headers>
* secure : <true/false, verify SSL certificate>
* toProxy: <true/false, explicitly specify if we are proxying to another proxy>
* prependPath: <true/false, Default: true - specify whether you want to prepend the target's path to the proxy path>
* ignorePath: <true/false, Default: false - specify whether you want to ignore the proxy path of the incoming request>
* localAddress : <Local interface string to bind for outgoing connections>
* changeOrigin: <true/false, Default: false - changes the origin of the host header to the target URL>
* preserveHeaderKeyCase: <true/false, Default: false - specify whether you want to keep letter case of response header key
>
* auth : Basic authentication i.e. 'user:password' to compute an Authorization header.
* hostRewrite: rewrites the location hostname on (301/302/307/308) redirects, Default: null.
* autoRewrite: rewrites the location host/port on (301/302/307/308) redirects based on requested host/port. Default: false
.
* protocolRewrite: rewrites the location protocol on (301/302/307/308) redirects to 'http' or 'https'. Default: null.
* }
*
* NOTE: `options.ws` and `options.ssl` are optional.
* `options.target and `options.forward` cannot be
* both missing
* }
*/
return new ProxyServer(options);
}
...
A new proxy is created by calling `createProxyServer` and passing
an `options` object as argument ([valid properties are available here](lib/http-proxy.js#L33-L50))
```javascript
var httpProxy = require('http-proxy');
var proxy = httpProxy.createProxyServer(options); // See (†)
```
†Unless listen(..) is invoked on the object, this does not create a webserver. See below.
An object will be returned with four methods:
* web `req, res, [options]` (used for proxying regular HTTP(S) requests)
* ws `req, socket, head, [options]` (used for proxying WS(S) requests)
...
function createProxyServer(options) {
/*
* `options` is needed and it must have the following layout:
*
* {
* target : <url string to be parsed with the url module>
* forward: <url string to be parsed with the url module>
* agent : <object to be passed to http(s).request>
* ssl : <object to be passed to https.createServer()>
* ws : <true/false, if you want to proxy websockets>
* xfwd : <true/false, adds x-forward headers>
* secure : <true/false, verify SSL certificate>
* toProxy: <true/false, explicitly specify if we are proxying to another proxy>
* prependPath: <true/false, Default: true - specify whether you want to prepend the target's path to the proxy path>
* ignorePath: <true/false, Default: false - specify whether you want to ignore the proxy path of the incoming request>
* localAddress : <Local interface string to bind for outgoing connections>
* changeOrigin: <true/false, Default: false - changes the origin of the host header to the target URL>
* preserveHeaderKeyCase: <true/false, Default: false - specify whether you want to keep letter case of response header key
>
* auth : Basic authentication i.e. 'user:password' to compute an Authorization header.
* hostRewrite: rewrites the location hostname on (301/302/307/308) redirects, Default: null.
* autoRewrite: rewrites the location host/port on (301/302/307/308) redirects based on requested host/port. Default: false
.
* protocolRewrite: rewrites the location protocol on (301/302/307/308) redirects to 'http' or 'https'. Default: null.
* }
*
* NOTE: `options.ws` and `options.ssl` are optional.
* `options.target and `options.forward` cannot be
* both missing
* }
*/
return new ProxyServer(options);
}
...
* ws `req, socket, head, [options]` (used for proxying WS(S) requests)
* listen `port` (a function that wraps the object in a webserver, for your convenience)
* close `[callback]` (a function that closes the inner webserver and stops listening on given port)
It is then possible to proxy requests by calling these functions
```javascript
http.createServer(function(req, res) {
proxy.web(req, res, { target: 'http://mytarget.com:8080' });
});
```
Errors can be listened on either using the Event Emitter API
```javascript
...
function EventEmitter() { /* Nothing to set */ }
n/a
after = function (type, passName, callback) { if (type !== 'ws' && type !== 'web') { throw new Error('type must be `web` or `ws`'); } var passes = (type === 'ws') ? this.wsPasses : this.webPasses, i = false; passes.forEach(function(v, idx) { if(v.name === passName) i = idx; }) if(i === false) throw new Error('No such pass'); passes.splice(i++, 0, callback); }
n/a
before = function (type, passName, callback) { if (type !== 'ws' && type !== 'web') { throw new Error('type must be `web` or `ws`'); } var passes = (type === 'ws') ? this.wsPasses : this.webPasses, i = false; passes.forEach(function(v, idx) { if(v.name === passName) i = idx; }) if(i === false) throw new Error('No such pass'); passes.splice(i, 0, callback); }
n/a
close = function (callback) { var self = this; if (this._server) { this._server.close(done); } // Wrap callback to nullify server after all open connections are closed. function done() { self._server = null; if (callback) { callback.apply(null, arguments); } }; }
...
var proxy = new httpProxy.createProxyServer({
target: {
host: 'localhost',
port: 1337
}
});
proxy.close();
```
**[Back to top](#table-of-contents)**
### Miscellaneous
#### ProxyTable API
...
listen = function (port, hostname) { var self = this, closure = function(req, res) { self.web(req, res); }; this._server = this.options.ssl ? https.createServer(this.options.ssl, closure) : http.createServer(closure); if(this.options.ws) { this._server.on('upgrade', function(req, socket, head) { self.ws(req, socket, head); }); } this._server.listen(port, hostname); return this; }
...
```js
var http = require('http'),
httpProxy = require('http-proxy');
//
// Create your proxy server and set the target in the options.
//
httpProxy.createProxyServer({target:'http://localhost:9000'}).listen(8000); //
See (†)
//
// Create your target server
//
http.createServer(function (req, res) {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.write('request successfully proxied!' + '\n' + JSON.stringify(req.headers, true, 2));
...
onError = function (err) { // // Remark: Replicate node core behavior using EE3 // so we force people to handle their own errors // if(this.listeners('error').length === 1) { throw err; } }
n/a
function EventEmitter() { /* Nothing to set */ }
n/a
function on(event, fn, context) { var listener = new EE(fn, context || this) , evt = prefix ? prefix + event : event; if (!this._events) this._events = prefix ? {} : Object.create(null); if (!this._events[evt]) this._events[evt] = listener; else { if (!this._events[evt].fn) this._events[evt].push(listener); else this._events[evt] = [ this._events[evt], listener ]; } return this; }
n/a
function emit(event, a1, a2, a3, a4, a5) { var evt = prefix ? prefix + event : event; if (!this._events || !this._events[evt]) return false; var listeners = this._events[evt] , len = arguments.length , args , i; if ('function' === typeof listeners.fn) { if (listeners.once) this.removeListener(event, listeners.fn, undefined, true); switch (len) { case 1: return listeners.fn.call(listeners.context), true; case 2: return listeners.fn.call(listeners.context, a1), true; case 3: return listeners.fn.call(listeners.context, a1, a2), true; case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true; case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true; case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true; } for (i = 1, args = new Array(len -1); i < len; i++) { args[i - 1] = arguments[i]; } listeners.fn.apply(listeners.context, args); } else { var length = listeners.length , j; for (i = 0; i < length; i++) { if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true); switch (len) { case 1: listeners[i].fn.call(listeners[i].context); break; case 2: listeners[i].fn.call(listeners[i].context, a1); break; case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break; default: if (!args) for (j = 1, args = new Array(len -1); j < len; j++) { args[j - 1] = arguments[j]; } listeners[i].fn.apply(listeners[i].context, args); } } } return true; }
...
['target', 'forward'].forEach(function(e) {
if (typeof options[e] === 'string')
options[e] = parse_url(options[e]);
});
if (!options.target && !options.forward) {
return this.emit('error', new Error('Must provide a proper URL as target
'));
}
for(var i=0; i < passes.length; i++) {
/**
* Call of passes functions
* pass(req, res, options, head)
*
...
function eventNames() { var events = this._events , names = [] , name; if (!events) return names; for (name in events) { if (has.call(events, name)) names.push(prefix ? name.slice(1) : name); } if (Object.getOwnPropertySymbols) { return names.concat(Object.getOwnPropertySymbols(events)); } return names; }
n/a
function listeners(event, exists) { var evt = prefix ? prefix + event : event , available = this._events && this._events[evt]; if (exists) return !!available; if (!available) return []; if (available.fn) return [available.fn]; for (var i = 0, l = available.length, ee = new Array(l); i < l; i++) { ee[i] = available[i].fn; } return ee; }
...
require('util').inherits(ProxyServer, EE3);
ProxyServer.prototype.onError = function (err) {
//
// Remark: Replicate node core behavior using EE3
// so we force people to handle their own errors
//
if(this.listeners('error').length === 1) {
throw err;
}
};
ProxyServer.prototype.listen = function(port, hostname) {
var self = this,
closure = function(req, res) { self.web(req, res); };
...
function removeListener(event, fn, context, once) { var evt = prefix ? prefix + event : event; if (!this._events || !this._events[evt]) return this; var listeners = this._events[evt] , events = []; if (fn) { if (listeners.fn) { if ( listeners.fn !== fn || (once && !listeners.once) || (context && listeners.context !== context) ) { events.push(listeners); } } else { for (var i = 0, length = listeners.length; i < length; i++) { if ( listeners[i].fn !== fn || (once && !listeners[i].once) || (context && listeners[i].context !== context) ) { events.push(listeners[i]); } } } } // // Reset the array, or remove it completely if we have no more listeners. // if (events.length) { this._events[evt] = events.length === 1 ? events[0] : events; } else { delete this._events[evt]; } return this; }
n/a
function on(event, fn, context) { var listener = new EE(fn, context || this) , evt = prefix ? prefix + event : event; if (!this._events) this._events = prefix ? {} : Object.create(null); if (!this._events[evt]) this._events[evt] = listener; else { if (!this._events[evt].fn) this._events[evt].push(listener); else this._events[evt] = [ this._events[evt], listener ]; } return this; }
...
proxy.web(req, res, { target: 'http://mytarget.com:8080' });
});
```
Errors can be listened on either using the Event Emitter API
```javascript
proxy.on('error', function(e) {
...
});
```
or using the callback API
```javascript
...
function once(event, fn, context) { var listener = new EE(fn, context || this, true) , evt = prefix ? prefix + event : event; if (!this._events) this._events = prefix ? {} : Object.create(null); if (!this._events[evt]) this._events[evt] = listener; else { if (!this._events[evt].fn) this._events[evt].push(listener); else this._events[evt] = [ this._events[evt], listener ]; } return this; }
n/a
function removeAllListeners(event) { if (!this._events) return this; if (event) delete this._events[prefix ? prefix + event : event]; else this._events = prefix ? {} : Object.create(null); return this; }
n/a
function removeListener(event, fn, context, once) { var evt = prefix ? prefix + event : event; if (!this._events || !this._events[evt]) return this; var listeners = this._events[evt] , events = []; if (fn) { if (listeners.fn) { if ( listeners.fn !== fn || (once && !listeners.once) || (context && listeners.context !== context) ) { events.push(listeners); } } else { for (var i = 0, length = listeners.length; i < length; i++) { if ( listeners[i].fn !== fn || (once && !listeners[i].once) || (context && listeners[i].context !== context) ) { events.push(listeners[i]); } } } } // // Reset the array, or remove it completely if we have no more listeners. // if (events.length) { this._events[evt] = events.length === 1 ? events[0] : events; } else { delete this._events[evt]; } return this; }
n/a
function setMaxListeners() { return this; }
n/a
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'] || ''; }
n/a
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']; } }
n/a
function stream(req, res, options, _, server, clb) { // And we begin! server.emit('start', req, res, options.target || options.forward); 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); } }); // 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); } 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(); }
n/a
function timeout(req, res, options) { if(options.timeout) { req.socket.setTimeout(options.timeout); } }
n/a
function removeChunked(req, res, proxyRes) { if (req.httpVersion === '1.0') { delete proxyRes.headers['transfer-encoding']; } }
n/a
function setConnection(req, res, proxyRes) { if (req.httpVersion === '1.0') { proxyRes.headers.connection = req.headers.connection || 'close'; } else if (req.httpVersion !== '2.0' && !proxyRes.headers.connection) { proxyRes.headers.connection = req.headers.connection || 'keep-alive'; } }
n/a
function setRedirectHostRewrite(req, res, proxyRes, options) { if ((options.hostRewrite || options.autoRewrite || options.protocolRewrite) && proxyRes.headers['location'] && redirectRegex.test(proxyRes.statusCode)) { var target = url.parse(options.target); var u = url.parse(proxyRes.headers['location']); // make sure the redirected host matches the target host before rewriting if (target.host != u.host) { return; } if (options.hostRewrite) { u.host = options.hostRewrite; } else if (options.autoRewrite) { u.host = req.headers['host']; } if (options.protocolRewrite) { u.protocol = options.protocolRewrite; } proxyRes.headers['location'] = u.format(); } }
n/a
function writeHeaders(req, res, proxyRes, options) { var rewriteCookieDomainConfig = options.cookieDomainRewrite, preserveHeaderKeyCase = options.preserveHeaderKeyCase, rawHeaderKeyMap, setHeader = function(key, header) { if (header == undefined) return; if (rewriteCookieDomainConfig && key.toLowerCase() === 'set-cookie') { header = common.rewriteCookieDomain(header, rewriteCookieDomainConfig); } res.setHeader(String(key).trim(), header); }; if (typeof rewriteCookieDomainConfig === 'string') { //also test for '' rewriteCookieDomainConfig = { '*': rewriteCookieDomainConfig }; } // message.rawHeaders is added in: v0.11.6 // https://nodejs.org/api/http.html#http_message_rawheaders if (preserveHeaderKeyCase && proxyRes.rawHeaders != undefined) { rawHeaderKeyMap = {}; for (var i = 0; i < proxyRes.rawHeaders.length; i += 2) { var key = proxyRes.rawHeaders[i]; rawHeaderKeyMap[key.toLowerCase()] = key; } } Object.keys(proxyRes.headers).forEach(function(key) { var header = proxyRes.headers[key]; if (preserveHeaderKeyCase && rawHeaderKeyMap) { key = rawHeaderKeyMap[key] || key; } setHeader(key, header); }); }
n/a
function writeStatusCode(req, res, proxyRes) { // From Node.js docs: response.writeHead(statusCode[, statusMessage][, headers]) if(proxyRes.statusMessage) { res.writeHead(proxyRes.statusCode, proxyRes.statusMessage); } else { res.writeHead(proxyRes.statusCode); } }
n/a
function XHeaders(req, socket, options) { if(!options.xfwd) return; var values = { for : req.connection.remoteAddress || req.socket.remoteAddress, port : common.getPort(req), proto: common.hasEncryptedConnection(req) ? 'wss' : 'ws' }; ['for', 'port', 'proto'].forEach(function(header) { req.headers['x-forwarded-' + header] = (req.headers['x-forwarded-' + header] || '') + (req.headers['x-forwarded-' + header] ? ',' : '') + values[header]; }); }
n/a
function checkMethodAndHeader(req, socket) { if (req.method !== 'GET' || !req.headers.upgrade) { socket.destroy(); return true; } if (req.headers.upgrade.toLowerCase() !== 'websocket') { socket.destroy(); return true; } }
n/a
function stream(req, socket, options, head, server, clb) { common.setupSocket(socket); if (head && head.length) socket.unshift(head); var proxyReq = (common.isSSL.test(options.target.protocol) ? https : http).request( common.setupOutgoing(options.ssl || {}, options, req) ); // Enable developers to modify the proxyReq before headers are sent if (server) { server.emit('proxyReqWs', proxyReq, req, socket, options, head); } // Error Handler proxyReq.on('error', onOutgoingError); proxyReq.on('response', function (res) { // if upgrade event isn't going to happen, close the socket if (!res.upgrade) socket.end(); }); proxyReq.on('upgrade', function(proxyRes, proxySocket, proxyHead) { proxySocket.on('error', onOutgoingError); // Allow us to listen when the websocket has completed proxySocket.on('end', function () { server.emit('close', proxyRes, proxySocket, proxyHead); }); // The pipe below will end proxySocket if socket closes cleanly, but not // if it errors (eg, vanishes from the net and starts returning // EHOSTUNREACH). We need to do that explicitly. socket.on('error', function () { proxySocket.end(); }); common.setupSocket(proxySocket); if (proxyHead && proxyHead.length) proxySocket.unshift(proxyHead); // // Remark: Handle writing the headers to the socket when switching protocols // Also handles when a header is an array // socket.write( Object.keys(proxyRes.headers).reduce(function (head, key) { var value = proxyRes.headers[key]; if (!Array.isArray(value)) { head.push(key + ': ' + value); return head; } for (var i = 0; i < value.length; i++) { head.push(key + ': ' + value[i]); } return head; }, ['HTTP/1.1 101 Switching Protocols']) .join('\r\n') + '\r\n\r\n' ); proxySocket.pipe(socket).pipe(proxySocket); server.emit('open', proxySocket); server.emit('proxySocket', proxySocket); //DEPRECATED. }); return proxyReq.end(); // XXX: CHECK IF THIS IS THIS CORRECT function onOutgoingError(err) { if (clb) { clb(err, req, socket); } else { server.emit('error', err, req, socket); } socket.end(); } }
n/a