Skip to content
Snippets Groups Projects
index.js 8.75 MiB
Newer Older
  • Learn to ignore specific revisions
  • Romain CREY's avatar
    Romain CREY committed
      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)
    
              self.clearTimeout()
    
    Romain CREY's avatar
    Romain CREY committed
              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
      }
    
      self.clearTimeout()
    
    Romain CREY's avatar
    Romain CREY committed
      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')
      }
    
      self.clearTimeout()
    
    Romain CREY's avatar
    Romain CREY committed
    
      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.clearTimeout()
    
    Romain CREY's avatar
    Romain CREY committed
    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
      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))
    }
    Request.prototype.oauth = function (_oauth) {
      var self = this
    
      self._oauth.onRequest(_oauth)
    
      return self
    }
    
    Request.prototype.jar = function (jar) {
      var self = this
      var cookies
    
      if (self._redirect.redirectsFollowed === 0) {
        self.originalCookieHeader = self.getHeader('cookie')
      }
    
      if (!jar) {
        // disable cookies
        cookies = false
        self._disableCookies = true
      } else {
    
        var targetCookieJar = jar.getCookieString ? jar : globalCookieJar
    
    Romain CREY's avatar
    Romain CREY committed
        var urihref = self.uri.href
        // fetch cookie in the Specified host
        if (targetCookieJar) {
          cookies = targetCookieJar.getCookieString(urihref)
        }
      }
    
      // if need cookie and cookie is not empty
      if (cookies && cookies.length) {
        if (self.originalCookieHeader) {
          // Don't overwrite existing Cookie header
          self.setHeader('cookie', self.originalCookieHeader + '; ' + cookies)
        } else {
          self.setHeader('cookie', cookies)
        }
      }
      self._jar = jar
      return self
    }
    
    // Stream API
    Request.prototype.pipe = function (dest, opts) {
      var self = this
    
      if (self.response) {
        if (self._destdata) {
          self.emit('error', new Error('You cannot pipe after data has been emitted from the response.'))
        } else if (self._ended) {
          self.emit('error', new Error('You cannot pipe after the response has been ended.'))
        } else {
          stream.Stream.prototype.pipe.call(self, dest, opts)
          self.pipeDest(dest)
          return dest
        }
      } else {
        self.dests.push(dest)
        stream.Stream.prototype.pipe.call(self, dest, opts)
        return dest
      }
    }
    Request.prototype.write = function () {
      var self = this
      if (self._aborted) { return }
    
      if (!self._started) {
        self.start()
      }
      if (self.req) {
        return self.req.write.apply(self.req, arguments)
      }
    }
    Request.prototype.end = function (chunk) {
      var self = this
      if (self._aborted) { return }
    
      if (chunk) {
        self.write(chunk)
      }
      if (!self._started) {
        self.start()
      }
      if (self.req) {
        self.req.end()
      }
    }
    Request.prototype.pause = function () {
      var self = this
      if (!self.responseContent) {
        self._paused = true
      } else {
        self.responseContent.pause.apply(self.responseContent, arguments)
      }
    }
    Request.prototype.resume = function () {
      var self = this
      if (!self.responseContent) {
        self._paused = false
      } else {
        self.responseContent.resume.apply(self.responseContent, arguments)
      }
    }
    Request.prototype.destroy = function () {
      var self = this
    
      this.clearTimeout()
    
    Romain CREY's avatar
    Romain CREY committed
      if (!self._ended) {
        self.end()
      } else if (self.response) {
        self.response.destroy()
      }
    }
    
    
    Request.prototype.clearTimeout = function () {
      if (this.timeoutTimer) {
        clearTimeout(this.timeoutTimer)
        this.timeoutTimer = null
      }
    }
    
    
    Romain CREY's avatar
    Romain CREY committed
    Request.defaultProxyHeaderWhiteList =
      Tunnel.defaultProxyHeaderWhiteList.slice()
    
    Request.defaultProxyHeaderExclusiveList =
      Tunnel.defaultProxyHeaderExclusiveList.slice()
    
    // Exports
    
    Request.prototype.toJSON = requestToJSON
    module.exports = Request
    
    
    /***/ }),
    
    Romain CREY's avatar
    Romain CREY committed
    /***/ (function(module, exports) {
    
    module.exports = require("http");
    
    /***/ }),
    
    Romain CREY's avatar
    Romain CREY committed
    /***/ (function(module, exports) {
    
    module.exports = require("https");
    
    /***/ }),
    
    Romain CREY's avatar
    Romain CREY committed
    /***/ (function(module, exports) {
    
    module.exports = require("stream");
    
    /***/ }),
    
    Romain CREY's avatar
    Romain CREY committed
    /***/ (function(module, exports) {
    
    module.exports = require("zlib");
    
    /***/ }),
    
    Romain CREY's avatar
    Romain CREY committed
    /***/ (function(module, exports, __webpack_require__) {
    
    
    /*!
     *  Copyright 2010 LearnBoost <dev@learnboost.com>
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *     http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    /**
     * Module dependencies.
     */
    
    
    var crypto = __webpack_require__(93)
      , parse = __webpack_require__(82).parse
    
    Romain CREY's avatar
    Romain CREY committed
      ;
    
    /**
     * Valid keys.
     */
    
    var keys = 
      [ 'acl'
      , 'location'
      , 'logging'
      , 'notification'
      , 'partNumber'
      , 'policy'
      , 'requestPayment'
      , 'torrent'
      , 'uploadId'
      , 'uploads'
      , 'versionId'
      , 'versioning'
      , 'versions'
      , 'website'
      ]
    
    /**
     * Return an "Authorization" header value with the given `options`
     * in the form of "AWS <key>:<signature>"
     *
     * @param {Object} options
     * @return {String}
     * @api private
     */
    
    function authorization (options) {
      return 'AWS ' + options.key + ':' + sign(options)
    }
    
    module.exports = authorization
    module.exports.authorization = authorization
    
    /**
     * Simple HMAC-SHA1 Wrapper
     *
     * @param {Object} options
     * @return {String}
     * @api private
     */ 
    
    function hmacSha1 (options) {
      return crypto.createHmac('sha1', options.secret).update(options.message).digest('base64')
    }
    
    module.exports.hmacSha1 = hmacSha1
    
    /**
     * Create a base64 sha1 HMAC for `options`. 
     * 
     * @param {Object} options
     * @return {String}
     * @api private
     */
    
    function sign (options) {
      options.message = stringToSign(options)
      return hmacSha1(options)
    }
    module.exports.sign = sign
    
    /**
     * Create a base64 sha1 HMAC for `options`. 
     *
     * Specifically to be used with S3 presigned URLs
     * 
     * @param {Object} options
     * @return {String}
     * @api private
     */
    
    function signQuery (options) {
      options.message = queryStringToSign(options)
      return hmacSha1(options)
    }
    module.exports.signQuery= signQuery
    
    /**
     * Return a string for sign() with the given `options`.
     *
     * Spec:
     * 
     *    <verb>\n
     *    <md5>\n
     *    <content-type>\n
     *    <date>\n
     *    [headers\n]
     *    <resource>
     *
     * @param {Object} options
     * @return {String}
     * @api private
     */
    
    function stringToSign (options) {
      var headers = options.amazonHeaders || ''
      if (headers) headers += '\n'
      var r = 
        [ options.verb
        , options.md5
        , options.contentType
        , options.date ? options.date.toUTCString() : ''
        , headers + options.resource
        ]
      return r.join('\n')
    }
    module.exports.stringToSign = stringToSign
    
    /**
     * Return a string for sign() with the given `options`, but is meant exclusively
     * for S3 presigned URLs
     *
     * Spec:
     * 
     *    <date>\n
     *    <resource>
     *
     * @param {Object} options
     * @return {String}
     * @api private
     */
    
    function queryStringToSign (options){
      return 'GET\n\n\n' + options.date + '\n' + options.resource
    }
    module.exports.queryStringToSign = queryStringToSign
    
    /**
     * Perform the following:
     *
     *  - ignore non-amazon headers
     *  - lowercase fields
     *  - sort lexicographically
     *  - trim whitespace between ":"
     *  - join with newline
     *
     * @param {Object} headers