| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244 | 'use strict'var net = require('net')  , tls = require('tls')  , http = require('http')  , https = require('https')  , events = require('events')  , assert = require('assert')  , util = require('util')  , Buffer = require('safe-buffer').Buffer  ;exports.httpOverHttp = httpOverHttpexports.httpsOverHttp = httpsOverHttpexports.httpOverHttps = httpOverHttpsexports.httpsOverHttps = httpsOverHttpsfunction httpOverHttp(options) {  var agent = new TunnelingAgent(options)  agent.request = http.request  return agent}function httpsOverHttp(options) {  var agent = new TunnelingAgent(options)  agent.request = http.request  agent.createSocket = createSecureSocket  agent.defaultPort = 443  return agent}function httpOverHttps(options) {  var agent = new TunnelingAgent(options)  agent.request = https.request  return agent}function httpsOverHttps(options) {  var agent = new TunnelingAgent(options)  agent.request = https.request  agent.createSocket = createSecureSocket  agent.defaultPort = 443  return agent}function TunnelingAgent(options) {  var self = this  self.options = options || {}  self.proxyOptions = self.options.proxy || {}  self.maxSockets = self.options.maxSockets || http.Agent.defaultMaxSockets  self.requests = []  self.sockets = []  self.on('free', function onFree(socket, host, port) {    for (var i = 0, len = self.requests.length; i < len; ++i) {      var pending = self.requests[i]      if (pending.host === host && pending.port === port) {        // Detect the request to connect same origin server,        // reuse the connection.        self.requests.splice(i, 1)        pending.request.onSocket(socket)        return      }    }    socket.destroy()    self.removeSocket(socket)  })}util.inherits(TunnelingAgent, events.EventEmitter)TunnelingAgent.prototype.addRequest = function addRequest(req, options) {  var self = this   // Legacy API: addRequest(req, host, port, path)  if (typeof options === 'string') {    options = {      host: options,      port: arguments[2],      path: arguments[3]    };  }  if (self.sockets.length >= this.maxSockets) {    // We are over limit so we'll add it to the queue.    self.requests.push({host: options.host, port: options.port, request: req})    return  }  // If we are under maxSockets create a new one.  self.createConnection({host: options.host, port: options.port, request: req})}TunnelingAgent.prototype.createConnection = function createConnection(pending) {  var self = this  self.createSocket(pending, function(socket) {    socket.on('free', onFree)    socket.on('close', onCloseOrRemove)    socket.on('agentRemove', onCloseOrRemove)    pending.request.onSocket(socket)    function onFree() {      self.emit('free', socket, pending.host, pending.port)    }    function onCloseOrRemove(err) {      self.removeSocket(socket)      socket.removeListener('free', onFree)      socket.removeListener('close', onCloseOrRemove)      socket.removeListener('agentRemove', onCloseOrRemove)    }  })}TunnelingAgent.prototype.createSocket = function createSocket(options, cb) {  var self = this  var placeholder = {}  self.sockets.push(placeholder)  var connectOptions = mergeOptions({}, self.proxyOptions,    { method: 'CONNECT'    , path: options.host + ':' + options.port    , agent: false    }  )  if (connectOptions.proxyAuth) {    connectOptions.headers = connectOptions.headers || {}    connectOptions.headers['Proxy-Authorization'] = 'Basic ' +        Buffer.from(connectOptions.proxyAuth).toString('base64')  }  debug('making CONNECT request')  var connectReq = self.request(connectOptions)  connectReq.useChunkedEncodingByDefault = false // for v0.6  connectReq.once('response', onResponse) // for v0.6  connectReq.once('upgrade', onUpgrade)   // for v0.6  connectReq.once('connect', onConnect)   // for v0.7 or later  connectReq.once('error', onError)  connectReq.end()  function onResponse(res) {    // Very hacky. This is necessary to avoid http-parser leaks.    res.upgrade = true  }  function onUpgrade(res, socket, head) {    // Hacky.    process.nextTick(function() {      onConnect(res, socket, head)    })  }  function onConnect(res, socket, head) {    connectReq.removeAllListeners()    socket.removeAllListeners()    if (res.statusCode === 200) {      assert.equal(head.length, 0)      debug('tunneling connection has established')      self.sockets[self.sockets.indexOf(placeholder)] = socket      cb(socket)    } else {      debug('tunneling socket could not be established, statusCode=%d', res.statusCode)      var error = new Error('tunneling socket could not be established, ' + 'statusCode=' + res.statusCode)      error.code = 'ECONNRESET'      options.request.emit('error', error)      self.removeSocket(placeholder)    }  }  function onError(cause) {    connectReq.removeAllListeners()    debug('tunneling socket could not be established, cause=%s\n', cause.message, cause.stack)    var error = new Error('tunneling socket could not be established, ' + 'cause=' + cause.message)    error.code = 'ECONNRESET'    options.request.emit('error', error)    self.removeSocket(placeholder)  }}TunnelingAgent.prototype.removeSocket = function removeSocket(socket) {  var pos = this.sockets.indexOf(socket)  if (pos === -1) return  this.sockets.splice(pos, 1)  var pending = this.requests.shift()  if (pending) {    // If we have pending requests and a socket gets closed a new one    // needs to be created to take over in the pool for the one that closed.    this.createConnection(pending)  }}function createSecureSocket(options, cb) {  var self = this  TunnelingAgent.prototype.createSocket.call(self, options, function(socket) {    // 0 is dummy port for v0.6    var secureSocket = tls.connect(0, mergeOptions({}, self.options,      { servername: options.host      , socket: socket      }    ))    self.sockets[self.sockets.indexOf(socket)] = secureSocket    cb(secureSocket)  })}function mergeOptions(target) {  for (var i = 1, len = arguments.length; i < len; ++i) {    var overrides = arguments[i]    if (typeof overrides === 'object') {      var keys = Object.keys(overrides)      for (var j = 0, keyLen = keys.length; j < keyLen; ++j) {        var k = keys[j]        if (overrides[k] !== undefined) {          target[k] = overrides[k]        }      }    }  }  return target}var debugif (process.env.NODE_DEBUG && /\btunnel\b/.test(process.env.NODE_DEBUG)) {  debug = function() {    var args = Array.prototype.slice.call(arguments)    if (typeof args[0] === 'string') {      args[0] = 'TUNNEL: ' + args[0]    } else {      args.unshift('TUNNEL:')    }    console.error.apply(console, args)  }} else {  debug = function() {}}exports.debug = debug // for test
 |