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/lib/index.js
CHANGED
|
@@ -1,137 +1,152 @@
|
|
|
1
|
-
var _
|
|
2
|
-
var httpProxy
|
|
3
|
-
var configFactory
|
|
4
|
-
var handlers
|
|
5
|
-
var contextMatcher = require('./context-matcher')
|
|
6
|
-
var PathRewriter
|
|
7
|
-
var Router
|
|
8
|
-
var logger
|
|
9
|
-
var getArrow
|
|
10
|
-
|
|
11
|
-
module.exports = HttpProxyMiddleware
|
|
12
|
-
|
|
13
|
-
function HttpProxyMiddleware(context, opts) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (proxyOptions.ws === true) {
|
|
46
|
-
catchUpgradeRequest(req.connection.server);
|
|
47
|
-
}
|
|
1
|
+
var _ = require('lodash')
|
|
2
|
+
var httpProxy = require('http-proxy')
|
|
3
|
+
var configFactory = require('./config-factory')
|
|
4
|
+
var handlers = require('./handlers')
|
|
5
|
+
var contextMatcher = require('./context-matcher')
|
|
6
|
+
var PathRewriter = require('./path-rewriter')
|
|
7
|
+
var Router = require('./router')
|
|
8
|
+
var logger = require('./logger').getInstance()
|
|
9
|
+
var getArrow = require('./logger').getArrow
|
|
10
|
+
|
|
11
|
+
module.exports = HttpProxyMiddleware
|
|
12
|
+
|
|
13
|
+
function HttpProxyMiddleware (context, opts) {
|
|
14
|
+
// https://github.com/chimurai/http-proxy-middleware/issues/57
|
|
15
|
+
var wsUpgradeDebounced = _.debounce(handleUpgrade)
|
|
16
|
+
var wsInitialized = false
|
|
17
|
+
var config = configFactory.createConfig(context, opts)
|
|
18
|
+
var proxyOptions = config.options
|
|
19
|
+
|
|
20
|
+
// create proxy
|
|
21
|
+
var proxy = httpProxy.createProxyServer({})
|
|
22
|
+
logger.info('[HPM] Proxy created:', config.context, ' -> ', proxyOptions.target)
|
|
23
|
+
|
|
24
|
+
var pathRewriter = PathRewriter.create(proxyOptions.pathRewrite) // returns undefined when "pathRewrite" is not provided
|
|
25
|
+
|
|
26
|
+
// attach handler to http-proxy events
|
|
27
|
+
handlers.init(proxy, proxyOptions)
|
|
28
|
+
|
|
29
|
+
// log errors for debug purpose
|
|
30
|
+
proxy.on('error', logError)
|
|
31
|
+
|
|
32
|
+
// https://github.com/chimurai/http-proxy-middleware/issues/19
|
|
33
|
+
// expose function to upgrade externally
|
|
34
|
+
middleware.upgrade = wsUpgradeDebounced
|
|
35
|
+
|
|
36
|
+
return middleware
|
|
37
|
+
|
|
38
|
+
function middleware (req, res, next) {
|
|
39
|
+
if (shouldProxy(config.context, req)) {
|
|
40
|
+
var activeProxyOptions = prepareProxyRequest(req)
|
|
41
|
+
proxy.web(req, res, activeProxyOptions)
|
|
42
|
+
} else {
|
|
43
|
+
next()
|
|
48
44
|
}
|
|
49
45
|
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
if (proxyOptions.ws === true) {
|
|
47
|
+
// use initial request to access the server object to subscribe to http upgrade event
|
|
48
|
+
catchUpgradeRequest(req.connection.server)
|
|
52
49
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function catchUpgradeRequest (server) {
|
|
53
|
+
// subscribe once; don't subscribe on every request...
|
|
54
|
+
// https://github.com/chimurai/http-proxy-middleware/issues/113
|
|
55
|
+
if (!wsInitialized) {
|
|
56
|
+
server.on('upgrade', wsUpgradeDebounced)
|
|
57
|
+
wsInitialized = true
|
|
60
58
|
}
|
|
59
|
+
}
|
|
61
60
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
* @private
|
|
66
|
-
* @return {Boolean}
|
|
67
|
-
*/
|
|
68
|
-
function shouldProxy(context, req) {
|
|
69
|
-
var path = (req.originalUrl || req.url);
|
|
70
|
-
return contextMatcher.match(context, path, req);
|
|
71
|
-
}
|
|
61
|
+
function handleUpgrade (req, socket, head) {
|
|
62
|
+
// set to initialized when used externally
|
|
63
|
+
wsInitialized = true
|
|
72
64
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
65
|
+
if (shouldProxy(config.context, req)) {
|
|
66
|
+
var activeProxyOptions = prepareProxyRequest(req)
|
|
67
|
+
proxy.ws(req, socket, head, activeProxyOptions)
|
|
68
|
+
logger.info('[HPM] Upgrading to WebSocket')
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Determine whether request should be proxied.
|
|
74
|
+
*
|
|
75
|
+
* @private
|
|
76
|
+
* @param {String} context [description]
|
|
77
|
+
* @param {Object} req [description]
|
|
78
|
+
* @return {Boolean}
|
|
79
|
+
*/
|
|
80
|
+
function shouldProxy (context, req) {
|
|
81
|
+
var path = (req.originalUrl || req.url)
|
|
82
|
+
return contextMatcher.match(context, path, req)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Apply option.router and option.pathRewrite
|
|
87
|
+
* Order matters:
|
|
88
|
+
* Router uses original path for routing;
|
|
89
|
+
* NOT the modified path, after it has been rewritten by pathRewrite
|
|
90
|
+
* @param {Object} req
|
|
91
|
+
* @return {Object} proxy options
|
|
92
|
+
*/
|
|
93
|
+
function prepareProxyRequest (req) {
|
|
94
|
+
// https://github.com/chimurai/http-proxy-middleware/issues/17
|
|
95
|
+
// https://github.com/chimurai/http-proxy-middleware/issues/94
|
|
96
|
+
req.url = (req.originalUrl || req.url)
|
|
97
|
+
|
|
98
|
+
// store uri before it gets rewritten for logging
|
|
99
|
+
var originalPath = req.url
|
|
100
|
+
var newProxyOptions = _.assign({}, proxyOptions)
|
|
101
|
+
|
|
102
|
+
// Apply in order:
|
|
103
|
+
// 1. option.router
|
|
104
|
+
// 2. option.pathRewrite
|
|
105
|
+
__applyRouter(req, newProxyOptions)
|
|
106
|
+
__applyPathRewrite(req, pathRewriter)
|
|
107
|
+
|
|
108
|
+
// debug logging for both http(s) and websockets
|
|
109
|
+
if (proxyOptions.logLevel === 'debug') {
|
|
110
|
+
var arrow = getArrow(originalPath, req.url, proxyOptions.target, newProxyOptions.target)
|
|
111
|
+
logger.debug('[HPM] %s %s %s %s', req.method, originalPath, arrow, newProxyOptions.target)
|
|
101
112
|
}
|
|
102
113
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
var newTarget;
|
|
114
|
+
return newProxyOptions
|
|
115
|
+
}
|
|
106
116
|
|
|
107
|
-
|
|
108
|
-
|
|
117
|
+
// Modify option.target when router present.
|
|
118
|
+
function __applyRouter (req, options) {
|
|
119
|
+
var newTarget
|
|
109
120
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
options.target = newTarget;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
121
|
+
if (options.router) {
|
|
122
|
+
newTarget = Router.getTarget(req, options)
|
|
116
123
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
124
|
+
if (newTarget) {
|
|
125
|
+
logger.debug('[HPM] Router new target: %s -> "%s"', options.target, newTarget)
|
|
126
|
+
options.target = newTarget
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// rewrite path
|
|
132
|
+
function __applyPathRewrite (req, pathRewriter) {
|
|
133
|
+
if (pathRewriter) {
|
|
134
|
+
var path = pathRewriter(req.url, req)
|
|
135
|
+
|
|
136
|
+
if (typeof path === 'string') {
|
|
137
|
+
req.url = path
|
|
138
|
+
} else {
|
|
139
|
+
logger.info('[HPM] pathRewrite: No rewritten path found. (%s)', req.url)
|
|
140
|
+
}
|
|
128
141
|
}
|
|
142
|
+
}
|
|
129
143
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
144
|
+
function logError (err, req, res) {
|
|
145
|
+
var hostname = (req.headers && req.headers.host) || (req.hostname || req.host) // (websocket) || (node0.10 || node 4/5)
|
|
146
|
+
var target = proxyOptions.target.host || proxyOptions.target
|
|
147
|
+
var errorMessage = '[HPM] Error occurred while trying to proxy request %s from %s to %s (%s) (%s)'
|
|
148
|
+
var errReference = 'https://nodejs.org/api/errors.html#errors_common_system_errors' // link to Node Common Systems Errors page
|
|
134
149
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
150
|
+
logger.error(errorMessage, req.url, hostname, target, err.code, errReference)
|
|
151
|
+
}
|
|
152
|
+
}
|
package/lib/logger.js
CHANGED
|
@@ -1,144 +1,144 @@
|
|
|
1
|
-
var util = require('util')
|
|
2
|
-
var _ = require('lodash')
|
|
1
|
+
var util = require('util')
|
|
2
|
+
var _ = require('lodash')
|
|
3
3
|
|
|
4
|
-
var loggerInstance
|
|
4
|
+
var loggerInstance
|
|
5
5
|
|
|
6
6
|
var defaultProvider = {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
7
|
+
log: console.log,
|
|
8
|
+
debug: console.log, // use .log(); since console does not have .debug()
|
|
9
|
+
info: console.info,
|
|
10
|
+
warn: console.warn,
|
|
11
|
+
error: console.error
|
|
12
|
+
}
|
|
13
13
|
|
|
14
14
|
// log level 'weight'
|
|
15
15
|
var LEVELS = {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
16
|
+
debug: 10,
|
|
17
|
+
info: 20,
|
|
18
|
+
warn: 30,
|
|
19
|
+
error: 50,
|
|
20
|
+
silent: 80
|
|
21
|
+
}
|
|
22
22
|
|
|
23
23
|
module.exports = {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
// singleton
|
|
25
|
+
getInstance: function () {
|
|
26
|
+
if (!loggerInstance) {
|
|
27
|
+
loggerInstance = new Logger()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return loggerInstance
|
|
31
|
+
},
|
|
32
|
+
getArrow: getArrow
|
|
33
|
+
}
|
|
29
34
|
|
|
30
|
-
|
|
35
|
+
function Logger () {
|
|
36
|
+
var logLevel
|
|
37
|
+
var provider
|
|
38
|
+
|
|
39
|
+
var api = {
|
|
40
|
+
log: log,
|
|
41
|
+
debug: debug,
|
|
42
|
+
info: info,
|
|
43
|
+
warn: warn,
|
|
44
|
+
error: error,
|
|
45
|
+
setLevel: function (v) {
|
|
46
|
+
if (isValidLevel(v)) {
|
|
47
|
+
logLevel = v
|
|
48
|
+
}
|
|
31
49
|
},
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
var logLevel;
|
|
37
|
-
var provider;
|
|
38
|
-
|
|
39
|
-
var api = {
|
|
40
|
-
log: log,
|
|
41
|
-
debug: debug,
|
|
42
|
-
info: info,
|
|
43
|
-
warn: warn,
|
|
44
|
-
error: error,
|
|
45
|
-
setLevel: function(v) {
|
|
46
|
-
if (isValidLevel(v)) {
|
|
47
|
-
logLevel = v;
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
|
-
setProvider: function(fn) {
|
|
51
|
-
if (fn && isValidProvider(fn)) {
|
|
52
|
-
provider = fn(defaultProvider);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
init();
|
|
58
|
-
|
|
59
|
-
return api;
|
|
60
|
-
|
|
61
|
-
function init() {
|
|
62
|
-
api.setLevel('info');
|
|
63
|
-
api.setProvider(function() {
|
|
64
|
-
return defaultProvider;
|
|
65
|
-
});
|
|
50
|
+
setProvider: function (fn) {
|
|
51
|
+
if (fn && isValidProvider(fn)) {
|
|
52
|
+
provider = fn(defaultProvider)
|
|
53
|
+
}
|
|
66
54
|
}
|
|
55
|
+
}
|
|
67
56
|
|
|
68
|
-
|
|
69
|
-
function log() {
|
|
70
|
-
provider.log(_interpolate.apply(null, arguments));
|
|
71
|
-
}
|
|
57
|
+
init()
|
|
72
58
|
|
|
73
|
-
|
|
74
|
-
if (_showLevel('debug')) {
|
|
75
|
-
provider.debug(_interpolate.apply(null, arguments));
|
|
76
|
-
}
|
|
77
|
-
}
|
|
59
|
+
return api
|
|
78
60
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
61
|
+
function init () {
|
|
62
|
+
api.setLevel('info')
|
|
63
|
+
api.setProvider(function () {
|
|
64
|
+
return defaultProvider
|
|
65
|
+
})
|
|
66
|
+
}
|
|
84
67
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
68
|
+
// log will log messages, regardless of logLevels
|
|
69
|
+
function log () {
|
|
70
|
+
provider.log(_interpolate.apply(null, arguments))
|
|
71
|
+
}
|
|
90
72
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
73
|
+
function debug () {
|
|
74
|
+
if (_showLevel('debug')) {
|
|
75
|
+
provider.debug(_interpolate.apply(null, arguments))
|
|
95
76
|
}
|
|
77
|
+
}
|
|
96
78
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
function _showLevel(showLevel) {
|
|
103
|
-
var result = false;
|
|
104
|
-
var currentLogLevel = LEVELS[logLevel];
|
|
79
|
+
function info () {
|
|
80
|
+
if (_showLevel('info')) {
|
|
81
|
+
provider.info(_interpolate.apply(null, arguments))
|
|
82
|
+
}
|
|
83
|
+
}
|
|
105
84
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
85
|
+
function warn () {
|
|
86
|
+
if (_showLevel('warn')) {
|
|
87
|
+
provider.warn(_interpolate.apply(null, arguments))
|
|
88
|
+
}
|
|
89
|
+
}
|
|
109
90
|
|
|
110
|
-
|
|
91
|
+
function error () {
|
|
92
|
+
if (_showLevel('error')) {
|
|
93
|
+
provider.error(_interpolate.apply(null, arguments))
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Decide to log or not to log, based on the log levels 'weight'
|
|
99
|
+
* @param {String} showLevel [debug, info, warn, error, silent]
|
|
100
|
+
* @return {Boolean}
|
|
101
|
+
*/
|
|
102
|
+
function _showLevel (showLevel) {
|
|
103
|
+
var result = false
|
|
104
|
+
var currentLogLevel = LEVELS[logLevel]
|
|
105
|
+
|
|
106
|
+
if (currentLogLevel && (currentLogLevel <= LEVELS[showLevel])) {
|
|
107
|
+
result = true
|
|
111
108
|
}
|
|
112
109
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
function _interpolate() {
|
|
116
|
-
var fn = _.spread(util.format);
|
|
117
|
-
var result = fn(_.slice(arguments));
|
|
110
|
+
return result
|
|
111
|
+
}
|
|
118
112
|
|
|
119
|
-
|
|
120
|
-
|
|
113
|
+
// make sure logged messages and its data are return interpolated
|
|
114
|
+
// make it possible for additional log data, such date/time or custom prefix.
|
|
115
|
+
function _interpolate () {
|
|
116
|
+
var fn = _.spread(util.format)
|
|
117
|
+
var result = fn(_.slice(arguments))
|
|
121
118
|
|
|
122
|
-
|
|
123
|
-
|
|
119
|
+
return result
|
|
120
|
+
}
|
|
124
121
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
122
|
+
function isValidProvider (fnProvider) {
|
|
123
|
+
var result = true
|
|
128
124
|
|
|
129
|
-
|
|
125
|
+
if (fnProvider && !_.isFunction(fnProvider)) {
|
|
126
|
+
throw new Error('[HPM] Log provider config error. Expecting a function.')
|
|
130
127
|
}
|
|
131
128
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
var isValid = _.includes(validLevels, levelName);
|
|
129
|
+
return result
|
|
130
|
+
}
|
|
135
131
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
132
|
+
function isValidLevel (levelName) {
|
|
133
|
+
var validLevels = _.keys(LEVELS)
|
|
134
|
+
var isValid = _.includes(validLevels, levelName)
|
|
139
135
|
|
|
140
|
-
|
|
136
|
+
if (!isValid) {
|
|
137
|
+
throw new Error('[HPM] Log level error. Invalid logLevel.')
|
|
141
138
|
}
|
|
139
|
+
|
|
140
|
+
return isValid
|
|
141
|
+
}
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
/**
|
|
@@ -146,13 +146,27 @@ function Logger() {
|
|
|
146
146
|
* => router
|
|
147
147
|
* ~> pathRewrite
|
|
148
148
|
* ≈> router + pathRewrite
|
|
149
|
+
*
|
|
150
|
+
* @param {String} originalPath
|
|
151
|
+
* @param {String} newPath
|
|
152
|
+
* @param {String} originalTarget
|
|
153
|
+
* @param {String} newTarget
|
|
154
|
+
* @return {String}
|
|
149
155
|
*/
|
|
150
|
-
function getArrow(originalPath, newPath, originalTarget, newTarget) {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
156
|
+
function getArrow (originalPath, newPath, originalTarget, newTarget) {
|
|
157
|
+
var arrow = ['>']
|
|
158
|
+
var isNewTarget = (originalTarget !== newTarget) // router
|
|
159
|
+
var isNewPath = (originalPath !== newPath) // pathRewrite
|
|
160
|
+
|
|
161
|
+
if (isNewPath && !isNewTarget) {
|
|
162
|
+
arrow.unshift('~')
|
|
163
|
+
} else if (!isNewPath && isNewTarget) {
|
|
164
|
+
arrow.unshift('=')
|
|
165
|
+
} else if (isNewPath && isNewTarget) {
|
|
166
|
+
arrow.unshift('≈')
|
|
167
|
+
} else {
|
|
168
|
+
arrow.unshift('-')
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return arrow.join('')
|
|
158
172
|
}
|