http-proxy-middleware 0.17.2-beta → 0.18.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 +19 -0
- package/README.md +25 -15
- package/index.js +4 -4
- package/lib/config-factory.js +83 -82
- package/lib/context-matcher.js +54 -54
- package/lib/errors.js +8 -0
- package/lib/handlers.js +57 -45
- package/lib/index.js +138 -123
- package/lib/logger.js +132 -118
- package/lib/path-rewriter.js +52 -51
- package/lib/router.js +38 -42
- package/package.json +37 -17
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [v0.18.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.18.0)
|
|
4
|
+
- fix(vulnerability): update micromatch to v3.x ([npm:braces:20180219](https://snyk.io/test/npm/http-proxy-middleware?tab=issues&severity=high&severity=medium&severity=low#npm:braces:20180219
|
|
5
|
+
))
|
|
6
|
+
- test(node): drop node 0.x support ([#212](https://github.com/chimurai/http-proxy-middleware/pull/212))
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
## [v0.17.4](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.17.4)
|
|
10
|
+
- fix(ntlm authentication): fixed bug preventing proxying with ntlm authentication. ([#132](https://github.com/chimurai/http-proxy-middleware/pull/149)) (Thanks: [EladBezalel](https://github.com/EladBezalel), [oshri551](https://github.com/oshri551))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
## [v0.17.3](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.17.3)
|
|
14
|
+
- fix(onError): improve default proxy error handling. http status codes (504, 502 and 500). ([#132](https://github.com/chimurai/http-proxy-middleware/pull/132)) ([graingert](https://github.com/graingert))
|
|
15
|
+
|
|
16
|
+
## [v0.17.2](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.17.2)
|
|
17
|
+
- feat(logging): improve error message & add link to Node errors page. ([#106](https://github.com/chimurai/http-proxy-middleware/pull/106)) ([cloudmu](https://github.com/cloudmu))
|
|
18
|
+
- feat(pathRewrite): path can be empty string. ([#110](https://github.com/chimurai/http-proxy-middleware/pull/110)) ([sunnylqm](https://github.com/sunnylqm))
|
|
19
|
+
- bug(websocket): memory leak when option 'ws:true' is used. ([#114](https://github.com/chimurai/http-proxy-middleware/pull/114)) ([julbra](https://github.com/julbra))
|
|
20
|
+
- chore(package.json): reduce package size. ([#109](https://github.com/chimurai/http-proxy-middleware/pull/109))
|
|
21
|
+
|
|
3
22
|
## [v0.17.1](https://github.com/chimurai/http-proxy-middleware/releases/tag/v0.17.1)
|
|
4
23
|
- fix(Express sub Router): 404 on non-proxy routes ([#94](https://github.com/chimurai/http-proxy-middleware/issues/94))
|
|
5
24
|
|
package/README.md
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
[](https://coveralls.io/r/chimurai/http-proxy-middleware)
|
|
5
5
|
[](https://david-dm.org/chimurai/http-proxy-middleware#info=dependencies)
|
|
6
6
|
[](https://snyk.io/test/npm/http-proxy-middleware)
|
|
7
|
+
[](https://standardjs.com)
|
|
7
8
|
|
|
8
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).
|
|
9
10
|
|
|
@@ -31,7 +32,7 @@ _All_ `http-proxy` [options](https://github.com/nodejitsu/node-http-proxy#option
|
|
|
31
32
|
|
|
32
33
|
## Table of Contents
|
|
33
34
|
|
|
34
|
-
<!-- MarkdownTOC autolink=true bracket=round depth=
|
|
35
|
+
<!-- MarkdownTOC autolink=true bracket=round depth=2 -->
|
|
35
36
|
|
|
36
37
|
- [Install](#install)
|
|
37
38
|
- [Core concept](#core-concept)
|
|
@@ -107,8 +108,8 @@ var options = {
|
|
|
107
108
|
changeOrigin: true, // needed for virtual hosted sites
|
|
108
109
|
ws: true, // proxy websockets
|
|
109
110
|
pathRewrite: {
|
|
110
|
-
'^/old
|
|
111
|
-
'^/remove/
|
|
111
|
+
'^/api/old-path' : '/api/new-path', // rewrite path
|
|
112
|
+
'^/api/remove/path' : '/path' // remove base path
|
|
112
113
|
},
|
|
113
114
|
router: {
|
|
114
115
|
// when request.headers.host == 'dev.localhost:3000',
|
|
@@ -130,7 +131,7 @@ var app = express();
|
|
|
130
131
|
|
|
131
132
|
Providing an alternative way to decide which requests should be proxied; In case you are not able to use the server's [`path` parameter](http://expressjs.com/en/4x/api.html#app.use) to mount the proxy or when you need more flexibility.
|
|
132
133
|
|
|
133
|
-
|
|
134
|
+
[RFC 3986 `path`](https://tools.ietf.org/html/rfc3986#section-3.3) is used for context matching.
|
|
134
135
|
|
|
135
136
|
```
|
|
136
137
|
foo://example.com:8042/over/there?name=ferret#nose
|
|
@@ -304,15 +305,29 @@ The following options are provided by the underlying [http-proxy](https://github
|
|
|
304
305
|
* **option.xfwd**: true/false, adds x-forward headers
|
|
305
306
|
* **option.secure**: true/false, if you want to verify the SSL Certs
|
|
306
307
|
* **option.toProxy**: true/false, passes the absolute URL as the `path` (useful for proxying to proxies)
|
|
307
|
-
* **option.prependPath**: true/false, Default: true - specify whether you want to prepend the target's path to the proxy path
|
|
308
|
-
* **option.ignorePath**: true/false, Default: false - specify whether you want to ignore the proxy path of the incoming request
|
|
308
|
+
* **option.prependPath**: true/false, Default: true - specify whether you want to prepend the target's path to the proxy path
|
|
309
|
+
* **option.ignorePath**: true/false, Default: false - specify whether you want to ignore the proxy path of the incoming request (note: you will have to append / manually if required).
|
|
309
310
|
* **option.localAddress** : Local interface string to bind for outgoing connections
|
|
310
|
-
* **option.changeOrigin**: true/false,
|
|
311
|
+
* **option.changeOrigin**: true/false, Default: false - changes the origin of the host header to the target URL
|
|
311
312
|
* **option.auth** : Basic authentication i.e. 'user:password' to compute an Authorization header.
|
|
312
313
|
* **option.hostRewrite**: rewrites the location hostname on (301/302/307/308) redirects.
|
|
313
314
|
* **option.autoRewrite**: rewrites the location host/port on (301/302/307/308) redirects based on requested host/port. Default: false.
|
|
314
315
|
* **option.protocolRewrite**: rewrites the location protocol on (301/302/307/308) redirects to 'http' or 'https'. Default: null.
|
|
316
|
+
* **option.cookieDomainRewrite**: rewrites domain of `set-cookie` headers. Possible values:
|
|
317
|
+
* `false` (default): disable cookie rewriting
|
|
318
|
+
* String: new domain, for example `cookieDomainRewrite: "new.domain"`. To remove the domain, use `cookieDomainRewrite: ""`.
|
|
319
|
+
* Object: mapping of domains to new domains, use `"*"` to match all domains.
|
|
320
|
+
For example keep one domain unchanged, rewrite one domain and remove other domains:
|
|
321
|
+
```
|
|
322
|
+
cookieDomainRewrite: {
|
|
323
|
+
"unchanged.domain": "unchanged.domain",
|
|
324
|
+
"old.domain": "new.domain",
|
|
325
|
+
"*": ""
|
|
326
|
+
}
|
|
327
|
+
```
|
|
315
328
|
* **option.headers**: object, adds [request headers](https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Request_fields). (Example: `{host:'www.example.org'}`)
|
|
329
|
+
* **option.proxyTimeout**: timeout (in millis) when proxy receives no response from target
|
|
330
|
+
|
|
316
331
|
|
|
317
332
|
|
|
318
333
|
## Shorthand
|
|
@@ -405,18 +420,13 @@ Run the test suite:
|
|
|
405
420
|
```bash
|
|
406
421
|
# install dependencies
|
|
407
422
|
$ npm install
|
|
408
|
-
```
|
|
409
423
|
|
|
410
|
-
|
|
424
|
+
# linting
|
|
425
|
+
$ npm run lint
|
|
411
426
|
|
|
412
|
-
```bash
|
|
413
427
|
# unit tests
|
|
414
428
|
$ npm test
|
|
415
|
-
```
|
|
416
|
-
|
|
417
|
-
coverage
|
|
418
429
|
|
|
419
|
-
```bash
|
|
420
430
|
# code coverage
|
|
421
431
|
$ npm run cover
|
|
422
432
|
```
|
|
@@ -430,4 +440,4 @@ $ npm run cover
|
|
|
430
440
|
|
|
431
441
|
The MIT License (MIT)
|
|
432
442
|
|
|
433
|
-
Copyright (c) 2015-
|
|
443
|
+
Copyright (c) 2015-2017 Steven Chim
|
package/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
var HPM
|
|
1
|
+
var HPM = require('./lib')
|
|
2
2
|
|
|
3
|
-
module.exports = function(context, opts) {
|
|
4
|
-
|
|
5
|
-
}
|
|
3
|
+
module.exports = function (context, opts) {
|
|
4
|
+
return new HPM(context, opts)
|
|
5
|
+
}
|
package/lib/config-factory.js
CHANGED
|
@@ -1,54 +1,55 @@
|
|
|
1
|
-
var _
|
|
2
|
-
var url
|
|
3
|
-
var
|
|
1
|
+
var _ = require('lodash')
|
|
2
|
+
var url = require('url')
|
|
3
|
+
var ERRORS = require('./errors')
|
|
4
|
+
var logger = require('./logger').getInstance()
|
|
4
5
|
|
|
5
6
|
module.exports = {
|
|
6
|
-
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
function createConfig(context, opts) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
// app.use('/api', proxy({target:'http://localhost:9000'}));
|
|
34
|
-
} else {
|
|
35
|
-
config.context = context;
|
|
36
|
-
config.options = _.assign(config.options, opts);
|
|
7
|
+
createConfig: createConfig
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function createConfig (context, opts) {
|
|
11
|
+
// structure of config object to be returned
|
|
12
|
+
var config = {
|
|
13
|
+
context: undefined,
|
|
14
|
+
options: {}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// app.use('/api', proxy({target:'http://localhost:9000'}));
|
|
18
|
+
if (isContextless(context, opts)) {
|
|
19
|
+
config.context = '/'
|
|
20
|
+
config.options = _.assign(config.options, context)
|
|
21
|
+
|
|
22
|
+
// app.use('/api', proxy('http://localhost:9000'));
|
|
23
|
+
// app.use(proxy('http://localhost:9000/api'));
|
|
24
|
+
} else if (isStringShortHand(context)) {
|
|
25
|
+
var oUrl = url.parse(context)
|
|
26
|
+
var target = [oUrl.protocol, '//', oUrl.host].join('')
|
|
27
|
+
|
|
28
|
+
config.context = oUrl.pathname || '/'
|
|
29
|
+
config.options = _.assign(config.options, {target: target}, opts)
|
|
30
|
+
|
|
31
|
+
if (oUrl.protocol === 'ws:' || oUrl.protocol === 'wss:') {
|
|
32
|
+
config.options.ws = true
|
|
37
33
|
}
|
|
34
|
+
// app.use('/api', proxy({target:'http://localhost:9000'}));
|
|
35
|
+
} else {
|
|
36
|
+
config.context = context
|
|
37
|
+
config.options = _.assign(config.options, opts)
|
|
38
|
+
}
|
|
38
39
|
|
|
39
|
-
|
|
40
|
+
configureLogger(config.options)
|
|
40
41
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
if (!config.options.target) {
|
|
43
|
+
throw new Error(ERRORS.ERR_CONFIG_FACTORY_TARGET_MISSING)
|
|
44
|
+
}
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
// Legacy option.proxyHost
|
|
47
|
+
config.options = mapLegacyProxyHostOption(config.options)
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
// Legacy option.proxyTable > option.router
|
|
50
|
+
config.options = mapLegacyProxyTableOption(config.options)
|
|
50
51
|
|
|
51
|
-
|
|
52
|
+
return config
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
/**
|
|
@@ -62,10 +63,10 @@ function createConfig(context, opts) {
|
|
|
62
63
|
* @param {String} context [description]
|
|
63
64
|
* @return {Boolean} [description]
|
|
64
65
|
*/
|
|
65
|
-
function isStringShortHand(context) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
function isStringShortHand (context) {
|
|
67
|
+
if (_.isString(context)) {
|
|
68
|
+
return !!(url.parse(context).host)
|
|
69
|
+
}
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
/**
|
|
@@ -79,48 +80,48 @@ function isStringShortHand(context) {
|
|
|
79
80
|
* @param {*} opts [description]
|
|
80
81
|
* @return {Boolean} [description]
|
|
81
82
|
*/
|
|
82
|
-
function isContextless(context, opts) {
|
|
83
|
-
|
|
83
|
+
function isContextless (context, opts) {
|
|
84
|
+
return (_.isPlainObject(context) && _.isEmpty(opts))
|
|
84
85
|
}
|
|
85
86
|
|
|
86
|
-
function mapLegacyProxyHostOption(options) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
options.headers = options.headers || {};
|
|
96
|
-
options.headers.host = options.proxyHost;
|
|
97
|
-
}
|
|
87
|
+
function mapLegacyProxyHostOption (options) {
|
|
88
|
+
// set options.headers.host when option.proxyHost is provided
|
|
89
|
+
if (options.proxyHost) {
|
|
90
|
+
logger.warn('*************************************')
|
|
91
|
+
logger.warn('[HPM] Deprecated "option.proxyHost"')
|
|
92
|
+
logger.warn(' Use "option.changeOrigin" or "option.headers.host" instead')
|
|
93
|
+
logger.warn(' "option.proxyHost" will be removed in future release.')
|
|
94
|
+
logger.warn('*************************************')
|
|
98
95
|
|
|
99
|
-
|
|
96
|
+
options.headers = options.headers || {}
|
|
97
|
+
options.headers.host = options.proxyHost
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return options
|
|
100
101
|
}
|
|
101
102
|
|
|
102
103
|
// Warn deprecated proxyTable api usage
|
|
103
|
-
function mapLegacyProxyTableOption(options) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
104
|
+
function mapLegacyProxyTableOption (options) {
|
|
105
|
+
if (options.proxyTable) {
|
|
106
|
+
logger.warn('*************************************')
|
|
107
|
+
logger.warn('[HPM] Deprecated "option.proxyTable"')
|
|
108
|
+
logger.warn(' Use "option.router" instead')
|
|
109
|
+
logger.warn(' "option.proxyTable" will be removed in future release.')
|
|
110
|
+
logger.warn('*************************************')
|
|
111
|
+
|
|
112
|
+
options.router = _.clone(options.proxyTable)
|
|
113
|
+
_.omit(options, 'proxyTable')
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return options
|
|
116
117
|
}
|
|
117
118
|
|
|
118
|
-
function configureLogger(options) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
119
|
+
function configureLogger (options) {
|
|
120
|
+
if (options.logLevel) {
|
|
121
|
+
logger.setLevel(options.logLevel)
|
|
122
|
+
}
|
|
122
123
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
124
|
+
if (options.logProvider) {
|
|
125
|
+
logger.setProvider(options.logProvider)
|
|
126
|
+
}
|
|
126
127
|
}
|
package/lib/context-matcher.js
CHANGED
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
var _ = require('lodash')
|
|
2
|
-
var url = require('url')
|
|
3
|
-
var isGlob = require('is-glob')
|
|
4
|
-
var micromatch = require('micromatch')
|
|
1
|
+
var _ = require('lodash')
|
|
2
|
+
var url = require('url')
|
|
3
|
+
var isGlob = require('is-glob')
|
|
4
|
+
var micromatch = require('micromatch')
|
|
5
|
+
var ERRORS = require('./errors')
|
|
5
6
|
|
|
6
7
|
module.exports = {
|
|
7
|
-
|
|
8
|
-
}
|
|
8
|
+
match: matchContext
|
|
9
|
+
}
|
|
9
10
|
|
|
10
|
-
function matchContext(context, uri, req) {
|
|
11
|
+
function matchContext (context, uri, req) {
|
|
12
|
+
// single path
|
|
13
|
+
if (isStringPath(context)) {
|
|
14
|
+
return matchSingleStringPath(context, uri)
|
|
15
|
+
}
|
|
11
16
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
17
|
+
// single glob path
|
|
18
|
+
if (isGlobPath(context)) {
|
|
19
|
+
return matchSingleGlobPath(context, uri)
|
|
20
|
+
}
|
|
16
21
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
// multi path
|
|
23
|
+
if (Array.isArray(context)) {
|
|
24
|
+
if (context.every(isStringPath)) {
|
|
25
|
+
return matchMultiPath(context, uri)
|
|
20
26
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (Array.isArray(context)) {
|
|
24
|
-
if (context.every(isStringPath)) {
|
|
25
|
-
return matchMultiPath(context, uri);
|
|
26
|
-
}
|
|
27
|
-
if (context.every(isGlobPath)) {
|
|
28
|
-
return matchMultiGlobPath(context, uri);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
throw new Error('[HPM] Invalid context. Expecting something like: ["/api", "/ajax"] or ["/api/**", "!**.html"]');
|
|
27
|
+
if (context.every(isGlobPath)) {
|
|
28
|
+
return matchMultiGlobPath(context, uri)
|
|
32
29
|
}
|
|
33
30
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
31
|
+
throw new Error(ERRORS.ERR_CONTEXT_MATCHER_INVALID_ARRAY)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// custom matching
|
|
35
|
+
if (_.isFunction(context)) {
|
|
36
|
+
var pathname = getUrlPathName(uri)
|
|
37
|
+
return context(pathname, req)
|
|
38
|
+
}
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
throw new Error(ERRORS.ERR_CONTEXT_MATCHER_GENERIC)
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
/**
|
|
@@ -45,34 +45,34 @@ function matchContext(context, uri, req) {
|
|
|
45
45
|
* @param {String} uri 'http://example.org/api/b/c/d.html'
|
|
46
46
|
* @return {Boolean}
|
|
47
47
|
*/
|
|
48
|
-
function matchSingleStringPath(context, uri) {
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
function matchSingleStringPath (context, uri) {
|
|
49
|
+
var pathname = getUrlPathName(uri)
|
|
50
|
+
return pathname.indexOf(context) === 0
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
function matchSingleGlobPath(pattern, uri) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
function matchSingleGlobPath (pattern, uri) {
|
|
54
|
+
var pathname = getUrlPathName(uri)
|
|
55
|
+
var matches = micromatch(pathname, pattern)
|
|
56
|
+
return matches && (matches.length > 0)
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
function matchMultiGlobPath(patternList, uri) {
|
|
60
|
-
|
|
59
|
+
function matchMultiGlobPath (patternList, uri) {
|
|
60
|
+
return matchSingleGlobPath(patternList, uri)
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
/**
|
|
64
|
-
* @param {String}
|
|
64
|
+
* @param {String} contextList ['/api', '/ajax']
|
|
65
65
|
* @param {String} uri 'http://example.org/api/b/c/d.html'
|
|
66
66
|
* @return {Boolean}
|
|
67
67
|
*/
|
|
68
|
-
function matchMultiPath(contextList, uri) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
68
|
+
function matchMultiPath (contextList, uri) {
|
|
69
|
+
for (var i = 0; i < contextList.length; i++) {
|
|
70
|
+
var context = contextList[i]
|
|
71
|
+
if (matchSingleStringPath(context, uri)) {
|
|
72
|
+
return true
|
|
74
73
|
}
|
|
75
|
-
|
|
74
|
+
}
|
|
75
|
+
return false
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
/**
|
|
@@ -81,14 +81,14 @@ function matchMultiPath(contextList, uri) {
|
|
|
81
81
|
* @param {String} uri from req.url
|
|
82
82
|
* @return {String} RFC 3986 path
|
|
83
83
|
*/
|
|
84
|
-
function getUrlPathName(uri) {
|
|
85
|
-
|
|
84
|
+
function getUrlPathName (uri) {
|
|
85
|
+
return uri && url.parse(uri).pathname
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
function isStringPath(context) {
|
|
89
|
-
|
|
88
|
+
function isStringPath (context) {
|
|
89
|
+
return _.isString(context) && !isGlob(context)
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
function isGlobPath(context) {
|
|
93
|
-
|
|
92
|
+
function isGlobPath (context) {
|
|
93
|
+
return isGlob(context)
|
|
94
94
|
}
|
package/lib/errors.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/* eslint-disable max-len */
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
ERR_CONFIG_FACTORY_TARGET_MISSING: '[HPM] Missing "target" option. Example: {target: "http://www.example.org"}',
|
|
5
|
+
ERR_CONTEXT_MATCHER_GENERIC: '[HPM] Invalid context. Expecting something like: "/api" or ["/api", "/ajax"]',
|
|
6
|
+
ERR_CONTEXT_MATCHER_INVALID_ARRAY: '[HPM] Invalid context. Expecting something like: ["/api", "/ajax"] or ["/api/**", "!**.html"]',
|
|
7
|
+
ERR_PATH_REWRITER_CONFIG: '[HPM] Invalid pathRewrite config. Expecting object with pathRewrite config or a rewrite function'
|
|
8
|
+
}
|
package/lib/handlers.js
CHANGED
|
@@ -1,62 +1,74 @@
|
|
|
1
|
-
var _
|
|
2
|
-
var logger = require('./logger').getInstance()
|
|
1
|
+
var _ = require('lodash')
|
|
2
|
+
var logger = require('./logger').getInstance()
|
|
3
3
|
|
|
4
4
|
module.exports = {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}
|
|
5
|
+
init: init,
|
|
6
|
+
getHandlers: getProxyEventHandlers
|
|
7
|
+
}
|
|
8
8
|
|
|
9
|
-
function init(proxy, opts) {
|
|
10
|
-
|
|
9
|
+
function init (proxy, opts) {
|
|
10
|
+
var handlers = getProxyEventHandlers(opts)
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
_.forIn(handlers, function (handler, eventName) {
|
|
13
|
+
proxy.on(eventName, handlers[eventName])
|
|
14
|
+
})
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
logger.debug('[HPM] Subscribed to http-proxy events: ', _.keys(handlers))
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
function getProxyEventHandlers(opts) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
}
|
|
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)
|
|
40
30
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
handlers.close = logClose;
|
|
31
|
+
if (_.isFunction(fnHandler)) {
|
|
32
|
+
handlers[event] = fnHandler
|
|
44
33
|
}
|
|
34
|
+
})
|
|
45
35
|
|
|
46
|
-
|
|
47
|
-
|
|
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
48
|
|
|
49
|
-
function defaultErrorHandler(err, req, res) {
|
|
50
|
-
|
|
49
|
+
function defaultErrorHandler (err, req, res) {
|
|
50
|
+
var host = (req.headers && req.headers.host)
|
|
51
|
+
var code = err.code
|
|
51
52
|
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
if (res.writeHead && !res.headersSent) {
|
|
54
|
+
if (/HPE_INVALID/.test(code)) {
|
|
55
|
+
res.writeHead(502)
|
|
56
|
+
} else {
|
|
57
|
+
switch (code) {
|
|
58
|
+
case 'ECONNRESET':
|
|
59
|
+
case 'ENOTFOUND':
|
|
60
|
+
case 'ECONNREFUSED':
|
|
61
|
+
res.writeHead(504)
|
|
62
|
+
break
|
|
63
|
+
default: res.writeHead(500)
|
|
64
|
+
}
|
|
54
65
|
}
|
|
66
|
+
}
|
|
55
67
|
|
|
56
|
-
|
|
68
|
+
res.end('Error occured while trying to proxy to: ' + host + req.url)
|
|
57
69
|
}
|
|
58
70
|
|
|
59
|
-
function logClose(req, socket, head) {
|
|
60
|
-
|
|
61
|
-
|
|
71
|
+
function logClose (req, socket, head) {
|
|
72
|
+
// view disconnected websocket connections
|
|
73
|
+
logger.info('[HPM] Client disconnected')
|
|
62
74
|
}
|