blob: 8da73048c9810e95c4ec73636b16b4baadc2da5d [file] [log] [blame]
var _ = require('lodash')
var httpProxy = require('http-proxy')
var configFactory = require('./config-factory')
var handlers = require('./handlers')
var contextMatcher = require('./context-matcher')
var PathRewriter = require('./path-rewriter')
var Router = require('./router')
var logger = require('./logger').getInstance()
var getArrow = require('./logger').getArrow
module.exports = HttpProxyMiddleware
function HttpProxyMiddleware(context, opts) {
// https://github.com/chimurai/http-proxy-middleware/issues/57
var wsUpgradeDebounced = _.debounce(handleUpgrade)
var wsInitialized = false
var config = configFactory.createConfig(context, opts)
var proxyOptions = config.options
// create proxy
var proxy = httpProxy.createProxyServer({})
logger.info(
'[HPM] Proxy created:',
config.context,
' -> ',
proxyOptions.target
)
var pathRewriter = PathRewriter.create(proxyOptions.pathRewrite) // returns undefined when "pathRewrite" is not provided
// attach handler to http-proxy events
handlers.init(proxy, proxyOptions)
// log errors for debug purpose
proxy.on('error', logError)
// https://github.com/chimurai/http-proxy-middleware/issues/19
// expose function to upgrade externally
middleware.upgrade = wsUpgradeDebounced
return middleware
function middleware(req, res, next) {
if (shouldProxy(config.context, req)) {
var activeProxyOptions = prepareProxyRequest(req)
proxy.web(req, res, activeProxyOptions)
} else {
next()
}
if (proxyOptions.ws === true) {
// use initial request to access the server object to subscribe to http upgrade event
catchUpgradeRequest(req.connection.server)
}
}
function catchUpgradeRequest(server) {
// subscribe once; don't subscribe on every request...
// https://github.com/chimurai/http-proxy-middleware/issues/113
if (!wsInitialized) {
server.on('upgrade', wsUpgradeDebounced)
wsInitialized = true
}
}
function handleUpgrade(req, socket, head) {
// set to initialized when used externally
wsInitialized = true
if (shouldProxy(config.context, req)) {
var activeProxyOptions = prepareProxyRequest(req)
proxy.ws(req, socket, head, activeProxyOptions)
logger.info('[HPM] Upgrading to WebSocket')
}
}
/**
* Determine whether request should be proxied.
*
* @private
* @param {String} context [description]
* @param {Object} req [description]
* @return {Boolean}
*/
function shouldProxy(context, req) {
var path = req.originalUrl || req.url
return contextMatcher.match(context, path, req)
}
/**
* Apply option.router and option.pathRewrite
* Order matters:
* Router uses original path for routing;
* NOT the modified path, after it has been rewritten by pathRewrite
* @param {Object} req
* @return {Object} proxy options
*/
function prepareProxyRequest(req) {
// https://github.com/chimurai/http-proxy-middleware/issues/17
// https://github.com/chimurai/http-proxy-middleware/issues/94
req.url = req.originalUrl || req.url
// store uri before it gets rewritten for logging
var originalPath = req.url
var newProxyOptions = _.assign({}, proxyOptions)
// Apply in order:
// 1. option.router
// 2. option.pathRewrite
__applyRouter(req, newProxyOptions)
__applyPathRewrite(req, pathRewriter)
// debug logging for both http(s) and websockets
if (proxyOptions.logLevel === 'debug') {
var arrow = getArrow(
originalPath,
req.url,
proxyOptions.target,
newProxyOptions.target
)
logger.debug(
'[HPM] %s %s %s %s',
req.method,
originalPath,
arrow,
newProxyOptions.target
)
}
return newProxyOptions
}
// Modify option.target when router present.
function __applyRouter(req, options) {
var newTarget
if (options.router) {
newTarget = Router.getTarget(req, options)
if (newTarget) {
logger.debug(
'[HPM] Router new target: %s -> "%s"',
options.target,
newTarget
)
options.target = newTarget
}
}
}
// rewrite path
function __applyPathRewrite(req, pathRewriter) {
if (pathRewriter) {
var path = pathRewriter(req.url, req)
if (typeof path === 'string') {
req.url = path
} else {
logger.info('[HPM] pathRewrite: No rewritten path found. (%s)', req.url)
}
}
}
function logError(err, req, res) {
var hostname =
(req.headers && req.headers.host) || (req.hostname || req.host) // (websocket) || (node0.10 || node 4/5)
var target = proxyOptions.target.host || proxyOptions.target
var errorMessage =
'[HPM] Error occurred while trying to proxy request %s from %s to %s (%s) (%s)'
var errReference =
'https://nodejs.org/api/errors.html#errors_common_system_errors' // link to Node Common Systems Errors page
logger.error(
errorMessage,
req.url,
hostname,
target,
err.code || err,
errReference
)
}
}