| 61 | } |
| 62 | |
| 63 | export async function requestWithBackoff( |
| 64 | api: API, |
| 65 | req: ApiRequest, |
| 66 | attempt = 1, |
| 67 | ): Promise<Response> { |
| 68 | if (api.rateLimiter) { |
| 69 | await api.rateLimiter.acquire(); |
| 70 | } |
| 71 | |
| 72 | try { |
| 73 | const builtRequest = await api.buildRequest(req); |
| 74 | const requestFunction = api.requestFunction || unsentRequest.performRequest; |
| 75 | const response: Response = await requestFunction(builtRequest); |
| 76 | if (response.status === 429) { |
| 77 | // GitLab/Bitbucket too many requests |
| 78 | const text = await response.text().catch(() => 'Too many requests'); |
| 79 | throw new Error(text); |
| 80 | } else if (response.status === 403) { |
| 81 | // GitHub too many requests |
| 82 | const json = await response.json().catch(() => ({ message: '' })); |
| 83 | if (json.message.match('API rate limit exceeded')) { |
| 84 | const now = new Date(); |
| 85 | const nextWindowInSeconds = response.headers.has('X-RateLimit-Reset') |
| 86 | ? parseInt(response.headers.get('X-RateLimit-Reset')!) |
| 87 | : now.getTime() / 1000 + 60; |
| 88 | |
| 89 | throw new RateLimitError(json.message, nextWindowInSeconds); |
| 90 | } |
| 91 | response.json = () => Promise.resolve(json); |
| 92 | } |
| 93 | return response; |
| 94 | } catch (err) { |
| 95 | if (attempt > 5 || err.message === "Can't refresh access token when using implicit auth") { |
| 96 | throw err; |
| 97 | } else { |
| 98 | if (!api.rateLimiter) { |
| 99 | const timeout = err.resetSeconds || attempt * attempt; |
| 100 | console.log( |
| 101 | `Pausing requests for ${timeout} ${ |
| 102 | attempt === 1 ? 'second' : 'seconds' |
| 103 | } due to fetch failures:`, |
| 104 | err.message, |
| 105 | ); |
| 106 | api.rateLimiter = asyncLock(); |
| 107 | api.rateLimiter.acquire(); |
| 108 | setTimeout(() => { |
| 109 | api.rateLimiter?.release(); |
| 110 | api.rateLimiter = undefined; |
| 111 | console.log(`Done pausing requests`); |
| 112 | }, 1000 * timeout); |
| 113 | } |
| 114 | return requestWithBackoff(api, req, attempt + 1); |
| 115 | } |
| 116 | } |
| 117 | } |
| 118 | |
| 119 | // Options is an object which contains all the standard network request properties |
| 120 | // for modifying HTTP requests and may contains `params` property |