"Fossies" - the Fresh Open Source Software Archive

Member "Atom/resources/app/apm/node_modules/npm-registry-client/lib/request.js" (7 Feb 2017, 7997 Bytes) of archive /windows/misc/atom-windows.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Javascript source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 module.exports = regRequest
    2 
    3 // npm: means
    4 // 1. https
    5 // 2. send authorization
    6 // 3. content-type is 'application/json' -- metadata
    7 //
    8 var assert = require('assert')
    9 var url = require('url')
   10 var zlib = require('zlib')
   11 var Stream = require('stream').Stream
   12 var STATUS_CODES = require('http').STATUS_CODES
   13 
   14 var request = require('request')
   15 var once = require('once')
   16 
   17 function regRequest (uri, params, cb_) {
   18   assert(typeof uri === 'string', 'must pass uri to request')
   19   assert(params && typeof params === 'object', 'must pass params to request')
   20   assert(typeof cb_ === 'function', 'must pass callback to request')
   21 
   22   params.method = params.method || 'GET'
   23   this.log.verbose('request', 'uri', uri)
   24 
   25   // Since there are multiple places where an error could occur,
   26   // don't let the cb be called more than once.
   27   var cb = once(cb_)
   28 
   29   if (uri.match(/^\/?favicon.ico/)) {
   30     return cb(new Error("favicon.ico isn't a package, it's a picture."))
   31   }
   32 
   33   var adduserChange = /\/?-\/user\/org\.couchdb\.user:([^/]+)\/-rev/
   34   var isUserChange = uri.match(adduserChange)
   35   var adduserNew = /\/?-\/user\/org\.couchdb\.user:([^/?]+)$/
   36   var isNewUser = uri.match(adduserNew)
   37   var alwaysAuth = params.auth && params.auth.alwaysAuth
   38   var isDelete = params.method === 'DELETE'
   39   var isWrite = params.body || isDelete
   40 
   41   if (isUserChange && !isWrite) {
   42     return cb(new Error('trying to change user document without writing(?!)'))
   43   }
   44 
   45   // new users can *not* use auth, because they don't *have* auth yet
   46   if (isUserChange) {
   47     this.log.verbose('request', 'updating existing user; sending authorization')
   48     params.authed = true
   49   } else if (isNewUser) {
   50     this.log.verbose('request', "new user, so can't send auth")
   51     params.authed = false
   52   } else if (alwaysAuth) {
   53     this.log.verbose('request', 'always-auth set; sending authorization')
   54     params.authed = true
   55   } else if (isWrite) {
   56     this.log.verbose('request', 'sending authorization for write operation')
   57     params.authed = true
   58   } else {
   59     // most of the time we don't want to auth
   60     this.log.verbose('request', 'no auth needed')
   61     params.authed = false
   62   }
   63 
   64   var self = this
   65   this.attempt(function (operation) {
   66     makeRequest.call(self, uri, params, function (er, parsed, raw, response) {
   67       if (response) {
   68         self.log.verbose('headers', response.headers)
   69         if (response.headers['npm-notice']) {
   70           self.log.warn('notice', response.headers['npm-notice'])
   71         }
   72       }
   73 
   74       if (!er || (er.message && er.message.match(/^SSL Error/))) {
   75         if (er) er.code = 'ESSL'
   76         return cb(er, parsed, raw, response)
   77       }
   78 
   79       // Only retry on 408, 5xx or no `response`.
   80       var statusCode = response && response.statusCode
   81 
   82       var timeout = statusCode === 408
   83       var serverError = statusCode >= 500
   84       var statusRetry = !statusCode || timeout || serverError
   85       if (er && statusRetry && operation.retry(er)) {
   86         self.log.info('retry', 'will retry, error on last attempt: ' + er)
   87         return undefined
   88       }
   89       cb.apply(null, arguments)
   90     })
   91   })
   92 }
   93 
   94 function makeRequest (uri, params, cb_) {
   95   var cb = once(cb_)
   96 
   97   var parsed = url.parse(uri)
   98   var headers = {}
   99 
  100   // metadata should be compressed
  101   headers['accept-encoding'] = 'gzip'
  102 
  103   var er = this.authify(params.authed, parsed, headers, params.auth)
  104   if (er) return cb_(er)
  105 
  106   var opts = this.initialize(
  107     parsed,
  108     params.method,
  109     'application/json',
  110     headers
  111   )
  112 
  113   opts.followRedirect = (typeof params.follow === 'boolean' ? params.follow : true)
  114   opts.encoding = null // tell request let body be Buffer instance
  115 
  116   if (params.etag) {
  117     this.log.verbose('etag', params.etag)
  118     headers[params.method === 'GET' ? 'if-none-match' : 'if-match'] = params.etag
  119   }
  120 
  121   if (params.lastModified && params.method === 'GET') {
  122     this.log.verbose('lastModified', params.lastModified)
  123     headers['if-modified-since'] = params.lastModified
  124   }
  125 
  126   // figure out wth body is
  127   if (params.body) {
  128     if (Buffer.isBuffer(params.body)) {
  129       opts.body = params.body
  130       headers['content-type'] = 'application/json'
  131       headers['content-length'] = params.body.length
  132     } else if (typeof params.body === 'string') {
  133       opts.body = params.body
  134       headers['content-type'] = 'application/json'
  135       headers['content-length'] = Buffer.byteLength(params.body)
  136     } else if (params.body instanceof Stream) {
  137       headers['content-type'] = 'application/octet-stream'
  138       if (params.body.size) headers['content-length'] = params.body.size
  139     } else {
  140       delete params.body._etag
  141       delete params.body._lastModified
  142       opts.json = params.body
  143     }
  144   }
  145 
  146   this.log.http('request', params.method, parsed.href || '/')
  147 
  148   var done = requestDone.call(this, params.method, uri, cb)
  149   var req = request(opts, decodeResponseBody(done))
  150 
  151   req.on('error', cb)
  152   req.on('socket', function (s) {
  153     s.on('error', cb)
  154   })
  155 
  156   if (params.body && (params.body instanceof Stream)) {
  157     params.body.pipe(req)
  158   }
  159 }
  160 
  161 function decodeResponseBody (cb) {
  162   return function (er, response, data) {
  163     if (er) return cb(er, response, data)
  164 
  165     // don't ever re-use connections that had server errors.
  166     // those sockets connect to the Bad Place!
  167     if (response.socket && response.statusCode > 500) {
  168       response.socket.destroy()
  169     }
  170 
  171     if (response.headers['content-encoding'] !== 'gzip') {
  172       return cb(er, response, data)
  173     }
  174 
  175     zlib.gunzip(data, function (er, buf) {
  176       if (er) return cb(er, response, data)
  177 
  178       cb(null, response, buf)
  179     })
  180   }
  181 }
  182 
  183 // cb(er, parsed, raw, response)
  184 function requestDone (method, where, cb) {
  185   return function (er, response, data) {
  186     if (er) return cb(er)
  187 
  188     var urlObj = url.parse(where)
  189     if (urlObj.auth) urlObj.auth = '***'
  190     this.log.http(response.statusCode, url.format(urlObj))
  191 
  192     if (Buffer.isBuffer(data)) {
  193       data = data.toString()
  194     }
  195 
  196     var parsed
  197     if (data && typeof data === 'string' && response.statusCode !== 304) {
  198       try {
  199         parsed = JSON.parse(data)
  200       } catch (ex) {
  201         ex.message += '\n' + data
  202         this.log.verbose('bad json', data)
  203         this.log.error('registry', 'error parsing json')
  204         return cb(ex, null, data, response)
  205       }
  206     } else if (data) {
  207       parsed = data
  208       data = JSON.stringify(parsed)
  209     }
  210 
  211     // expect data with any error codes
  212     if (!data && response.statusCode >= 400) {
  213       var code = response.statusCode
  214       return cb(
  215         makeError(code + ' ' + STATUS_CODES[code], null, code),
  216         null,
  217         data,
  218         response
  219       )
  220     }
  221 
  222     er = null
  223     if (parsed && response.headers.etag) {
  224       parsed._etag = response.headers.etag
  225     }
  226 
  227     if (parsed && response.headers['last-modified']) {
  228       parsed._lastModified = response.headers['last-modified']
  229     }
  230 
  231     // for the search endpoint, the 'error' property can be an object
  232     if (parsed && parsed.error && typeof parsed.error !== 'object' ||
  233         response.statusCode >= 400) {
  234       var w = url.parse(where).pathname.substr(1)
  235       var name
  236       if (!w.match(/^-/)) {
  237         w = w.split('/')
  238         name = decodeURIComponent(w[w.indexOf('_rewrite') + 1])
  239       }
  240 
  241       if (!parsed.error) {
  242         er = makeError(
  243           'Registry returned ' + response.statusCode +
  244           ' for ' + method +
  245           ' on ' + where,
  246           name,
  247           response.statusCode
  248         )
  249       } else if (name && parsed.error === 'not_found') {
  250         er = makeError('404 Not Found: ' + name, name, response.statusCode)
  251       } else {
  252         er = makeError(
  253           parsed.error + ' ' + (parsed.reason || '') + ': ' + (name || w),
  254           name,
  255           response.statusCode
  256         )
  257       }
  258     }
  259     return cb(er, parsed, data, response)
  260   }.bind(this)
  261 }
  262 
  263 function makeError (message, name, code) {
  264   var er = new Error(message)
  265   if (name) er.pkgid = name
  266   if (code) {
  267     er.statusCode = code
  268     er.code = 'E' + code
  269   }
  270   return er
  271 }