UNPKG

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