UNPKG

7.94 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.isNetworkError = isNetworkError;
7exports.isRetryableError = isRetryableError;
8exports.isSafeRequestError = isSafeRequestError;
9exports.isIdempotentRequestError = isIdempotentRequestError;
10exports.isNetworkOrIdempotentRequestError = isNetworkOrIdempotentRequestError;
11exports.exponentialDelay = exponentialDelay;
12exports.default = axiosRetry;
13
14var _isRetryAllowed = require('is-retry-allowed');
15
16var _isRetryAllowed2 = _interopRequireDefault(_isRetryAllowed);
17
18function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
19
20var namespace = 'axios-retry';
21
22/**
23 * @param {Error} error
24 * @return {boolean}
25 */
26function isNetworkError(error) {
27 return !error.response && Boolean(error.code) && // Prevents retrying cancelled requests
28 error.code !== 'ECONNABORTED' && // Prevents retrying timed out requests
29 (0, _isRetryAllowed2.default)(error); // Prevents retrying unsafe errors
30}
31
32var SAFE_HTTP_METHODS = ['get', 'head', 'options'];
33var IDEMPOTENT_HTTP_METHODS = SAFE_HTTP_METHODS.concat(['put', 'delete']);
34
35/**
36 * @param {Error} error
37 * @return {boolean}
38 */
39function isRetryableError(error) {
40 return error.code !== 'ECONNABORTED' && (!error.response || error.response.status >= 500 && error.response.status <= 599);
41}
42
43/**
44 * @param {Error} error
45 * @return {boolean}
46 */
47function isSafeRequestError(error) {
48 if (!error.config) {
49 // Cannot determine if the request can be retried
50 return false;
51 }
52
53 return isRetryableError(error) && SAFE_HTTP_METHODS.indexOf(error.config.method) !== -1;
54}
55
56/**
57 * @param {Error} error
58 * @return {boolean}
59 */
60function isIdempotentRequestError(error) {
61 if (!error.config) {
62 // Cannot determine if the request can be retried
63 return false;
64 }
65
66 return isRetryableError(error) && IDEMPOTENT_HTTP_METHODS.indexOf(error.config.method) !== -1;
67}
68
69/**
70 * @param {Error} error
71 * @return {boolean}
72 */
73function isNetworkOrIdempotentRequestError(error) {
74 return isNetworkError(error) || isIdempotentRequestError(error);
75}
76
77/**
78 * @return {number} - delay in milliseconds, always 0
79 */
80function noDelay() {
81 return 0;
82}
83
84/**
85 * @param {number} [retryNumber=0]
86 * @return {number} - delay in milliseconds
87 */
88function exponentialDelay() {
89 var retryNumber = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
90
91 var delay = Math.pow(2, retryNumber) * 100;
92 var randomSum = delay * 0.2 * Math.random(); // 0-20% of the delay
93 return delay + randomSum;
94}
95
96/**
97 * Initializes and returns the retry state for the given request/config
98 * @param {AxiosRequestConfig} config
99 * @return {Object}
100 */
101function getCurrentState(config) {
102 var currentState = config[namespace] || {};
103 currentState.retryCount = currentState.retryCount || 0;
104 config[namespace] = currentState;
105 return currentState;
106}
107
108/**
109 * Returns the axios-retry options for the current request
110 * @param {AxiosRequestConfig} config
111 * @param {AxiosRetryConfig} defaultOptions
112 * @return {AxiosRetryConfig}
113 */
114function getRequestOptions(config, defaultOptions) {
115 return Object.assign({}, defaultOptions, config[namespace]);
116}
117
118/**
119 * @param {Axios} axios
120 * @param {AxiosRequestConfig} config
121 */
122function fixConfig(axios, config) {
123 if (axios.defaults.agent === config.agent) {
124 delete config.agent;
125 }
126 if (axios.defaults.httpAgent === config.httpAgent) {
127 delete config.httpAgent;
128 }
129 if (axios.defaults.httpsAgent === config.httpsAgent) {
130 delete config.httpsAgent;
131 }
132}
133
134/**
135 * Adds response interceptors to an axios instance to retry requests failed due to network issues
136 *
137 * @example
138 *
139 * import axios from 'axios';
140 *
141 * axiosRetry(axios, { retries: 3 });
142 *
143 * axios.get('http://example.com/test') // The first request fails and the second returns 'ok'
144 * .then(result => {
145 * result.data; // 'ok'
146 * });
147 *
148 * // Exponential back-off retry delay between requests
149 * axiosRetry(axios, { retryDelay : axiosRetry.exponentialDelay});
150 *
151 * // Custom retry delay
152 * axiosRetry(axios, { retryDelay : (retryCount) => {
153 * return retryCount * 1000;
154 * }});
155 *
156 * // Also works with custom axios instances
157 * const client = axios.create({ baseURL: 'http://example.com' });
158 * axiosRetry(client, { retries: 3 });
159 *
160 * client.get('/test') // The first request fails and the second returns 'ok'
161 * .then(result => {
162 * result.data; // 'ok'
163 * });
164 *
165 * // Allows request-specific configuration
166 * client
167 * .get('/test', {
168 * 'axios-retry': {
169 * retries: 0
170 * }
171 * })
172 * .catch(error => { // The first request fails
173 * error !== undefined
174 * });
175 *
176 * @param {Axios} axios An axios instance (the axios object or one created from axios.create)
177 * @param {Object} [defaultOptions]
178 * @param {number} [defaultOptions.retries=3] Number of retries
179 * @param {boolean} [defaultOptions.shouldResetTimeout=false]
180 * Defines if the timeout should be reset between retries
181 * @param {Function} [defaultOptions.retryCondition=isNetworkOrIdempotentRequestError]
182 * A function to determine if the error can be retried
183 * @param {Function} [defaultOptions.retryDelay=noDelay]
184 * A function to determine the delay between retry requests
185 */
186function axiosRetry(axios, defaultOptions) {
187 axios.interceptors.request.use(function (config) {
188 var currentState = getCurrentState(config);
189 currentState.lastRequestTime = Date.now();
190 return config;
191 });
192
193 axios.interceptors.response.use(null, function (error) {
194 var config = error.config;
195
196 // If we have no information to retry the request
197 if (!config) {
198 return Promise.reject(error);
199 }
200
201 var _getRequestOptions = getRequestOptions(config, defaultOptions),
202 _getRequestOptions$re = _getRequestOptions.retries,
203 retries = _getRequestOptions$re === undefined ? 3 : _getRequestOptions$re,
204 _getRequestOptions$re2 = _getRequestOptions.retryCondition,
205 retryCondition = _getRequestOptions$re2 === undefined ? isNetworkOrIdempotentRequestError : _getRequestOptions$re2,
206 _getRequestOptions$re3 = _getRequestOptions.retryDelay,
207 retryDelay = _getRequestOptions$re3 === undefined ? noDelay : _getRequestOptions$re3,
208 _getRequestOptions$sh = _getRequestOptions.shouldResetTimeout,
209 shouldResetTimeout = _getRequestOptions$sh === undefined ? false : _getRequestOptions$sh;
210
211 var currentState = getCurrentState(config);
212
213 var shouldRetry = retryCondition(error) && currentState.retryCount < retries;
214
215 if (shouldRetry) {
216 currentState.retryCount += 1;
217 var delay = retryDelay(currentState.retryCount, error);
218
219 // Axios fails merging this configuration to the default configuration because it has an issue
220 // with circular structures: https://github.com/mzabriskie/axios/issues/370
221 fixConfig(axios, config);
222
223 if (!shouldResetTimeout && config.timeout && currentState.lastRequestTime) {
224 var lastRequestDuration = Date.now() - currentState.lastRequestTime;
225 // Minimum 1ms timeout (passing 0 or less to XHR means no timeout)
226 config.timeout = Math.max(config.timeout - lastRequestDuration - delay, 1);
227 }
228
229 config.transformRequest = [function (data) {
230 return data;
231 }];
232
233 return new Promise(function (resolve) {
234 return setTimeout(function () {
235 return resolve(axios(config));
236 }, delay);
237 });
238 }
239
240 return Promise.reject(error);
241 });
242}
243
244// Compatibility with CommonJS
245axiosRetry.isNetworkError = isNetworkError;
246axiosRetry.isSafeRequestError = isSafeRequestError;
247axiosRetry.isIdempotentRequestError = isIdempotentRequestError;
248axiosRetry.isNetworkOrIdempotentRequestError = isNetworkOrIdempotentRequestError;
249axiosRetry.exponentialDelay = exponentialDelay;
250axiosRetry.isRetryableError = isRetryableError;
251//# sourceMappingURL=index.js.map
\No newline at end of file