source: trip-planner-front/node_modules/make-fetch-happen/lib/remote.js@ 59329aa

Last change on this file since 59329aa was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 3.4 KB
Line 
1const Minipass = require('minipass')
2const MinipassPipeline = require('minipass-pipeline')
3const fetch = require('minipass-fetch')
4const promiseRetry = require('promise-retry')
5const ssri = require('ssri')
6
7const getAgent = require('./agent.js')
8const pkg = require('../package.json')
9
10const USER_AGENT = `${pkg.name}/${pkg.version} (+https://npm.im/${pkg.name})`
11
12const RETRY_ERRORS = [
13 'ECONNRESET', // remote socket closed on us
14 'ECONNREFUSED', // remote host refused to open connection
15 'EADDRINUSE', // failed to bind to a local port (proxy?)
16 'ETIMEDOUT', // someone in the transaction is WAY TOO SLOW
17 'ERR_SOCKET_TIMEOUT', // same as above, but this one comes from agentkeepalive
18 // Known codes we do NOT retry on:
19 // ENOTFOUND (getaddrinfo failure. Either bad hostname, or offline)
20]
21
22const RETRY_TYPES = [
23 'request-timeout',
24]
25
26// make a request directly to the remote source,
27// retrying certain classes of errors as well as
28// following redirects (through the cache if necessary)
29// and verifying response integrity
30const remoteFetch = (request, options) => {
31 const agent = getAgent(request.url, options)
32 if (!request.headers.has('connection'))
33 request.headers.set('connection', agent ? 'keep-alive' : 'close')
34
35 if (!request.headers.has('user-agent'))
36 request.headers.set('user-agent', USER_AGENT)
37
38 // keep our own options since we're overriding the agent
39 // and the redirect mode
40 const _opts = {
41 ...options,
42 agent,
43 redirect: 'manual',
44 }
45
46 return promiseRetry(async (retryHandler, attemptNum) => {
47 const req = new fetch.Request(request, _opts)
48 try {
49 let res = await fetch(req, _opts)
50 if (_opts.integrity && res.status === 200) {
51 // we got a 200 response and the user has specified an expected
52 // integrity value, so wrap the response in an ssri stream to verify it
53 const integrityStream = ssri.integrityStream({ integrity: _opts.integrity })
54 res = new fetch.Response(new MinipassPipeline(res.body, integrityStream), res)
55 }
56
57 res.headers.set('x-fetch-attempts', attemptNum)
58
59 // do not retry POST requests, or requests with a streaming body
60 // do retry requests with a 408, 420, 429 or 500+ status in the response
61 const isStream = Minipass.isStream(req.body)
62 const isRetriable = req.method !== 'POST' &&
63 !isStream &&
64 ([408, 420, 429].includes(res.status) || res.status >= 500)
65
66 if (isRetriable) {
67 if (typeof options.onRetry === 'function')
68 options.onRetry(res)
69
70 return retryHandler(res)
71 }
72
73 return res
74 } catch (err) {
75 const code = (err.code === 'EPROMISERETRY')
76 ? err.retried.code
77 : err.code
78
79 // err.retried will be the thing that was thrown from above
80 // if it's a response, we just got a bad status code and we
81 // can re-throw to allow the retry
82 const isRetryError = err.retried instanceof fetch.Response ||
83 (RETRY_ERRORS.includes(code) && RETRY_TYPES.includes(err.type))
84
85 if (req.method === 'POST' || isRetryError)
86 throw err
87
88 if (typeof options.onRetry === 'function')
89 options.onRetry(err)
90
91 return retryHandler(err)
92 }
93 }, options.retry).catch((err) => {
94 // don't reject for http errors, just return them
95 if (err.status >= 400 && err.type !== 'system')
96 return err
97
98 throw err
99 })
100}
101
102module.exports = remoteFetch
Note: See TracBrowser for help on using the repository browser.