Skip to content
Snippets Groups Projects
index.js 6.29 MiB
Newer Older
  • Learn to ignore specific revisions
  • 13001 13002 13003 13004 13005 13006 13007 13008 13009 13010 13011 13012 13013 13014 13015 13016 13017 13018 13019 13020 13021 13022 13023 13024 13025 13026 13027 13028 13029 13030 13031 13032 13033 13034 13035 13036 13037 13038 13039 13040 13041 13042 13043 13044 13045 13046 13047 13048 13049 13050 13051 13052 13053 13054 13055 13056 13057 13058 13059 13060 13061 13062 13063 13064 13065 13066 13067 13068 13069 13070 13071 13072 13073 13074 13075 13076 13077 13078 13079 13080 13081 13082 13083 13084 13085 13086 13087 13088 13089 13090 13091 13092 13093 13094 13095 13096 13097 13098 13099 13100 13101 13102 13103 13104 13105 13106 13107 13108 13109 13110 13111 13112 13113 13114 13115 13116 13117 13118 13119 13120 13121 13122 13123 13124 13125 13126 13127 13128 13129 13130 13131 13132 13133 13134 13135 13136 13137 13138 13139 13140 13141 13142 13143 13144 13145 13146 13147 13148 13149 13150 13151 13152 13153 13154 13155 13156 13157 13158 13159 13160 13161 13162 13163 13164 13165 13166 13167 13168 13169 13170 13171 13172 13173 13174 13175 13176 13177 13178 13179 13180 13181 13182 13183 13184 13185 13186 13187 13188 13189 13190 13191 13192 13193 13194 13195 13196 13197 13198 13199 13200 13201 13202 13203 13204 13205 13206 13207 13208 13209 13210 13211 13212 13213 13214 13215 13216 13217 13218 13219 13220 13221 13222 13223 13224 13225 13226 13227 13228 13229 13230 13231 13232 13233 13234 13235 13236 13237 13238 13239 13240 13241 13242 13243 13244 13245 13246 13247 13248 13249 13250 13251 13252 13253 13254 13255 13256 13257 13258 13259 13260 13261 13262 13263 13264 13265 13266 13267 13268 13269 13270 13271 13272 13273 13274 13275 13276 13277 13278 13279 13280 13281 13282 13283 13284 13285 13286 13287 13288 13289 13290 13291 13292 13293 13294 13295 13296 13297 13298 13299 13300 13301 13302 13303 13304 13305 13306 13307 13308 13309 13310 13311 13312 13313 13314 13315 13316 13317 13318 13319 13320 13321 13322 13323 13324 13325 13326 13327 13328 13329 13330 13331 13332 13333 13334 13335 13336 13337 13338 13339 13340 13341 13342 13343 13344 13345 13346 13347 13348 13349 13350 13351 13352 13353 13354 13355 13356 13357 13358 13359 13360 13361 13362 13363 13364 13365 13366 13367 13368 13369 13370 13371 13372 13373 13374 13375 13376 13377 13378 13379 13380 13381 13382 13383 13384 13385 13386 13387 13388 13389 13390 13391 13392 13393 13394 13395 13396 13397 13398 13399 13400 13401 13402 13403 13404 13405 13406 13407 13408 13409 13410 13411 13412 13413 13414 13415 13416 13417 13418 13419 13420 13421 13422 13423 13424 13425 13426 13427 13428 13429 13430 13431 13432 13433 13434 13435 13436 13437 13438 13439 13440 13441 13442 13443 13444 13445 13446 13447 13448 13449 13450 13451 13452 13453 13454 13455 13456 13457 13458 13459 13460 13461 13462 13463 13464 13465 13466 13467 13468 13469 13470 13471 13472 13473 13474 13475 13476 13477 13478 13479 13480 13481 13482 13483 13484 13485 13486 13487 13488 13489 13490 13491 13492 13493 13494 13495 13496 13497 13498 13499 13500 13501 13502 13503 13504 13505 13506 13507 13508 13509 13510 13511 13512 13513 13514 13515 13516 13517 13518 13519 13520 13521 13522 13523 13524 13525 13526 13527 13528 13529 13530 13531 13532 13533 13534 13535 13536 13537 13538 13539 13540 13541 13542 13543 13544 13545 13546 13547 13548 13549 13550 13551 13552 13553 13554 13555 13556 13557 13558 13559 13560 13561 13562 13563 13564 13565 13566 13567 13568 13569 13570 13571 13572 13573 13574 13575 13576 13577 13578 13579 13580 13581 13582 13583 13584 13585 13586 13587 13588 13589 13590 13591 13592 13593 13594 13595 13596 13597 13598 13599 13600 13601 13602 13603 13604 13605 13606 13607 13608 13609 13610 13611 13612 13613 13614 13615 13616 13617 13618 13619 13620 13621 13622 13623 13624 13625 13626 13627 13628 13629 13630 13631 13632 13633 13634 13635 13636 13637 13638 13639 13640 13641 13642 13643 13644 13645 13646 13647 13648 13649 13650 13651 13652 13653 13654 13655 13656 13657 13658 13659 13660 13661 13662 13663 13664 13665 13666 13667 13668 13669 13670 13671 13672 13673 13674 13675 13676 13677 13678 13679 13680 13681 13682 13683 13684 13685 13686 13687 13688 13689 13690 13691 13692 13693 13694 13695 13696 13697 13698 13699 13700 13701 13702 13703 13704 13705 13706 13707 13708 13709 13710 13711 13712 13713 13714 13715 13716 13717 13718 13719 13720 13721 13722 13723 13724 13725 13726 13727 13728 13729 13730 13731 13732 13733 13734 13735 13736 13737 13738 13739 13740 13741 13742 13743 13744 13745 13746 13747 13748 13749 13750 13751 13752 13753 13754 13755 13756 13757 13758 13759 13760 13761 13762 13763 13764 13765 13766 13767 13768 13769 13770 13771 13772 13773 13774 13775 13776 13777 13778 13779 13780 13781 13782 13783 13784 13785 13786 13787 13788 13789 13790 13791 13792 13793 13794 13795 13796 13797 13798 13799 13800 13801 13802 13803 13804 13805 13806 13807 13808 13809 13810 13811 13812 13813 13814 13815 13816 13817 13818 13819 13820 13821 13822 13823 13824 13825 13826 13827 13828 13829 13830 13831 13832 13833 13834 13835 13836 13837 13838 13839 13840 13841 13842 13843 13844 13845 13846 13847 13848 13849 13850 13851 13852 13853 13854 13855 13856 13857 13858 13859 13860 13861 13862 13863 13864 13865 13866 13867 13868 13869 13870 13871 13872 13873 13874 13875 13876 13877 13878 13879 13880 13881 13882 13883 13884 13885 13886 13887 13888 13889 13890 13891 13892 13893 13894 13895 13896 13897 13898 13899 13900 13901 13902 13903 13904 13905 13906 13907 13908 13909 13910 13911 13912 13913 13914 13915 13916 13917 13918 13919 13920 13921 13922 13923 13924 13925 13926 13927 13928 13929 13930 13931 13932 13933 13934 13935 13936 13937 13938 13939 13940 13941 13942 13943 13944 13945 13946 13947 13948 13949 13950 13951 13952 13953 13954 13955 13956 13957 13958 13959 13960 13961 13962 13963 13964 13965 13966 13967 13968 13969 13970 13971 13972 13973 13974 13975 13976 13977 13978 13979 13980 13981 13982 13983 13984 13985 13986 13987 13988 13989 13990 13991 13992 13993 13994 13995 13996 13997 13998 13999 14000
            length = self.body.reduce(function (a, b) { return a + b.length }, 0)
          } else {
            length = self.body.length
          }
    
          if (length) {
            self.setHeader('content-length', length)
          } else {
            self.emit('error', new Error('Argument error, options.body.'))
          }
        }
      }
      if (self.body && !isstream(self.body)) {
        setContentLength()
      }
    
      if (options.oauth) {
        self.oauth(options.oauth)
      } else if (self._oauth.params && self.hasHeader('authorization')) {
        self.oauth(self._oauth.params)
      }
    
      var protocol = self.proxy && !self.tunnel ? self.proxy.protocol : self.uri.protocol
      var defaultModules = {'http:': http, 'https:': https}
      var httpModules = self.httpModules || {}
    
      self.httpModule = httpModules[protocol] || defaultModules[protocol]
    
      if (!self.httpModule) {
        return self.emit('error', new Error('Invalid protocol: ' + protocol))
      }
    
      if (options.ca) {
        self.ca = options.ca
      }
    
      if (!self.agent) {
        if (options.agentOptions) {
          self.agentOptions = options.agentOptions
        }
    
        if (options.agentClass) {
          self.agentClass = options.agentClass
        } else if (options.forever) {
          var v = version()
          // use ForeverAgent in node 0.10- only
          if (v.major === 0 && v.minor <= 10) {
            self.agentClass = protocol === 'http:' ? ForeverAgent : ForeverAgent.SSL
          } else {
            self.agentClass = self.httpModule.Agent
            self.agentOptions = self.agentOptions || {}
            self.agentOptions.keepAlive = true
          }
        } else {
          self.agentClass = self.httpModule.Agent
        }
      }
    
      if (self.pool === false) {
        self.agent = false
      } else {
        self.agent = self.agent || self.getNewAgent()
      }
    
      self.on('pipe', function (src) {
        if (self.ntick && self._started) {
          self.emit('error', new Error('You cannot pipe to this stream after the outbound request has started.'))
        }
        self.src = src
        if (isReadStream(src)) {
          if (!self.hasHeader('content-type')) {
            self.setHeader('content-type', mime.lookup(src.path))
          }
        } else {
          if (src.headers) {
            for (var i in src.headers) {
              if (!self.hasHeader(i)) {
                self.setHeader(i, src.headers[i])
              }
            }
          }
          if (self._json && !self.hasHeader('content-type')) {
            self.setHeader('content-type', 'application/json')
          }
          if (src.method && !self.explicitMethod) {
            self.method = src.method
          }
        }
    
      // self.on('pipe', function () {
      //   console.error('You have already piped to this stream. Pipeing twice is likely to break the request.')
      // })
      })
    
      defer(function () {
        if (self._aborted) {
          return
        }
    
        var end = function () {
          if (self._form) {
            if (!self._auth.hasAuth) {
              self._form.pipe(self)
            } else if (self._auth.hasAuth && self._auth.sentAuth) {
              self._form.pipe(self)
            }
          }
          if (self._multipart && self._multipart.chunked) {
            self._multipart.body.pipe(self)
          }
          if (self.body) {
            if (isstream(self.body)) {
              self.body.pipe(self)
            } else {
              setContentLength()
              if (Array.isArray(self.body)) {
                self.body.forEach(function (part) {
                  self.write(part)
                })
              } else {
                self.write(self.body)
              }
              self.end()
            }
          } else if (self.requestBodyStream) {
            console.warn('options.requestBodyStream is deprecated, please pass the request object to stream.pipe.')
            self.requestBodyStream.pipe(self)
          } else if (!self.src) {
            if (self._auth.hasAuth && !self._auth.sentAuth) {
              self.end()
              return
            }
            if (self.method !== 'GET' && typeof self.method !== 'undefined') {
              self.setHeader('content-length', 0)
            }
            self.end()
          }
        }
    
        if (self._form && !self.hasHeader('content-length')) {
          // Before ending the request, we had to compute the length of the whole form, asyncly
          self.setHeader(self._form.getHeaders(), true)
          self._form.getLength(function (err, length) {
            if (!err && !isNaN(length)) {
              self.setHeader('content-length', length)
            }
            end()
          })
        } else {
          end()
        }
    
        self.ntick = true
      })
    }
    
    Request.prototype.getNewAgent = function () {
      var self = this
      var Agent = self.agentClass
      var options = {}
      if (self.agentOptions) {
        for (var i in self.agentOptions) {
          options[i] = self.agentOptions[i]
        }
      }
      if (self.ca) {
        options.ca = self.ca
      }
      if (self.ciphers) {
        options.ciphers = self.ciphers
      }
      if (self.secureProtocol) {
        options.secureProtocol = self.secureProtocol
      }
      if (self.secureOptions) {
        options.secureOptions = self.secureOptions
      }
      if (typeof self.rejectUnauthorized !== 'undefined') {
        options.rejectUnauthorized = self.rejectUnauthorized
      }
    
      if (self.cert && self.key) {
        options.key = self.key
        options.cert = self.cert
      }
    
      if (self.pfx) {
        options.pfx = self.pfx
      }
    
      if (self.passphrase) {
        options.passphrase = self.passphrase
      }
    
      var poolKey = ''
    
      // different types of agents are in different pools
      if (Agent !== self.httpModule.Agent) {
        poolKey += Agent.name
      }
    
      // ca option is only relevant if proxy or destination are https
      var proxy = self.proxy
      if (typeof proxy === 'string') {
        proxy = url.parse(proxy)
      }
      var isHttps = (proxy && proxy.protocol === 'https:') || this.uri.protocol === 'https:'
    
      if (isHttps) {
        if (options.ca) {
          if (poolKey) {
            poolKey += ':'
          }
          poolKey += options.ca
        }
    
        if (typeof options.rejectUnauthorized !== 'undefined') {
          if (poolKey) {
            poolKey += ':'
          }
          poolKey += options.rejectUnauthorized
        }
    
        if (options.cert) {
          if (poolKey) {
            poolKey += ':'
          }
          poolKey += options.cert.toString('ascii') + options.key.toString('ascii')
        }
    
        if (options.pfx) {
          if (poolKey) {
            poolKey += ':'
          }
          poolKey += options.pfx.toString('ascii')
        }
    
        if (options.ciphers) {
          if (poolKey) {
            poolKey += ':'
          }
          poolKey += options.ciphers
        }
    
        if (options.secureProtocol) {
          if (poolKey) {
            poolKey += ':'
          }
          poolKey += options.secureProtocol
        }
    
        if (options.secureOptions) {
          if (poolKey) {
            poolKey += ':'
          }
          poolKey += options.secureOptions
        }
      }
    
      if (self.pool === globalPool && !poolKey && Object.keys(options).length === 0 && self.httpModule.globalAgent) {
        // not doing anything special.  Use the globalAgent
        return self.httpModule.globalAgent
      }
    
      // we're using a stored agent.  Make sure it's protocol-specific
      poolKey = self.uri.protocol + poolKey
    
      // generate a new agent for this setting if none yet exists
      if (!self.pool[poolKey]) {
        self.pool[poolKey] = new Agent(options)
        // properly set maxSockets on new agents
        if (self.pool.maxSockets) {
          self.pool[poolKey].maxSockets = self.pool.maxSockets
        }
      }
    
      return self.pool[poolKey]
    }
    
    Request.prototype.start = function () {
      // start() is called once we are ready to send the outgoing HTTP request.
      // this is usually called on the first write(), end() or on nextTick()
      var self = this
    
      if (self.timing) {
        // All timings will be relative to this request's startTime.  In order to do this,
        // we need to capture the wall-clock start time (via Date), immediately followed
        // by the high-resolution timer (via now()).  While these two won't be set
        // at the _exact_ same time, they should be close enough to be able to calculate
        // high-resolution, monotonically non-decreasing timestamps relative to startTime.
        var startTime = new Date().getTime()
        var startTimeNow = now()
      }
    
      if (self._aborted) {
        return
      }
    
      self._started = true
      self.method = self.method || 'GET'
      self.href = self.uri.href
    
      if (self.src && self.src.stat && self.src.stat.size && !self.hasHeader('content-length')) {
        self.setHeader('content-length', self.src.stat.size)
      }
      if (self._aws) {
        self.aws(self._aws, true)
      }
    
      // We have a method named auth, which is completely different from the http.request
      // auth option.  If we don't remove it, we're gonna have a bad time.
      var reqOptions = copy(self)
      delete reqOptions.auth
    
      debug('make request', self.uri.href)
    
      // node v6.8.0 now supports a `timeout` value in `http.request()`, but we
      // should delete it for now since we handle timeouts manually for better
      // consistency with node versions before v6.8.0
      delete reqOptions.timeout
    
      try {
        self.req = self.httpModule.request(reqOptions)
      } catch (err) {
        self.emit('error', err)
        return
      }
    
      if (self.timing) {
        self.startTime = startTime
        self.startTimeNow = startTimeNow
    
        // Timing values will all be relative to startTime (by comparing to startTimeNow
        // so we have an accurate clock)
        self.timings = {}
      }
    
      var timeout
      if (self.timeout && !self.timeoutTimer) {
        if (self.timeout < 0) {
          timeout = 0
        } else if (typeof self.timeout === 'number' && isFinite(self.timeout)) {
          timeout = self.timeout
        }
      }
    
      self.req.on('response', self.onRequestResponse.bind(self))
      self.req.on('error', self.onRequestError.bind(self))
      self.req.on('drain', function () {
        self.emit('drain')
      })
    
      self.req.on('socket', function (socket) {
        // `._connecting` was the old property which was made public in node v6.1.0
        var isConnecting = socket._connecting || socket.connecting
        if (self.timing) {
          self.timings.socket = now() - self.startTimeNow
    
          if (isConnecting) {
            var onLookupTiming = function () {
              self.timings.lookup = now() - self.startTimeNow
            }
    
            var onConnectTiming = function () {
              self.timings.connect = now() - self.startTimeNow
            }
    
            socket.once('lookup', onLookupTiming)
            socket.once('connect', onConnectTiming)
    
            // clean up timing event listeners if needed on error
            self.req.once('error', function () {
              socket.removeListener('lookup', onLookupTiming)
              socket.removeListener('connect', onConnectTiming)
            })
          }
        }
    
        var setReqTimeout = function () {
          // This timeout sets the amount of time to wait *between* bytes sent
          // from the server once connected.
          //
          // In particular, it's useful for erroring if the server fails to send
          // data halfway through streaming a response.
          self.req.setTimeout(timeout, function () {
            if (self.req) {
              self.abort()
              var e = new Error('ESOCKETTIMEDOUT')
              e.code = 'ESOCKETTIMEDOUT'
              e.connect = false
              self.emit('error', e)
            }
          })
        }
        if (timeout !== undefined) {
          // Only start the connection timer if we're actually connecting a new
          // socket, otherwise if we're already connected (because this is a
          // keep-alive connection) do not bother. This is important since we won't
          // get a 'connect' event for an already connected socket.
          if (isConnecting) {
            var onReqSockConnect = function () {
              socket.removeListener('connect', onReqSockConnect)
              clearTimeout(self.timeoutTimer)
              self.timeoutTimer = null
              setReqTimeout()
            }
    
            socket.on('connect', onReqSockConnect)
    
            self.req.on('error', function (err) { // eslint-disable-line handle-callback-err
              socket.removeListener('connect', onReqSockConnect)
            })
    
            // Set a timeout in memory - this block will throw if the server takes more
            // than `timeout` to write the HTTP status and headers (corresponding to
            // the on('response') event on the client). NB: this measures wall-clock
            // time, not the time between bytes sent by the server.
            self.timeoutTimer = setTimeout(function () {
              socket.removeListener('connect', onReqSockConnect)
              self.abort()
              var e = new Error('ETIMEDOUT')
              e.code = 'ETIMEDOUT'
              e.connect = true
              self.emit('error', e)
            }, timeout)
          } else {
            // We're already connected
            setReqTimeout()
          }
        }
        self.emit('socket', socket)
      })
    
      self.emit('request', self.req)
    }
    
    Request.prototype.onRequestError = function (error) {
      var self = this
      if (self._aborted) {
        return
      }
      if (self.req && self.req._reusedSocket && error.code === 'ECONNRESET' &&
        self.agent.addRequestNoreuse) {
        self.agent = { addRequest: self.agent.addRequestNoreuse.bind(self.agent) }
        self.start()
        self.req.end()
        return
      }
      if (self.timeout && self.timeoutTimer) {
        clearTimeout(self.timeoutTimer)
        self.timeoutTimer = null
      }
      self.emit('error', error)
    }
    
    Request.prototype.onRequestResponse = function (response) {
      var self = this
    
      if (self.timing) {
        self.timings.response = now() - self.startTimeNow
      }
    
      debug('onRequestResponse', self.uri.href, response.statusCode, response.headers)
      response.on('end', function () {
        if (self.timing) {
          self.timings.end = now() - self.startTimeNow
          response.timingStart = self.startTime
    
          // fill in the blanks for any periods that didn't trigger, such as
          // no lookup or connect due to keep alive
          if (!self.timings.socket) {
            self.timings.socket = 0
          }
          if (!self.timings.lookup) {
            self.timings.lookup = self.timings.socket
          }
          if (!self.timings.connect) {
            self.timings.connect = self.timings.lookup
          }
          if (!self.timings.response) {
            self.timings.response = self.timings.connect
          }
    
          debug('elapsed time', self.timings.end)
    
          // elapsedTime includes all redirects
          self.elapsedTime += Math.round(self.timings.end)
    
          // NOTE: elapsedTime is deprecated in favor of .timings
          response.elapsedTime = self.elapsedTime
    
          // timings is just for the final fetch
          response.timings = self.timings
    
          // pre-calculate phase timings as well
          response.timingPhases = {
            wait: self.timings.socket,
            dns: self.timings.lookup - self.timings.socket,
            tcp: self.timings.connect - self.timings.lookup,
            firstByte: self.timings.response - self.timings.connect,
            download: self.timings.end - self.timings.response,
            total: self.timings.end
          }
        }
        debug('response end', self.uri.href, response.statusCode, response.headers)
      })
    
      if (self._aborted) {
        debug('aborted', self.uri.href)
        response.resume()
        return
      }
    
      self.response = response
      response.request = self
      response.toJSON = responseToJSON
    
      // XXX This is different on 0.10, because SSL is strict by default
      if (self.httpModule === https &&
        self.strictSSL && (!response.hasOwnProperty('socket') ||
        !response.socket.authorized)) {
        debug('strict ssl error', self.uri.href)
        var sslErr = response.hasOwnProperty('socket') ? response.socket.authorizationError : self.uri.href + ' does not support SSL'
        self.emit('error', new Error('SSL Error: ' + sslErr))
        return
      }
    
      // Save the original host before any redirect (if it changes, we need to
      // remove any authorization headers).  Also remember the case of the header
      // name because lots of broken servers expect Host instead of host and we
      // want the caller to be able to specify this.
      self.originalHost = self.getHeader('host')
      if (!self.originalHostHeaderName) {
        self.originalHostHeaderName = self.hasHeader('host')
      }
      if (self.setHost) {
        self.removeHeader('host')
      }
      if (self.timeout && self.timeoutTimer) {
        clearTimeout(self.timeoutTimer)
        self.timeoutTimer = null
      }
    
      var targetCookieJar = (self._jar && self._jar.setCookie) ? self._jar : globalCookieJar
      var addCookie = function (cookie) {
        // set the cookie if it's domain in the href's domain.
        try {
          targetCookieJar.setCookie(cookie, self.uri.href, {ignoreError: true})
        } catch (e) {
          self.emit('error', e)
        }
      }
    
      response.caseless = caseless(response.headers)
    
      if (response.caseless.has('set-cookie') && (!self._disableCookies)) {
        var headerName = response.caseless.has('set-cookie')
        if (Array.isArray(response.headers[headerName])) {
          response.headers[headerName].forEach(addCookie)
        } else {
          addCookie(response.headers[headerName])
        }
      }
    
      if (self._redirect.onResponse(response)) {
        return // Ignore the rest of the response
      } else {
        // Be a good stream and emit end when the response is finished.
        // Hack to emit end on close because of a core bug that never fires end
        response.on('close', function () {
          if (!self._ended) {
            self.response.emit('end')
          }
        })
    
        response.once('end', function () {
          self._ended = true
        })
    
        var noBody = function (code) {
          return (
            self.method === 'HEAD' ||
            // Informational
            (code >= 100 && code < 200) ||
            // No Content
            code === 204 ||
            // Not Modified
            code === 304
          )
        }
    
        var responseContent
        if (self.gzip && !noBody(response.statusCode)) {
          var contentEncoding = response.headers['content-encoding'] || 'identity'
          contentEncoding = contentEncoding.trim().toLowerCase()
    
          // Be more lenient with decoding compressed responses, since (very rarely)
          // servers send slightly invalid gzip responses that are still accepted
          // by common browsers.
          // Always using Z_SYNC_FLUSH is what cURL does.
          var zlibOptions = {
            flush: zlib.Z_SYNC_FLUSH,
            finishFlush: zlib.Z_SYNC_FLUSH
          }
    
          if (contentEncoding === 'gzip') {
            responseContent = zlib.createGunzip(zlibOptions)
            response.pipe(responseContent)
          } else if (contentEncoding === 'deflate') {
            responseContent = zlib.createInflate(zlibOptions)
            response.pipe(responseContent)
          } else {
            // Since previous versions didn't check for Content-Encoding header,
            // ignore any invalid values to preserve backwards-compatibility
            if (contentEncoding !== 'identity') {
              debug('ignoring unrecognized Content-Encoding ' + contentEncoding)
            }
            responseContent = response
          }
        } else {
          responseContent = response
        }
    
        if (self.encoding) {
          if (self.dests.length !== 0) {
            console.error('Ignoring encoding parameter as this stream is being piped to another stream which makes the encoding option invalid.')
          } else {
            responseContent.setEncoding(self.encoding)
          }
        }
    
        if (self._paused) {
          responseContent.pause()
        }
    
        self.responseContent = responseContent
    
        self.emit('response', response)
    
        self.dests.forEach(function (dest) {
          self.pipeDest(dest)
        })
    
        responseContent.on('data', function (chunk) {
          if (self.timing && !self.responseStarted) {
            self.responseStartTime = (new Date()).getTime()
    
            // NOTE: responseStartTime is deprecated in favor of .timings
            response.responseStartTime = self.responseStartTime
          }
          self._destdata = true
          self.emit('data', chunk)
        })
        responseContent.once('end', function (chunk) {
          self.emit('end', chunk)
        })
        responseContent.on('error', function (error) {
          self.emit('error', error)
        })
        responseContent.on('close', function () { self.emit('close') })
    
        if (self.callback) {
          self.readResponseBody(response)
        } else { // if no callback
          self.on('end', function () {
            if (self._aborted) {
              debug('aborted', self.uri.href)
              return
            }
            self.emit('complete', response)
          })
        }
      }
      debug('finish init function', self.uri.href)
    }
    
    Request.prototype.readResponseBody = function (response) {
      var self = this
      debug("reading response's body")
      var buffers = []
      var bufferLength = 0
      var strings = []
    
      self.on('data', function (chunk) {
        if (!Buffer.isBuffer(chunk)) {
          strings.push(chunk)
        } else if (chunk.length) {
          bufferLength += chunk.length
          buffers.push(chunk)
        }
      })
      self.on('end', function () {
        debug('end event', self.uri.href)
        if (self._aborted) {
          debug('aborted', self.uri.href)
          // `buffer` is defined in the parent scope and used in a closure it exists for the life of the request.
          // This can lead to leaky behavior if the user retains a reference to the request object.
          buffers = []
          bufferLength = 0
          return
        }
    
        if (bufferLength) {
          debug('has body', self.uri.href, bufferLength)
          response.body = Buffer.concat(buffers, bufferLength)
          if (self.encoding !== null) {
            response.body = response.body.toString(self.encoding)
          }
          // `buffer` is defined in the parent scope and used in a closure it exists for the life of the Request.
          // This can lead to leaky behavior if the user retains a reference to the request object.
          buffers = []
          bufferLength = 0
        } else if (strings.length) {
          // The UTF8 BOM [0xEF,0xBB,0xBF] is converted to [0xFE,0xFF] in the JS UTC16/UCS2 representation.
          // Strip this value out when the encoding is set to 'utf8', as upstream consumers won't expect it and it breaks JSON.parse().
          if (self.encoding === 'utf8' && strings[0].length > 0 && strings[0][0] === '\uFEFF') {
            strings[0] = strings[0].substring(1)
          }
          response.body = strings.join('')
        }
    
        if (self._json) {
          try {
            response.body = JSON.parse(response.body, self._jsonReviver)
          } catch (e) {
            debug('invalid JSON received', self.uri.href)
          }
        }
        debug('emitting complete', self.uri.href)
        if (typeof response.body === 'undefined' && !self._json) {
          response.body = self.encoding === null ? Buffer.alloc(0) : ''
        }
        self.emit('complete', response, response.body)
      })
    }
    
    Request.prototype.abort = function () {
      var self = this
      self._aborted = true
    
      if (self.req) {
        self.req.abort()
      } else if (self.response) {
        self.response.destroy()
      }
    
      self.emit('abort')
    }
    
    Request.prototype.pipeDest = function (dest) {
      var self = this
      var response = self.response
      // Called after the response is received
      if (dest.headers && !dest.headersSent) {
        if (response.caseless.has('content-type')) {
          var ctname = response.caseless.has('content-type')
          if (dest.setHeader) {
            dest.setHeader(ctname, response.headers[ctname])
          } else {
            dest.headers[ctname] = response.headers[ctname]
          }
        }
    
        if (response.caseless.has('content-length')) {
          var clname = response.caseless.has('content-length')
          if (dest.setHeader) {
            dest.setHeader(clname, response.headers[clname])
          } else {
            dest.headers[clname] = response.headers[clname]
          }
        }
      }
      if (dest.setHeader && !dest.headersSent) {
        for (var i in response.headers) {
          // If the response content is being decoded, the Content-Encoding header
          // of the response doesn't represent the piped content, so don't pass it.
          if (!self.gzip || i !== 'content-encoding') {
            dest.setHeader(i, response.headers[i])
          }
        }
        dest.statusCode = response.statusCode
      }
      if (self.pipefilter) {
        self.pipefilter(response, dest)
      }
    }
    
    Request.prototype.qs = function (q, clobber) {
      var self = this
      var base
      if (!clobber && self.uri.query) {
        base = self._qs.parse(self.uri.query)
      } else {
        base = {}
      }
    
      for (var i in q) {
        base[i] = q[i]
      }
    
      var qs = self._qs.stringify(base)
    
      if (qs === '') {
        return self
      }
    
      self.uri = url.parse(self.uri.href.split('?')[0] + '?' + qs)
      self.url = self.uri
      self.path = self.uri.path
    
      if (self.uri.host === 'unix') {
        self.enableUnixSocket()
      }
    
      return self
    }
    Request.prototype.form = function (form) {
      var self = this
      if (form) {
        if (!/^application\/x-www-form-urlencoded\b/.test(self.getHeader('content-type'))) {
          self.setHeader('content-type', 'application/x-www-form-urlencoded')
        }
        self.body = (typeof form === 'string')
          ? self._qs.rfc3986(form.toString('utf8'))
          : self._qs.stringify(form).toString('utf8')
        return self
      }
      // create form-data object
      self._form = new FormData()
      self._form.on('error', function (err) {
        err.message = 'form-data: ' + err.message
        self.emit('error', err)
        self.abort()
      })
      return self._form
    }
    Request.prototype.multipart = function (multipart) {
      var self = this
    
      self._multipart.onRequest(multipart)
    
      if (!self._multipart.chunked) {
        self.body = self._multipart.body
      }
    
      return self
    }
    Request.prototype.json = function (val) {
      var self = this
    
      if (!self.hasHeader('accept')) {
        self.setHeader('accept', 'application/json')
      }
    
      if (typeof self.jsonReplacer === 'function') {
        self._jsonReplacer = self.jsonReplacer
      }
    
      self._json = true
      if (typeof val === 'boolean') {
        if (self.body !== undefined) {
          if (!/^application\/x-www-form-urlencoded\b/.test(self.getHeader('content-type'))) {
            self.body = safeStringify(self.body, self._jsonReplacer)
          } else {
            self.body = self._qs.rfc3986(self.body)
          }
          if (!self.hasHeader('content-type')) {
            self.setHeader('content-type', 'application/json')
          }
        }
      } else {
        self.body = safeStringify(val, self._jsonReplacer)
        if (!self.hasHeader('content-type')) {
          self.setHeader('content-type', 'application/json')
        }
      }
    
      if (typeof self.jsonReviver === 'function') {
        self._jsonReviver = self.jsonReviver
      }
    
      return self
    }
    Request.prototype.getHeader = function (name, headers) {
      var self = this
      var result, re, match
      if (!headers) {
        headers = self.headers
      }
      Object.keys(headers).forEach(function (key) {
        if (key.length !== name.length) {
          return
        }
        re = new RegExp(name, 'i')
        match = key.match(re)
        if (match) {
          result = headers[key]
        }
      })
      return result
    }
    Request.prototype.enableUnixSocket = function () {
      // Get the socket & request paths from the URL
      var unixParts = this.uri.path.split(':')
      var host = unixParts[0]
      var path = unixParts[1]
      // Apply unix properties to request
      this.socketPath = host
      this.uri.pathname = path
      this.uri.path = path
      this.uri.host = host
      this.uri.hostname = host
      this.uri.isUnix = true
    }
    
    Request.prototype.auth = function (user, pass, sendImmediately, bearer) {
      var self = this
    
      self._auth.onRequest(user, pass, sendImmediately, bearer)
    
      return self
    }
    Request.prototype.aws = function (opts, now) {
      var self = this
    
      if (!now) {
        self._aws = opts
        return self
      }
    
      if (opts.sign_version === 4 || opts.sign_version === '4') {
        // use aws4
        var options = {
          host: self.uri.host,
          path: self.uri.path,
          method: self.method,
          headers: self.headers,
          body: self.body
        }
        if (opts.service) {
          options.service = opts.service
        }
        var signRes = aws4.sign(options, {
          accessKeyId: opts.key,
          secretAccessKey: opts.secret,
          sessionToken: opts.session
        })
        self.setHeader('authorization', signRes.headers.Authorization)
        self.setHeader('x-amz-date', signRes.headers['X-Amz-Date'])
        if (signRes.headers['X-Amz-Security-Token']) {
          self.setHeader('x-amz-security-token', signRes.headers['X-Amz-Security-Token'])
        }
      } else {
        // default: use aws-sign2
        var date = new Date()
        self.setHeader('date', date.toUTCString())
        var auth = {
          key: opts.key,
          secret: opts.secret,
          verb: self.method.toUpperCase(),
          date: date,
          contentType: self.getHeader('content-type') || '',
          md5: self.getHeader('content-md5') || '',
          amazonHeaders: aws2.canonicalizeHeaders(self.headers)
        }
        var path = self.uri.path
        if (opts.bucket && path) {
          auth.resource = '/' + opts.bucket + path
        } else if (opts.bucket && !path) {
          auth.resource = '/' + opts.bucket
        } else if (!opts.bucket && path) {
          auth.resource = path
        } else if (!opts.bucket && !path) {
          auth.resource = '/'
        }
        auth.resource = aws2.canonicalizeResource(auth.resource)
        self.setHeader('authorization', aws2.authorization(auth))
      }
    
      return self
    }
    Request.prototype.httpSignature = function (opts) {
      var self = this
      httpSignature.signRequest({
        getHeader: function (header) {
          return self.getHeader(header, self.headers)
        },
        setHeader: function (header, value) {
          self.setHeader(header, value)
        },
        method: self.method,
        path: self.path
      }, opts)
      debug('httpSignature authorization', self.getHeader('authorization'))
    
      return self
    }
    Request.prototype.hawk = function (opts) {
      var self = this
      self.setHeader('Authorization', hawk.header(self.uri, self.method, opts))