Implement following redirects over `fetch` supporting cookies.
`node-fetch` does not support sending cookies during redirect. However,
this is needed to not end-up in a redirect loop for a sign-in callback.
Fix integration tests failing due to redirects and 403 errors:
- Many redirects from `answers.microsoft.com` was throwing: throwing
`FetchError: maximum redirect reached` error. It was caused by not
having cookies when following redirects therefore having an infinite
sign-in callback for the webpage.
- Fixes integration tests failing due to additional referer header being
sent by the application. It adds support for making exceptions to
additional header sending through a list of regexes.
Add in-depth documentation for URL status checking.
45 lines
1.8 KiB
TypeScript
45 lines
1.8 KiB
TypeScript
import { sleepAsync } from '@/infrastructure/Threading/AsyncSleep';
|
||
import { IUrlStatus } from './IUrlStatus';
|
||
|
||
const DefaultBaseRetryIntervalInMs = 5 /* sec */ * 1000;
|
||
|
||
export async function retryWithExponentialBackOffAsync(
|
||
action: () => Promise<IUrlStatus>,
|
||
baseRetryIntervalInMs: number = DefaultBaseRetryIntervalInMs,
|
||
currentRetry = 1): Promise<IUrlStatus> {
|
||
const maxTries: number = 3;
|
||
const status = await action();
|
||
if (shouldRetry(status)) {
|
||
if (currentRetry <= maxTries) {
|
||
const exponentialBackOffInMs = getRetryTimeoutInMs(currentRetry, baseRetryIntervalInMs);
|
||
// tslint:disable-next-line: no-console
|
||
console.log(`Retrying (${currentRetry}) in ${exponentialBackOffInMs / 1000} seconds`, status);
|
||
await sleepAsync(exponentialBackOffInMs);
|
||
return retryWithExponentialBackOffAsync(action, baseRetryIntervalInMs, currentRetry + 1);
|
||
}
|
||
}
|
||
return status;
|
||
}
|
||
|
||
function shouldRetry(status: IUrlStatus) {
|
||
if (status.error) {
|
||
return true;
|
||
}
|
||
return isTransientError(status.code)
|
||
|| status.code === 429; // Too Many Requests
|
||
}
|
||
|
||
function isTransientError(statusCode: number) {
|
||
return statusCode >= 500 && statusCode <= 599;
|
||
}
|
||
|
||
function getRetryTimeoutInMs(currentRetry: number, baseRetryIntervalInMs: number = DefaultBaseRetryIntervalInMs) {
|
||
const retryRandomFactor = 0.5; // Retry intervals are between 50% and 150%
|
||
// of the exponentially increasing base amount
|
||
const minRandom = 1 - retryRandomFactor;
|
||
const maxRandom = 1 + retryRandomFactor;
|
||
const randomization = (Math.random() * (maxRandom - minRandom)) + maxRandom;
|
||
const exponential = Math.pow(2, currentRetry - 1);
|
||
return Math.ceil(exponential * baseRetryIntervalInMs * randomization);
|
||
}
|