'use strict'
var url = require('url') var tunnel = require('tunnel-agent')
var defaultProxyHeaderWhiteList = [
'accept', 'accept-charset', 'accept-encoding', 'accept-language', 'accept-ranges', 'cache-control', 'content-encoding', 'content-language', 'content-location', 'content-md5', 'content-range', 'content-type', 'connection', 'date', 'expect', 'max-forwards', 'pragma', 'referer', 'te', 'user-agent', 'via'
]
var defaultProxyHeaderExclusiveList = [
'proxy-authorization'
]
function constructProxyHost (uriObject) {
var port = uriObject.port var protocol = uriObject.protocol var proxyHost = uriObject.hostname + ':' if (port) { proxyHost += port } else if (protocol === 'https:') { proxyHost += '443' } else { proxyHost += '80' } return proxyHost
}
function constructProxyHeaderWhiteList (headers, proxyHeaderWhiteList) {
var whiteList = proxyHeaderWhiteList .reduce(function (set, header) { set[header.toLowerCase()] = true return set }, {}) return Object.keys(headers) .filter(function (header) { return whiteList[header.toLowerCase()] }) .reduce(function (set, header) { set[header] = headers[header] return set }, {})
}
function constructTunnelOptions (request, proxyHeaders) {
var proxy = request.proxy var tunnelOptions = { proxy: { host: proxy.hostname, port: +proxy.port, proxyAuth: proxy.auth, headers: proxyHeaders }, headers: request.headers, ca: request.ca, cert: request.cert, key: request.key, passphrase: request.passphrase, pfx: request.pfx, ciphers: request.ciphers, rejectUnauthorized: request.rejectUnauthorized, secureOptions: request.secureOptions, secureProtocol: request.secureProtocol } return tunnelOptions
}
function constructTunnelFnName (uri, proxy) {
var uriProtocol = (uri.protocol === 'https:' ? 'https' : 'http') var proxyProtocol = (proxy.protocol === 'https:' ? 'Https' : 'Http') return [uriProtocol, proxyProtocol].join('Over')
}
function getTunnelFn (request) {
var uri = request.uri var proxy = request.proxy var tunnelFnName = constructTunnelFnName(uri, proxy) return tunnel[tunnelFnName]
}
function Tunnel (request) {
this.request = request this.proxyHeaderWhiteList = defaultProxyHeaderWhiteList this.proxyHeaderExclusiveList = [] if (typeof request.tunnel !== 'undefined') { this.tunnelOverride = request.tunnel }
}
Tunnel.prototype.isEnabled = function () {
var self = this var request = self.request // Tunnel HTTPS by default. Allow the user to override this setting. // If self.tunnelOverride is set (the user specified a value), use it. if (typeof self.tunnelOverride !== 'undefined') { return self.tunnelOverride } // If the destination is HTTPS, tunnel. if (request.uri.protocol === 'https:') { return true } // Otherwise, do not use tunnel. return false
}
Tunnel.prototype.setup = function (options) {
var self = this var request = self.request options = options || {} if (typeof request.proxy === 'string') { request.proxy = url.parse(request.proxy) } if (!request.proxy || !request.tunnel) { return false } // Setup Proxy Header Exclusive List and White List if (options.proxyHeaderWhiteList) { self.proxyHeaderWhiteList = options.proxyHeaderWhiteList } if (options.proxyHeaderExclusiveList) { self.proxyHeaderExclusiveList = options.proxyHeaderExclusiveList } var proxyHeaderExclusiveList = self.proxyHeaderExclusiveList.concat(defaultProxyHeaderExclusiveList) var proxyHeaderWhiteList = self.proxyHeaderWhiteList.concat(proxyHeaderExclusiveList) // Setup Proxy Headers and Proxy Headers Host // Only send the Proxy White Listed Header names var proxyHeaders = constructProxyHeaderWhiteList(request.headers, proxyHeaderWhiteList) proxyHeaders.host = constructProxyHost(request.uri) proxyHeaderExclusiveList.forEach(request.removeHeader, request) // Set Agent from Tunnel Data var tunnelFn = getTunnelFn(request) var tunnelOptions = constructTunnelOptions(request, proxyHeaders) request.agent = tunnelFn(tunnelOptions) return true
}
Tunnel.defaultProxyHeaderWhiteList = defaultProxyHeaderWhiteList Tunnel.defaultProxyHeaderExclusiveList = defaultProxyHeaderExclusiveList exports.Tunnel = Tunnel