split up http into adapters
This commit is contained in:
parent
afc1843fdb
commit
d67d07cba6
11 changed files with 230 additions and 228 deletions
53
libs/http/src/adapters/axios-adapter.ts
Normal file
53
libs/http/src/adapters/axios-adapter.ts
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import axios, { type AxiosRequestConfig, type AxiosResponse } from 'axios';
|
||||
import type { RequestConfig, HttpResponse } from '../types.js';
|
||||
import type { RequestAdapter } from './types.js';
|
||||
import { ProxyManager } from '../proxy-manager.js';
|
||||
import { HttpError } from '../types.js';
|
||||
|
||||
/**
|
||||
* Axios adapter for SOCKS proxies
|
||||
*/
|
||||
export class AxiosAdapter implements RequestAdapter {
|
||||
canHandle(config: RequestConfig): boolean {
|
||||
// Axios handles SOCKS proxies
|
||||
return Boolean(config.proxy && (config.proxy.protocol === 'socks4' || config.proxy.protocol === 'socks5'));
|
||||
}
|
||||
|
||||
async request<T = any>(config: RequestConfig, signal: AbortSignal): Promise<HttpResponse<T>> {
|
||||
const { url, method = 'GET', headers, data, proxy } = config;
|
||||
|
||||
if (!proxy) {
|
||||
throw new Error('Axios adapter requires proxy configuration');
|
||||
}
|
||||
|
||||
// Create proxy configuration using ProxyManager
|
||||
const axiosConfig: AxiosRequestConfig = {
|
||||
...ProxyManager.createAxiosConfig(proxy),
|
||||
url,
|
||||
method,
|
||||
headers,
|
||||
data,
|
||||
signal,
|
||||
// Don't throw on non-2xx status codes - let caller handle
|
||||
validateStatus: () => true,
|
||||
}; const response: AxiosResponse<T> = await axios(axiosConfig);
|
||||
|
||||
const httpResponse: HttpResponse<T> = {
|
||||
data: response.data,
|
||||
status: response.status,
|
||||
headers: response.headers as Record<string, string>,
|
||||
ok: response.status >= 200 && response.status < 300,
|
||||
};
|
||||
|
||||
// Throw HttpError for non-2xx status codes
|
||||
if (!httpResponse.ok) {
|
||||
throw new HttpError(
|
||||
`Request failed with status ${response.status}`,
|
||||
response.status,
|
||||
httpResponse
|
||||
);
|
||||
}
|
||||
|
||||
return httpResponse;
|
||||
}
|
||||
}
|
||||
28
libs/http/src/adapters/factory.ts
Normal file
28
libs/http/src/adapters/factory.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import type { RequestConfig } from '../types.js';
|
||||
import type { RequestAdapter } from './types.js';
|
||||
import { FetchAdapter } from './fetch-adapter.js';
|
||||
import { AxiosAdapter } from './axios-adapter.js';
|
||||
|
||||
/**
|
||||
* Factory for creating the appropriate request adapter
|
||||
*/
|
||||
export class AdapterFactory {
|
||||
private static adapters: RequestAdapter[] = [
|
||||
new AxiosAdapter(), // Check SOCKS first
|
||||
new FetchAdapter(), // Fallback to fetch for everything else
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the appropriate adapter for the given configuration
|
||||
*/
|
||||
static getAdapter(config: RequestConfig): RequestAdapter {
|
||||
for (const adapter of this.adapters) {
|
||||
if (adapter.canHandle(config)) {
|
||||
return adapter;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to fetch adapter
|
||||
return new FetchAdapter();
|
||||
}
|
||||
}
|
||||
66
libs/http/src/adapters/fetch-adapter.ts
Normal file
66
libs/http/src/adapters/fetch-adapter.ts
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
import type { RequestConfig, HttpResponse } from '../types.js';
|
||||
import type { RequestAdapter } from './types.js';
|
||||
import { ProxyManager } from '../proxy-manager.js';
|
||||
import { HttpError } from '../types.js';
|
||||
|
||||
/**
|
||||
* Fetch adapter for HTTP/HTTPS proxies and non-proxy requests
|
||||
*/
|
||||
export class FetchAdapter implements RequestAdapter {
|
||||
canHandle(config: RequestConfig): boolean {
|
||||
// Fetch handles non-proxy requests and HTTP/HTTPS proxies
|
||||
return !config.proxy || config.proxy.protocol === 'http' || config.proxy.protocol === 'https';
|
||||
}
|
||||
|
||||
async request<T = any>(config: RequestConfig, signal: AbortSignal): Promise<HttpResponse<T>> {
|
||||
const { url, method = 'GET', headers, data, proxy } = config;
|
||||
|
||||
// Prepare fetch options
|
||||
const fetchOptions: RequestInit = {
|
||||
method,
|
||||
headers,
|
||||
signal,
|
||||
};
|
||||
|
||||
// Add body for non-GET requests
|
||||
if (data && method !== 'GET') {
|
||||
fetchOptions.body = typeof data === 'string' ? data : JSON.stringify(data);
|
||||
if (typeof data === 'object') {
|
||||
fetchOptions.headers = { 'Content-Type': 'application/json', ...fetchOptions.headers };
|
||||
}
|
||||
}
|
||||
|
||||
// Add proxy if needed (using Bun's built-in proxy support)
|
||||
if (proxy) {
|
||||
(fetchOptions as any).proxy = ProxyManager.createProxyUrl(proxy);
|
||||
} const response = await fetch(url, fetchOptions);
|
||||
|
||||
// Parse response based on content type
|
||||
let responseData: T;
|
||||
const contentType = response.headers.get('content-type') || '';
|
||||
|
||||
if (contentType.includes('application/json')) {
|
||||
responseData = await response.json() as T;
|
||||
} else {
|
||||
responseData = await response.text() as T;
|
||||
}
|
||||
|
||||
const httpResponse: HttpResponse<T> = {
|
||||
data: responseData,
|
||||
status: response.status,
|
||||
headers: Object.fromEntries(response.headers.entries()),
|
||||
ok: response.ok,
|
||||
};
|
||||
|
||||
// Throw HttpError for non-2xx status codes
|
||||
if (!response.ok) {
|
||||
throw new HttpError(
|
||||
`Request failed with status ${response.status}`,
|
||||
response.status,
|
||||
httpResponse
|
||||
);
|
||||
}
|
||||
|
||||
return httpResponse;
|
||||
}
|
||||
}
|
||||
4
libs/http/src/adapters/index.ts
Normal file
4
libs/http/src/adapters/index.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
export * from './types.js';
|
||||
export * from './fetch-adapter.js';
|
||||
export * from './axios-adapter.js';
|
||||
export * from './factory.js';
|
||||
16
libs/http/src/adapters/types.ts
Normal file
16
libs/http/src/adapters/types.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import type { RequestConfig, HttpResponse } from '../types.js';
|
||||
|
||||
/**
|
||||
* Request adapter interface for different HTTP implementations
|
||||
*/
|
||||
export interface RequestAdapter {
|
||||
/**
|
||||
* Execute an HTTP request
|
||||
*/
|
||||
request<T = any>(config: RequestConfig, signal: AbortSignal): Promise<HttpResponse<T>>;
|
||||
|
||||
/**
|
||||
* Check if this adapter can handle the given configuration
|
||||
*/
|
||||
canHandle(config: RequestConfig): boolean;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue