lib/request.js
function requestDone (method, where, cb) {
  return function (er, response, data) {
    if (er) return cb(er)

    var urlObj = url.parse(where)
    if (urlObj.auth) urlObj.auth = '***'
    this.log.http(response.statusCode, url.format(urlObj))

    if (Buffer.isBuffer(data)) {
      data = data.toString()
    }

    var parsed
    if (data && typeof data === 'string' && response.statusCode !== 304) {
      try {
        parsed = JSON.parse(data)
      } catch (ex) {
        ex.message += '\n' + data
        this.log.verbose('bad json', data)
        this.log.error('registry', 'error parsing json')
        return cb(ex, null, data, response)
      }
    } else if (data) {
      parsed = data
      data = JSON.stringify(parsed)
    }

    // expect data with any error codes
    if (!data && response.statusCode >= 400) {
      var code = response.statusCode
      return cb(
        makeError(code + ' ' + STATUS_CODES[code], null, code),
        null,
        data,
        response
      )
    }

    er = null
    if (parsed && response.headers.etag) {
      parsed._etag = response.headers.etag
    }

    if (parsed && response.headers['last-modified']) {
      parsed._lastModified = response.headers['last-modified']
    }

    // for the search endpoint, the 'error' property can be an object
    if ((parsed && parsed.error && typeof parsed.error !== 'object') ||
        response.statusCode >= 400) {
      var w = url.parse(where).pathname.substr(1)
      var name
      if (!w.match(/^-/)) {
        w = w.split('/')
        var index = w.indexOf('_rewrite')
        if (index === -1) {
          index = w.length - 1
        } else {
          index++
        }
        name = decodeURIComponent(w[index])
      }

      if (!parsed.error) {
        if (response.statusCode === 401 && response.headers['www-authenticate']) {
          const auth = response.headers['www-authenticate'].split(/,\s*/).map(s => s.toLowerCase())
          if (auth.indexOf('ipaddress') !== -1) {
            er = makeError('Login is not allowed from your IP address', name, response.statusCode, 'EAUTHIP')
          } else if (auth.indexOf('otp') !== -1) {
            er = makeError('OTP required for this operation', name, response.statusCode, 'EOTP')
          } else {
            er = makeError('Unable to authenticate, need: ' + response.headers['www-authenticate'], name, response.statusCode, 'EAUTHUNKNOWN')
          }
        } else {
          const msg = parsed.message ? ': ' + parsed.message : ''
          er = makeError(
            'Registry returned ' + response.statusCode +
            ' for ' + method +
            ' on ' + where +
            msg,
            name,
            response.statusCode
          )
        }
      } else if (name && parsed.error === 'not_found') {
        er = makeError('404 Not Found: ' + name, name, response.statusCode)
      } else if (name && parsed.error === 'User not found') {
        er = makeError('User not found. Check `npm whoami` and make sure you have a NPM account.', name, response.statusCode)
      } else {
        er = makeError(
          parsed.error + ' ' + (parsed.reason || '') + ': ' + (name || w),
          name,
          response.statusCode
        )
      }
    }
    return cb(er, parsed, data, response)
  }.bind(this)
}
function requestDone (method, where, cb) {
  return function (er, response, data) {
    if (er) return cb(er)

    var urlObj = url.parse(where)
    if (urlObj.auth) urlObj.auth = '***'
    this.log.http(response.statusCode, url.format(urlObj))

    if (Buffer.isBuffer(data)) {
      data = data.toString()
    }

    var parsed
    if (data && typeof data === 'string' && response.statusCode !== 304) {
      try {
        parsed = JSON.parse(data)
      } catch (ex) {
        ex.message += '\n' + data
        this.log.verbose('bad json', data)
        this.log.error('registry', 'error parsing json')
        return cb(ex, null, data, response)
      }
    } else if (data) {
      parsed = data
      data = JSON.stringify(parsed)
    }

    // expect data with any error codes
    if (!data && response.statusCode >= 400) {
      var code = response.statusCode
      return cb(
        makeError(code + ' ' + STATUS_CODES[code], null, code),
        null,
        data,
        response
      )
    }

    er = null
    if (parsed && response.headers.etag) {
      parsed._etag = response.headers.etag
    }

    if (parsed && response.headers['last-modified']) {
      parsed._lastModified = response.headers['last-modified']
    }

    // for the search endpoint, the 'error' property can be an object
    if ((parsed && parsed.error && typeof parsed.error !== 'object') ||
        response.statusCode >= 400) {
      var w = url.parse(where).pathname.substr(1)
      var name
      if (!w.match(/^-/)) {
        w = w.split('/')
        var index = w.indexOf('_rewrite')
        if (index === -1) {
          index = w.length - 1
        } else {
          index++
        }
        name = decodeURIComponent(w[index])
      }

      if (!parsed.error) {
        if (response.statusCode === 401 && response.headers['www-authenticate']) {
          const auth = response.headers['www-authenticate'].split(/,\s*/).map(s => s.toLowerCase())
          if (auth.indexOf('ipaddress') !== -1) {
            er = makeError('Login is not allowed from your IP address', name, response.statusCode, 'EAUTHIP')
          } else if (auth.indexOf('otp') !== -1) {
            er = makeError('OTP required for this operation', name, response.statusCode, 'EOTP')
          } else {
            er = makeError('Unable to authenticate, need: ' + response.headers['www-authenticate'], name, response.statusCode, 'EAUTHUNKNOWN')
          }
        } else {
          const msg = parsed.message ? ': ' + parsed.message : ''
          er = makeError(
            'Registry returned ' + response.statusCode +
            ' for ' + method +
            ' on ' + where +
            msg,
            name,
            response.statusCode
          )
        }
      } else if (name && parsed.error === 'not_found') {
        er = makeError('404 Not Found: ' + name, name, response.statusCode)
      } else if (name && parsed.error === 'User not found') {
        er = makeError('User not found. Check `npm whoami` and make sure you have a NPM account.', name, response.statusCode)
      } else {
        er = makeError(
          parsed.error + ' ' + (parsed.reason || '') + ': ' + (name || w),
          name,
          response.statusCode
        )
      }
    }
    return cb(er, parsed, data, response)
  }.bind(this)
}
cb(er, parsed, raw, response)