diff --git a/src/error/TimeoutError.ts b/src/error/TimeoutError.ts new file mode 100644 index 0000000..18f20e4 --- /dev/null +++ b/src/error/TimeoutError.ts @@ -0,0 +1 @@ +export class TimeoutError extends Error {} diff --git a/src/error/index.ts b/src/error/index.ts index 2392dd4..831e515 100644 --- a/src/error/index.ts +++ b/src/error/index.ts @@ -3,3 +3,4 @@ export * from './HttpError'; export * from './NotFoundError'; export * from './QueueIsFullError'; export * from './TooManyRequestsError'; +export * from './TimeoutError'; diff --git a/src/types.ts b/src/types.ts index 124ff96..c13ae27 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,7 +1,5 @@ import { Manifest, ManifestConfig } from 'stremio-addon-sdk'; -export const TIMEOUT = 'timeout'; - export interface Context { id: string; ip: string; diff --git a/src/utils/Fetcher.test.ts b/src/utils/Fetcher.test.ts index 05e630e..e311e75 100644 --- a/src/utils/Fetcher.test.ts +++ b/src/utils/Fetcher.test.ts @@ -2,7 +2,7 @@ import winston from 'winston'; import fetchMock from 'fetch-mock'; import { Fetcher } from './Fetcher'; import { Context } from '../types'; -import { BlockedError, HttpError, NotFoundError, QueueIsFullError, TooManyRequestsError } from '../error'; +import { BlockedError, HttpError, NotFoundError, QueueIsFullError, TimeoutError, TooManyRequestsError } from '../error'; fetchMock.mockGlobal(); const fetcher = new Fetcher(winston.createLogger({ transports: [new winston.transports.Console({ level: 'nope' })] })); @@ -182,7 +182,7 @@ describe('fetch', () => { test('times out', async () => { fetchMock.get('https://some-timeout-url.test/', 200, { delay: 20 }); - await expect(fetcher.text(ctx, new URL('https://some-timeout-url.test/'), { timeout: 10 })).rejects.toBeInstanceOf(DOMException); + await expect(fetcher.text(ctx, new URL('https://some-timeout-url.test/'), { timeout: 10 })).rejects.toBeInstanceOf(TimeoutError); }); test('queues requests and throws if queue is full', async () => { diff --git a/src/utils/Fetcher.ts b/src/utils/Fetcher.ts index 58918a0..a864185 100644 --- a/src/utils/Fetcher.ts +++ b/src/utils/Fetcher.ts @@ -3,8 +3,8 @@ import TTLCache from '@isaacs/ttlcache'; import winston from 'winston'; import { Semaphore, SemaphoreInterface, withTimeout } from 'async-mutex'; import { Cookie, CookieJar } from 'tough-cookie'; -import { BlockedReason, Context, TIMEOUT } from '../types'; -import { BlockedError, HttpError, NotFoundError, QueueIsFullError, TooManyRequestsError } from '../error'; +import { BlockedReason, Context } from '../types'; +import { BlockedError, HttpError, NotFoundError, QueueIsFullError, TimeoutError, TooManyRequestsError } from '../error'; import { clearTimeout } from 'node:timers'; import { envGet } from './env'; @@ -229,11 +229,17 @@ export class Fetcher { } const controller = new AbortController(); - const timer = setTimeout(() => controller.abort(TIMEOUT), init?.timeout ?? this.DEFAULT_TIMEOUT); + const timer = setTimeout(() => controller.abort(), init?.timeout ?? this.DEFAULT_TIMEOUT); let response; try { response = await fetch(url, { ...init, keepalive: true, signal: controller.signal }); + } catch (error) { + if (error instanceof DOMException && error.name === 'AbortError') { + throw new TimeoutError(); + } + + throw error; } finally { clearTimeout(timer); } diff --git a/src/utils/StreamResolver.test.ts b/src/utils/StreamResolver.test.ts index 76e40ba..5e7e0d9 100644 --- a/src/utils/StreamResolver.test.ts +++ b/src/utils/StreamResolver.test.ts @@ -3,8 +3,8 @@ import winston from 'winston'; import { createExtractors, Extractor, ExtractorRegistry } from '../extractor'; import { StreamResolver } from './StreamResolver'; import { Source, SourceResult, MeineCloud, MostraGuarda } from '../source'; -import { BlockedReason, Context, CountryCode, TIMEOUT, UrlResult } from '../types'; -import { BlockedError, HttpError, NotFoundError, QueueIsFullError, TooManyRequestsError } from '../error'; +import { BlockedReason, Context, CountryCode, UrlResult } from '../types'; +import { BlockedError, HttpError, NotFoundError, QueueIsFullError, TimeoutError, TooManyRequestsError } from '../error'; import { ImdbId } from './id'; import { FetcherMock } from './FetcherMock'; @@ -128,7 +128,7 @@ describe('resolve', () => { { url: new URL('https://example2.com'), isExternal: true, - error: TIMEOUT, + error: new TimeoutError(), label: 'hoster.com', sourceId: '', meta: { diff --git a/src/utils/StreamResolver.ts b/src/utils/StreamResolver.ts index a35ae43..6bfbdf0 100644 --- a/src/utils/StreamResolver.ts +++ b/src/utils/StreamResolver.ts @@ -1,9 +1,9 @@ import { ContentType, Stream } from 'stremio-addon-sdk'; import winston from 'winston'; import bytes from 'bytes'; -import { Context, TIMEOUT, UrlResult } from '../types'; +import { Context, UrlResult } from '../types'; import { Source } from '../source'; -import { BlockedError, HttpError, NotFoundError, QueueIsFullError, TooManyRequestsError } from '../error'; +import { BlockedError, HttpError, NotFoundError, QueueIsFullError, TimeoutError, TooManyRequestsError } from '../error'; import { flagFromCountryCode, languageFromCountryCode } from './language'; import { envGetAppName } from './env'; import { Id } from './id'; @@ -169,7 +169,7 @@ export class StreamResolver { return '🚦 Request was rate-limited. Please try again later or consider self-hosting.'; } - if (error === TIMEOUT) { + if (error instanceof TimeoutError) { this.logger.warn(`${source}: Request timed out.`, ctx); return '🐢 Request timed out.';