]> gerrit.simantics Code Review - simantics/district.git/blob - org.simantics.maps.server/node/node-v4.8.0-win-x64/node_modules/npm/node_modules/npm-registry-client/lib/request.js
Adding integrated tile server
[simantics/district.git] / org.simantics.maps.server / node / node-v4.8.0-win-x64 / node_modules / npm / node_modules / npm-registry-client / lib / request.js
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 socket
96   var cb = once(function (er, parsed, raw, response) {
97     if (socket) {
98       // The socket might be returned to a pool for re-use, so don’t keep
99       // the 'error' listener from here attached.
100       socket.removeListener('error', cb)
101     }
102
103     return cb_(er, parsed, raw, response)
104   })
105
106   var parsed = url.parse(uri)
107   var headers = {}
108
109   // metadata should be compressed
110   headers['accept-encoding'] = 'gzip'
111
112   var er = this.authify(params.authed, parsed, headers, params.auth)
113   if (er) return cb_(er)
114
115   var opts = this.initialize(
116     parsed,
117     params.method,
118     'application/json',
119     headers
120   )
121
122   opts.followRedirect = (typeof params.follow === 'boolean' ? params.follow : true)
123   opts.encoding = null // tell request let body be Buffer instance
124
125   if (params.etag) {
126     this.log.verbose('etag', params.etag)
127     headers[params.method === 'GET' ? 'if-none-match' : 'if-match'] = params.etag
128   }
129
130   if (params.lastModified && params.method === 'GET') {
131     this.log.verbose('lastModified', params.lastModified)
132     headers['if-modified-since'] = params.lastModified
133   }
134
135   // figure out wth body is
136   if (params.body) {
137     if (Buffer.isBuffer(params.body)) {
138       opts.body = params.body
139       headers['content-type'] = 'application/json'
140       headers['content-length'] = params.body.length
141     } else if (typeof params.body === 'string') {
142       opts.body = params.body
143       headers['content-type'] = 'application/json'
144       headers['content-length'] = Buffer.byteLength(params.body)
145     } else if (params.body instanceof Stream) {
146       headers['content-type'] = 'application/octet-stream'
147       if (params.body.size) headers['content-length'] = params.body.size
148     } else {
149       delete params.body._etag
150       delete params.body._lastModified
151       opts.json = params.body
152     }
153   }
154
155   this.log.http('request', params.method, parsed.href || '/')
156
157   var done = requestDone.call(this, params.method, uri, cb)
158   var req = request(opts, params.streaming ? undefined : decodeResponseBody(done))
159
160   req.on('error', cb)
161
162   // This should not be necessary, as the HTTP implementation in Node
163   // passes errors occurring on the socket to the request itself. Being overly
164   // cautious comes at a low cost, though.
165   req.on('socket', function (s) {
166     socket = s
167     socket.on('error', cb)
168   })
169
170   if (params.streaming) {
171     req.on('response', function (response) {
172       if (response.statusCode >= 400) {
173         var parts = []
174         response.on('data', function (data) {
175           parts.push(data)
176         })
177         response.on('end', function () {
178           decodeResponseBody(done)(null, response, Buffer.concat(parts))
179         })
180       } else {
181         response.on('end', function () {
182           // don't ever re-use connections that had server errors.
183           // those sockets connect to the Bad Place!
184           if (response.socket && response.statusCode > 500) {
185             response.socket.destroy()
186           }
187         })
188
189         return cb(null, response)
190       }
191     })
192   }
193
194   if (params.body && (params.body instanceof Stream)) {
195     params.body.pipe(req)
196   }
197 }
198
199 function decodeResponseBody (cb) {
200   return function (er, response, data) {
201     if (er) return cb(er, response, data)
202
203     // don't ever re-use connections that had server errors.
204     // those sockets connect to the Bad Place!
205     if (response.socket && response.statusCode > 500) {
206       response.socket.destroy()
207     }
208
209     if (response.headers['content-encoding'] !== 'gzip') {
210       return cb(er, response, data)
211     }
212
213     zlib.gunzip(data, function (er, buf) {
214       if (er) return cb(er, response, data)
215
216       cb(null, response, buf)
217     })
218   }
219 }
220
221 // cb(er, parsed, raw, response)
222 function requestDone (method, where, cb) {
223   return function (er, response, data) {
224     if (er) return cb(er)
225
226     var urlObj = url.parse(where)
227     if (urlObj.auth) urlObj.auth = '***'
228     this.log.http(response.statusCode, url.format(urlObj))
229
230     if (Buffer.isBuffer(data)) {
231       data = data.toString()
232     }
233
234     var parsed
235     if (data && typeof data === 'string' && response.statusCode !== 304) {
236       try {
237         parsed = JSON.parse(data)
238       } catch (ex) {
239         ex.message += '\n' + data
240         this.log.verbose('bad json', data)
241         this.log.error('registry', 'error parsing json')
242         return cb(ex, null, data, response)
243       }
244     } else if (data) {
245       parsed = data
246       data = JSON.stringify(parsed)
247     }
248
249     // expect data with any error codes
250     if (!data && response.statusCode >= 400) {
251       var code = response.statusCode
252       return cb(
253         makeError(code + ' ' + STATUS_CODES[code], null, code),
254         null,
255         data,
256         response
257       )
258     }
259
260     er = null
261     if (parsed && response.headers.etag) {
262       parsed._etag = response.headers.etag
263     }
264
265     if (parsed && response.headers['last-modified']) {
266       parsed._lastModified = response.headers['last-modified']
267     }
268
269     // for the search endpoint, the 'error' property can be an object
270     if (parsed && parsed.error && typeof parsed.error !== 'object' ||
271         response.statusCode >= 400) {
272       var w = url.parse(where).pathname.substr(1)
273       var name
274       if (!w.match(/^-/)) {
275         w = w.split('/')
276         name = decodeURIComponent(w[w.indexOf('_rewrite') + 1])
277       }
278
279       if (!parsed.error) {
280         er = makeError(
281           'Registry returned ' + response.statusCode +
282           ' for ' + method +
283           ' on ' + where,
284           name,
285           response.statusCode
286         )
287       } else if (name && parsed.error === 'not_found') {
288         er = makeError('404 Not Found: ' + name, name, response.statusCode)
289       } else {
290         er = makeError(
291           parsed.error + ' ' + (parsed.reason || '') + ': ' + (name || w),
292           name,
293           response.statusCode
294         )
295       }
296     }
297     return cb(er, parsed, data, response)
298   }.bind(this)
299 }
300
301 function makeError (message, name, code) {
302   var er = new Error(message)
303   if (name) er.pkgid = name
304   if (code) {
305     er.statusCode = code
306     er.code = 'E' + code
307   }
308   return er
309 }