text: '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-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
      , 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 = []
    if (typeof request.tunnel !== 'undefined') {
      this.tunnelOverride = request.tunnel
    }
  }
  
  Tunnel.prototype.isEnabled = function () {
    var self = this
      , 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
      , 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
  