twitst4tz

twitter statistics web application
Log | Files | Refs | README | LICENSE

request.js (44466B)


      1 'use strict'
      2 
      3 var http = require('http')
      4 var https = require('https')
      5 var url = require('url')
      6 var util = require('util')
      7 var stream = require('stream')
      8 var zlib = require('zlib')
      9 var aws2 = require('aws-sign2')
     10 var aws4 = require('aws4')
     11 var httpSignature = require('http-signature')
     12 var mime = require('mime-types')
     13 var caseless = require('caseless')
     14 var ForeverAgent = require('forever-agent')
     15 var FormData = require('form-data')
     16 var extend = require('extend')
     17 var isstream = require('isstream')
     18 var isTypedArray = require('is-typedarray').strict
     19 var helpers = require('./lib/helpers')
     20 var cookies = require('./lib/cookies')
     21 var getProxyFromURI = require('./lib/getProxyFromURI')
     22 var Querystring = require('./lib/querystring').Querystring
     23 var Har = require('./lib/har').Har
     24 var Auth = require('./lib/auth').Auth
     25 var OAuth = require('./lib/oauth').OAuth
     26 var hawk = require('./lib/hawk')
     27 var Multipart = require('./lib/multipart').Multipart
     28 var Redirect = require('./lib/redirect').Redirect
     29 var Tunnel = require('./lib/tunnel').Tunnel
     30 var now = require('performance-now')
     31 var Buffer = require('safe-buffer').Buffer
     32 
     33 var safeStringify = helpers.safeStringify
     34 var isReadStream = helpers.isReadStream
     35 var toBase64 = helpers.toBase64
     36 var defer = helpers.defer
     37 var copy = helpers.copy
     38 var version = helpers.version
     39 var globalCookieJar = cookies.jar()
     40 
     41 var globalPool = {}
     42 
     43 function filterForNonReserved (reserved, options) {
     44   // Filter out properties that are not reserved.
     45   // Reserved values are passed in at call site.
     46 
     47   var object = {}
     48   for (var i in options) {
     49     var notReserved = (reserved.indexOf(i) === -1)
     50     if (notReserved) {
     51       object[i] = options[i]
     52     }
     53   }
     54   return object
     55 }
     56 
     57 function filterOutReservedFunctions (reserved, options) {
     58   // Filter out properties that are functions and are reserved.
     59   // Reserved values are passed in at call site.
     60 
     61   var object = {}
     62   for (var i in options) {
     63     var isReserved = !(reserved.indexOf(i) === -1)
     64     var isFunction = (typeof options[i] === 'function')
     65     if (!(isReserved && isFunction)) {
     66       object[i] = options[i]
     67     }
     68   }
     69   return object
     70 }
     71 
     72 // Return a simpler request object to allow serialization
     73 function requestToJSON () {
     74   var self = this
     75   return {
     76     uri: self.uri,
     77     method: self.method,
     78     headers: self.headers
     79   }
     80 }
     81 
     82 // Return a simpler response object to allow serialization
     83 function responseToJSON () {
     84   var self = this
     85   return {
     86     statusCode: self.statusCode,
     87     body: self.body,
     88     headers: self.headers,
     89     request: requestToJSON.call(self.request)
     90   }
     91 }
     92 
     93 function Request (options) {
     94   // if given the method property in options, set property explicitMethod to true
     95 
     96   // extend the Request instance with any non-reserved properties
     97   // remove any reserved functions from the options object
     98   // set Request instance to be readable and writable
     99   // call init
    100 
    101   var self = this
    102 
    103   // start with HAR, then override with additional options
    104   if (options.har) {
    105     self._har = new Har(self)
    106     options = self._har.options(options)
    107   }
    108 
    109   stream.Stream.call(self)
    110   var reserved = Object.keys(Request.prototype)
    111   var nonReserved = filterForNonReserved(reserved, options)
    112 
    113   extend(self, nonReserved)
    114   options = filterOutReservedFunctions(reserved, options)
    115 
    116   self.readable = true
    117   self.writable = true
    118   if (options.method) {
    119     self.explicitMethod = true
    120   }
    121   self._qs = new Querystring(self)
    122   self._auth = new Auth(self)
    123   self._oauth = new OAuth(self)
    124   self._multipart = new Multipart(self)
    125   self._redirect = new Redirect(self)
    126   self._tunnel = new Tunnel(self)
    127   self.init(options)
    128 }
    129 
    130 util.inherits(Request, stream.Stream)
    131 
    132 // Debugging
    133 Request.debug = process.env.NODE_DEBUG && /\brequest\b/.test(process.env.NODE_DEBUG)
    134 function debug () {
    135   if (Request.debug) {
    136     console.error('REQUEST %s', util.format.apply(util, arguments))
    137   }
    138 }
    139 Request.prototype.debug = debug
    140 
    141 Request.prototype.init = function (options) {
    142   // init() contains all the code to setup the request object.
    143   // the actual outgoing request is not started until start() is called
    144   // this function is called from both the constructor and on redirect.
    145   var self = this
    146   if (!options) {
    147     options = {}
    148   }
    149   self.headers = self.headers ? copy(self.headers) : {}
    150 
    151   // Delete headers with value undefined since they break
    152   // ClientRequest.OutgoingMessage.setHeader in node 0.12
    153   for (var headerName in self.headers) {
    154     if (typeof self.headers[headerName] === 'undefined') {
    155       delete self.headers[headerName]
    156     }
    157   }
    158 
    159   caseless.httpify(self, self.headers)
    160 
    161   if (!self.method) {
    162     self.method = options.method || 'GET'
    163   }
    164   if (!self.localAddress) {
    165     self.localAddress = options.localAddress
    166   }
    167 
    168   self._qs.init(options)
    169 
    170   debug(options)
    171   if (!self.pool && self.pool !== false) {
    172     self.pool = globalPool
    173   }
    174   self.dests = self.dests || []
    175   self.__isRequestRequest = true
    176 
    177   // Protect against double callback
    178   if (!self._callback && self.callback) {
    179     self._callback = self.callback
    180     self.callback = function () {
    181       if (self._callbackCalled) {
    182         return // Print a warning maybe?
    183       }
    184       self._callbackCalled = true
    185       self._callback.apply(self, arguments)
    186     }
    187     self.on('error', self.callback.bind())
    188     self.on('complete', self.callback.bind(self, null))
    189   }
    190 
    191   // People use this property instead all the time, so support it
    192   if (!self.uri && self.url) {
    193     self.uri = self.url
    194     delete self.url
    195   }
    196 
    197   // If there's a baseUrl, then use it as the base URL (i.e. uri must be
    198   // specified as a relative path and is appended to baseUrl).
    199   if (self.baseUrl) {
    200     if (typeof self.baseUrl !== 'string') {
    201       return self.emit('error', new Error('options.baseUrl must be a string'))
    202     }
    203 
    204     if (typeof self.uri !== 'string') {
    205       return self.emit('error', new Error('options.uri must be a string when using options.baseUrl'))
    206     }
    207 
    208     if (self.uri.indexOf('//') === 0 || self.uri.indexOf('://') !== -1) {
    209       return self.emit('error', new Error('options.uri must be a path when using options.baseUrl'))
    210     }
    211 
    212     // Handle all cases to make sure that there's only one slash between
    213     // baseUrl and uri.
    214     var baseUrlEndsWithSlash = self.baseUrl.lastIndexOf('/') === self.baseUrl.length - 1
    215     var uriStartsWithSlash = self.uri.indexOf('/') === 0
    216 
    217     if (baseUrlEndsWithSlash && uriStartsWithSlash) {
    218       self.uri = self.baseUrl + self.uri.slice(1)
    219     } else if (baseUrlEndsWithSlash || uriStartsWithSlash) {
    220       self.uri = self.baseUrl + self.uri
    221     } else if (self.uri === '') {
    222       self.uri = self.baseUrl
    223     } else {
    224       self.uri = self.baseUrl + '/' + self.uri
    225     }
    226     delete self.baseUrl
    227   }
    228 
    229   // A URI is needed by this point, emit error if we haven't been able to get one
    230   if (!self.uri) {
    231     return self.emit('error', new Error('options.uri is a required argument'))
    232   }
    233 
    234   // If a string URI/URL was given, parse it into a URL object
    235   if (typeof self.uri === 'string') {
    236     self.uri = url.parse(self.uri)
    237   }
    238 
    239   // Some URL objects are not from a URL parsed string and need href added
    240   if (!self.uri.href) {
    241     self.uri.href = url.format(self.uri)
    242   }
    243 
    244   // DEPRECATED: Warning for users of the old Unix Sockets URL Scheme
    245   if (self.uri.protocol === 'unix:') {
    246     return self.emit('error', new Error('`unix://` URL scheme is no longer supported. Please use the format `http://unix:SOCKET:PATH`'))
    247   }
    248 
    249   // Support Unix Sockets
    250   if (self.uri.host === 'unix') {
    251     self.enableUnixSocket()
    252   }
    253 
    254   if (self.strictSSL === false) {
    255     self.rejectUnauthorized = false
    256   }
    257 
    258   if (!self.uri.pathname) { self.uri.pathname = '/' }
    259 
    260   if (!(self.uri.host || (self.uri.hostname && self.uri.port)) && !self.uri.isUnix) {
    261     // Invalid URI: it may generate lot of bad errors, like 'TypeError: Cannot call method `indexOf` of undefined' in CookieJar
    262     // Detect and reject it as soon as possible
    263     var faultyUri = url.format(self.uri)
    264     var message = 'Invalid URI "' + faultyUri + '"'
    265     if (Object.keys(options).length === 0) {
    266       // No option ? This can be the sign of a redirect
    267       // As this is a case where the user cannot do anything (they didn't call request directly with this URL)
    268       // they should be warned that it can be caused by a redirection (can save some hair)
    269       message += '. This can be caused by a crappy redirection.'
    270     }
    271     // This error was fatal
    272     self.abort()
    273     return self.emit('error', new Error(message))
    274   }
    275 
    276   if (!self.hasOwnProperty('proxy')) {
    277     self.proxy = getProxyFromURI(self.uri)
    278   }
    279 
    280   self.tunnel = self._tunnel.isEnabled()
    281   if (self.proxy) {
    282     self._tunnel.setup(options)
    283   }
    284 
    285   self._redirect.onRequest(options)
    286 
    287   self.setHost = false
    288   if (!self.hasHeader('host')) {
    289     var hostHeaderName = self.originalHostHeaderName || 'host'
    290     self.setHeader(hostHeaderName, self.uri.host)
    291     // Drop :port suffix from Host header if known protocol.
    292     if (self.uri.port) {
    293       if ((self.uri.port === '80' && self.uri.protocol === 'http:') ||
    294           (self.uri.port === '443' && self.uri.protocol === 'https:')) {
    295         self.setHeader(hostHeaderName, self.uri.hostname)
    296       }
    297     }
    298     self.setHost = true
    299   }
    300 
    301   self.jar(self._jar || options.jar)
    302 
    303   if (!self.uri.port) {
    304     if (self.uri.protocol === 'http:') { self.uri.port = 80 } else if (self.uri.protocol === 'https:') { self.uri.port = 443 }
    305   }
    306 
    307   if (self.proxy && !self.tunnel) {
    308     self.port = self.proxy.port
    309     self.host = self.proxy.hostname
    310   } else {
    311     self.port = self.uri.port
    312     self.host = self.uri.hostname
    313   }
    314 
    315   if (options.form) {
    316     self.form(options.form)
    317   }
    318 
    319   if (options.formData) {
    320     var formData = options.formData
    321     var requestForm = self.form()
    322     var appendFormValue = function (key, value) {
    323       if (value && value.hasOwnProperty('value') && value.hasOwnProperty('options')) {
    324         requestForm.append(key, value.value, value.options)
    325       } else {
    326         requestForm.append(key, value)
    327       }
    328     }
    329     for (var formKey in formData) {
    330       if (formData.hasOwnProperty(formKey)) {
    331         var formValue = formData[formKey]
    332         if (formValue instanceof Array) {
    333           for (var j = 0; j < formValue.length; j++) {
    334             appendFormValue(formKey, formValue[j])
    335           }
    336         } else {
    337           appendFormValue(formKey, formValue)
    338         }
    339       }
    340     }
    341   }
    342 
    343   if (options.qs) {
    344     self.qs(options.qs)
    345   }
    346 
    347   if (self.uri.path) {
    348     self.path = self.uri.path
    349   } else {
    350     self.path = self.uri.pathname + (self.uri.search || '')
    351   }
    352 
    353   if (self.path.length === 0) {
    354     self.path = '/'
    355   }
    356 
    357   // Auth must happen last in case signing is dependent on other headers
    358   if (options.aws) {
    359     self.aws(options.aws)
    360   }
    361 
    362   if (options.hawk) {
    363     self.hawk(options.hawk)
    364   }
    365 
    366   if (options.httpSignature) {
    367     self.httpSignature(options.httpSignature)
    368   }
    369 
    370   if (options.auth) {
    371     if (Object.prototype.hasOwnProperty.call(options.auth, 'username')) {
    372       options.auth.user = options.auth.username
    373     }
    374     if (Object.prototype.hasOwnProperty.call(options.auth, 'password')) {
    375       options.auth.pass = options.auth.password
    376     }
    377 
    378     self.auth(
    379       options.auth.user,
    380       options.auth.pass,
    381       options.auth.sendImmediately,
    382       options.auth.bearer
    383     )
    384   }
    385 
    386   if (self.gzip && !self.hasHeader('accept-encoding')) {
    387     self.setHeader('accept-encoding', 'gzip, deflate')
    388   }
    389 
    390   if (self.uri.auth && !self.hasHeader('authorization')) {
    391     var uriAuthPieces = self.uri.auth.split(':').map(function (item) { return self._qs.unescape(item) })
    392     self.auth(uriAuthPieces[0], uriAuthPieces.slice(1).join(':'), true)
    393   }
    394 
    395   if (!self.tunnel && self.proxy && self.proxy.auth && !self.hasHeader('proxy-authorization')) {
    396     var proxyAuthPieces = self.proxy.auth.split(':').map(function (item) { return self._qs.unescape(item) })
    397     var authHeader = 'Basic ' + toBase64(proxyAuthPieces.join(':'))
    398     self.setHeader('proxy-authorization', authHeader)
    399   }
    400 
    401   if (self.proxy && !self.tunnel) {
    402     self.path = (self.uri.protocol + '//' + self.uri.host + self.path)
    403   }
    404 
    405   if (options.json) {
    406     self.json(options.json)
    407   }
    408   if (options.multipart) {
    409     self.multipart(options.multipart)
    410   }
    411 
    412   if (options.time) {
    413     self.timing = true
    414 
    415     // NOTE: elapsedTime is deprecated in favor of .timings
    416     self.elapsedTime = self.elapsedTime || 0
    417   }
    418 
    419   function setContentLength () {
    420     if (isTypedArray(self.body)) {
    421       self.body = Buffer.from(self.body)
    422     }
    423 
    424     if (!self.hasHeader('content-length')) {
    425       var length
    426       if (typeof self.body === 'string') {
    427         length = Buffer.byteLength(self.body)
    428       } else if (Array.isArray(self.body)) {
    429         length = self.body.reduce(function (a, b) { return a + b.length }, 0)
    430       } else {
    431         length = self.body.length
    432       }
    433 
    434       if (length) {
    435         self.setHeader('content-length', length)
    436       } else {
    437         self.emit('error', new Error('Argument error, options.body.'))
    438       }
    439     }
    440   }
    441   if (self.body && !isstream(self.body)) {
    442     setContentLength()
    443   }
    444 
    445   if (options.oauth) {
    446     self.oauth(options.oauth)
    447   } else if (self._oauth.params && self.hasHeader('authorization')) {
    448     self.oauth(self._oauth.params)
    449   }
    450 
    451   var protocol = self.proxy && !self.tunnel ? self.proxy.protocol : self.uri.protocol
    452   var defaultModules = {'http:': http, 'https:': https}
    453   var httpModules = self.httpModules || {}
    454 
    455   self.httpModule = httpModules[protocol] || defaultModules[protocol]
    456 
    457   if (!self.httpModule) {
    458     return self.emit('error', new Error('Invalid protocol: ' + protocol))
    459   }
    460 
    461   if (options.ca) {
    462     self.ca = options.ca
    463   }
    464 
    465   if (!self.agent) {
    466     if (options.agentOptions) {
    467       self.agentOptions = options.agentOptions
    468     }
    469 
    470     if (options.agentClass) {
    471       self.agentClass = options.agentClass
    472     } else if (options.forever) {
    473       var v = version()
    474       // use ForeverAgent in node 0.10- only
    475       if (v.major === 0 && v.minor <= 10) {
    476         self.agentClass = protocol === 'http:' ? ForeverAgent : ForeverAgent.SSL
    477       } else {
    478         self.agentClass = self.httpModule.Agent
    479         self.agentOptions = self.agentOptions || {}
    480         self.agentOptions.keepAlive = true
    481       }
    482     } else {
    483       self.agentClass = self.httpModule.Agent
    484     }
    485   }
    486 
    487   if (self.pool === false) {
    488     self.agent = false
    489   } else {
    490     self.agent = self.agent || self.getNewAgent()
    491   }
    492 
    493   self.on('pipe', function (src) {
    494     if (self.ntick && self._started) {
    495       self.emit('error', new Error('You cannot pipe to this stream after the outbound request has started.'))
    496     }
    497     self.src = src
    498     if (isReadStream(src)) {
    499       if (!self.hasHeader('content-type')) {
    500         self.setHeader('content-type', mime.lookup(src.path))
    501       }
    502     } else {
    503       if (src.headers) {
    504         for (var i in src.headers) {
    505           if (!self.hasHeader(i)) {
    506             self.setHeader(i, src.headers[i])
    507           }
    508         }
    509       }
    510       if (self._json && !self.hasHeader('content-type')) {
    511         self.setHeader('content-type', 'application/json')
    512       }
    513       if (src.method && !self.explicitMethod) {
    514         self.method = src.method
    515       }
    516     }
    517 
    518   // self.on('pipe', function () {
    519   //   console.error('You have already piped to this stream. Pipeing twice is likely to break the request.')
    520   // })
    521   })
    522 
    523   defer(function () {
    524     if (self._aborted) {
    525       return
    526     }
    527 
    528     var end = function () {
    529       if (self._form) {
    530         if (!self._auth.hasAuth) {
    531           self._form.pipe(self)
    532         } else if (self._auth.hasAuth && self._auth.sentAuth) {
    533           self._form.pipe(self)
    534         }
    535       }
    536       if (self._multipart && self._multipart.chunked) {
    537         self._multipart.body.pipe(self)
    538       }
    539       if (self.body) {
    540         if (isstream(self.body)) {
    541           self.body.pipe(self)
    542         } else {
    543           setContentLength()
    544           if (Array.isArray(self.body)) {
    545             self.body.forEach(function (part) {
    546               self.write(part)
    547             })
    548           } else {
    549             self.write(self.body)
    550           }
    551           self.end()
    552         }
    553       } else if (self.requestBodyStream) {
    554         console.warn('options.requestBodyStream is deprecated, please pass the request object to stream.pipe.')
    555         self.requestBodyStream.pipe(self)
    556       } else if (!self.src) {
    557         if (self._auth.hasAuth && !self._auth.sentAuth) {
    558           self.end()
    559           return
    560         }
    561         if (self.method !== 'GET' && typeof self.method !== 'undefined') {
    562           self.setHeader('content-length', 0)
    563         }
    564         self.end()
    565       }
    566     }
    567 
    568     if (self._form && !self.hasHeader('content-length')) {
    569       // Before ending the request, we had to compute the length of the whole form, asyncly
    570       self.setHeader(self._form.getHeaders(), true)
    571       self._form.getLength(function (err, length) {
    572         if (!err && !isNaN(length)) {
    573           self.setHeader('content-length', length)
    574         }
    575         end()
    576       })
    577     } else {
    578       end()
    579     }
    580 
    581     self.ntick = true
    582   })
    583 }
    584 
    585 Request.prototype.getNewAgent = function () {
    586   var self = this
    587   var Agent = self.agentClass
    588   var options = {}
    589   if (self.agentOptions) {
    590     for (var i in self.agentOptions) {
    591       options[i] = self.agentOptions[i]
    592     }
    593   }
    594   if (self.ca) {
    595     options.ca = self.ca
    596   }
    597   if (self.ciphers) {
    598     options.ciphers = self.ciphers
    599   }
    600   if (self.secureProtocol) {
    601     options.secureProtocol = self.secureProtocol
    602   }
    603   if (self.secureOptions) {
    604     options.secureOptions = self.secureOptions
    605   }
    606   if (typeof self.rejectUnauthorized !== 'undefined') {
    607     options.rejectUnauthorized = self.rejectUnauthorized
    608   }
    609 
    610   if (self.cert && self.key) {
    611     options.key = self.key
    612     options.cert = self.cert
    613   }
    614 
    615   if (self.pfx) {
    616     options.pfx = self.pfx
    617   }
    618 
    619   if (self.passphrase) {
    620     options.passphrase = self.passphrase
    621   }
    622 
    623   var poolKey = ''
    624 
    625   // different types of agents are in different pools
    626   if (Agent !== self.httpModule.Agent) {
    627     poolKey += Agent.name
    628   }
    629 
    630   // ca option is only relevant if proxy or destination are https
    631   var proxy = self.proxy
    632   if (typeof proxy === 'string') {
    633     proxy = url.parse(proxy)
    634   }
    635   var isHttps = (proxy && proxy.protocol === 'https:') || this.uri.protocol === 'https:'
    636 
    637   if (isHttps) {
    638     if (options.ca) {
    639       if (poolKey) {
    640         poolKey += ':'
    641       }
    642       poolKey += options.ca
    643     }
    644 
    645     if (typeof options.rejectUnauthorized !== 'undefined') {
    646       if (poolKey) {
    647         poolKey += ':'
    648       }
    649       poolKey += options.rejectUnauthorized
    650     }
    651 
    652     if (options.cert) {
    653       if (poolKey) {
    654         poolKey += ':'
    655       }
    656       poolKey += options.cert.toString('ascii') + options.key.toString('ascii')
    657     }
    658 
    659     if (options.pfx) {
    660       if (poolKey) {
    661         poolKey += ':'
    662       }
    663       poolKey += options.pfx.toString('ascii')
    664     }
    665 
    666     if (options.ciphers) {
    667       if (poolKey) {
    668         poolKey += ':'
    669       }
    670       poolKey += options.ciphers
    671     }
    672 
    673     if (options.secureProtocol) {
    674       if (poolKey) {
    675         poolKey += ':'
    676       }
    677       poolKey += options.secureProtocol
    678     }
    679 
    680     if (options.secureOptions) {
    681       if (poolKey) {
    682         poolKey += ':'
    683       }
    684       poolKey += options.secureOptions
    685     }
    686   }
    687 
    688   if (self.pool === globalPool && !poolKey && Object.keys(options).length === 0 && self.httpModule.globalAgent) {
    689     // not doing anything special.  Use the globalAgent
    690     return self.httpModule.globalAgent
    691   }
    692 
    693   // we're using a stored agent.  Make sure it's protocol-specific
    694   poolKey = self.uri.protocol + poolKey
    695 
    696   // generate a new agent for this setting if none yet exists
    697   if (!self.pool[poolKey]) {
    698     self.pool[poolKey] = new Agent(options)
    699     // properly set maxSockets on new agents
    700     if (self.pool.maxSockets) {
    701       self.pool[poolKey].maxSockets = self.pool.maxSockets
    702     }
    703   }
    704 
    705   return self.pool[poolKey]
    706 }
    707 
    708 Request.prototype.start = function () {
    709   // start() is called once we are ready to send the outgoing HTTP request.
    710   // this is usually called on the first write(), end() or on nextTick()
    711   var self = this
    712 
    713   if (self.timing) {
    714     // All timings will be relative to this request's startTime.  In order to do this,
    715     // we need to capture the wall-clock start time (via Date), immediately followed
    716     // by the high-resolution timer (via now()).  While these two won't be set
    717     // at the _exact_ same time, they should be close enough to be able to calculate
    718     // high-resolution, monotonically non-decreasing timestamps relative to startTime.
    719     var startTime = new Date().getTime()
    720     var startTimeNow = now()
    721   }
    722 
    723   if (self._aborted) {
    724     return
    725   }
    726 
    727   self._started = true
    728   self.method = self.method || 'GET'
    729   self.href = self.uri.href
    730 
    731   if (self.src && self.src.stat && self.src.stat.size && !self.hasHeader('content-length')) {
    732     self.setHeader('content-length', self.src.stat.size)
    733   }
    734   if (self._aws) {
    735     self.aws(self._aws, true)
    736   }
    737 
    738   // We have a method named auth, which is completely different from the http.request
    739   // auth option.  If we don't remove it, we're gonna have a bad time.
    740   var reqOptions = copy(self)
    741   delete reqOptions.auth
    742 
    743   debug('make request', self.uri.href)
    744 
    745   // node v6.8.0 now supports a `timeout` value in `http.request()`, but we
    746   // should delete it for now since we handle timeouts manually for better
    747   // consistency with node versions before v6.8.0
    748   delete reqOptions.timeout
    749 
    750   try {
    751     self.req = self.httpModule.request(reqOptions)
    752   } catch (err) {
    753     self.emit('error', err)
    754     return
    755   }
    756 
    757   if (self.timing) {
    758     self.startTime = startTime
    759     self.startTimeNow = startTimeNow
    760 
    761     // Timing values will all be relative to startTime (by comparing to startTimeNow
    762     // so we have an accurate clock)
    763     self.timings = {}
    764   }
    765 
    766   var timeout
    767   if (self.timeout && !self.timeoutTimer) {
    768     if (self.timeout < 0) {
    769       timeout = 0
    770     } else if (typeof self.timeout === 'number' && isFinite(self.timeout)) {
    771       timeout = self.timeout
    772     }
    773   }
    774 
    775   self.req.on('response', self.onRequestResponse.bind(self))
    776   self.req.on('error', self.onRequestError.bind(self))
    777   self.req.on('drain', function () {
    778     self.emit('drain')
    779   })
    780 
    781   self.req.on('socket', function (socket) {
    782     // `._connecting` was the old property which was made public in node v6.1.0
    783     var isConnecting = socket._connecting || socket.connecting
    784     if (self.timing) {
    785       self.timings.socket = now() - self.startTimeNow
    786 
    787       if (isConnecting) {
    788         var onLookupTiming = function () {
    789           self.timings.lookup = now() - self.startTimeNow
    790         }
    791 
    792         var onConnectTiming = function () {
    793           self.timings.connect = now() - self.startTimeNow
    794         }
    795 
    796         socket.once('lookup', onLookupTiming)
    797         socket.once('connect', onConnectTiming)
    798 
    799         // clean up timing event listeners if needed on error
    800         self.req.once('error', function () {
    801           socket.removeListener('lookup', onLookupTiming)
    802           socket.removeListener('connect', onConnectTiming)
    803         })
    804       }
    805     }
    806 
    807     var setReqTimeout = function () {
    808       // This timeout sets the amount of time to wait *between* bytes sent
    809       // from the server once connected.
    810       //
    811       // In particular, it's useful for erroring if the server fails to send
    812       // data halfway through streaming a response.
    813       self.req.setTimeout(timeout, function () {
    814         if (self.req) {
    815           self.abort()
    816           var e = new Error('ESOCKETTIMEDOUT')
    817           e.code = 'ESOCKETTIMEDOUT'
    818           e.connect = false
    819           self.emit('error', e)
    820         }
    821       })
    822     }
    823     if (timeout !== undefined) {
    824       // Only start the connection timer if we're actually connecting a new
    825       // socket, otherwise if we're already connected (because this is a
    826       // keep-alive connection) do not bother. This is important since we won't
    827       // get a 'connect' event for an already connected socket.
    828       if (isConnecting) {
    829         var onReqSockConnect = function () {
    830           socket.removeListener('connect', onReqSockConnect)
    831           self.clearTimeout()
    832           setReqTimeout()
    833         }
    834 
    835         socket.on('connect', onReqSockConnect)
    836 
    837         self.req.on('error', function (err) { // eslint-disable-line handle-callback-err
    838           socket.removeListener('connect', onReqSockConnect)
    839         })
    840 
    841         // Set a timeout in memory - this block will throw if the server takes more
    842         // than `timeout` to write the HTTP status and headers (corresponding to
    843         // the on('response') event on the client). NB: this measures wall-clock
    844         // time, not the time between bytes sent by the server.
    845         self.timeoutTimer = setTimeout(function () {
    846           socket.removeListener('connect', onReqSockConnect)
    847           self.abort()
    848           var e = new Error('ETIMEDOUT')
    849           e.code = 'ETIMEDOUT'
    850           e.connect = true
    851           self.emit('error', e)
    852         }, timeout)
    853       } else {
    854         // We're already connected
    855         setReqTimeout()
    856       }
    857     }
    858     self.emit('socket', socket)
    859   })
    860 
    861   self.emit('request', self.req)
    862 }
    863 
    864 Request.prototype.onRequestError = function (error) {
    865   var self = this
    866   if (self._aborted) {
    867     return
    868   }
    869   if (self.req && self.req._reusedSocket && error.code === 'ECONNRESET' &&
    870     self.agent.addRequestNoreuse) {
    871     self.agent = { addRequest: self.agent.addRequestNoreuse.bind(self.agent) }
    872     self.start()
    873     self.req.end()
    874     return
    875   }
    876   self.clearTimeout()
    877   self.emit('error', error)
    878 }
    879 
    880 Request.prototype.onRequestResponse = function (response) {
    881   var self = this
    882 
    883   if (self.timing) {
    884     self.timings.response = now() - self.startTimeNow
    885   }
    886 
    887   debug('onRequestResponse', self.uri.href, response.statusCode, response.headers)
    888   response.on('end', function () {
    889     if (self.timing) {
    890       self.timings.end = now() - self.startTimeNow
    891       response.timingStart = self.startTime
    892 
    893       // fill in the blanks for any periods that didn't trigger, such as
    894       // no lookup or connect due to keep alive
    895       if (!self.timings.socket) {
    896         self.timings.socket = 0
    897       }
    898       if (!self.timings.lookup) {
    899         self.timings.lookup = self.timings.socket
    900       }
    901       if (!self.timings.connect) {
    902         self.timings.connect = self.timings.lookup
    903       }
    904       if (!self.timings.response) {
    905         self.timings.response = self.timings.connect
    906       }
    907 
    908       debug('elapsed time', self.timings.end)
    909 
    910       // elapsedTime includes all redirects
    911       self.elapsedTime += Math.round(self.timings.end)
    912 
    913       // NOTE: elapsedTime is deprecated in favor of .timings
    914       response.elapsedTime = self.elapsedTime
    915 
    916       // timings is just for the final fetch
    917       response.timings = self.timings
    918 
    919       // pre-calculate phase timings as well
    920       response.timingPhases = {
    921         wait: self.timings.socket,
    922         dns: self.timings.lookup - self.timings.socket,
    923         tcp: self.timings.connect - self.timings.lookup,
    924         firstByte: self.timings.response - self.timings.connect,
    925         download: self.timings.end - self.timings.response,
    926         total: self.timings.end
    927       }
    928     }
    929     debug('response end', self.uri.href, response.statusCode, response.headers)
    930   })
    931 
    932   if (self._aborted) {
    933     debug('aborted', self.uri.href)
    934     response.resume()
    935     return
    936   }
    937 
    938   self.response = response
    939   response.request = self
    940   response.toJSON = responseToJSON
    941 
    942   // XXX This is different on 0.10, because SSL is strict by default
    943   if (self.httpModule === https &&
    944     self.strictSSL && (!response.hasOwnProperty('socket') ||
    945     !response.socket.authorized)) {
    946     debug('strict ssl error', self.uri.href)
    947     var sslErr = response.hasOwnProperty('socket') ? response.socket.authorizationError : self.uri.href + ' does not support SSL'
    948     self.emit('error', new Error('SSL Error: ' + sslErr))
    949     return
    950   }
    951 
    952   // Save the original host before any redirect (if it changes, we need to
    953   // remove any authorization headers).  Also remember the case of the header
    954   // name because lots of broken servers expect Host instead of host and we
    955   // want the caller to be able to specify this.
    956   self.originalHost = self.getHeader('host')
    957   if (!self.originalHostHeaderName) {
    958     self.originalHostHeaderName = self.hasHeader('host')
    959   }
    960   if (self.setHost) {
    961     self.removeHeader('host')
    962   }
    963   self.clearTimeout()
    964 
    965   var targetCookieJar = (self._jar && self._jar.setCookie) ? self._jar : globalCookieJar
    966   var addCookie = function (cookie) {
    967     // set the cookie if it's domain in the href's domain.
    968     try {
    969       targetCookieJar.setCookie(cookie, self.uri.href, {ignoreError: true})
    970     } catch (e) {
    971       self.emit('error', e)
    972     }
    973   }
    974 
    975   response.caseless = caseless(response.headers)
    976 
    977   if (response.caseless.has('set-cookie') && (!self._disableCookies)) {
    978     var headerName = response.caseless.has('set-cookie')
    979     if (Array.isArray(response.headers[headerName])) {
    980       response.headers[headerName].forEach(addCookie)
    981     } else {
    982       addCookie(response.headers[headerName])
    983     }
    984   }
    985 
    986   if (self._redirect.onResponse(response)) {
    987     return // Ignore the rest of the response
    988   } else {
    989     // Be a good stream and emit end when the response is finished.
    990     // Hack to emit end on close because of a core bug that never fires end
    991     response.on('close', function () {
    992       if (!self._ended) {
    993         self.response.emit('end')
    994       }
    995     })
    996 
    997     response.once('end', function () {
    998       self._ended = true
    999     })
   1000 
   1001     var noBody = function (code) {
   1002       return (
   1003         self.method === 'HEAD' ||
   1004         // Informational
   1005         (code >= 100 && code < 200) ||
   1006         // No Content
   1007         code === 204 ||
   1008         // Not Modified
   1009         code === 304
   1010       )
   1011     }
   1012 
   1013     var responseContent
   1014     if (self.gzip && !noBody(response.statusCode)) {
   1015       var contentEncoding = response.headers['content-encoding'] || 'identity'
   1016       contentEncoding = contentEncoding.trim().toLowerCase()
   1017 
   1018       // Be more lenient with decoding compressed responses, since (very rarely)
   1019       // servers send slightly invalid gzip responses that are still accepted
   1020       // by common browsers.
   1021       // Always using Z_SYNC_FLUSH is what cURL does.
   1022       var zlibOptions = {
   1023         flush: zlib.Z_SYNC_FLUSH,
   1024         finishFlush: zlib.Z_SYNC_FLUSH
   1025       }
   1026 
   1027       if (contentEncoding === 'gzip') {
   1028         responseContent = zlib.createGunzip(zlibOptions)
   1029         response.pipe(responseContent)
   1030       } else if (contentEncoding === 'deflate') {
   1031         responseContent = zlib.createInflate(zlibOptions)
   1032         response.pipe(responseContent)
   1033       } else {
   1034         // Since previous versions didn't check for Content-Encoding header,
   1035         // ignore any invalid values to preserve backwards-compatibility
   1036         if (contentEncoding !== 'identity') {
   1037           debug('ignoring unrecognized Content-Encoding ' + contentEncoding)
   1038         }
   1039         responseContent = response
   1040       }
   1041     } else {
   1042       responseContent = response
   1043     }
   1044 
   1045     if (self.encoding) {
   1046       if (self.dests.length !== 0) {
   1047         console.error('Ignoring encoding parameter as this stream is being piped to another stream which makes the encoding option invalid.')
   1048       } else {
   1049         responseContent.setEncoding(self.encoding)
   1050       }
   1051     }
   1052 
   1053     if (self._paused) {
   1054       responseContent.pause()
   1055     }
   1056 
   1057     self.responseContent = responseContent
   1058 
   1059     self.emit('response', response)
   1060 
   1061     self.dests.forEach(function (dest) {
   1062       self.pipeDest(dest)
   1063     })
   1064 
   1065     responseContent.on('data', function (chunk) {
   1066       if (self.timing && !self.responseStarted) {
   1067         self.responseStartTime = (new Date()).getTime()
   1068 
   1069         // NOTE: responseStartTime is deprecated in favor of .timings
   1070         response.responseStartTime = self.responseStartTime
   1071       }
   1072       self._destdata = true
   1073       self.emit('data', chunk)
   1074     })
   1075     responseContent.once('end', function (chunk) {
   1076       self.emit('end', chunk)
   1077     })
   1078     responseContent.on('error', function (error) {
   1079       self.emit('error', error)
   1080     })
   1081     responseContent.on('close', function () { self.emit('close') })
   1082 
   1083     if (self.callback) {
   1084       self.readResponseBody(response)
   1085     } else { // if no callback
   1086       self.on('end', function () {
   1087         if (self._aborted) {
   1088           debug('aborted', self.uri.href)
   1089           return
   1090         }
   1091         self.emit('complete', response)
   1092       })
   1093     }
   1094   }
   1095   debug('finish init function', self.uri.href)
   1096 }
   1097 
   1098 Request.prototype.readResponseBody = function (response) {
   1099   var self = this
   1100   debug("reading response's body")
   1101   var buffers = []
   1102   var bufferLength = 0
   1103   var strings = []
   1104 
   1105   self.on('data', function (chunk) {
   1106     if (!Buffer.isBuffer(chunk)) {
   1107       strings.push(chunk)
   1108     } else if (chunk.length) {
   1109       bufferLength += chunk.length
   1110       buffers.push(chunk)
   1111     }
   1112   })
   1113   self.on('end', function () {
   1114     debug('end event', self.uri.href)
   1115     if (self._aborted) {
   1116       debug('aborted', self.uri.href)
   1117       // `buffer` is defined in the parent scope and used in a closure it exists for the life of the request.
   1118       // This can lead to leaky behavior if the user retains a reference to the request object.
   1119       buffers = []
   1120       bufferLength = 0
   1121       return
   1122     }
   1123 
   1124     if (bufferLength) {
   1125       debug('has body', self.uri.href, bufferLength)
   1126       response.body = Buffer.concat(buffers, bufferLength)
   1127       if (self.encoding !== null) {
   1128         response.body = response.body.toString(self.encoding)
   1129       }
   1130       // `buffer` is defined in the parent scope and used in a closure it exists for the life of the Request.
   1131       // This can lead to leaky behavior if the user retains a reference to the request object.
   1132       buffers = []
   1133       bufferLength = 0
   1134     } else if (strings.length) {
   1135       // The UTF8 BOM [0xEF,0xBB,0xBF] is converted to [0xFE,0xFF] in the JS UTC16/UCS2 representation.
   1136       // Strip this value out when the encoding is set to 'utf8', as upstream consumers won't expect it and it breaks JSON.parse().
   1137       if (self.encoding === 'utf8' && strings[0].length > 0 && strings[0][0] === '\uFEFF') {
   1138         strings[0] = strings[0].substring(1)
   1139       }
   1140       response.body = strings.join('')
   1141     }
   1142 
   1143     if (self._json) {
   1144       try {
   1145         response.body = JSON.parse(response.body, self._jsonReviver)
   1146       } catch (e) {
   1147         debug('invalid JSON received', self.uri.href)
   1148       }
   1149     }
   1150     debug('emitting complete', self.uri.href)
   1151     if (typeof response.body === 'undefined' && !self._json) {
   1152       response.body = self.encoding === null ? Buffer.alloc(0) : ''
   1153     }
   1154     self.emit('complete', response, response.body)
   1155   })
   1156 }
   1157 
   1158 Request.prototype.abort = function () {
   1159   var self = this
   1160   self._aborted = true
   1161 
   1162   if (self.req) {
   1163     self.req.abort()
   1164   } else if (self.response) {
   1165     self.response.destroy()
   1166   }
   1167 
   1168   self.clearTimeout()
   1169   self.emit('abort')
   1170 }
   1171 
   1172 Request.prototype.pipeDest = function (dest) {
   1173   var self = this
   1174   var response = self.response
   1175   // Called after the response is received
   1176   if (dest.headers && !dest.headersSent) {
   1177     if (response.caseless.has('content-type')) {
   1178       var ctname = response.caseless.has('content-type')
   1179       if (dest.setHeader) {
   1180         dest.setHeader(ctname, response.headers[ctname])
   1181       } else {
   1182         dest.headers[ctname] = response.headers[ctname]
   1183       }
   1184     }
   1185 
   1186     if (response.caseless.has('content-length')) {
   1187       var clname = response.caseless.has('content-length')
   1188       if (dest.setHeader) {
   1189         dest.setHeader(clname, response.headers[clname])
   1190       } else {
   1191         dest.headers[clname] = response.headers[clname]
   1192       }
   1193     }
   1194   }
   1195   if (dest.setHeader && !dest.headersSent) {
   1196     for (var i in response.headers) {
   1197       // If the response content is being decoded, the Content-Encoding header
   1198       // of the response doesn't represent the piped content, so don't pass it.
   1199       if (!self.gzip || i !== 'content-encoding') {
   1200         dest.setHeader(i, response.headers[i])
   1201       }
   1202     }
   1203     dest.statusCode = response.statusCode
   1204   }
   1205   if (self.pipefilter) {
   1206     self.pipefilter(response, dest)
   1207   }
   1208 }
   1209 
   1210 Request.prototype.qs = function (q, clobber) {
   1211   var self = this
   1212   var base
   1213   if (!clobber && self.uri.query) {
   1214     base = self._qs.parse(self.uri.query)
   1215   } else {
   1216     base = {}
   1217   }
   1218 
   1219   for (var i in q) {
   1220     base[i] = q[i]
   1221   }
   1222 
   1223   var qs = self._qs.stringify(base)
   1224 
   1225   if (qs === '') {
   1226     return self
   1227   }
   1228 
   1229   self.uri = url.parse(self.uri.href.split('?')[0] + '?' + qs)
   1230   self.url = self.uri
   1231   self.path = self.uri.path
   1232 
   1233   if (self.uri.host === 'unix') {
   1234     self.enableUnixSocket()
   1235   }
   1236 
   1237   return self
   1238 }
   1239 Request.prototype.form = function (form) {
   1240   var self = this
   1241   if (form) {
   1242     if (!/^application\/x-www-form-urlencoded\b/.test(self.getHeader('content-type'))) {
   1243       self.setHeader('content-type', 'application/x-www-form-urlencoded')
   1244     }
   1245     self.body = (typeof form === 'string')
   1246       ? self._qs.rfc3986(form.toString('utf8'))
   1247       : self._qs.stringify(form).toString('utf8')
   1248     return self
   1249   }
   1250   // create form-data object
   1251   self._form = new FormData()
   1252   self._form.on('error', function (err) {
   1253     err.message = 'form-data: ' + err.message
   1254     self.emit('error', err)
   1255     self.abort()
   1256   })
   1257   return self._form
   1258 }
   1259 Request.prototype.multipart = function (multipart) {
   1260   var self = this
   1261 
   1262   self._multipart.onRequest(multipart)
   1263 
   1264   if (!self._multipart.chunked) {
   1265     self.body = self._multipart.body
   1266   }
   1267 
   1268   return self
   1269 }
   1270 Request.prototype.json = function (val) {
   1271   var self = this
   1272 
   1273   if (!self.hasHeader('accept')) {
   1274     self.setHeader('accept', 'application/json')
   1275   }
   1276 
   1277   if (typeof self.jsonReplacer === 'function') {
   1278     self._jsonReplacer = self.jsonReplacer
   1279   }
   1280 
   1281   self._json = true
   1282   if (typeof val === 'boolean') {
   1283     if (self.body !== undefined) {
   1284       if (!/^application\/x-www-form-urlencoded\b/.test(self.getHeader('content-type'))) {
   1285         self.body = safeStringify(self.body, self._jsonReplacer)
   1286       } else {
   1287         self.body = self._qs.rfc3986(self.body)
   1288       }
   1289       if (!self.hasHeader('content-type')) {
   1290         self.setHeader('content-type', 'application/json')
   1291       }
   1292     }
   1293   } else {
   1294     self.body = safeStringify(val, self._jsonReplacer)
   1295     if (!self.hasHeader('content-type')) {
   1296       self.setHeader('content-type', 'application/json')
   1297     }
   1298   }
   1299 
   1300   if (typeof self.jsonReviver === 'function') {
   1301     self._jsonReviver = self.jsonReviver
   1302   }
   1303 
   1304   return self
   1305 }
   1306 Request.prototype.getHeader = function (name, headers) {
   1307   var self = this
   1308   var result, re, match
   1309   if (!headers) {
   1310     headers = self.headers
   1311   }
   1312   Object.keys(headers).forEach(function (key) {
   1313     if (key.length !== name.length) {
   1314       return
   1315     }
   1316     re = new RegExp(name, 'i')
   1317     match = key.match(re)
   1318     if (match) {
   1319       result = headers[key]
   1320     }
   1321   })
   1322   return result
   1323 }
   1324 Request.prototype.enableUnixSocket = function () {
   1325   // Get the socket & request paths from the URL
   1326   var unixParts = this.uri.path.split(':')
   1327   var host = unixParts[0]
   1328   var path = unixParts[1]
   1329   // Apply unix properties to request
   1330   this.socketPath = host
   1331   this.uri.pathname = path
   1332   this.uri.path = path
   1333   this.uri.host = host
   1334   this.uri.hostname = host
   1335   this.uri.isUnix = true
   1336 }
   1337 
   1338 Request.prototype.auth = function (user, pass, sendImmediately, bearer) {
   1339   var self = this
   1340 
   1341   self._auth.onRequest(user, pass, sendImmediately, bearer)
   1342 
   1343   return self
   1344 }
   1345 Request.prototype.aws = function (opts, now) {
   1346   var self = this
   1347 
   1348   if (!now) {
   1349     self._aws = opts
   1350     return self
   1351   }
   1352 
   1353   if (opts.sign_version === 4 || opts.sign_version === '4') {
   1354     // use aws4
   1355     var options = {
   1356       host: self.uri.host,
   1357       path: self.uri.path,
   1358       method: self.method,
   1359       headers: self.headers,
   1360       body: self.body
   1361     }
   1362     if (opts.service) {
   1363       options.service = opts.service
   1364     }
   1365     var signRes = aws4.sign(options, {
   1366       accessKeyId: opts.key,
   1367       secretAccessKey: opts.secret,
   1368       sessionToken: opts.session
   1369     })
   1370     self.setHeader('authorization', signRes.headers.Authorization)
   1371     self.setHeader('x-amz-date', signRes.headers['X-Amz-Date'])
   1372     if (signRes.headers['X-Amz-Security-Token']) {
   1373       self.setHeader('x-amz-security-token', signRes.headers['X-Amz-Security-Token'])
   1374     }
   1375   } else {
   1376     // default: use aws-sign2
   1377     var date = new Date()
   1378     self.setHeader('date', date.toUTCString())
   1379     var auth = {
   1380       key: opts.key,
   1381       secret: opts.secret,
   1382       verb: self.method.toUpperCase(),
   1383       date: date,
   1384       contentType: self.getHeader('content-type') || '',
   1385       md5: self.getHeader('content-md5') || '',
   1386       amazonHeaders: aws2.canonicalizeHeaders(self.headers)
   1387     }
   1388     var path = self.uri.path
   1389     if (opts.bucket && path) {
   1390       auth.resource = '/' + opts.bucket + path
   1391     } else if (opts.bucket && !path) {
   1392       auth.resource = '/' + opts.bucket
   1393     } else if (!opts.bucket && path) {
   1394       auth.resource = path
   1395     } else if (!opts.bucket && !path) {
   1396       auth.resource = '/'
   1397     }
   1398     auth.resource = aws2.canonicalizeResource(auth.resource)
   1399     self.setHeader('authorization', aws2.authorization(auth))
   1400   }
   1401 
   1402   return self
   1403 }
   1404 Request.prototype.httpSignature = function (opts) {
   1405   var self = this
   1406   httpSignature.signRequest({
   1407     getHeader: function (header) {
   1408       return self.getHeader(header, self.headers)
   1409     },
   1410     setHeader: function (header, value) {
   1411       self.setHeader(header, value)
   1412     },
   1413     method: self.method,
   1414     path: self.path
   1415   }, opts)
   1416   debug('httpSignature authorization', self.getHeader('authorization'))
   1417 
   1418   return self
   1419 }
   1420 Request.prototype.hawk = function (opts) {
   1421   var self = this
   1422   self.setHeader('Authorization', hawk.header(self.uri, self.method, opts))
   1423 }
   1424 Request.prototype.oauth = function (_oauth) {
   1425   var self = this
   1426 
   1427   self._oauth.onRequest(_oauth)
   1428 
   1429   return self
   1430 }
   1431 
   1432 Request.prototype.jar = function (jar) {
   1433   var self = this
   1434   var cookies
   1435 
   1436   if (self._redirect.redirectsFollowed === 0) {
   1437     self.originalCookieHeader = self.getHeader('cookie')
   1438   }
   1439 
   1440   if (!jar) {
   1441     // disable cookies
   1442     cookies = false
   1443     self._disableCookies = true
   1444   } else {
   1445     var targetCookieJar = jar.getCookieString ? jar : globalCookieJar
   1446     var urihref = self.uri.href
   1447     // fetch cookie in the Specified host
   1448     if (targetCookieJar) {
   1449       cookies = targetCookieJar.getCookieString(urihref)
   1450     }
   1451   }
   1452 
   1453   // if need cookie and cookie is not empty
   1454   if (cookies && cookies.length) {
   1455     if (self.originalCookieHeader) {
   1456       // Don't overwrite existing Cookie header
   1457       self.setHeader('cookie', self.originalCookieHeader + '; ' + cookies)
   1458     } else {
   1459       self.setHeader('cookie', cookies)
   1460     }
   1461   }
   1462   self._jar = jar
   1463   return self
   1464 }
   1465 
   1466 // Stream API
   1467 Request.prototype.pipe = function (dest, opts) {
   1468   var self = this
   1469 
   1470   if (self.response) {
   1471     if (self._destdata) {
   1472       self.emit('error', new Error('You cannot pipe after data has been emitted from the response.'))
   1473     } else if (self._ended) {
   1474       self.emit('error', new Error('You cannot pipe after the response has been ended.'))
   1475     } else {
   1476       stream.Stream.prototype.pipe.call(self, dest, opts)
   1477       self.pipeDest(dest)
   1478       return dest
   1479     }
   1480   } else {
   1481     self.dests.push(dest)
   1482     stream.Stream.prototype.pipe.call(self, dest, opts)
   1483     return dest
   1484   }
   1485 }
   1486 Request.prototype.write = function () {
   1487   var self = this
   1488   if (self._aborted) { return }
   1489 
   1490   if (!self._started) {
   1491     self.start()
   1492   }
   1493   if (self.req) {
   1494     return self.req.write.apply(self.req, arguments)
   1495   }
   1496 }
   1497 Request.prototype.end = function (chunk) {
   1498   var self = this
   1499   if (self._aborted) { return }
   1500 
   1501   if (chunk) {
   1502     self.write(chunk)
   1503   }
   1504   if (!self._started) {
   1505     self.start()
   1506   }
   1507   if (self.req) {
   1508     self.req.end()
   1509   }
   1510 }
   1511 Request.prototype.pause = function () {
   1512   var self = this
   1513   if (!self.responseContent) {
   1514     self._paused = true
   1515   } else {
   1516     self.responseContent.pause.apply(self.responseContent, arguments)
   1517   }
   1518 }
   1519 Request.prototype.resume = function () {
   1520   var self = this
   1521   if (!self.responseContent) {
   1522     self._paused = false
   1523   } else {
   1524     self.responseContent.resume.apply(self.responseContent, arguments)
   1525   }
   1526 }
   1527 Request.prototype.destroy = function () {
   1528   var self = this
   1529   this.clearTimeout()
   1530   if (!self._ended) {
   1531     self.end()
   1532   } else if (self.response) {
   1533     self.response.destroy()
   1534   }
   1535 }
   1536 
   1537 Request.prototype.clearTimeout = function () {
   1538   if (this.timeoutTimer) {
   1539     clearTimeout(this.timeoutTimer)
   1540     this.timeoutTimer = null
   1541   }
   1542 }
   1543 
   1544 Request.defaultProxyHeaderWhiteList =
   1545   Tunnel.defaultProxyHeaderWhiteList.slice()
   1546 
   1547 Request.defaultProxyHeaderExclusiveList =
   1548   Tunnel.defaultProxyHeaderExclusiveList.slice()
   1549 
   1550 // Exports
   1551 
   1552 Request.prototype.toJSON = requestToJSON
   1553 module.exports = Request