diff --git a/packages/auth-fetch/src/auth-fetch.ts b/packages/auth-fetch/src/auth-fetch.ts index 469af63fb..6867e306d 100644 --- a/packages/auth-fetch/src/auth-fetch.ts +++ b/packages/auth-fetch/src/auth-fetch.ts @@ -91,20 +91,26 @@ export function createAuthFetch( const initialResponse = await h({ ...options, signal: controller.signal, - ignoreStatusCode: true, + // need to intercept the status code to check for 401. + // all other status codes will be handled according to the initial request options. + checkStatusCode(statusCode) { + // can handle a 401 if an credential is provided. + // however, not providing a credential is also valid, and should + // fall through to the normal response handling which may be interested + // in the 401 response. + if (statusCode === 401 && options.credential) + return true; + if (options?.checkStatusCode === undefined || options?.checkStatusCode) { + const checker = typeof options?.checkStatusCode === 'function' ? options.checkStatusCode : checkStatus; + return checker(statusCode); + } + return true; + }, responseType: 'readable', }); - if (initialResponse.statusCode !== 401 || !options.credential) { - if (!options?.ignoreStatusCode) { - try { - checkStatus(initialResponse.statusCode); - } - catch (e) { - controller.abort('Invalid status code'); - throw e; - } - } + // if it's not a 401, just return the response. + if (initialResponse.statusCode !== 401) { return { ...initialResponse, body: await parser(initialResponse.body, options.responseType), diff --git a/plugins/hikvision/src/probe.ts b/plugins/hikvision/src/probe.ts index 88456be41..fda0b374f 100644 --- a/plugins/hikvision/src/probe.ts +++ b/plugins/hikvision/src/probe.ts @@ -5,7 +5,7 @@ export async function getDeviceInfo(credential: AuthFetchCredentialState, addres const response = await authHttpFetch({ credential, url: `http://${address}/ISAPI/System/deviceInfo`, - ignoreStatusCode: true, + checkStatusCode: false, responseType: 'text', rejectUnauthorized: false, }); diff --git a/plugins/tapo/src/tapo-api.ts b/plugins/tapo/src/tapo-api.ts index 2d0ca12ae..69be4cd02 100644 --- a/plugins/tapo/src/tapo-api.ts +++ b/plugins/tapo/src/tapo-api.ts @@ -30,7 +30,7 @@ export class TapoAPI { const response = await authHttpFetch({ credential: undefined, url: url, - ignoreStatusCode: true, + checkStatusCode: false, method: 'POST', headers: { 'Content-Type': 'multipart/mixed; boundary=--client-stream-boundary--', diff --git a/server/src/fetch/http-fetch.ts b/server/src/fetch/http-fetch.ts index e328063ab..0aa45eae5 100644 --- a/server/src/fetch/http-fetch.ts +++ b/server/src/fetch/http-fetch.ts @@ -122,9 +122,12 @@ export async function httpFetch>(options: T try { const [response] = await once(request, 'response') as [IncomingMessage]; - if (!options?.ignoreStatusCode) { + + if (options?.checkStatusCode === undefined || options?.checkStatusCode) { try { - checkStatus(response.statusCode); + const checker = typeof options?.checkStatusCode === 'function' ? options.checkStatusCode : checkStatus; + if (!checker(response.statusCode)) + throw new Error(`http response statusCode ${response.statusCode}`); } catch (e) { readMessageBuffer(response).catch(() => { }); diff --git a/server/src/fetch/index.ts b/server/src/fetch/index.ts index f390d3fad..96ecb2eb5 100644 --- a/server/src/fetch/index.ts +++ b/server/src/fetch/index.ts @@ -7,7 +7,10 @@ export interface HttpFetchOptionsBase { signal?: AbortSignal, timeout?: number; rejectUnauthorized?: boolean; - ignoreStatusCode?: boolean; + /** + * Checks the status code. Defaults to true. + */ + checkStatusCode?: boolean | ((statusCode: number) => boolean); body?: B | string | ArrayBufferView | any; withCredentials?: boolean; } @@ -40,6 +43,7 @@ export function fetchStatusCodeOk(statusCode: number) { export function checkStatus(statusCode: number) { if (!fetchStatusCodeOk(statusCode)) throw new Error(`http response statusCode ${statusCode}`); + return true; } export function getFetchMethod(options: HttpFetchOptions) { @@ -190,9 +194,11 @@ export async function domFetch>(options: T) body, }); - if (!options?.ignoreStatusCode) { + if (options?.checkStatusCode === undefined || options?.checkStatusCode) { try { - checkStatus(response.status); + const checker = typeof options?.checkStatusCode === 'function' ? options.checkStatusCode : checkStatus; + if (!checker(response.status)) + throw new Error(`http response statusCode ${response.status}`); } catch (e) { response.arrayBuffer().catch(() => { });