added signal to both got and fetch

This commit is contained in:
Bojan Kucera 2025-06-07 13:00:54 -04:00
parent f70a8be1cb
commit e87acb5e18

View file

@ -36,8 +36,7 @@ export class HttpClient {
async patch<T = any>(url: string, body?: any, config: Omit<RequestConfig, 'method' | 'url' | 'body'> = {}): Promise<HttpResponse<T>> { async patch<T = any>(url: string, body?: any, config: Omit<RequestConfig, 'method' | 'url' | 'body'> = {}): Promise<HttpResponse<T>> {
return this.request<T>({ ...config, method: 'PATCH', url, body }); return this.request<T>({ ...config, method: 'PATCH', url, body });
} } /**
/**
* Main request method - unified and simplified * Main request method - unified and simplified
*/ */
async request<T = any>(config: RequestConfig): Promise<HttpResponse<T>> { async request<T = any>(config: RequestConfig): Promise<HttpResponse<T>> {
@ -54,9 +53,7 @@ export class HttpClient {
const proxy = finalConfig.proxy; const proxy = finalConfig.proxy;
const useBunFetch = !proxy || ProxyManager.shouldUseBunFetch(proxy); const useBunFetch = !proxy || ProxyManager.shouldUseBunFetch(proxy);
const response = useBunFetch const response = await this.makeRequest<T>(finalConfig, useBunFetch);
? await this.fetchRequest<T>(finalConfig)
: await this.gotRequest<T>(finalConfig);
this.logger?.debug('HTTP request successful', { this.logger?.debug('HTTP request successful', {
method: finalConfig.method, method: finalConfig.method,
@ -76,39 +73,66 @@ export class HttpClient {
} }
/** /**
* Bun fetch implementation (simplified) * Unified request method with consolidated timeout handling
*/ */
private async fetchRequest<T>(config: RequestConfig): Promise<HttpResponse<T>> { private async makeRequest<T>(config: RequestConfig, useBunFetch: boolean): Promise<HttpResponse<T>> {
const timeout = config.timeout ?? this.config.timeout ?? 30000; const timeout = config.timeout ?? this.config.timeout ?? 30000;
const controller = new AbortController(); const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout); const timeoutId = setTimeout(() => controller.abort(), timeout);
try { try {
const options = this.buildFetchOptions(config, controller.signal); const response = useBunFetch
const response = await fetch(config.url, options); ? await this.fetchRequest<T>(config, controller.signal)
: await this.gotRequest<T>(config, controller.signal);
clearTimeout(timeoutId); clearTimeout(timeoutId);
return this.parseFetchResponse<T>(response); return response;
} catch (error) { } catch (error) {
clearTimeout(timeoutId); clearTimeout(timeoutId);
throw controller.signal.aborted
? new HttpError(`Request timeout after ${timeout}ms`) // Unified timeout error handling
: new HttpError(`Request failed: ${(error as Error).message}`); 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<T>(config: RequestConfig, signal: AbortSignal): Promise<HttpResponse<T>> {
try {
const options = this.buildFetchOptions(config, signal);
const response = await fetch(config.url, options);
return this.parseFetchResponse<T>(response);
} catch (error) {
throw signal.aborted
? new HttpError(`Request timeout`)
: new HttpError(`Request failed: ${(error as Error).message}`);
}
} /**
* Got implementation (simplified for SOCKS proxies) * Got implementation (simplified for SOCKS proxies)
*/ */
private async gotRequest<T>(config: RequestConfig): Promise<HttpResponse<T>> { private async gotRequest<T>(config: RequestConfig, signal: AbortSignal): Promise<HttpResponse<T>> {
const timeout = config.timeout ?? this.config.timeout ?? 30000;
try { try {
const options = this.buildGotOptions(config, timeout); const options = this.buildGotOptions(config, signal);
const response = await got(config.url, options); const response = await got(config.url, options);
return this.parseGotResponse<T>(response); return this.parseGotResponse<T>(response);
} catch (error) { } 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}`); throw new HttpError(`Request failed: ${(error as Error).message}`);
} }
} }
@ -138,18 +162,14 @@ export class HttpClient {
} }
return options; return options;
} } /**
/**
* Build Got options (extracted for clarity) * Build Got options (extracted for clarity)
*/ */
private buildGotOptions(config: RequestConfig, timeout: number): any { private buildGotOptions(config: RequestConfig, signal: AbortSignal): any {
const options: any = { const options: any = {
method: config.method || 'GET', method: config.method || 'GET',
headers: config.headers || {}, headers: config.headers || {},
timeout: { signal, // Use AbortSignal instead of Got's timeout
request: timeout,
connect: 10000
},
retry: { retry: {
limit: 3, limit: 3,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'] methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH']