http-proxy-middleware 0.19.2 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,8 +1,14 @@
1
1
  # Changelog
2
2
 
3
- ## [v0.19.2](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.19.2)
4
-
5
- - chore(deps): http-proxy 1.18.1
3
+ ## [v0.20.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.20.0)
4
+
5
+ - fix(ws): concurrent websocket requests do not get upgraded ([#335](https://github.com/chimurai/http-proxy-middleware/issues/335))
6
+ - chore: drop node 6 (BREAKING CHANGE)
7
+ - chore: update to micromatch@4 ([BREAKING CHANGE](https://github.com/micromatch/micromatch/blob/master/CHANGELOG.md#400---2019-03-20))
8
+ - chore: update dev dependencies
9
+ - refactor: migrate to typescript ([#328](https://github.com/chimurai/http-proxy-middleware/pull/328))
10
+ - feat(middleware): Promise / async support ([#328](https://github.com/chimurai/http-proxy-middleware/pull/328/files#diff-7890bfeb41abb0fc0ef2670749c84077R50))
11
+ - refactor: remove legacy options `proxyHost` and `proxyTable` (BREAKING CHANGE)
6
12
 
7
13
  ## [v0.19.1](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.19.1)
8
14
 
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![Build Status](https://img.shields.io/travis/chimurai/http-proxy-middleware/master.svg?style=flat-square)](https://travis-ci.org/chimurai/http-proxy-middleware)
4
4
  [![Coveralls](https://img.shields.io/coveralls/chimurai/http-proxy-middleware.svg?style=flat-square)](https://coveralls.io/r/chimurai/http-proxy-middleware)
5
5
  [![dependency Status](https://img.shields.io/david/chimurai/http-proxy-middleware.svg?style=flat-square)](https://david-dm.org/chimurai/http-proxy-middleware#info=dependencies)
6
- [![dependency Status](https://snyk.io/test/npm/http-proxy-middleware/badge.svg)](https://snyk.io/test/npm/http-proxy-middleware)
6
+ [![dependency Status](https://snyk.io/test/npm/http-proxy-middleware/badge.svg?style=flat-square)](https://snyk.io/test/npm/http-proxy-middleware)
7
7
  [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)
8
8
 
9
9
  Node.js proxying made simple. Configure proxy middleware with ease for [connect](https://github.com/senchalabs/connect), [express](https://github.com/strongloop/express), [browser-sync](https://github.com/BrowserSync/browser-sync) and [many more](#compatible-servers).
@@ -15,13 +15,16 @@ Powered by the popular Nodejitsu [`http-proxy`](https://github.com/nodejitsu/nod
15
15
  Proxy `/api` requests to `http://www.example.org`
16
16
 
17
17
  ```javascript
18
- var express = require('express')
19
- var proxy = require('http-proxy-middleware')
18
+ var express = require('express');
19
+ var proxy = require('http-proxy-middleware');
20
20
 
21
- var app = express()
21
+ var app = express();
22
22
 
23
- app.use('/api', proxy({ target: 'http://www.example.org', changeOrigin: true }))
24
- app.listen(3000)
23
+ app.use(
24
+ '/api',
25
+ proxy({ target: 'http://www.example.org', changeOrigin: true })
26
+ );
27
+ app.listen(3000);
25
28
 
26
29
  // http://localhost:3000/api/foo/bar -> http://www.example.org/api/foo/bar
27
30
  ```
@@ -68,9 +71,9 @@ Proxy middleware configuration.
68
71
  #### proxy([context,] config)
69
72
 
70
73
  ```javascript
71
- var proxy = require('http-proxy-middleware')
74
+ var proxy = require('http-proxy-middleware');
72
75
 
73
- var apiProxy = proxy('/api', { target: 'http://www.example.org' })
76
+ var apiProxy = proxy('/api', { target: 'http://www.example.org' });
74
77
  // \____/ \_____________________________/
75
78
  // | |
76
79
  // context options
@@ -88,7 +91,7 @@ var apiProxy = proxy('/api', { target: 'http://www.example.org' })
88
91
 
89
92
  ```javascript
90
93
  // shorthand syntax for the example above:
91
- var apiProxy = proxy('http://www.example.org/api')
94
+ var apiProxy = proxy('http://www.example.org/api');
92
95
  ```
93
96
 
94
97
  More about the [shorthand configuration](#shorthand).
@@ -99,8 +102,8 @@ An example with `express` server.
99
102
 
100
103
  ```javascript
101
104
  // include dependencies
102
- var express = require('express')
103
- var proxy = require('http-proxy-middleware')
105
+ var express = require('express');
106
+ var proxy = require('http-proxy-middleware');
104
107
 
105
108
  // proxy middleware options
106
109
  var options = {
@@ -116,15 +119,15 @@ var options = {
116
119
  // override target 'http://www.example.org' to 'http://localhost:8000'
117
120
  'dev.localhost:3000': 'http://localhost:8000'
118
121
  }
119
- }
122
+ };
120
123
 
121
124
  // create the proxy (without context)
122
- var exampleProxy = proxy(options)
125
+ var exampleProxy = proxy(options);
123
126
 
124
127
  // mount `exampleProxy` in web server
125
- var app = express()
126
- app.use('/api', exampleProxy)
127
- app.listen(3000)
128
+ var app = express();
129
+ app.use('/api', exampleProxy);
130
+ app.listen(3000);
128
131
  ```
129
132
 
130
133
  ## Context matching
@@ -172,10 +175,10 @@ Providing an alternative way to decide which requests should be proxied; In case
172
175
  * @return {Boolean}
173
176
  */
174
177
  var filter = function(pathname, req) {
175
- return pathname.match('^/api') && req.method === 'GET'
176
- }
178
+ return pathname.match('^/api') && req.method === 'GET';
179
+ };
177
180
 
178
- var apiProxy = proxy(filter, { target: 'http://www.example.org' })
181
+ var apiProxy = proxy(filter, { target: 'http://www.example.org' });
179
182
  ```
180
183
 
181
184
  ## Options
@@ -224,14 +227,14 @@ Providing an alternative way to decide which requests should be proxied; In case
224
227
  // simple replace
225
228
  function logProvider(provider) {
226
229
  // replace the default console log provider.
227
- return require('winston')
230
+ return require('winston');
228
231
  }
229
232
  ```
230
233
 
231
234
  ```javascript
232
235
  // verbose replacement
233
236
  function logProvider(provider) {
234
- var logger = new (require('winston')).Logger()
237
+ var logger = new (require('winston')).Logger();
235
238
 
236
239
  var myCustomProvider = {
237
240
  log: logger.log,
@@ -239,14 +242,11 @@ Providing an alternative way to decide which requests should be proxied; In case
239
242
  info: logger.info,
240
243
  warn: logger.warn,
241
244
  error: logger.error
242
- }
243
- return myCustomProvider
245
+ };
246
+ return myCustomProvider;
244
247
  }
245
248
  ```
246
249
 
247
- - (DEPRECATED) **option.proxyHost**: Use `option.changeOrigin = true` instead.
248
- - (DEPRECATED) **option.proxyTable**: Use `option.router` instead.
249
-
250
250
  ### http-proxy events
251
251
 
252
252
  Subscribe to [http-proxy events](https://github.com/nodejitsu/node-http-proxy#listening-for-proxy-events):
@@ -257,10 +257,10 @@ Subscribe to [http-proxy events](https://github.com/nodejitsu/node-http-proxy#li
257
257
  function onError(err, req, res) {
258
258
  res.writeHead(500, {
259
259
  'Content-Type': 'text/plain'
260
- })
260
+ });
261
261
  res.end(
262
262
  'Something went wrong. And we are reporting a custom error message.'
263
- )
263
+ );
264
264
  }
265
265
  ```
266
266
 
@@ -268,8 +268,8 @@ Subscribe to [http-proxy events](https://github.com/nodejitsu/node-http-proxy#li
268
268
 
269
269
  ```javascript
270
270
  function onProxyRes(proxyRes, req, res) {
271
- proxyRes.headers['x-added'] = 'foobar' // add new header to response
272
- delete proxyRes.headers['x-removed'] // remove header from response
271
+ proxyRes.headers['x-added'] = 'foobar'; // add new header to response
272
+ delete proxyRes.headers['x-removed']; // remove header from response
273
273
  }
274
274
  ```
275
275
 
@@ -278,7 +278,7 @@ Subscribe to [http-proxy events](https://github.com/nodejitsu/node-http-proxy#li
278
278
  ```javascript
279
279
  function onProxyReq(proxyReq, req, res) {
280
280
  // add custom header to request
281
- proxyReq.setHeader('x-added', 'foobar')
281
+ proxyReq.setHeader('x-added', 'foobar');
282
282
  // or log the req
283
283
  }
284
284
  ```
@@ -288,7 +288,7 @@ Subscribe to [http-proxy events](https://github.com/nodejitsu/node-http-proxy#li
288
288
  ```javascript
289
289
  function onProxyReqWs(proxyReq, req, socket, options, head) {
290
290
  // add custom header
291
- proxyReq.setHeader('X-Special-Proxy-Header', 'foobar')
291
+ proxyReq.setHeader('X-Special-Proxy-Header', 'foobar');
292
292
  }
293
293
  ```
294
294
 
@@ -297,7 +297,7 @@ Subscribe to [http-proxy events](https://github.com/nodejitsu/node-http-proxy#li
297
297
  ```javascript
298
298
  function onOpen(proxySocket) {
299
299
  // listen for messages coming FROM the target here
300
- proxySocket.on('data', hybiParseAndLogMessage)
300
+ proxySocket.on('data', hybiParseAndLogMessage);
301
301
  }
302
302
  ```
303
303
 
@@ -305,7 +305,7 @@ Subscribe to [http-proxy events](https://github.com/nodejitsu/node-http-proxy#li
305
305
  ```javascript
306
306
  function onClose(res, socket, head) {
307
307
  // view disconnected websocket connections
308
- console.log('Client disconnected')
308
+ console.log('Client disconnected');
309
309
  }
310
310
  ```
311
311
 
@@ -383,13 +383,13 @@ The following options are provided by the underlying [http-proxy](https://github
383
383
  Use the shorthand syntax when verbose configuration is not needed. The `context` and `option.target` will be automatically configured when shorthand is used. Options can still be used if needed.
384
384
 
385
385
  ```javascript
386
- proxy('http://www.example.org:8000/api')
386
+ proxy('http://www.example.org:8000/api');
387
387
  // proxy('/api', {target: 'http://www.example.org:8000'});
388
388
 
389
- proxy('http://www.example.org:8000/api/books/*/**.json')
389
+ proxy('http://www.example.org:8000/api/books/*/**.json');
390
390
  // proxy('/api/books/*/**.json', {target: 'http://www.example.org:8000'});
391
391
 
392
- proxy('http://www.example.org:8000/api', { changeOrigin: true })
392
+ proxy('http://www.example.org:8000/api', { changeOrigin: true });
393
393
  // proxy('/api', {target: 'http://www.example.org:8000', changeOrigin: true});
394
394
  ```
395
395
 
@@ -399,7 +399,10 @@ If you want to use the server's `app.use` `path` parameter to match requests;
399
399
  Create and mount the proxy without the http-proxy-middleware `context` parameter:
400
400
 
401
401
  ```javascript
402
- app.use('/api', proxy({ target: 'http://www.example.org', changeOrigin: true }))
402
+ app.use(
403
+ '/api',
404
+ proxy({ target: 'http://www.example.org', changeOrigin: true })
405
+ );
403
406
  ```
404
407
 
405
408
  `app.use` documentation:
@@ -411,13 +414,13 @@ app.use('/api', proxy({ target: 'http://www.example.org', changeOrigin: true }))
411
414
 
412
415
  ```javascript
413
416
  // verbose api
414
- proxy('/', { target: 'http://echo.websocket.org', ws: true })
417
+ proxy('/', { target: 'http://echo.websocket.org', ws: true });
415
418
 
416
419
  // shorthand
417
- proxy('http://echo.websocket.org', { ws: true })
420
+ proxy('http://echo.websocket.org', { ws: true });
418
421
 
419
422
  // shorter shorthand
420
- proxy('ws://echo.websocket.org')
423
+ proxy('ws://echo.websocket.org');
421
424
  ```
422
425
 
423
426
  ### External WebSocket upgrade
@@ -425,13 +428,13 @@ proxy('ws://echo.websocket.org')
425
428
  In the previous WebSocket examples, http-proxy-middleware relies on a initial http request in order to listen to the http `upgrade` event. If you need to proxy WebSockets without the initial http request, you can subscribe to the server's http `upgrade` event manually.
426
429
 
427
430
  ```javascript
428
- var wsProxy = proxy('ws://echo.websocket.org', { changeOrigin: true })
431
+ var wsProxy = proxy('ws://echo.websocket.org', { changeOrigin: true });
429
432
 
430
- var app = express()
431
- app.use(wsProxy)
433
+ var app = express();
434
+ app.use(wsProxy);
432
435
 
433
- var server = app.listen(3000)
434
- server.on('upgrade', wsProxy.upgrade) // <-- subscribe to http 'upgrade'
436
+ var server = app.listen(3000);
437
+ server.on('upgrade', wsProxy.upgrade); // <-- subscribe to http 'upgrade'
435
438
  ```
436
439
 
437
440
  ## Working examples
@@ -468,16 +471,20 @@ Run the test suite:
468
471
 
469
472
  ```bash
470
473
  # install dependencies
471
- $ npm install
474
+ $ yarn
472
475
 
473
476
  # linting
474
- $ npm run lint
477
+ $ yarn lint
478
+ $ yarn lint:fix
479
+
480
+ # building (compile typescript to js)
481
+ $ yarn build
475
482
 
476
483
  # unit tests
477
- $ npm test
484
+ $ yarn test
478
485
 
479
486
  # code coverage
480
- $ npm run cover
487
+ $ yarn cover
481
488
  ```
482
489
 
483
490
  ## Changelog
@@ -488,4 +495,4 @@ $ npm run cover
488
495
 
489
496
  The MIT License (MIT)
490
497
 
491
- Copyright (c) 2015-2018 Steven Chim
498
+ Copyright (c) 2015-2019 Steven Chim
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const _ = require("lodash");
4
+ const url = require("url");
5
+ const errors_1 = require("./errors");
6
+ const logger_1 = require("./logger");
7
+ const logger = logger_1.getInstance();
8
+ function createConfig(context, opts) {
9
+ // structure of config object to be returned
10
+ const config = {
11
+ context: undefined,
12
+ options: {}
13
+ };
14
+ // app.use('/api', proxy({target:'http://localhost:9000'}));
15
+ if (isContextless(context, opts)) {
16
+ config.context = '/';
17
+ config.options = _.assign(config.options, context);
18
+ // app.use('/api', proxy('http://localhost:9000'));
19
+ // app.use(proxy('http://localhost:9000/api'));
20
+ }
21
+ else if (isStringShortHand(context)) {
22
+ const oUrl = url.parse(context);
23
+ const target = [oUrl.protocol, '//', oUrl.host].join('');
24
+ config.context = oUrl.pathname || '/';
25
+ config.options = _.assign(config.options, { target }, opts);
26
+ if (oUrl.protocol === 'ws:' || oUrl.protocol === 'wss:') {
27
+ config.options.ws = true;
28
+ }
29
+ // app.use('/api', proxy({target:'http://localhost:9000'}));
30
+ }
31
+ else {
32
+ config.context = context;
33
+ config.options = _.assign(config.options, opts);
34
+ }
35
+ configureLogger(config.options);
36
+ if (!config.options.target) {
37
+ throw new Error(errors_1.ERRORS.ERR_CONFIG_FACTORY_TARGET_MISSING);
38
+ }
39
+ return config;
40
+ }
41
+ exports.createConfig = createConfig;
42
+ /**
43
+ * Checks if a String only target/config is provided.
44
+ * This can be just the host or with the optional path.
45
+ *
46
+ * @example
47
+ * app.use('/api', proxy('http://localhost:9000'));
48
+ * app.use(proxy('http://localhost:9000/api'));
49
+ *
50
+ * @param {String} context [description]
51
+ * @return {Boolean} [description]
52
+ */
53
+ function isStringShortHand(context) {
54
+ if (_.isString(context)) {
55
+ return !!url.parse(context).host;
56
+ }
57
+ }
58
+ /**
59
+ * Checks if a Object only config is provided, without a context.
60
+ * In this case the all paths will be proxied.
61
+ *
62
+ * @example
63
+ * app.use('/api', proxy({target:'http://localhost:9000'}));
64
+ *
65
+ * @param {Object} context [description]
66
+ * @param {*} opts [description]
67
+ * @return {Boolean} [description]
68
+ */
69
+ function isContextless(context, opts) {
70
+ return _.isPlainObject(context) && _.isEmpty(opts);
71
+ }
72
+ function configureLogger(options) {
73
+ if (options.logLevel) {
74
+ logger.setLevel(options.logLevel);
75
+ }
76
+ if (options.logProvider) {
77
+ logger.setProvider(options.logProvider);
78
+ }
79
+ }
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const isGlob = require("is-glob");
4
+ const _ = require("lodash");
5
+ const micromatch = require("micromatch");
6
+ const url = require("url");
7
+ const errors_1 = require("./errors");
8
+ function match(context, uri, req) {
9
+ // single path
10
+ if (isStringPath(context)) {
11
+ return matchSingleStringPath(context, uri);
12
+ }
13
+ // single glob path
14
+ if (isGlobPath(context)) {
15
+ return matchSingleGlobPath(context, uri);
16
+ }
17
+ // multi path
18
+ if (Array.isArray(context)) {
19
+ if (context.every(isStringPath)) {
20
+ return matchMultiPath(context, uri);
21
+ }
22
+ if (context.every(isGlobPath)) {
23
+ return matchMultiGlobPath(context, uri);
24
+ }
25
+ throw new Error(errors_1.ERRORS.ERR_CONTEXT_MATCHER_INVALID_ARRAY);
26
+ }
27
+ // custom matching
28
+ if (_.isFunction(context)) {
29
+ const pathname = getUrlPathName(uri);
30
+ return context(pathname, req);
31
+ }
32
+ throw new Error(errors_1.ERRORS.ERR_CONTEXT_MATCHER_GENERIC);
33
+ }
34
+ exports.match = match;
35
+ /**
36
+ * @param {String} context '/api'
37
+ * @param {String} uri 'http://example.org/api/b/c/d.html'
38
+ * @return {Boolean}
39
+ */
40
+ function matchSingleStringPath(context, uri) {
41
+ const pathname = getUrlPathName(uri);
42
+ return pathname.indexOf(context) === 0;
43
+ }
44
+ function matchSingleGlobPath(pattern, uri) {
45
+ const pathname = getUrlPathName(uri);
46
+ const matches = micromatch([pathname], pattern);
47
+ return matches && matches.length > 0;
48
+ }
49
+ function matchMultiGlobPath(patternList, uri) {
50
+ return matchSingleGlobPath(patternList, uri);
51
+ }
52
+ /**
53
+ * @param {String} contextList ['/api', '/ajax']
54
+ * @param {String} uri 'http://example.org/api/b/c/d.html'
55
+ * @return {Boolean}
56
+ */
57
+ function matchMultiPath(contextList, uri) {
58
+ let isMultiPath = false;
59
+ for (const context of contextList) {
60
+ if (matchSingleStringPath(context, uri)) {
61
+ isMultiPath = true;
62
+ break;
63
+ }
64
+ }
65
+ return isMultiPath;
66
+ }
67
+ /**
68
+ * Parses URI and returns RFC 3986 path
69
+ *
70
+ * @param {String} uri from req.url
71
+ * @return {String} RFC 3986 path
72
+ */
73
+ function getUrlPathName(uri) {
74
+ return uri && url.parse(uri).pathname;
75
+ }
76
+ function isStringPath(context) {
77
+ return _.isString(context) && !isGlob(context);
78
+ }
79
+ function isGlobPath(context) {
80
+ return isGlob(context);
81
+ }
package/dist/errors.js ADDED
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ var ERRORS;
4
+ (function (ERRORS) {
5
+ ERRORS["ERR_CONFIG_FACTORY_TARGET_MISSING"] = "[HPM] Missing \"target\" option. Example: {target: \"http://www.example.org\"}";
6
+ ERRORS["ERR_CONTEXT_MATCHER_GENERIC"] = "[HPM] Invalid context. Expecting something like: \"/api\" or [\"/api\", \"/ajax\"]";
7
+ ERRORS["ERR_CONTEXT_MATCHER_INVALID_ARRAY"] = "[HPM] Invalid context. Expecting something like: [\"/api\", \"/ajax\"] or [\"/api/**\", \"!**.html\"]";
8
+ ERRORS["ERR_PATH_REWRITER_CONFIG"] = "[HPM] Invalid pathRewrite config. Expecting object with pathRewrite config or a rewrite function";
9
+ })(ERRORS = exports.ERRORS || (exports.ERRORS = {}));
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const _ = require("lodash");
4
+ const logger_1 = require("./logger");
5
+ const logger = logger_1.getInstance();
6
+ function init(proxy, option) {
7
+ const handlers = getHandlers(option);
8
+ for (const eventName of Object.keys(handlers)) {
9
+ proxy.on(eventName, handlers[eventName]);
10
+ }
11
+ logger.debug('[HPM] Subscribed to http-proxy events:', Object.keys(handlers));
12
+ }
13
+ exports.init = init;
14
+ function getHandlers(options) {
15
+ // https://github.com/nodejitsu/node-http-proxy#listening-for-proxy-events
16
+ const proxyEvents = [
17
+ 'error',
18
+ 'proxyReq',
19
+ 'proxyReqWs',
20
+ 'proxyRes',
21
+ 'open',
22
+ 'close'
23
+ ];
24
+ const handlers = {};
25
+ for (const event of proxyEvents) {
26
+ // all handlers for the http-proxy events are prefixed with 'on'.
27
+ // loop through options and try to find these handlers
28
+ // and add them to the handlers object for subscription in init().
29
+ const eventName = _.camelCase('on ' + event);
30
+ const fnHandler = _.get(options, eventName);
31
+ if (_.isFunction(fnHandler)) {
32
+ handlers[event] = fnHandler;
33
+ }
34
+ }
35
+ // add default error handler in absence of error handler
36
+ if (!_.isFunction(handlers.error)) {
37
+ handlers.error = defaultErrorHandler;
38
+ }
39
+ // add default close handler in absence of close handler
40
+ if (!_.isFunction(handlers.close)) {
41
+ handlers.close = logClose;
42
+ }
43
+ return handlers;
44
+ }
45
+ exports.getHandlers = getHandlers;
46
+ function defaultErrorHandler(err, req, res) {
47
+ const host = req.headers && req.headers.host;
48
+ const code = err.code;
49
+ if (res.writeHead && !res.headersSent) {
50
+ if (/HPE_INVALID/.test(code)) {
51
+ res.writeHead(502);
52
+ }
53
+ else {
54
+ switch (code) {
55
+ case 'ECONNRESET':
56
+ case 'ENOTFOUND':
57
+ case 'ECONNREFUSED':
58
+ res.writeHead(504);
59
+ break;
60
+ default:
61
+ res.writeHead(500);
62
+ }
63
+ }
64
+ }
65
+ res.end('Error occured while trying to proxy to: ' + host + req.url);
66
+ }
67
+ function logClose(req, socket, head) {
68
+ // view disconnected websocket connections
69
+ logger.info('[HPM] Client disconnected');
70
+ }