diff --git a/CHANGELOG.md b/CHANGELOG.md index d625c94..1a2ffcb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ This is the changelog for [Authress SDK](readme.md). ## 2.0 ## * Enable passing just the access token as a string to `AuthressClient`. * Fix the issuer path for service client tokens to include the accountId when the custom domain is not specified. The default issuer is converted from `api.authress.io` to `accountId.api.authress.io`. if this fallback issuer domain was specified in your authorizer, upgrading this library without changing your defined issuer, which prevent future access. +* Add automatic retries to all requests. ## 1.3 ## * Add new `Pagination` type which pagination `next.cursor` to enable paging through resources. diff --git a/src/httpClient.js b/src/httpClient.js index be454be..22d2423 100644 --- a/src/httpClient.js +++ b/src/httpClient.js @@ -6,6 +6,24 @@ const defaultHeaders = { 'Content-Type': 'application/json' }; +async function retryExecutor(func) { + let lastError = null; + for (let iteration = 0; iteration < 5; iteration++) { + try { + const result = await func(); + return result; + } catch (error) { + lastError = error; + if (error.code === 'EPIPE' || error.code === 'ECONNABORTED' || error.code === 'ETIMEDOUT' || error.code === 'ECONNRESET' || error.status >= 500) { + await new Promise(resolve => setTimeout(resolve, 10 * 2 ** iteration)); + continue; + } + throw error; + } + } + throw lastError; +} + class HttpClient { constructor(baseUrl, tokenProvider) { this.baseUrl = new URL(`https://${baseUrl.replace(/^(https?:\/\/)/, '')}`).toString().replace(/\/$/, ''); @@ -65,34 +83,44 @@ class HttpClient { } get(url, headers, type = 'json') { - return this.client.get(url.toString(), { - headers: Object.assign({}, defaultHeaders, headers), - responseType: type + return retryExecutor(() => { + return this.client.get(url.toString(), { + headers: Object.assign({}, defaultHeaders, headers), + responseType: type + }); }); } delete(url, headers, type = 'json') { - return this.client.delete(url.toString(), { - headers: Object.assign({}, defaultHeaders, headers), - responseType: type + return retryExecutor(() => { + return this.client.delete(url.toString(), { + headers: Object.assign({}, defaultHeaders, headers), + responseType: type + }); }); } post(url, data, headers) { - return this.client.post(url.toString(), data, { - headers: Object.assign({}, defaultHeaders, headers) + return retryExecutor(() => { + return this.client.post(url.toString(), data, { + headers: Object.assign({}, defaultHeaders, headers) + }); }); } put(url, data, headers) { - return this.client.put(url.toString(), data, { - headers: Object.assign({}, defaultHeaders, headers) + return retryExecutor(() => { + return this.client.put(url.toString(), data, { + headers: Object.assign({}, defaultHeaders, headers) + }); }); } patch(url, data, headers) { - return this.client.patch(url.toString(), data, { - headers: Object.assign({}, defaultHeaders, headers) + return retryExecutor(() => { + return this.client.patch(url.toString(), data, { + headers: Object.assign({}, defaultHeaders, headers) + }); }); } }