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.
@@ -1,94 +0,0 @@
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')
6
-
7
- module.exports = {
8
- match: matchContext
9
- }
10
-
11
- function matchContext(context, uri, req) {
12
- // single path
13
- if (isStringPath(context)) {
14
- return matchSingleStringPath(context, uri)
15
- }
16
-
17
- // single glob path
18
- if (isGlobPath(context)) {
19
- return matchSingleGlobPath(context, uri)
20
- }
21
-
22
- // multi path
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(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
-
40
- throw new Error(ERRORS.ERR_CONTEXT_MATCHER_GENERIC)
41
- }
42
-
43
- /**
44
- * @param {String} context '/api'
45
- * @param {String} uri 'http://example.org/api/b/c/d.html'
46
- * @return {Boolean}
47
- */
48
- function matchSingleStringPath(context, uri) {
49
- var pathname = getUrlPathName(uri)
50
- return pathname.indexOf(context) === 0
51
- }
52
-
53
- function matchSingleGlobPath(pattern, uri) {
54
- var pathname = getUrlPathName(uri)
55
- var matches = micromatch(pathname, pattern)
56
- return matches && matches.length > 0
57
- }
58
-
59
- function matchMultiGlobPath(patternList, uri) {
60
- return matchSingleGlobPath(patternList, uri)
61
- }
62
-
63
- /**
64
- * @param {String} contextList ['/api', '/ajax']
65
- * @param {String} uri 'http://example.org/api/b/c/d.html'
66
- * @return {Boolean}
67
- */
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
73
- }
74
- }
75
- return false
76
- }
77
-
78
- /**
79
- * Parses URI and returns RFC 3986 path
80
- *
81
- * @param {String} uri from req.url
82
- * @return {String} RFC 3986 path
83
- */
84
- function getUrlPathName(uri) {
85
- return uri && url.parse(uri).pathname
86
- }
87
-
88
- function isStringPath(context) {
89
- return _.isString(context) && !isGlob(context)
90
- }
91
-
92
- function isGlobPath(context) {
93
- return isGlob(context)
94
- }
package/lib/errors.js DELETED
@@ -1,12 +0,0 @@
1
- /* eslint-disable max-len */
2
-
3
- module.exports = {
4
- ERR_CONFIG_FACTORY_TARGET_MISSING:
5
- '[HPM] Missing "target" option. Example: {target: "http://www.example.org"}',
6
- ERR_CONTEXT_MATCHER_GENERIC:
7
- '[HPM] Invalid context. Expecting something like: "/api" or ["/api", "/ajax"]',
8
- ERR_CONTEXT_MATCHER_INVALID_ARRAY:
9
- '[HPM] Invalid context. Expecting something like: ["/api", "/ajax"] or ["/api/**", "!**.html"]',
10
- ERR_PATH_REWRITER_CONFIG:
11
- '[HPM] Invalid pathRewrite config. Expecting object with pathRewrite config or a rewrite function'
12
- }
package/lib/handlers.js DELETED
@@ -1,82 +0,0 @@
1
- var _ = require('lodash')
2
- var logger = require('./logger').getInstance()
3
-
4
- module.exports = {
5
- init: init,
6
- getHandlers: getProxyEventHandlers
7
- }
8
-
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 = [
22
- 'error',
23
- 'proxyReq',
24
- 'proxyReqWs',
25
- 'proxyRes',
26
- 'open',
27
- 'close'
28
- ]
29
- var handlers = {}
30
-
31
- _.forEach(proxyEvents, function(event) {
32
- // all handlers for the http-proxy events are prefixed with 'on'.
33
- // loop through options and try to find these handlers
34
- // and add them to the handlers object for subscription in init().
35
- var eventName = _.camelCase('on ' + event)
36
- var fnHandler = _.get(opts, eventName)
37
-
38
- if (_.isFunction(fnHandler)) {
39
- handlers[event] = fnHandler
40
- }
41
- })
42
-
43
- // add default error handler in absence of error handler
44
- if (!_.isFunction(handlers.error)) {
45
- handlers.error = defaultErrorHandler
46
- }
47
-
48
- // add default close handler in absence of close handler
49
- if (!_.isFunction(handlers.close)) {
50
- handlers.close = logClose
51
- }
52
-
53
- return handlers
54
- }
55
-
56
- function defaultErrorHandler(err, req, res) {
57
- var host = req.headers && req.headers.host
58
- var code = err.code
59
-
60
- if (res.writeHead && !res.headersSent) {
61
- if (/HPE_INVALID/.test(code)) {
62
- res.writeHead(502)
63
- } else {
64
- switch (code) {
65
- case 'ECONNRESET':
66
- case 'ENOTFOUND':
67
- case 'ECONNREFUSED':
68
- res.writeHead(504)
69
- break
70
- default:
71
- res.writeHead(500)
72
- }
73
- }
74
- }
75
-
76
- res.end('Error occured while trying to proxy to: ' + host + req.url)
77
- }
78
-
79
- function logClose(req, socket, head) {
80
- // view disconnected websocket connections
81
- logger.info('[HPM] Client disconnected')
82
- }
package/lib/index.js DELETED
@@ -1,182 +0,0 @@
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(
23
- '[HPM] Proxy created:',
24
- config.context,
25
- ' -> ',
26
- proxyOptions.target
27
- )
28
-
29
- var pathRewriter = PathRewriter.create(proxyOptions.pathRewrite) // returns undefined when "pathRewrite" is not provided
30
-
31
- // attach handler to http-proxy events
32
- handlers.init(proxy, proxyOptions)
33
-
34
- // log errors for debug purpose
35
- proxy.on('error', logError)
36
-
37
- // https://github.com/chimurai/http-proxy-middleware/issues/19
38
- // expose function to upgrade externally
39
- middleware.upgrade = wsUpgradeDebounced
40
-
41
- return middleware
42
-
43
- function middleware(req, res, next) {
44
- if (shouldProxy(config.context, req)) {
45
- var activeProxyOptions = prepareProxyRequest(req)
46
- proxy.web(req, res, activeProxyOptions)
47
- } else {
48
- next()
49
- }
50
-
51
- if (proxyOptions.ws === true) {
52
- // use initial request to access the server object to subscribe to http upgrade event
53
- catchUpgradeRequest(req.connection.server)
54
- }
55
- }
56
-
57
- function catchUpgradeRequest(server) {
58
- // subscribe once; don't subscribe on every request...
59
- // https://github.com/chimurai/http-proxy-middleware/issues/113
60
- if (!wsInitialized) {
61
- server.on('upgrade', wsUpgradeDebounced)
62
- wsInitialized = true
63
- }
64
- }
65
-
66
- function handleUpgrade(req, socket, head) {
67
- // set to initialized when used externally
68
- wsInitialized = true
69
-
70
- if (shouldProxy(config.context, req)) {
71
- var activeProxyOptions = prepareProxyRequest(req)
72
- proxy.ws(req, socket, head, activeProxyOptions)
73
- logger.info('[HPM] Upgrading to WebSocket')
74
- }
75
- }
76
-
77
- /**
78
- * Determine whether request should be proxied.
79
- *
80
- * @private
81
- * @param {String} context [description]
82
- * @param {Object} req [description]
83
- * @return {Boolean}
84
- */
85
- function shouldProxy(context, req) {
86
- var path = req.originalUrl || req.url
87
- return contextMatcher.match(context, path, req)
88
- }
89
-
90
- /**
91
- * Apply option.router and option.pathRewrite
92
- * Order matters:
93
- * Router uses original path for routing;
94
- * NOT the modified path, after it has been rewritten by pathRewrite
95
- * @param {Object} req
96
- * @return {Object} proxy options
97
- */
98
- function prepareProxyRequest(req) {
99
- // https://github.com/chimurai/http-proxy-middleware/issues/17
100
- // https://github.com/chimurai/http-proxy-middleware/issues/94
101
- req.url = req.originalUrl || req.url
102
-
103
- // store uri before it gets rewritten for logging
104
- var originalPath = req.url
105
- var newProxyOptions = _.assign({}, proxyOptions)
106
-
107
- // Apply in order:
108
- // 1. option.router
109
- // 2. option.pathRewrite
110
- __applyRouter(req, newProxyOptions)
111
- __applyPathRewrite(req, pathRewriter)
112
-
113
- // debug logging for both http(s) and websockets
114
- if (proxyOptions.logLevel === 'debug') {
115
- var arrow = getArrow(
116
- originalPath,
117
- req.url,
118
- proxyOptions.target,
119
- newProxyOptions.target
120
- )
121
- logger.debug(
122
- '[HPM] %s %s %s %s',
123
- req.method,
124
- originalPath,
125
- arrow,
126
- newProxyOptions.target
127
- )
128
- }
129
-
130
- return newProxyOptions
131
- }
132
-
133
- // Modify option.target when router present.
134
- function __applyRouter(req, options) {
135
- var newTarget
136
-
137
- if (options.router) {
138
- newTarget = Router.getTarget(req, options)
139
-
140
- if (newTarget) {
141
- logger.debug(
142
- '[HPM] Router new target: %s -> "%s"',
143
- options.target,
144
- newTarget
145
- )
146
- options.target = newTarget
147
- }
148
- }
149
- }
150
-
151
- // rewrite path
152
- function __applyPathRewrite(req, pathRewriter) {
153
- if (pathRewriter) {
154
- var path = pathRewriter(req.url, req)
155
-
156
- if (typeof path === 'string') {
157
- req.url = path
158
- } else {
159
- logger.info('[HPM] pathRewrite: No rewritten path found. (%s)', req.url)
160
- }
161
- }
162
- }
163
-
164
- function logError(err, req, res) {
165
- var hostname =
166
- (req.headers && req.headers.host) || (req.hostname || req.host) // (websocket) || (node0.10 || node 4/5)
167
- var target = proxyOptions.target.host || proxyOptions.target
168
- var errorMessage =
169
- '[HPM] Error occurred while trying to proxy request %s from %s to %s (%s) (%s)'
170
- var errReference =
171
- 'https://nodejs.org/api/errors.html#errors_common_system_errors' // link to Node Common Systems Errors page
172
-
173
- logger.error(
174
- errorMessage,
175
- req.url,
176
- hostname,
177
- target,
178
- err.code || err,
179
- errReference
180
- )
181
- }
182
- }
package/lib/logger.js DELETED
@@ -1,172 +0,0 @@
1
- var util = require('util')
2
- var _ = require('lodash')
3
-
4
- var loggerInstance
5
-
6
- var defaultProvider = {
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
-
14
- // log level 'weight'
15
- var LEVELS = {
16
- debug: 10,
17
- info: 20,
18
- warn: 30,
19
- error: 50,
20
- silent: 80
21
- }
22
-
23
- module.exports = {
24
- // singleton
25
- getInstance: function() {
26
- if (!loggerInstance) {
27
- loggerInstance = new Logger()
28
- }
29
-
30
- return loggerInstance
31
- },
32
- getArrow: getArrow
33
- }
34
-
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
- }
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
- })
66
- }
67
-
68
- // log will log messages, regardless of logLevels
69
- function log() {
70
- provider.log(_interpolate.apply(null, arguments))
71
- }
72
-
73
- function debug() {
74
- if (_showLevel('debug')) {
75
- provider.debug(_interpolate.apply(null, arguments))
76
- }
77
- }
78
-
79
- function info() {
80
- if (_showLevel('info')) {
81
- provider.info(_interpolate.apply(null, arguments))
82
- }
83
- }
84
-
85
- function warn() {
86
- if (_showLevel('warn')) {
87
- provider.warn(_interpolate.apply(null, arguments))
88
- }
89
- }
90
-
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
108
- }
109
-
110
- return result
111
- }
112
-
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))
118
-
119
- return result
120
- }
121
-
122
- function isValidProvider(fnProvider) {
123
- var result = true
124
-
125
- if (fnProvider && !_.isFunction(fnProvider)) {
126
- throw new Error('[HPM] Log provider config error. Expecting a function.')
127
- }
128
-
129
- return result
130
- }
131
-
132
- function isValidLevel(levelName) {
133
- var validLevels = _.keys(LEVELS)
134
- var isValid = _.includes(validLevels, levelName)
135
-
136
- if (!isValid) {
137
- throw new Error('[HPM] Log level error. Invalid logLevel.')
138
- }
139
-
140
- return isValid
141
- }
142
- }
143
-
144
- /**
145
- * -> normal proxy
146
- * => router
147
- * ~> pathRewrite
148
- * ≈> router + pathRewrite
149
- *
150
- * @param {String} originalPath
151
- * @param {String} newPath
152
- * @param {String} originalTarget
153
- * @param {String} newTarget
154
- * @return {String}
155
- */
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('')
172
- }
@@ -1,79 +0,0 @@
1
- var _ = require('lodash')
2
- var logger = require('./logger').getInstance()
3
- var ERRORS = require('./errors')
4
-
5
- module.exports = {
6
- create: createPathRewriter
7
- }
8
-
9
- /**
10
- * Create rewrite function, to cache parsed rewrite rules.
11
- *
12
- * @param {Object} rewriteConfig
13
- * @return {Function} Function to rewrite paths; This function should accept `path` (request.url) as parameter
14
- */
15
- function createPathRewriter(rewriteConfig) {
16
- var rulesCache
17
-
18
- if (!isValidRewriteConfig(rewriteConfig)) {
19
- return
20
- }
21
-
22
- if (_.isFunction(rewriteConfig)) {
23
- var customRewriteFn = rewriteConfig
24
- return customRewriteFn
25
- } else {
26
- rulesCache = parsePathRewriteRules(rewriteConfig)
27
- return rewritePath
28
- }
29
-
30
- function rewritePath(path) {
31
- var result = path
32
-
33
- _.forEach(rulesCache, function(rule) {
34
- if (rule.regex.test(path)) {
35
- result = result.replace(rule.regex, rule.value)
36
- logger.debug('[HPM] Rewriting path from "%s" to "%s"', path, result)
37
- return false
38
- }
39
- })
40
-
41
- return result
42
- }
43
- }
44
-
45
- function isValidRewriteConfig(rewriteConfig) {
46
- if (_.isFunction(rewriteConfig)) {
47
- return true
48
- } else if (!_.isEmpty(rewriteConfig) && _.isPlainObject(rewriteConfig)) {
49
- return true
50
- } else if (
51
- _.isUndefined(rewriteConfig) ||
52
- _.isNull(rewriteConfig) ||
53
- _.isEqual(rewriteConfig, {})
54
- ) {
55
- return false
56
- } else {
57
- throw new Error(ERRORS.ERR_PATH_REWRITER_CONFIG)
58
- }
59
- }
60
-
61
- function parsePathRewriteRules(rewriteConfig) {
62
- var rules = []
63
-
64
- if (_.isPlainObject(rewriteConfig)) {
65
- _.forIn(rewriteConfig, function(value, key) {
66
- rules.push({
67
- regex: new RegExp(key),
68
- value: rewriteConfig[key]
69
- })
70
- logger.info(
71
- '[HPM] Proxy rewrite rule created: "%s" ~> "%s"',
72
- key,
73
- rewriteConfig[key]
74
- )
75
- })
76
- }
77
-
78
- return rules
79
- }
package/lib/router.js DELETED
@@ -1,51 +0,0 @@
1
- var _ = require('lodash')
2
- var logger = require('./logger.js').getInstance()
3
-
4
- module.exports = {
5
- getTarget: getTarget
6
- }
7
-
8
- function getTarget(req, config) {
9
- var newTarget
10
- var router = config.router
11
-
12
- if (_.isPlainObject(router)) {
13
- newTarget = getTargetFromProxyTable(req, router)
14
- } else if (_.isFunction(router)) {
15
- newTarget = router(req)
16
- }
17
-
18
- return newTarget
19
- }
20
-
21
- function getTargetFromProxyTable(req, table) {
22
- var result
23
- var host = req.headers.host
24
- var path = req.url
25
-
26
- var hostAndPath = host + path
27
-
28
- _.forIn(table, function(value, key) {
29
- if (containsPath(key)) {
30
- if (hostAndPath.indexOf(key) > -1) {
31
- // match 'localhost:3000/api'
32
- result = table[key]
33
- logger.debug('[HPM] Router table match: "%s"', key)
34
- return false
35
- }
36
- } else {
37
- if (key === host) {
38
- // match 'localhost:3000'
39
- result = table[key]
40
- logger.debug('[HPM] Router table match: "%s"', host)
41
- return false
42
- }
43
- }
44
- })
45
-
46
- return result
47
- }
48
-
49
- function containsPath(v) {
50
- return v.indexOf('/') > -1
51
- }