Skip to content

Commit

Permalink
Merge pull request #1631 from simov/extract-tunnel
Browse files Browse the repository at this point in the history
Move tunnel logic into separate module
  • Loading branch information
simov committed Jun 15, 2015
2 parents e0b6921 + 6e50b35 commit 7d0ee92
Show file tree
Hide file tree
Showing 4 changed files with 201 additions and 176 deletions.
10 changes: 0 additions & 10 deletions lib/copy.js

This file was deleted.

9 changes: 9 additions & 0 deletions lib/helpers.js
Expand Up @@ -46,10 +46,19 @@ function toBase64 (str) {
return (new Buffer(str || '', 'utf8')).toString('base64')
}

function copy (obj) {
var o = {}
Object.keys(obj).forEach(function (i) {
o[i] = obj[i]
})
return o
}

exports.isFunction = isFunction
exports.paramsHaveRequestBody = paramsHaveRequestBody
exports.safeStringify = safeStringify
exports.md5 = md5
exports.isReadStream = isReadStream
exports.toBase64 = toBase64
exports.copy = copy
exports.defer = deferMethod()
183 changes: 183 additions & 0 deletions lib/tunnel.js
@@ -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

0 comments on commit 7d0ee92

Please sign in to comment.