diff --git a/api-extractor/report/hls.js.api.md b/api-extractor/report/hls.js.api.md index c20d2761315..90b54180636 100644 --- a/api-extractor/report/hls.js.api.md +++ b/api-extractor/report/hls.js.api.md @@ -2954,6 +2954,7 @@ export type RetryConfig = { retryDelayMs: number; maxRetryDelayMs: number; backoff?: 'exponential' | 'linear'; + shouldRetry?: (retryConfig: RetryConfig | null | undefined, retryCount: number, isTimeout: boolean, httpStatus: number | undefined, retry: boolean) => boolean; }; // Warning: (ae-missing-release-tag) "SourceBufferName" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) diff --git a/docs/API.md b/docs/API.md index ed0d49cc3b3..5325fb79fa5 100644 --- a/docs/API.md +++ b/docs/API.md @@ -64,6 +64,7 @@ See [API Reference](https://hlsjs-dev.video-dev.org/api-docs/) for a complete li - [`retryDelayMs: number`](#retrydelayms-number) - [`maxRetryDelayMs: number`](#maxretrydelayms-number) - [`backoff?: 'exponential' | 'linear'`](#backoff-exponential--linear) + - [`shouldRetry`](#shouldretry) - [`startFragPrefetch`](#startfragprefetch) - [`testBandwidth`](#testbandwidth) - [`progressive`](#progressive) @@ -871,6 +872,12 @@ Maximum delay between retries in milliseconds. With each retry, the delay is inc Used to determine retry backoff duration: Retry delay = 2^retryCount \* retryDelayMs (exponential). +##### `shouldRetry` + +(default: internal shouldRetry function, type: `(retryConfig: RetryConfig | null | undefined, retryCount: number, isTimeout: boolean, httpStatus: number | undefined,retry: boolean) => boolean`) + +Override default shouldRetry check + ### `startFragPrefetch` (default: `false`) diff --git a/src/config.ts b/src/config.ts index c2c13915a0c..2fe2cef207e 100644 --- a/src/config.ts +++ b/src/config.ts @@ -189,6 +189,13 @@ export type RetryConfig = { retryDelayMs: number; // Retry delay = 2^retryCount * retryDelayMs (exponential) or retryCount * retryDelayMs (linear) maxRetryDelayMs: number; // Maximum delay between retries backoff?: 'exponential' | 'linear'; // used to determine retry backoff duration (see retryDelayMs) + shouldRetry?: ( + retryConfig: RetryConfig | null | undefined, + retryCount: number, + isTimeout: boolean, + httpStatus: number | undefined, + retry: boolean + ) => boolean; }; export type StreamControllerConfig = { diff --git a/src/utils/error-helper.ts b/src/utils/error-helper.ts index 7ffccc4002b..b7dbc60d1c3 100644 --- a/src/utils/error-helper.ts +++ b/src/utils/error-helper.ts @@ -52,11 +52,21 @@ export function shouldRetry( isTimeout: boolean, httpStatus?: number | undefined, ): retryConfig is RetryConfig & boolean { - return ( - !!retryConfig && + if (!retryConfig) { + return false; + } + const retry = retryCount < retryConfig.maxNumRetry && - (retryForHttpStatus(httpStatus) || !!isTimeout) - ); + (retryForHttpStatus(httpStatus) || !!isTimeout); + return retryConfig.shouldRetry + ? retryConfig.shouldRetry( + retryConfig, + retryCount, + isTimeout, + httpStatus, + retry + ) + : retry; } export function retryForHttpStatus(httpStatus: number | undefined) { diff --git a/tests/index.js b/tests/index.js index d42b873799e..17e3f85fd80 100644 --- a/tests/index.js +++ b/tests/index.js @@ -38,6 +38,7 @@ import './unit/loader/playlist-loader'; import './unit/utils/attr-list'; import './unit/utils/binary-search'; import './unit/utils/buffer-helper'; +import './unit/utils/error-helper'; import './unit/utils/discontinuities'; import './unit/utils/exp-golomb'; import './unit/utils/output-filter'; diff --git a/tests/unit/utils/error-helper.js b/tests/unit/utils/error-helper.js new file mode 100644 index 00000000000..010285af1cb --- /dev/null +++ b/tests/unit/utils/error-helper.js @@ -0,0 +1,33 @@ +import { shouldRetry } from '../../../src/utils/error-helper'; + +describe('ErrorHelper', function () { + it('shouldRetry', function () { + const retryConfig = { + maxNumRetry: 3, + }; + expect(shouldRetry(retryConfig, 3, false, '502')).to.be.false; + expect(shouldRetry(null, 3, false, '502')).to.be.false; + expect(shouldRetry(retryConfig, 2, false, '502')).to.be.true; + expect(shouldRetry(retryConfig, 2, false, '404')).to.be.false; + + retryConfig.shouldRetry = ( + _retryConfig, + _retryCount, + _isTimeout, + httpStatus, + retry + ) => { + if (!retry && httpStatus === '404') { + return true; + } + + return false; + }; + expect(shouldRetry(retryConfig, 5, false, '404', false)).to.be.true; + + retryConfig.shouldRetry = (retryConfig, retryCount) => { + return retryConfig.maxNumRetry <= retryCount; + }; + expect(shouldRetry(retryConfig, 2, false, '502', true)).to.be.false; + }); +});