You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

175 lines
4.3 KiB

  1. 'use strict'
  2. var url = require('url')
  3. var tunnel = require('tunnel-agent')
  4. var defaultProxyHeaderWhiteList = [
  5. 'accept',
  6. 'accept-charset',
  7. 'accept-encoding',
  8. 'accept-language',
  9. 'accept-ranges',
  10. 'cache-control',
  11. 'content-encoding',
  12. 'content-language',
  13. 'content-location',
  14. 'content-md5',
  15. 'content-range',
  16. 'content-type',
  17. 'connection',
  18. 'date',
  19. 'expect',
  20. 'max-forwards',
  21. 'pragma',
  22. 'referer',
  23. 'te',
  24. 'user-agent',
  25. 'via'
  26. ]
  27. var defaultProxyHeaderExclusiveList = [
  28. 'proxy-authorization'
  29. ]
  30. function constructProxyHost (uriObject) {
  31. var port = uriObject.port
  32. var protocol = uriObject.protocol
  33. var proxyHost = uriObject.hostname + ':'
  34. if (port) {
  35. proxyHost += port
  36. } else if (protocol === 'https:') {
  37. proxyHost += '443'
  38. } else {
  39. proxyHost += '80'
  40. }
  41. return proxyHost
  42. }
  43. function constructProxyHeaderWhiteList (headers, proxyHeaderWhiteList) {
  44. var whiteList = proxyHeaderWhiteList
  45. .reduce(function (set, header) {
  46. set[header.toLowerCase()] = true
  47. return set
  48. }, {})
  49. return Object.keys(headers)
  50. .filter(function (header) {
  51. return whiteList[header.toLowerCase()]
  52. })
  53. .reduce(function (set, header) {
  54. set[header] = headers[header]
  55. return set
  56. }, {})
  57. }
  58. function constructTunnelOptions (request, proxyHeaders) {
  59. var proxy = request.proxy
  60. var tunnelOptions = {
  61. proxy: {
  62. host: proxy.hostname,
  63. port: +proxy.port,
  64. proxyAuth: proxy.auth,
  65. headers: proxyHeaders
  66. },
  67. headers: request.headers,
  68. ca: request.ca,
  69. cert: request.cert,
  70. key: request.key,
  71. passphrase: request.passphrase,
  72. pfx: request.pfx,
  73. ciphers: request.ciphers,
  74. rejectUnauthorized: request.rejectUnauthorized,
  75. secureOptions: request.secureOptions,
  76. secureProtocol: request.secureProtocol
  77. }
  78. return tunnelOptions
  79. }
  80. function constructTunnelFnName (uri, proxy) {
  81. var uriProtocol = (uri.protocol === 'https:' ? 'https' : 'http')
  82. var proxyProtocol = (proxy.protocol === 'https:' ? 'Https' : 'Http')
  83. return [uriProtocol, proxyProtocol].join('Over')
  84. }
  85. function getTunnelFn (request) {
  86. var uri = request.uri
  87. var proxy = request.proxy
  88. var tunnelFnName = constructTunnelFnName(uri, proxy)
  89. return tunnel[tunnelFnName]
  90. }
  91. function Tunnel (request) {
  92. this.request = request
  93. this.proxyHeaderWhiteList = defaultProxyHeaderWhiteList
  94. this.proxyHeaderExclusiveList = []
  95. if (typeof request.tunnel !== 'undefined') {
  96. this.tunnelOverride = request.tunnel
  97. }
  98. }
  99. Tunnel.prototype.isEnabled = function () {
  100. var self = this
  101. var request = self.request
  102. // Tunnel HTTPS by default. Allow the user to override this setting.
  103. // If self.tunnelOverride is set (the user specified a value), use it.
  104. if (typeof self.tunnelOverride !== 'undefined') {
  105. return self.tunnelOverride
  106. }
  107. // If the destination is HTTPS, tunnel.
  108. if (request.uri.protocol === 'https:') {
  109. return true
  110. }
  111. // Otherwise, do not use tunnel.
  112. return false
  113. }
  114. Tunnel.prototype.setup = function (options) {
  115. var self = this
  116. var request = self.request
  117. options = options || {}
  118. if (typeof request.proxy === 'string') {
  119. request.proxy = url.parse(request.proxy)
  120. }
  121. if (!request.proxy || !request.tunnel) {
  122. return false
  123. }
  124. // Setup Proxy Header Exclusive List and White List
  125. if (options.proxyHeaderWhiteList) {
  126. self.proxyHeaderWhiteList = options.proxyHeaderWhiteList
  127. }
  128. if (options.proxyHeaderExclusiveList) {
  129. self.proxyHeaderExclusiveList = options.proxyHeaderExclusiveList
  130. }
  131. var proxyHeaderExclusiveList = self.proxyHeaderExclusiveList.concat(defaultProxyHeaderExclusiveList)
  132. var proxyHeaderWhiteList = self.proxyHeaderWhiteList.concat(proxyHeaderExclusiveList)
  133. // Setup Proxy Headers and Proxy Headers Host
  134. // Only send the Proxy White Listed Header names
  135. var proxyHeaders = constructProxyHeaderWhiteList(request.headers, proxyHeaderWhiteList)
  136. proxyHeaders.host = constructProxyHost(request.uri)
  137. proxyHeaderExclusiveList.forEach(request.removeHeader, request)
  138. // Set Agent from Tunnel Data
  139. var tunnelFn = getTunnelFn(request)
  140. var tunnelOptions = constructTunnelOptions(request, proxyHeaders)
  141. request.agent = tunnelFn(tunnelOptions)
  142. return true
  143. }
  144. Tunnel.defaultProxyHeaderWhiteList = defaultProxyHeaderWhiteList
  145. Tunnel.defaultProxyHeaderExclusiveList = defaultProxyHeaderExclusiveList
  146. exports.Tunnel = Tunnel