Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1631 from simov/extract-tunnel
Move tunnel logic into separate module
- Loading branch information
Showing
4 changed files
with
201 additions
and
176 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
'use strict' | ||
|
||
var url = require('url') | ||
, tunnel = require('tunnel-agent') | ||
|
||
var defaultProxyHeaderWhiteList = [ | ||
'accept', | ||
'accept-charset', | ||
'accept-encoding', | ||
'accept-language', | ||
'accept-ranges', | ||
'cache-control', | ||
'content-encoding', | ||
'content-language', | ||
'content-length', | ||
'content-location', | ||
'content-md5', | ||
'content-range', | ||
'content-type', | ||
'connection', | ||
'date', | ||
'expect', | ||
'max-forwards', | ||
'pragma', | ||
'referer', | ||
'te', | ||
'transfer-encoding', | ||
'user-agent', | ||
'via' | ||
] | ||
|
||
var defaultProxyHeaderExclusiveList = [ | ||
'proxy-authorization' | ||
] | ||
|
||
function constructProxyHost(uriObject) { | ||
var port = uriObject.portA | ||
, protocol = uriObject.protocol | ||
, 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 = [] | ||
} | ||
|
||
Tunnel.prototype.isEnabled = function (options) { | ||
var request = this.request | ||
// Tunnel HTTPS by default, or if a previous request in the redirect chain | ||
// was tunneled. Allow the user to override this setting. | ||
|
||
// If self.tunnel is already set (because this is a redirect), use the | ||
// existing value. | ||
if (typeof request.tunnel !== 'undefined') { | ||
return request.tunnel | ||
} | ||
|
||
// If options.tunnel is set (the user specified a value), use it. | ||
if (typeof options.tunnel !== 'undefined') { | ||
return options.tunnel | ||
} | ||
|
||
// If the destination is HTTPS, tunnel. | ||
if (request.uri.protocol === 'https:') { | ||
return true | ||
} | ||
|
||
// Otherwise, leave tunnel unset, because if a later request in the redirect | ||
// chain is HTTPS then that request (and any subsequent ones) should be | ||
// tunneled. | ||
return undefined | ||
} | ||
|
||
Tunnel.prototype.setup = function (options) { | ||
var self = this | ||
, 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 |
Oops, something went wrong.