From 6edfdb803513bdc0325614ce73d83a72cfaba676 Mon Sep 17 00:00:00 2001 From: Chukwuemeka Ajima Date: Thu, 14 Oct 2021 14:28:51 +0200 Subject: [PATCH] feat(http-middleware): add retryOnAbort config - add config to enable retry an aborted request --- packages/sdk-middleware-http/src/http.js | 35 ++++++-- .../sdk-middleware-http/test/http.spec.js | 85 +++++++++++++++++++ types/sdk.js | 1 + 3 files changed, 112 insertions(+), 9 deletions(-) diff --git a/packages/sdk-middleware-http/src/http.js b/packages/sdk-middleware-http/src/http.js index 47f4e2df8..783d1e7f4 100644 --- a/packages/sdk-middleware-http/src/http.js +++ b/packages/sdk-middleware-http/src/http.js @@ -65,6 +65,7 @@ export default function createHttpMiddleware({ backoff = true, retryDelay = 200, maxDelay = Infinity, + retryOnAbort = false, } = {}, fetch: fetcher, abortController: _abortController, @@ -98,13 +99,6 @@ export default function createHttpMiddleware({ response: MiddlewareResponse ) => { let abortController: any - if (timeout || getAbortController || _abortController) - // eslint-disable-next-line - abortController = - (getAbortController ? getAbortController() : null) || - _abortController || - new AbortController() - const url = host.replace(/\/$/, '') + request.uri const body = typeof request.body === 'string' || Buffer.isBuffer(request.body) @@ -126,15 +120,38 @@ export default function createHttpMiddleware({ if (credentialsMode) { fetchOptions.credentials = credentialsMode } - if (abortController) { - fetchOptions.signal = abortController.signal + + if (!retryOnAbort) { + if (timeout || getAbortController || _abortController) + // eslint-disable-next-line + abortController = + (getAbortController ? getAbortController() : null) || + _abortController || + new AbortController() + + if (abortController) { + fetchOptions.signal = abortController.signal + } } + if (body) { fetchOptions.body = body } let retryCount = 0 // wrap in a fn so we can retry if error occur function executeFetch() { + if (retryOnAbort) { + if (timeout || getAbortController || _abortController) + // eslint-disable-next-line + abortController = + (getAbortController ? getAbortController() : null) || + _abortController || + new AbortController() + + if (abortController) { + fetchOptions.signal = abortController.signal + } + } // Kick off timer for abortController directly before fetch. let timer if (timeout) diff --git a/packages/sdk-middleware-http/test/http.spec.js b/packages/sdk-middleware-http/test/http.spec.js index 2505b1dd8..17ba8ef07 100644 --- a/packages/sdk-middleware-http/test/http.spec.js +++ b/packages/sdk-middleware-http/test/http.spec.js @@ -1116,4 +1116,89 @@ describe('Http', () => { httpMiddleware(next)(request, response) })) + + test('should retry when request is aborted (success)', () => { + expect.assertions(1) + return new Promise((resolve, reject) => { + const request = createTestRequest({ + uri: '/foo/bar', + }) + const response = { resolve, reject } + const next = (req, res) => { + expect(res).toEqual({ + ...response, + body: { foo: 'bar' }, + statusCode: 200, + }) + resolve() + } + // Use default options + const httpMiddleware = createHttpMiddleware({ + host: testHost, + timeout: 100, + fetch, + enableRetry: true, + retryConfig: { + retryOnAbort: true, + }, + getAbortController: () => new AbortController(), + }) + nock(testHost) + .defaultReplyHeaders({ + 'Content-Type': 'application/json', + }) + .get('/foo/bar') + .once() + .delay(200) // delay response to fail + .reply(200, { foo: 'bar' }) + .get('/foo/bar') + .delay(50) // delay lower then timeout + .reply(200, { foo: 'bar' }) + + httpMiddleware(next)(request, response) + }) + }) + + test('should retry when requests are aborted (fail)', () => { + expect.assertions(1) + return new Promise((resolve, reject) => { + const request = createTestRequest({ + uri: '/foo/bar', + }) + const response = { resolve, reject } + const next = (req, res) => { + expect(res).toEqual({ + ...response, + error: expect.any(Error), + statusCode: 0, + }) + resolve() + } + // Use default options + const httpMiddleware = createHttpMiddleware({ + host: testHost, + timeout: 100, // time out after 10ms + fetch, + enableRetry: true, + retryConfig: { + maxRetries: 1, + retryOnAbort: true, + }, + getAbortController: () => new AbortController(), + }) + nock(testHost) + .defaultReplyHeaders({ + 'Content-Type': 'application/json', + }) + .get('/foo/bar') + .once() + .delay(150) // delay response to fail (higher than timeout) + .reply(200, { foo: 'bar' }) + .get('/foo/bar') + .delay(150) // delay response to fail (higher than timeout) + .reply(200, { foo: 'bar' }) + + httpMiddleware(next)(request, response) + }) + }) }) diff --git a/types/sdk.js b/types/sdk.js index 4ca6f0fd0..867728b63 100644 --- a/types/sdk.js +++ b/types/sdk.js @@ -241,6 +241,7 @@ export type HttpMiddlewareOptions = { retryDelay?: number, backoff?: boolean, maxDelay?: number, + retryOnAbort: boolean }, fetch?: typeof fetch, /**