proxy.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. var url = require('url')
  2. var httpProxy = require('http-proxy')
  3. var _ = require('lodash')
  4. var log = require('../logger').create('proxy')
  5. var parseProxyConfig = function (proxies, config) {
  6. var endsWithSlash = function (str) {
  7. return str.substr(-1) === '/'
  8. }
  9. if (!proxies) {
  10. return []
  11. }
  12. return _.sortBy(_.map(proxies, function (proxyConfiguration, proxyPath) {
  13. if (typeof proxyConfiguration === 'string') {
  14. proxyConfiguration = {target: proxyConfiguration}
  15. }
  16. var proxyUrl = proxyConfiguration.target
  17. var proxyDetails = url.parse(proxyUrl)
  18. var pathname = proxyDetails.pathname
  19. // normalize the proxies config
  20. // should we move this to lib/config.js ?
  21. if (endsWithSlash(proxyPath) && !endsWithSlash(proxyUrl)) {
  22. log.warn('proxy "%s" normalized to "%s"', proxyUrl, proxyUrl + '/')
  23. proxyUrl += '/'
  24. pathname += '/'
  25. }
  26. if (!endsWithSlash(proxyPath) && endsWithSlash(proxyUrl)) {
  27. log.warn('proxy "%s" normalized to "%s"', proxyPath, proxyPath + '/')
  28. proxyPath += '/'
  29. }
  30. if (pathname === '/' && !endsWithSlash(proxyUrl)) {
  31. pathname = ''
  32. }
  33. var hostname = proxyDetails.hostname || config.hostname
  34. var protocol = proxyDetails.protocol || config.protocol
  35. var https = proxyDetails.protocol === 'https:'
  36. var port
  37. if (proxyDetails.port) {
  38. port = proxyDetails.port
  39. } else if (proxyDetails.protocol) {
  40. port = proxyDetails.protocol === 'https:' ? '443' : '80'
  41. } else {
  42. port = config.port
  43. }
  44. var changeOrigin = 'changeOrigin' in proxyConfiguration ? proxyConfiguration.changeOrigin : false
  45. var proxy = httpProxy.createProxyServer({
  46. target: {
  47. host: hostname,
  48. port: port,
  49. https: https,
  50. protocol: protocol
  51. },
  52. xfwd: true,
  53. changeOrigin: changeOrigin,
  54. secure: config.proxyValidateSSL
  55. })
  56. ;['proxyReq', 'proxyRes'].forEach(function (name) {
  57. var callback = proxyDetails[name] || config[name]
  58. if (callback) {
  59. proxy.on(name, callback)
  60. }
  61. })
  62. proxy.on('error', function proxyError (err, req, res) {
  63. if (err.code === 'ECONNRESET' && req.socket.destroyed) {
  64. log.debug('failed to proxy %s (browser hung up the socket)', req.url)
  65. } else {
  66. log.warn('failed to proxy %s (%s)', req.url, err.message)
  67. }
  68. res.destroy()
  69. })
  70. return {
  71. path: proxyPath,
  72. baseUrl: pathname,
  73. host: hostname,
  74. port: port,
  75. https: https,
  76. proxy: proxy
  77. }
  78. }), 'path').reverse()
  79. }
  80. /**
  81. * Returns a handler which understands the proxies and its redirects, along with the proxy to use
  82. * @param proxies An array of proxy record objects
  83. * @param urlRoot The URL root that karma is mounted on
  84. * @return {Function} handler function
  85. */
  86. var createProxyHandler = function (proxies, urlRoot) {
  87. if (!proxies.length) {
  88. var nullProxy = function createNullProxy (request, response, next) {
  89. return next()
  90. }
  91. nullProxy.upgrade = function upgradeNullProxy () {}
  92. return nullProxy
  93. }
  94. var middleware = function createProxy (request, response, next) {
  95. var proxyRecord = _.find(proxies, function (p) {
  96. return request.url.indexOf(p.path) === 0
  97. })
  98. if (!proxyRecord) {
  99. return next()
  100. }
  101. log.debug('proxying request - %s to %s:%s', request.url, proxyRecord.host, proxyRecord.port)
  102. request.url = request.url.replace(proxyRecord.path, proxyRecord.baseUrl)
  103. proxyRecord.proxy.web(request, response)
  104. }
  105. middleware.upgrade = function upgradeProxy (request, socket, head) {
  106. // special-case karma's route to avoid upgrading it
  107. if (request.url.indexOf(urlRoot) === 0) {
  108. log.debug('NOT upgrading proxyWebSocketRequest %s', request.url)
  109. return
  110. }
  111. var proxyRecord = _.find(proxies, function (p) {
  112. return request.url.indexOf(p.path) === 0
  113. })
  114. if (!proxyRecord) {
  115. return
  116. }
  117. log.debug('upgrade proxyWebSocketRequest %s to %s:%s',
  118. request.url, proxyRecord.host, proxyRecord.port)
  119. request.url = request.url.replace(proxyRecord.path, proxyRecord.baseUrl)
  120. proxyRecord.proxy.ws(request, socket, head)
  121. }
  122. return middleware
  123. }
  124. exports.create = function (/* config */config, /* config.proxies */proxies) {
  125. return createProxyHandler(parseProxyConfig(proxies, config), config.urlRoot)
  126. }