UNPKG

6.43 kBJavaScriptView Raw
1"use strict";
2// Copyright 2018 Google LLC
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14Object.defineProperty(exports, "__esModule", { value: true });
15exports.getRetryConfig = getRetryConfig;
16async function getRetryConfig(err) {
17 let config = getConfig(err);
18 if (!err || !err.config || (!config && !err.config.retry)) {
19 return { shouldRetry: false };
20 }
21 config = config || {};
22 config.currentRetryAttempt = config.currentRetryAttempt || 0;
23 config.retry =
24 config.retry === undefined || config.retry === null ? 3 : config.retry;
25 config.httpMethodsToRetry = config.httpMethodsToRetry || [
26 'GET',
27 'HEAD',
28 'PUT',
29 'OPTIONS',
30 'DELETE',
31 ];
32 config.noResponseRetries =
33 config.noResponseRetries === undefined || config.noResponseRetries === null
34 ? 2
35 : config.noResponseRetries;
36 config.retryDelayMultiplier = config.retryDelayMultiplier
37 ? config.retryDelayMultiplier
38 : 2;
39 config.timeOfFirstRequest = config.timeOfFirstRequest
40 ? config.timeOfFirstRequest
41 : Date.now();
42 config.totalTimeout = config.totalTimeout
43 ? config.totalTimeout
44 : Number.MAX_SAFE_INTEGER;
45 config.maxRetryDelay = config.maxRetryDelay
46 ? config.maxRetryDelay
47 : Number.MAX_SAFE_INTEGER;
48 // If this wasn't in the list of status codes where we want
49 // to automatically retry, return.
50 const retryRanges = [
51 // https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
52 // 1xx - Retry (Informational, request still processing)
53 // 2xx - Do not retry (Success)
54 // 3xx - Do not retry (Redirect)
55 // 4xx - Do not retry (Client errors)
56 // 408 - Retry ("Request Timeout")
57 // 429 - Retry ("Too Many Requests")
58 // 5xx - Retry (Server errors)
59 [100, 199],
60 [408, 408],
61 [429, 429],
62 [500, 599],
63 ];
64 config.statusCodesToRetry = config.statusCodesToRetry || retryRanges;
65 // Put the config back into the err
66 err.config.retryConfig = config;
67 // Determine if we should retry the request
68 const shouldRetryFn = config.shouldRetry || shouldRetryRequest;
69 if (!(await shouldRetryFn(err))) {
70 return { shouldRetry: false, config: err.config };
71 }
72 const delay = getNextRetryDelay(config);
73 // We're going to retry! Incremenent the counter.
74 err.config.retryConfig.currentRetryAttempt += 1;
75 // Create a promise that invokes the retry after the backOffDelay
76 const backoff = config.retryBackoff
77 ? config.retryBackoff(err, delay)
78 : new Promise(resolve => {
79 setTimeout(resolve, delay);
80 });
81 // Notify the user if they added an `onRetryAttempt` handler
82 if (config.onRetryAttempt) {
83 config.onRetryAttempt(err);
84 }
85 // Return the promise in which recalls Gaxios to retry the request
86 await backoff;
87 return { shouldRetry: true, config: err.config };
88}
89/**
90 * Determine based on config if we should retry the request.
91 * @param err The GaxiosError passed to the interceptor.
92 */
93function shouldRetryRequest(err) {
94 var _a;
95 const config = getConfig(err);
96 // node-fetch raises an AbortError if signaled:
97 // https://github.com/bitinn/node-fetch#request-cancellation-with-abortsignal
98 if (err.name === 'AbortError' || ((_a = err.error) === null || _a === void 0 ? void 0 : _a.name) === 'AbortError') {
99 return false;
100 }
101 // If there's no config, or retries are disabled, return.
102 if (!config || config.retry === 0) {
103 return false;
104 }
105 // Check if this error has no response (ETIMEDOUT, ENOTFOUND, etc)
106 if (!err.response &&
107 (config.currentRetryAttempt || 0) >= config.noResponseRetries) {
108 return false;
109 }
110 // Only retry with configured HttpMethods.
111 if (!err.config.method ||
112 config.httpMethodsToRetry.indexOf(err.config.method.toUpperCase()) < 0) {
113 return false;
114 }
115 // If this wasn't in the list of status codes where we want
116 // to automatically retry, return.
117 if (err.response && err.response.status) {
118 let isInRange = false;
119 for (const [min, max] of config.statusCodesToRetry) {
120 const status = err.response.status;
121 if (status >= min && status <= max) {
122 isInRange = true;
123 break;
124 }
125 }
126 if (!isInRange) {
127 return false;
128 }
129 }
130 // If we are out of retry attempts, return
131 config.currentRetryAttempt = config.currentRetryAttempt || 0;
132 if (config.currentRetryAttempt >= config.retry) {
133 return false;
134 }
135 return true;
136}
137/**
138 * Acquire the raxConfig object from an GaxiosError if available.
139 * @param err The Gaxios error with a config object.
140 */
141function getConfig(err) {
142 if (err && err.config && err.config.retryConfig) {
143 return err.config.retryConfig;
144 }
145 return;
146}
147/**
148 * Gets the delay to wait before the next retry.
149 *
150 * @param {RetryConfig} config The current set of retry options
151 * @returns {number} the amount of ms to wait before the next retry attempt.
152 */
153function getNextRetryDelay(config) {
154 var _a;
155 // Calculate time to wait with exponential backoff.
156 // If this is the first retry, look for a configured retryDelay.
157 const retryDelay = config.currentRetryAttempt ? 0 : (_a = config.retryDelay) !== null && _a !== void 0 ? _a : 100;
158 // Formula: retryDelay + ((retryDelayMultiplier^currentRetryAttempt - 1 / 2) * 1000)
159 const calculatedDelay = retryDelay +
160 ((Math.pow(config.retryDelayMultiplier, config.currentRetryAttempt) - 1) /
161 2) *
162 1000;
163 const maxAllowableDelay = config.totalTimeout - (Date.now() - config.timeOfFirstRequest);
164 return Math.min(calculatedDelay, maxAllowableDelay, config.maxRetryDelay);
165}
166//# sourceMappingURL=retry.js.map
\No newline at end of file