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