3 var url = require('url')
4 , tunnel = require('tunnel-agent')
6 var defaultProxyHeaderWhiteList = [
32 var defaultProxyHeaderExclusiveList = [
36 function constructProxyHost(uriObject) {
37 var port = uriObject.port
38 , protocol = uriObject.protocol
39 , proxyHost = uriObject.hostname + ':'
43 } else if (protocol === 'https:') {
52 function constructProxyHeaderWhiteList(headers, proxyHeaderWhiteList) {
53 var whiteList = proxyHeaderWhiteList
54 .reduce(function (set, header) {
55 set[header.toLowerCase()] = true
59 return Object.keys(headers)
60 .filter(function (header) {
61 return whiteList[header.toLowerCase()]
63 .reduce(function (set, header) {
64 set[header] = headers[header]
69 function constructTunnelOptions (request, proxyHeaders) {
70 var proxy = request.proxy
74 host : proxy.hostname,
76 proxyAuth : proxy.auth,
77 headers : proxyHeaders
79 headers : request.headers,
83 passphrase : request.passphrase,
85 ciphers : request.ciphers,
86 rejectUnauthorized : request.rejectUnauthorized,
87 secureOptions : request.secureOptions,
88 secureProtocol : request.secureProtocol
94 function 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')
100 function getTunnelFn(request) {
101 var uri = request.uri
102 var proxy = request.proxy
103 var tunnelFnName = constructTunnelFnName(uri, proxy)
104 return tunnel[tunnelFnName]
108 function Tunnel (request) {
109 this.request = request
110 this.proxyHeaderWhiteList = defaultProxyHeaderWhiteList
111 this.proxyHeaderExclusiveList = []
112 if (typeof request.tunnel !== 'undefined') {
113 this.tunnelOverride = request.tunnel
117 Tunnel.prototype.isEnabled = function () {
119 , request = self.request
120 // Tunnel HTTPS by default. Allow the user to override this setting.
122 // If self.tunnelOverride is set (the user specified a value), use it.
123 if (typeof self.tunnelOverride !== 'undefined') {
124 return self.tunnelOverride
127 // If the destination is HTTPS, tunnel.
128 if (request.uri.protocol === 'https:') {
132 // Otherwise, do not use tunnel.
136 Tunnel.prototype.setup = function (options) {
138 , request = self.request
140 options = options || {}
142 if (typeof request.proxy === 'string') {
143 request.proxy = url.parse(request.proxy)
146 if (!request.proxy || !request.tunnel) {
150 // Setup Proxy Header Exclusive List and White List
151 if (options.proxyHeaderWhiteList) {
152 self.proxyHeaderWhiteList = options.proxyHeaderWhiteList
154 if (options.proxyHeaderExclusiveList) {
155 self.proxyHeaderExclusiveList = options.proxyHeaderExclusiveList
158 var proxyHeaderExclusiveList = self.proxyHeaderExclusiveList.concat(defaultProxyHeaderExclusiveList)
159 var proxyHeaderWhiteList = self.proxyHeaderWhiteList.concat(proxyHeaderExclusiveList)
161 // Setup Proxy Headers and Proxy Headers Host
162 // Only send the Proxy White Listed Header names
163 var proxyHeaders = constructProxyHeaderWhiteList(request.headers, proxyHeaderWhiteList)
164 proxyHeaders.host = constructProxyHost(request.uri)
166 proxyHeaderExclusiveList.forEach(request.removeHeader, request)
168 // Set Agent from Tunnel Data
169 var tunnelFn = getTunnelFn(request)
170 var tunnelOptions = constructTunnelOptions(request, proxyHeaders)
171 request.agent = tunnelFn(tunnelOptions)
176 Tunnel.defaultProxyHeaderWhiteList = defaultProxyHeaderWhiteList
177 Tunnel.defaultProxyHeaderExclusiveList = defaultProxyHeaderExclusiveList
178 exports.Tunnel = Tunnel