index.js 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. /**
  2. * Dependencies
  3. */
  4. const { URL } = require('url')
  5. const HttpProxy = require('http-proxy')
  6. const pathMatch = require('path-match')
  7. /**
  8. * Constants
  9. */
  10. const proxy = HttpProxy.createProxyServer()
  11. const route = pathMatch({
  12. // path-to-regexp options
  13. sensitive: false,
  14. strict: false,
  15. end: false
  16. })
  17. let eventRegistered = false
  18. /**
  19. * Koa Http Proxy Middleware
  20. */
  21. module.exports = (path, options) => (ctx, next) => {
  22. // create a match function
  23. const match = route(path)
  24. if (!match(ctx.path)) return next()
  25. let opts = Object.assign({}, options)
  26. if (typeof options === 'function') {
  27. const params = match(ctx.path)
  28. opts = options.call(options, params)
  29. }
  30. // object-rest-spread is still in stage-3
  31. // https://github.com/tc39/proposal-object-rest-spread
  32. const { logs, rewrite, events } = opts
  33. const httpProxyOpts = Object.keys(opts)
  34. .filter(n => ['logs', 'rewrite', 'events'].indexOf(n) < 0)
  35. .reduce((prev, cur) => {
  36. prev[cur] = opts[cur]
  37. return prev
  38. }, {})
  39. return new Promise((resolve, reject) => {
  40. ctx.req.oldPath = ctx.req.url
  41. if (typeof rewrite === 'function') {
  42. ctx.req.url = rewrite(ctx.req.url, ctx)
  43. }
  44. if (logs) {
  45. typeof logs === 'function' ? logs(ctx, opts.target) : logger(ctx, opts.target)
  46. }
  47. if (events && typeof events === 'object' && !eventRegistered) {
  48. Object.entries(events).forEach(([event, handler]) => {
  49. proxy.on(event, handler)
  50. })
  51. eventRegistered = true
  52. }
  53. // Let the promise be solved correctly after the proxy.web.
  54. // The solution comes from https://github.com/nodejitsu/node-http-proxy/issues/951#issuecomment-179904134
  55. ctx.res.on('close', () => {
  56. reject(new Error(`Http response closed while proxying ${ctx.req.oldPath}`))
  57. })
  58. ctx.res.on('finish', () => {
  59. resolve()
  60. })
  61. proxy.web(ctx.req, ctx.res, httpProxyOpts, e => {
  62. const status = {
  63. ECONNREFUSED: 503,
  64. ETIMEOUT: 504
  65. }[e.code]
  66. ctx.status = status || 500
  67. resolve()
  68. })
  69. })
  70. }
  71. module.exports.proxy = proxy
  72. function logger (ctx, target) {
  73. console.log('%s - %s %s proxy to -> %s', new Date().toISOString(), ctx.req.method, ctx.req.oldPath, new URL(ctx.req.url, target))
  74. }