183 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			183 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
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
 | 
						|
    )
 | 
						|
  }
 | 
						|
}
 |