Commit cfe34a20 authored by Leo Iannacone's avatar Leo Iannacone

updated http-proxy - eventemitter3

parent 6a701184
...@@ -23,7 +23,7 @@ proxies and load balancers. ...@@ -23,7 +23,7 @@ proxies and load balancers.
### Core Concept ### Core Concept
A new proxy is created by calling `createProxyServer` and passing A new proxy is created by calling `createProxyServer` and passing
an `options` object as argument ([valid properties are available here](lib/http-proxy.js#L26-L39)) an `options` object as argument ([valid properties are available here](lib/http-proxy.js#L34-L51))
```javascript ```javascript
var httpProxy = require('http-proxy'); var httpProxy = require('http-proxy');
...@@ -283,6 +283,7 @@ If you are using the `proxyServer.listen` method, the following options are also ...@@ -283,6 +283,7 @@ If you are using the `proxyServer.listen` method, the following options are also
* **ssl**: object to be passed to https.createServer() * **ssl**: object to be passed to https.createServer()
* **ws**: true/false, if you want to proxy websockets * **ws**: true/false, if you want to proxy websockets
* **xfwd**: true/false, adds x-forward headers * **xfwd**: true/false, adds x-forward headers
* **toProxy**: passes the absolute URL as the `path` (useful for proxying to proxies)
### Test ### Test
......
...@@ -16,7 +16,7 @@ Check the [README.md](https://github.com/nodejitsu/node-http-proxy/blob/caronte/ ...@@ -16,7 +16,7 @@ Check the [README.md](https://github.com/nodejitsu/node-http-proxy/blob/caronte/
## Proxying ## Proxying
Web proying is done by calling the `.web()` method on a Proxy instance. You can check among some use cases in the [examples folder](https://github.com/nodejitsu/node-http-proxy/tree/caronte/examples/http) Web proxying is done by calling the `.web()` method on a Proxy instance. You can check among some use cases in the [examples folder](https://github.com/nodejitsu/node-http-proxy/tree/caronte/examples/http)
```javascript ```javascript
// //
......
...@@ -8,13 +8,6 @@ var http = require('http'), ...@@ -8,13 +8,6 @@ var http = require('http'),
*/ */
module.exports = httpProxy.Server; module.exports = httpProxy.Server;
module.exports.createProxy = function(options) {
return {
web: httpProxy.createRightProxy('web')(options),
ws: httpProxy.createRightProxy('ws')(options)
};
}
/** /**
* Creates the proxy server. * Creates the proxy server.
* *
...@@ -30,7 +23,9 @@ module.exports.createProxy = function(options) { ...@@ -30,7 +23,9 @@ module.exports.createProxy = function(options) {
* @api public * @api public
*/ */
module.exports.createProxyServer = module.exports.createServer = function createProxyServer(options) { module.exports.createProxyServer =
module.exports.createServer =
module.exports.createProxy = function createProxyServer(options) {
/* /*
* `options` is needed and it must have the following layout: * `options` is needed and it must have the following layout:
* *
...@@ -42,6 +37,8 @@ module.exports.createProxyServer = module.exports.createServer = function create ...@@ -42,6 +37,8 @@ module.exports.createProxyServer = module.exports.createServer = function create
* ws : <true/false, if you want to proxy websockets> * ws : <true/false, if you want to proxy websockets>
* xfwd : <true/false, adds x-forward headers> * xfwd : <true/false, adds x-forward headers>
* secure : <true/false, verify SSL certificate> * secure : <true/false, verify SSL certificate>
* toProxy: <true/false, explicitly specify if we are proxying to another proxy>
* localAddress : <Local interface string to bind for outgoing connections>
* } * }
* *
* NOTE: `options.ws` and `options.ssl` are optional. * NOTE: `options.ws` and `options.ssl` are optional.
......
...@@ -44,7 +44,26 @@ common.setupOutgoing = function(outgoing, options, req, forward) { ...@@ -44,7 +44,26 @@ common.setupOutgoing = function(outgoing, options, req, forward) {
outgoing.agent = options.agent || false; outgoing.agent = options.agent || false;
outgoing.path = url.parse(req.url).path; 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'
|| outgoing.headers.connection.toLowerCase() !== 'upgrade'
) { outgoing.headers.connection = 'close'; }
}
//
// Remark: Can we somehow not use url.parse as a perf optimization?
//
outgoing.path = !options.toProxy
? url.parse(req.url).path
: req.url;
return outgoing; return outgoing;
}; };
...@@ -75,7 +94,7 @@ common.setupSocket = function(socket) { ...@@ -75,7 +94,7 @@ common.setupSocket = function(socket) {
}; };
common.getPort = function(req) { common.getPort = function(req) {
var res = req.headers.host.match(/:(\d+)/); var res = req.headers.host ? req.headers.host.match(/:(\d+)/) : "";
return res ? return res ?
res[1] : res[1] :
req.connection.pair ? '443' : '80' ; req.connection.pair ? '443' : '80' ;
......
...@@ -26,15 +26,10 @@ httpProxy.Server = ProxyServer; ...@@ -26,15 +26,10 @@ httpProxy.Server = ProxyServer;
*/ */
function createRightProxy(type) { function createRightProxy(type) {
var webPasses = Object.keys(web).map(function(pass) {
return web[pass];
});
var wsPasses = Object.keys(ws).map(function(pass) {
return ws[pass];
});
return function(options) { return function(options) {
return function(req, res /*, [head], [opts] */) { return function(req, res /*, [head], [opts] */) {
var passes = (type === 'ws') ? (this.wsPasses || wsPasses) : (this.webPasses || webPasses), var passes = (type === 'ws') ? this.wsPasses : this.webPasses,
args = [].slice.call(arguments), args = [].slice.call(arguments),
cntr = args.length - 1, cntr = args.length - 1,
head, cbl; head, cbl;
......
...@@ -90,6 +90,11 @@ web_o = Object.keys(web_o).map(function(pass) { ...@@ -90,6 +90,11 @@ web_o = Object.keys(web_o).map(function(pass) {
*/ */
function stream(req, res, options, _, server, clb) { function stream(req, res, options, _, server, clb) {
// And we begin!
if (!clb) {
server.emit('start', req, res, options.target)
}
if(options.forward) { if(options.forward) {
// If forward enable, so just pipe the request // If forward enable, so just pipe the request
var forwardReq = (options.forward.protocol === 'https:' ? https : http).request( var forwardReq = (options.forward.protocol === 'https:' ? https : http).request(
...@@ -104,24 +109,40 @@ web_o = Object.keys(web_o).map(function(pass) { ...@@ -104,24 +109,40 @@ web_o = Object.keys(web_o).map(function(pass) {
common.setupOutgoing(options.ssl || {}, options, req) common.setupOutgoing(options.ssl || {}, options, req)
); );
// 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 // Error Handler
proxyReq.on('error', function(err){ proxyReq.on('error', proxyError);
if(options.buffer) { options.buffer.destroy(); }
function proxyError (err){
if (clb) { if (clb) {
clb(err, req, res); clb(err, req, res);
} else { } else {
server.emit('error', err, req, res); server.emit('error', err, req, res);
} }
}); }
(options.buffer || req).pipe(proxyReq); (options.buffer || req).pipe(proxyReq);
proxyReq.on('response', function(proxyRes) { proxyReq.on('response', function(proxyRes) {
if(server) { server.emit('proxyRes', proxyRes); } if(server) { server.emit('proxyRes', proxyRes, req, res); }
for(var i=0; i < web_o.length; i++) { for(var i=0; i < web_o.length; i++) {
if(web_o[i](req, res, proxyRes)) { break; } if(web_o[i](req, res, proxyRes)) { break; }
} }
// Allow us to listen when the proxy has completed
proxyRes.on('end', function () {
if (!clb) {
server.emit('end', req, res, proxyRes);
}
})
proxyRes.pipe(res); proxyRes.pipe(res);
}); });
......
...@@ -40,6 +40,8 @@ EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) { ...@@ -40,6 +40,8 @@ EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {
, i; , i;
if (1 === length) { if (1 === length) {
if (fn.__EE3_once) this.removeListener(event, fn);
switch (len) { switch (len) {
case 1: case 1:
fn.call(fn.__EE3_context || this); fn.call(fn.__EE3_context || this);
...@@ -67,16 +69,14 @@ EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) { ...@@ -67,16 +69,14 @@ EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {
fn.apply(fn.__EE3_context || this, args); fn.apply(fn.__EE3_context || this, args);
} }
if (fn.__EE3_once) this.removeListener(event, fn);
} else { } else {
for (i = 1, args = new Array(len -1); i < len; i++) { for (i = 1, args = new Array(len -1); i < len; i++) {
args[i - 1] = arguments[i]; args[i - 1] = arguments[i];
} }
for (i = 0; i < length; fn = listeners[++i]) { for (i = 0; i < length; fn = listeners[++i]) {
fn.apply(fn.__EE3_context || this, args);
if (fn.__EE3_once) this.removeListener(event, fn); if (fn.__EE3_once) this.removeListener(event, fn);
fn.apply(fn.__EE3_context || this, args);
} }
} }
......
{ {
"name": "eventemitter3", "name": "eventemitter3",
"version": "0.1.1", "version": "0.1.2",
"description": "EventEmitter3 focuses on performance while maintaining a Node.js AND browser compatible interface. This the source of the same EventEmitter that is used in Primus.", "description": "EventEmitter3 focuses on performance while maintaining a Node.js AND browser compatible interface. This the source of the same EventEmitter that is used in Primus.",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
...@@ -17,8 +17,12 @@ ...@@ -17,8 +17,12 @@
"Events", "Events",
"reactor", "reactor",
"pub/sub", "pub/sub",
"publish",
"subscribe",
"event", "event",
"emitter" "emitter",
"addListener",
"addEventListener"
], ],
"author": { "author": {
"name": "Arnout Kazemier" "name": "Arnout Kazemier"
...@@ -28,12 +32,16 @@ ...@@ -28,12 +32,16 @@
"url": "https://github.com/3rd-Eden/EventEmitter3/issues" "url": "https://github.com/3rd-Eden/EventEmitter3/issues"
}, },
"devDependencies": { "devDependencies": {
"mocha": "1.13.x", "mocha": "1.18.x",
"pre-commit": "0.0.x", "pre-commit": "0.0.x",
"chai": "1.8.x" "chai": "1.9.x"
}, },
"readme": "# EventEmitter3\n\nEventEmitter3 is a faster alternative to EventEmitter2 and the build-in\nEventEmitter that ships within Node.js. It removes some features that you might\nnot need:\n\n- Domain support.\n- Thrown errors when there are no error listeners specified.\n- That a `newListener` event is emitted when an event is emitted.\n- No silly `setMaxListeners`.\n- No silly `listenerCount` function.. Just do `EventEmitter.listeners(event).length`\n\nAnd adds some features you want:\n\n- Emit events with a custom context without binding: `EE.on(event, fn, context)`\n which also works with once `EE.once(event, fn, context)`\n\nIt's a drop in replacement of your existing EventEmitters, but just faster. Free\nperformance, who wouldn't want that.\n\nThe source of the EventEmitter is compatible for browser usage, no fancy pancy\n`Array.isArray` stuff is used, it's just plain ol JavaScript that should even\nwork IE5 if you want to. This module currently serves it's use in\n[Primus](http://github.com/primus/primus)'s client file.\n\n## Installation\n\n```bash\n$ npm install --save eventemitter3\n```\nor as a [component](http://component.io)\n\n```bash\n$ component install eventemitter3\n```\n\nthen\n\n```js\nvar EventEmitter = require('eventemitter3');\n\n// or\n\nvar EventEmitter = require('eventemitter3').EventEmitter;\n```\n\nFor API methods see the official Node.js documentation: \n\nhttp://nodejs.org/api/events.html\n", "readme": "# EventEmitter3\n\nEventEmitter3 is a faster alternative to EventEmitter2 and the build-in\nEventEmitter that ships within Node.js. It removes some features that you might\nnot need:\n\n- Domain support.\n- Thrown errors when there are no error listeners specified.\n- That a `newListener` event is emitted when an event is emitted.\n- No silly `setMaxListeners`.\n- No silly `listenerCount` function.. Just do `EventEmitter.listeners(event).length`\n\nAnd adds some features you want:\n\n- Emit events with a custom context without binding: `EE.on(event, fn, context)`\n which also works with once `EE.once(event, fn, context)`\n\nIt's a drop in replacement of your existing EventEmitters, but just faster. Free\nperformance, who wouldn't want that.\n\nThe source of the EventEmitter is compatible for browser usage, no fancy pancy\n`Array.isArray` stuff is used, it's just plain ol JavaScript that should even\nwork IE5 if you want to. This module currently serves it's use in\n[Primus](http://github.com/primus/primus)'s client file.\n\n## Installation\n\n```bash\n$ npm install --save eventemitter3\n```\nor as a [component](http://component.io)\n\n```bash\n$ component install eventemitter3\n```\n\nthen\n\n```js\nvar EventEmitter = require('eventemitter3');\n\n// or\n\nvar EventEmitter = require('eventemitter3').EventEmitter;\n```\n\nFor API methods see the official Node.js documentation: \n\nhttp://nodejs.org/api/events.html\n",
"readmeFilename": "README.md", "readmeFilename": "README.md",
"_id": "eventemitter3@0.1.1", "_id": "eventemitter3@0.1.2",
"_from": "eventemitter3@*" "dist": {
"shasum": "82f49c44c217e6d8cf13be4be6af527f76fecc3b"
},
"_from": "eventemitter3@*",
"_resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-0.1.2.tgz"
} }
...@@ -122,6 +122,20 @@ describe('EventEmitter', function tests() { ...@@ -122,6 +122,20 @@ describe('EventEmitter', function tests() {
expect(calls).to.equal(1); expect(calls).to.equal(1);
}); });
it('only emits once if emits are nested inside the listener', function () {
var e = new EventEmitter()
, calls = 0;
e.once('foo', function () {
calls++;
e.emit('foo');
});
e.emit('foo');
expect(e.listeners('foo').length).to.equal(0);
expect(calls).to.equal(1);
});
it('only emits once for multiple events', function () { it('only emits once for multiple events', function () {
var e = new EventEmitter() var e = new EventEmitter()
, multi = 0 , multi = 0
......
This diff is collapsed.
...@@ -15,6 +15,7 @@ describe('lib/http-proxy/common.js', function () { ...@@ -15,6 +15,7 @@ describe('lib/http-proxy/common.js', function () {
port : 'you', port : 'you',
}, },
headers: {'fizz': 'bang', 'overwritten':true}, headers: {'fizz': 'bang', 'overwritten':true},
localAddress: 'local.address',
}, },
{ {
method : 'i', method : 'i',
...@@ -34,6 +35,49 @@ describe('lib/http-proxy/common.js', function () { ...@@ -34,6 +35,49 @@ describe('lib/http-proxy/common.js', function () {
expect(outgoing.headers.pro).to.eql('xy'); expect(outgoing.headers.pro).to.eql('xy');
expect(outgoing.headers.fizz).to.eql('bang'); expect(outgoing.headers.fizz).to.eql('bang');
expect(outgoing.headers.overwritten).to.eql(true); expect(outgoing.headers.overwritten).to.eql(true);
expect(outgoing.localAddress).to.eql('local.address');
});
it('should not override agentless upgrade header', function () {
var outgoing = {};
common.setupOutgoing(outgoing,
{
agent: undefined,
target: {
host : 'hey',
hostname : 'how',
socketPath: 'are',
port : 'you',
},
headers: {'connection': 'upgrade'},
},
{
method : 'i',
url : 'am',
headers : {'pro':'xy','overwritten':false}
});
expect(outgoing.headers.connection).to.eql('upgrade');
});
it('should override agentless non-upgrade header to close', function () {
var outgoing = {};
common.setupOutgoing(outgoing,
{
agent: undefined,
target: {
host : 'hey',
hostname : 'how',
socketPath: 'are',
port : 'you',
},
headers: {'connection': 'xyz'},
},
{
method : 'i',
url : 'am',
headers : {'pro':'xy','overwritten':false}
});
expect(outgoing.headers.connection).to.eql('close');
}); });
it('should set the agent to false if none is given', function () { it('should set the agent to false if none is given', function () {
......
...@@ -127,4 +127,32 @@ describe('#createProxyServer.web() using own http server', function () { ...@@ -127,4 +127,32 @@ describe('#createProxyServer.web() using own http server', function () {
method: 'GET', method: 'GET',
}, function() {}).end(); }, function() {}).end();
}); });
it('should proxy the request and provide a proxyRes event with the request and response parameters', function(done) {
var proxy = httpProxy.createProxyServer({
target: 'http://127.0.0.1:8080'
});
function requestHandler(req, res) {
proxy.once('proxyRes', function (proxyRes, pReq, pRes) {
source.close();
proxyServer.close();
expect(pReq).to.be.equal(req);
expect(pRes).to.be.equal(res);
done();
});
proxy.web(req, res);
}
var proxyServer = http.createServer(requestHandler);
var source = http.createServer(function(req, res) {
res.end('Response');
});
proxyServer.listen('8084');
source.listen('8080');
http.request('http://127.0.0.1:8084', function() {}).end();
});
}); });
\ No newline at end of file
...@@ -159,6 +159,11 @@ describe('lib/http-proxy.js', function() { ...@@ -159,6 +159,11 @@ describe('lib/http-proxy.js', function() {
timeout: 3 timeout: 3
}).listen(ports.proxy); }).listen(ports.proxy);
proxy.on('error', function (e) {
expect(e).to.be.an(Error);
expect(e.code).to.be.eql('ECONNRESET');
});
var source = http.createServer(function(req, res) { var source = http.createServer(function(req, res) {
setTimeout(function () { setTimeout(function () {
res.end('At this point the socket should be closed'); res.end('At this point the socket should be closed');
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment