Skip to content

Commit 9fdc84f

Browse files
committed
Merge pull request #56 from chimurai/proxyReqWs
feature(options): onProxyReqWs, onOpen, onClose
2 parents e88d6e0 + 84f8e7f commit 9fdc84f

File tree

5 files changed

+151
-45
lines changed

5 files changed

+151
-45
lines changed

README.md

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ var server = app.listen(3000);
191191
}
192192
```
193193

194-
* **option.onError**: function, subscribe to http-proxy's error event for custom error handling.
194+
* **option.onError**: function, subscribe to http-proxy's `error` event for custom error handling.
195195
```javascript
196196
function onError(err, req, res) {
197197
res.writeHead(500, {
@@ -201,15 +201,15 @@ var server = app.listen(3000);
201201
}
202202
```
203203

204-
* **option.onProxyRes**: function, subscribe to http-proxy's proxyRes event.
204+
* **option.onProxyRes**: function, subscribe to http-proxy's `proxyRes` event.
205205
```javascript
206206
function onProxyRes(proxyRes, req, res) {
207207
proxyRes.headers['x-added'] = 'foobar'; // add new header to response
208208
delete proxyRes.headers['x-removed']; // remove header from response
209209
}
210210
```
211211

212-
* **option.onProxyReq**: function, subscribe to http-proxy's proxyReq event.
212+
* **option.onProxyReq**: function, subscribe to http-proxy's `proxyReq` event.
213213
```javascript
214214
function onProxyReq(proxyReq, req, res) {
215215
// add custom header to request
@@ -218,6 +218,30 @@ var server = app.listen(3000);
218218
}
219219
```
220220

221+
* **option.onProxyReqWs**: function, subscribe to http-proxy's `proxyReqWs` event.
222+
```javascript
223+
function onProxyReqWs(proxyReq, req, socket, options, head) {
224+
// add custom header
225+
proxyReq.setHeader('X-Special-Proxy-Header', 'foobar');
226+
}
227+
```
228+
229+
* **option.onOpen**: function, subscribe to http-proxy's `open` event.
230+
```javascript
231+
function onOpen(proxySocket) {
232+
// listen for messages coming FROM the target here
233+
proxySocket.on('data', hybiParseAndLogMessage);
234+
}
235+
```
236+
237+
* **option.onClose**: function, subscribe to http-proxy's `close` event.
238+
```javascript
239+
function onClose(res, socket, head) {
240+
// view disconnected websocket connections
241+
console.log('Client disconnected');
242+
}
243+
```
244+
221245
* (DEPRECATED) **option.proxyHost**: Use `option.changeOrigin = true` instead.
222246

223247
The following options are provided by the underlying [http-proxy](https://www.npmjs.com/package/http-proxy).
@@ -238,9 +262,7 @@ The following options are provided by the underlying [http-proxy](https://www.np
238262
* **option.hostRewrite**: rewrites the location hostname on (301/302/307/308) redirects.
239263
* **option.autoRewrite**: rewrites the location host/port on (301/302/307/308) redirects based on requested host/port. Default: false.
240264
* **option.protocolRewrite**: rewrites the location protocol on (301/302/307/308) redirects to 'http' or 'https'. Default: null.
241-
242-
Undocumented options are provided by the underlying http-proxy
243-
* **option.headers**: object, adds [request headers](https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Request_fields). (Example: `{host:'www.example.org'}`) [source](https://github.com/nodejitsu/node-http-proxy/blob/master/examples/http/proxy-http-to-https.js#L41)
265+
* **option.headers**: object, adds [request headers](https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Request_fields). (Example: `{host:'www.example.org'}`)
244266

245267
## Recipes
246268

index.js

Lines changed: 5 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
var _ = require('lodash');
21
var httpProxy = require('http-proxy');
32
var configFactory = require('./lib/config-factory');
43
var handlers = require('./lib/handlers');
@@ -19,27 +18,11 @@ var httpProxyMiddleware = function(context, opts) {
1918

2019
var pathRewriter = PathRewriter.create(proxyOptions.pathRewrite); // returns undefined when "pathRewrite" is not provided
2120

22-
// Custom listener for the `proxyRes` event on `proxy`.
23-
if (_.isFunction(proxyOptions.onProxyRes)) {
24-
proxy.on('proxyRes', proxyOptions.onProxyRes);
25-
}
26-
27-
// Custom listener for the `proxyReq` event on `proxy`.
28-
if (_.isFunction(proxyOptions.onProxyReq)) {
29-
proxy.on('proxyReq', proxyOptions.onProxyReq);
30-
}
31-
32-
// Custom listener for the `error` event on `proxy`.
33-
var onProxyError = getProxyErrorHandler();
34-
// handle error and close connection properly
35-
proxy.on('error', onProxyError);
36-
proxy.on('error', proxyErrorLogger);
21+
// attach handler to http-proxy events
22+
handlers.init(proxy, proxyOptions);
3723

38-
// Listen for the `close` event on `proxy`.
39-
proxy.on('close', function(req, socket, head) {
40-
// view disconnected websocket connections
41-
logger.info('[HPM] Client disconnected');
42-
});
24+
// log errors for debug purpose
25+
proxy.on('error', logError);
4326

4427
// https://github.com/chimurai/http-proxy-middleware/issues/19
4528
// expose function to upgrade externally
@@ -128,15 +111,7 @@ var httpProxyMiddleware = function(context, opts) {
128111
}
129112
}
130113

131-
function getProxyErrorHandler() {
132-
if (_.isFunction(proxyOptions.onError)) {
133-
return proxyOptions.onError; // custom error listener
134-
}
135-
136-
return handlers.proxyError; // otherwise fall back to default
137-
}
138-
139-
function proxyErrorLogger(err, req, res) {
114+
function logError(err, req, res) {
140115
var hostname = (req.hostname || req.host) || (req.headers && req.headers.host); // (node0.10 || node 4/5) || (websocket)
141116
var targetUri = (proxyOptions.target.host || proxyOptions.target) + req.url;
142117

lib/handlers.js

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,62 @@
1+
var _ = require('lodash');
2+
var logger = require('./logger').getInstance();
3+
14
module.exports = {
2-
proxyError: proxyError
5+
init: init,
6+
getHandlers: getProxyEventHandlers
37
};
48

5-
function proxyError(err, req, res) {
9+
function init(proxy, opts) {
10+
var handlers = getProxyEventHandlers(opts);
11+
12+
_.forIn(handlers, function(handler, eventName) {
13+
proxy.on(eventName, handlers[eventName]);
14+
});
15+
16+
logger.debug('[HPM] Subscribed to http-proxy events: ', _.keys(handlers));
17+
}
18+
19+
function getProxyEventHandlers(opts) {
20+
// https://github.com/nodejitsu/node-http-proxy#listening-for-proxy-events
21+
var proxyEvents = ['error', 'proxyReq', 'proxyReqWs', 'proxyRes', 'open', 'close'];
22+
var handlers = {};
23+
24+
_.forEach(proxyEvents, function(event) {
25+
// all handlers for the http-proxy events are prefixed with 'on'.
26+
// loop through options and try to find these handlers
27+
// and add them to the handlers object for subscription in init().
28+
var eventName = _.camelCase('on ' + event);
29+
var fnHandler = _.get(opts, eventName);
30+
31+
if (_.isFunction(fnHandler)) {
32+
handlers[event] = fnHandler;
33+
}
34+
});
35+
36+
// add default error handler in absence of error handler
37+
if (!_.isFunction(handlers.error)) {
38+
handlers.error = defaultErrorHandler;
39+
}
40+
41+
// add default close handler in absence of close handler
42+
if (!_.isFunction(handlers.close)) {
43+
handlers.close = logClose;
44+
}
45+
46+
return handlers;
47+
};
48+
49+
function defaultErrorHandler(err, req, res) {
650
var host = (req.headers && req.headers.host);
751

852
if (res.writeHead && !res.headersSent) {
953
res.writeHead(500);
1054
}
1155

1256
res.end('Error occured while trying to proxy to: ' + host + req.url);
13-
};
57+
}
58+
59+
function logClose(req, socket, head) {
60+
// view disconnected websocket connections
61+
logger.info('[HPM] Client disconnected');
62+
}

lib/proxy-table.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
var url = require('url');
21
var _ = require('lodash');
32
var logger = require('./logger.js').getInstance();
43

test/handlers.spec.js

Lines changed: 66 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,61 @@
11
var expect = require('chai').expect;
22
var handlers = require('../lib/handlers');
33

4-
describe('handlers.proxyError(err, req, res, proxyOptions)', function() {
4+
describe('handlers factory', function() {
5+
var handlersMap;
6+
7+
it('should return default handlers when no handlers are provided', function() {
8+
handlersMap = handlers.getHandlers();
9+
expect(handlersMap.error).to.be.a('function');
10+
expect(handlersMap.close).to.be.a('function');
11+
});
12+
13+
describe('custom handlers', function() {
14+
beforeEach(function() {
15+
var fnCustom = function() {
16+
return 42;
17+
};
18+
19+
var proxyOptions = {
20+
target: 'http://www.example.org',
21+
onError: fnCustom,
22+
onOpen: fnCustom,
23+
onClose: fnCustom,
24+
onProxyReq: fnCustom,
25+
onProxyReqWs: fnCustom,
26+
onProxyRes: fnCustom,
27+
onDummy: fnCustom,
28+
foobar: fnCustom
29+
};
30+
31+
handlersMap = handlers.getHandlers(proxyOptions);
32+
});
33+
34+
it('should only return http-proxy handlers', function() {
35+
expect(handlersMap.error).to.be.a('function');
36+
expect(handlersMap.open).to.be.a('function');
37+
expect(handlersMap.close).to.be.a('function');
38+
expect(handlersMap.proxyReq).to.be.a('function');
39+
expect(handlersMap.proxyReqWs).to.be.a('function');
40+
expect(handlersMap.proxyRes).to.be.a('function');
41+
expect(handlersMap.dummy).to.be.undefined;
42+
expect(handlersMap.foobar).to.be.undefined;
43+
expect(handlersMap.target).to.be.undefined;
44+
});
45+
46+
it('should use the provided custom handlers', function() {
47+
expect(handlersMap.error()).to.equal(42);
48+
expect(handlersMap.open()).to.equal(42);
49+
expect(handlersMap.close()).to.equal(42);
50+
expect(handlersMap.proxyReq()).to.equal(42);
51+
expect(handlersMap.proxyReqWs()).to.equal(42);
52+
expect(handlersMap.proxyRes()).to.equal(42);
53+
});
54+
55+
});
56+
});
57+
58+
describe('default proxy error handler', function() {
559

660
var mockError = {
761
code: 'ECONNREFUSED'
@@ -35,30 +89,37 @@ describe('handlers.proxyError(err, req, res, proxyOptions)', function() {
3589
headersSent: false
3690
};
3791

92+
var proxyError;
93+
94+
beforeEach(function() {
95+
var handlersMap = handlers.getHandlers();
96+
proxyError = handlersMap.error;
97+
});
98+
3899
afterEach(function() {
39100
httpErrorCode = undefined;
40101
errorMessage = undefined;
41102
});
42103

43104
it('should set the http status code to: 500', function() {
44-
handlers.proxyError(mockError, mockReq, mockRes, proxyOptions);
105+
proxyError(mockError, mockReq, mockRes, proxyOptions);
45106
expect(httpErrorCode).to.equal(500);
46107
});
47108

48109
it('should end the response and return error message', function() {
49-
handlers.proxyError(mockError, mockReq, mockRes, proxyOptions);
110+
proxyError(mockError, mockReq, mockRes, proxyOptions);
50111
expect(errorMessage).to.equal('Error occured while trying to proxy to: localhost:3000/api');
51112
});
52113

53114
it('should not set the http status code to: 500 if headers have already been sent', function() {
54115
mockRes.headersSent = true;
55-
handlers.proxyError(mockError, mockReq, mockRes, proxyOptions);
116+
proxyError(mockError, mockReq, mockRes, proxyOptions);
56117
expect(httpErrorCode).to.equal(undefined);
57118
});
58119

59120
it('should end the response and return error message', function() {
60121
mockRes.headersSent = true;
61-
handlers.proxyError(mockError, mockReq, mockRes, proxyOptions);
122+
proxyError(mockError, mockReq, mockRes, proxyOptions);
62123
expect(errorMessage).to.equal('Error occured while trying to proxy to: localhost:3000/api');
63124
});
64125

0 commit comments

Comments
 (0)