From e87acb5e18cfc225128340d099cf31215e7526c9 Mon Sep 17 00:00:00 2001 From: Bojan Kucera Date: Sat, 7 Jun 2025 13:00:54 -0400 Subject: [PATCH] added signal to both got and fetch --- libs/http-client/src/client.ts | 70 ++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 25 deletions(-) diff --git a/libs/http-client/src/client.ts b/libs/http-client/src/client.ts index 45d671a..670662b 100644 --- a/libs/http-client/src/client.ts +++ b/libs/http-client/src/client.ts @@ -36,8 +36,7 @@ export class HttpClient { async patch(url: string, body?: any, config: Omit = {}): Promise> { return this.request({ ...config, method: 'PATCH', url, body }); - } - /** + } /** * Main request method - unified and simplified */ async request(config: RequestConfig): Promise> { @@ -54,9 +53,7 @@ export class HttpClient { const proxy = finalConfig.proxy; const useBunFetch = !proxy || ProxyManager.shouldUseBunFetch(proxy); - const response = useBunFetch - ? await this.fetchRequest(finalConfig) - : await this.gotRequest(finalConfig); + const response = await this.makeRequest(finalConfig, useBunFetch); this.logger?.debug('HTTP request successful', { method: finalConfig.method, @@ -76,39 +73,66 @@ export class HttpClient { } /** - * Bun fetch implementation (simplified) + * Unified request method with consolidated timeout handling */ - private async fetchRequest(config: RequestConfig): Promise> { + private async makeRequest(config: RequestConfig, useBunFetch: boolean): Promise> { const timeout = config.timeout ?? this.config.timeout ?? 30000; const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout); try { - const options = this.buildFetchOptions(config, controller.signal); - const response = await fetch(config.url, options); + const response = useBunFetch + ? await this.fetchRequest(config, controller.signal) + : await this.gotRequest(config, controller.signal); clearTimeout(timeoutId); - return this.parseFetchResponse(response); + return response; } catch (error) { clearTimeout(timeoutId); - throw controller.signal.aborted - ? new HttpError(`Request timeout after ${timeout}ms`) - : new HttpError(`Request failed: ${(error as Error).message}`); + + // Unified timeout error handling + if (controller.signal.aborted) { + throw new HttpError(`Request timeout after ${timeout}ms`); + } + if ((error as any).name === 'TimeoutError') { + throw new HttpError(`Request timeout after ${timeout}ms`); + } + + throw error; // Re-throw other errors as-is } } - /** + * Bun fetch implementation (simplified) + */ + private async fetchRequest(config: RequestConfig, signal: AbortSignal): Promise> { + try { + const options = this.buildFetchOptions(config, signal); + const response = await fetch(config.url, options); + + return this.parseFetchResponse(response); + } catch (error) { + throw signal.aborted + ? new HttpError(`Request timeout`) + : new HttpError(`Request failed: ${(error as Error).message}`); + } + } /** * Got implementation (simplified for SOCKS proxies) */ - private async gotRequest(config: RequestConfig): Promise> { - const timeout = config.timeout ?? this.config.timeout ?? 30000; - + private async gotRequest(config: RequestConfig, signal: AbortSignal): Promise> { try { - const options = this.buildGotOptions(config, timeout); + const options = this.buildGotOptions(config, signal); const response = await got(config.url, options); return this.parseGotResponse(response); } catch (error) { + // Handle both AbortSignal timeout and Got-specific timeout errors + if (signal.aborted) { + throw new HttpError(`Request timeout`); + } + if ((error as any).name === 'TimeoutError') { + throw new HttpError(`Request timeout`); + } + throw new HttpError(`Request failed: ${(error as Error).message}`); } } @@ -138,18 +162,14 @@ export class HttpClient { } return options; - } - /** + } /** * Build Got options (extracted for clarity) */ - private buildGotOptions(config: RequestConfig, timeout: number): any { + private buildGotOptions(config: RequestConfig, signal: AbortSignal): any { const options: any = { method: config.method || 'GET', headers: config.headers || {}, - timeout: { - request: timeout, - connect: 10000 - }, + signal, // Use AbortSignal instead of Got's timeout retry: { limit: 3, methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH']