• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict'
2
3const config = require('./config.js')
4const errors = require('./errors.js')
5const LRU = require('lru-cache')
6
7module.exports = checkResponse
8function checkResponse (method, res, registry, startTime, opts) {
9  opts = config(opts)
10  if (res.headers.has('npm-notice') && !res.headers.has('x-local-cache')) {
11    opts.log.notice('', res.headers.get('npm-notice'))
12  }
13  checkWarnings(res, registry, opts)
14  if (res.status >= 400) {
15    logRequest(method, res, startTime, opts)
16    return checkErrors(method, res, startTime, opts)
17  } else {
18    res.body.on('end', () => logRequest(method, res, startTime, opts))
19    if (opts.ignoreBody) {
20      res.body.resume()
21      res.body = null
22    }
23    return res
24  }
25}
26
27function logRequest (method, res, startTime, opts) {
28  const elapsedTime = Date.now() - startTime
29  const attempt = res.headers.get('x-fetch-attempts')
30  const attemptStr = attempt && attempt > 1 ? ` attempt #${attempt}` : ''
31  const cacheStr = res.headers.get('x-local-cache') ? ' (from cache)' : ''
32
33  let urlStr
34  try {
35    const URL = require('url').URL
36    const url = new URL(res.url)
37    if (url.password) {
38      url.password = '***'
39    }
40    urlStr = url.toString()
41  } catch (er) {
42    urlStr = res.url
43  }
44
45  opts.log.http(
46    'fetch',
47    `${method.toUpperCase()} ${res.status} ${urlStr} ${elapsedTime}ms${attemptStr}${cacheStr}`
48  )
49}
50
51const WARNING_REGEXP = /^\s*(\d{3})\s+(\S+)\s+"(.*)"\s+"([^"]+)"/
52const BAD_HOSTS = new LRU({ max: 50 })
53
54function checkWarnings (res, registry, opts) {
55  if (res.headers.has('warning') && !BAD_HOSTS.has(registry)) {
56    const warnings = {}
57    res.headers.raw()['warning'].forEach(w => {
58      const match = w.match(WARNING_REGEXP)
59      if (match) {
60        warnings[match[1]] = {
61          code: match[1],
62          host: match[2],
63          message: match[3],
64          date: new Date(match[4])
65        }
66      }
67    })
68    BAD_HOSTS.set(registry, true)
69    if (warnings['199']) {
70      if (warnings['199'].message.match(/ENOTFOUND/)) {
71        opts.log.warn('registry', `Using stale data from ${registry} because the host is inaccessible -- are you offline?`)
72      } else {
73        opts.log.warn('registry', `Unexpected warning for ${registry}: ${warnings['199'].message}`)
74      }
75    }
76    if (warnings['111']) {
77      // 111 Revalidation failed -- we're using stale data
78      opts.log.warn(
79        'registry',
80        `Using stale data from ${registry} due to a request error during revalidation.`
81      )
82    }
83  }
84}
85
86function checkErrors (method, res, startTime, opts) {
87  return res.buffer()
88    .catch(() => null)
89    .then(body => {
90      let parsed = body
91      try {
92        parsed = JSON.parse(body.toString('utf8'))
93      } catch (e) {}
94      if (res.status === 401 && res.headers.get('www-authenticate')) {
95        const auth = res.headers.get('www-authenticate')
96          .split(/,\s*/)
97          .map(s => s.toLowerCase())
98        if (auth.indexOf('ipaddress') !== -1) {
99          throw new errors.HttpErrorAuthIPAddress(
100            method, res, parsed, opts.spec
101          )
102        } else if (auth.indexOf('otp') !== -1) {
103          throw new errors.HttpErrorAuthOTP(
104            method, res, parsed, opts.spec
105          )
106        } else {
107          throw new errors.HttpErrorAuthUnknown(
108            method, res, parsed, opts.spec
109          )
110        }
111      } else if (res.status === 401 && body != null && /one-time pass/.test(body.toString('utf8'))) {
112        // Heuristic for malformed OTP responses that don't include the www-authenticate header.
113        throw new errors.HttpErrorAuthOTP(
114          method, res, parsed, opts.spec
115        )
116      } else {
117        throw new errors.HttpErrorGeneral(
118          method, res, parsed, opts.spec
119        )
120      }
121    })
122}
123