chore: introduce HttpError and show status in result
This commit is contained in:
parent
119e338292
commit
d8f0c4602c
7 changed files with 56 additions and 8 deletions
11
src/error/HttpError.ts
Normal file
11
src/error/HttpError.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
export class HttpError extends Error {
|
||||
public readonly status: number;
|
||||
public readonly headers: Record<string, string[] | string | undefined>;
|
||||
|
||||
constructor(status: number, headers: Record<string, string[] | string | undefined>) {
|
||||
super();
|
||||
|
||||
this.status = status;
|
||||
this.headers = headers;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
export * from './BlockedError';
|
||||
export * from './HttpError';
|
||||
export * from './NotFoundError';
|
||||
export * from './QueueIsFullError';
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import winston from 'winston';
|
|||
import fetchMock from 'fetch-mock';
|
||||
import { Fetcher } from './Fetcher';
|
||||
import { Context } from '../types';
|
||||
import { BlockedError, NotFoundError, QueueIsFullError } from '../error';
|
||||
import { BlockedError, HttpError, NotFoundError, QueueIsFullError } from '../error';
|
||||
fetchMock.mockGlobal();
|
||||
|
||||
const fetcher = new Fetcher(winston.createLogger({ transports: [new winston.transports.Console({ level: 'nope' })] }));
|
||||
|
|
@ -143,11 +143,16 @@ describe('fetch', () => {
|
|||
}
|
||||
});
|
||||
|
||||
test('passes through other errors with detailed infos', async () => {
|
||||
test('passes through other error as HttpError', async () => {
|
||||
fetchMock.get('https://some-error-url.test/', { status: 500, headers: { 'x-foo': 'bar' } });
|
||||
|
||||
await expect(fetcher.text(ctx, new URL('https://some-error-url.test/'))).rejects
|
||||
.toThrow('Fetcher error: 500: Internal Server Error, response headers: {"content-length":"0","x-foo":"bar","age":"0","date":"Wed, 11 Apr 1990 12:34:56 GMT"}');
|
||||
try {
|
||||
await fetcher.text(ctx, new URL('https://some-error-url.test/'));
|
||||
fail();
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(HttpError);
|
||||
expect(error).toMatchObject({ status: 500, headers: { 'x-foo': 'bar' } });
|
||||
}
|
||||
});
|
||||
|
||||
test('passes through exceptions', async () => {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import winston from 'winston';
|
|||
import { Mutex } from 'async-mutex';
|
||||
import { Cookie, CookieJar } from 'tough-cookie';
|
||||
import { Context, TIMEOUT } from '../types';
|
||||
import { BlockedError, NotFoundError, QueueIsFullError } from '../error';
|
||||
import { BlockedError, HttpError, NotFoundError, QueueIsFullError } from '../error';
|
||||
import { clearTimeout } from 'node:timers';
|
||||
import { envGet } from './env';
|
||||
|
||||
|
|
@ -157,7 +157,7 @@ export class Fetcher {
|
|||
throw new BlockedError('unknown', httpCacheItem.policy.responseHeaders());
|
||||
}
|
||||
|
||||
throw new Error(`Fetcher error: ${httpCacheItem.status}: ${httpCacheItem.statusText}, response headers: ${JSON.stringify(responseHeaders)}`);
|
||||
throw new HttpError(httpCacheItem.status, responseHeaders);
|
||||
};
|
||||
|
||||
private readonly determineTtl = (httpCacheItem: HttpCacheItem): number => {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { StreamResolver } from './StreamResolver';
|
|||
import { Handler, MeineCloud, MostraGuarda } from '../handler';
|
||||
import { Fetcher } from './Fetcher';
|
||||
import { Context, CountryCode, TIMEOUT, UrlResult } from '../types';
|
||||
import { BlockedError, NotFoundError, QueueIsFullError } from '../error';
|
||||
import { BlockedError, HttpError, NotFoundError, QueueIsFullError } from '../error';
|
||||
jest.mock('../utils/Fetcher');
|
||||
|
||||
const logger = winston.createLogger({ transports: [new winston.transports.Console({ level: 'nope' })] });
|
||||
|
|
@ -113,6 +113,16 @@ describe('resolve', () => {
|
|||
countryCode: 'de',
|
||||
},
|
||||
},
|
||||
{
|
||||
url: new URL('https://example4.com'),
|
||||
isExternal: true,
|
||||
error: new HttpError(500, { 'x-foo': 'bar' }),
|
||||
label: 'hoster.com',
|
||||
sourceId: '',
|
||||
meta: {
|
||||
countryCode: 'de',
|
||||
},
|
||||
},
|
||||
];
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import winston from 'winston';
|
|||
import bytes from 'bytes';
|
||||
import { Context, TIMEOUT, UrlResult } from '../types';
|
||||
import { Handler } from '../handler';
|
||||
import { BlockedError, NotFoundError, QueueIsFullError } from '../error';
|
||||
import { BlockedError, HttpError, NotFoundError, QueueIsFullError } from '../error';
|
||||
import { languageFromCountryCode } from './languageFromCountryCode';
|
||||
import { envGetAppName } from './env';
|
||||
|
||||
|
|
@ -165,6 +165,11 @@ export class StreamResolver {
|
|||
return '⏳ Request queue is full. Please try again later or consider self-hosting.';
|
||||
}
|
||||
|
||||
if (error instanceof HttpError) {
|
||||
this.logger.error(`${source}: HTTP status ${error.status}, headers: ${JSON.stringify(error.headers)}.`, ctx);
|
||||
return `❌ Request failed with status ${error.status}. Request-id: ${ctx.id}.`;
|
||||
}
|
||||
|
||||
const cause = (error as Error & { cause?: unknown }).cause;
|
||||
this.logger.error(`${source} error: ${error}, cause: ${cause}`, ctx);
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,14 @@ exports[`resolve adds error info 1`] = `
|
|||
🔗 hoster.com
|
||||
⏳ Request queue is full. Please try again later or consider self-hosting.",
|
||||
},
|
||||
{
|
||||
"behaviorHints": {},
|
||||
"externalUrl": "https://example4.com/",
|
||||
"name": "WebStreamr N/A ⚠️ external",
|
||||
"title": "🌐 German 🇩🇪
|
||||
🔗 hoster.com
|
||||
❌ Request failed with status 500. Request-id: id.",
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
|
@ -90,6 +98,14 @@ exports[`resolve adds error info 2`] = `
|
|||
⏳ Request queue is full. Please try again later or consider self-hosting.",
|
||||
"ytId": "E4WlUXrJgy4",
|
||||
},
|
||||
{
|
||||
"behaviorHints": {},
|
||||
"name": "WebStreamr N/A",
|
||||
"title": "🌐 German 🇩🇪
|
||||
🔗 hoster.com
|
||||
❌ Request failed with status 500. Request-id: id.",
|
||||
"ytId": "E4WlUXrJgy4",
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
|
|
|||
Loading…
Reference in a new issue