well seems like no socks on bun
This commit is contained in:
parent
93578bd030
commit
6380165a94
6 changed files with 116 additions and 137 deletions
|
|
@ -6,7 +6,7 @@ import type {
|
|||
} from './types.js';
|
||||
import { HttpError } from './types.js';
|
||||
import { ProxyManager } from './proxy-manager.js';
|
||||
import got from 'got';
|
||||
import axios, { type AxiosResponse, AxiosError } from 'axios';
|
||||
import { clear } from 'node:console';
|
||||
|
||||
export class HttpClient {
|
||||
|
|
@ -51,8 +51,7 @@ export class HttpClient {
|
|||
hasProxy: !!finalConfig.proxy
|
||||
});
|
||||
|
||||
try {
|
||||
// Single decision point for proxy type - only request-level proxy
|
||||
try { // Single decision point for proxy type - only request-level proxy
|
||||
const proxy = finalConfig.proxy;
|
||||
const useBunFetch = !proxy || ProxyManager.shouldUseBunFetch(proxy);
|
||||
|
||||
|
|
@ -92,12 +91,10 @@ export class HttpClient {
|
|||
controller.abort();
|
||||
reject(new HttpError(`Request timeout after ${timeout}ms`));
|
||||
}, timeout);
|
||||
});
|
||||
|
||||
// Create request promise (don't await here!)
|
||||
}); // Create request promise (don't await here!)
|
||||
const requestPromise = useBunFetch
|
||||
? this.fetchRequest<T>(config, controller.signal)
|
||||
: this.gotRequest<T>(config, controller.signal);
|
||||
: this.axiosRequest<T>(config, controller.signal);
|
||||
|
||||
try {
|
||||
// Race the promises
|
||||
|
|
@ -125,12 +122,9 @@ export class HttpClient {
|
|||
*/
|
||||
private async fetchRequest<T>(config: RequestConfig, signal: AbortSignal): Promise<HttpResponse<T>> {
|
||||
try {
|
||||
|
||||
const options = this.buildFetchOptions(config, signal);
|
||||
// console.log('Using fetch with proxy:', config.proxy);
|
||||
// const options = config.proxy? { proxy: config.proxy.protocol + '://' + config.proxy?.host + ':' + config.proxy?.port } : {}//this.buildGotOptions(config, signal);
|
||||
const options = this.buildFetchOptions(config, signal);
|
||||
|
||||
this.logger?.debug('Making request with fetch: ', { url: config.url, options })
|
||||
this.logger?.debug('Making request with fetch: ', { url: config.url, options })
|
||||
|
||||
const response = await fetch(config.url, options);
|
||||
|
||||
|
|
@ -141,27 +135,44 @@ export class HttpClient {
|
|||
: new HttpError(`Request failed: ${(error as Error).message}`);
|
||||
}
|
||||
} /**
|
||||
* Got implementation (simplified for SOCKS proxies)
|
||||
* Axios implementation (for SOCKS proxies)
|
||||
*/
|
||||
private async gotRequest<T>(config: RequestConfig, signal: AbortSignal): Promise<HttpResponse<T>> {
|
||||
private async axiosRequest<T>(config: RequestConfig, signal: AbortSignal): Promise<HttpResponse<T>> {
|
||||
if(config.proxy) {
|
||||
try {
|
||||
const gotClient = await ProxyManager.createGotInstance(config.proxy);
|
||||
const response = await gotClient.get(config.url);
|
||||
return this.parseGotResponse<T>(response);
|
||||
const axiosProxy = await ProxyManager.createAxiosConfig(config.proxy);
|
||||
axiosProxy.url = config.url;
|
||||
axiosProxy.method = config.method || 'GET';
|
||||
// console.log(axiosProxy)
|
||||
// const axiosConfig = {
|
||||
// ...axiosProxy,
|
||||
// url: config.url,
|
||||
// method: config.method || 'GET',
|
||||
// // headers: config.headers || {},
|
||||
// // data: config.body,
|
||||
// // signal, // Axios supports AbortSignal
|
||||
// };
|
||||
|
||||
// console.log('Making request with Axios: ', axiosConfig );
|
||||
|
||||
const response: AxiosResponse<T> = await axios.request(axiosProxy);
|
||||
return this.parseAxiosResponse<T>(response);
|
||||
} catch (error) {
|
||||
console.error('Got request error:', error);
|
||||
// Handle both AbortSignal timeout and Got-specific timeout errors
|
||||
console.error('Axios request error:', error);
|
||||
|
||||
// Handle AbortSignal timeout
|
||||
if (signal.aborted) {
|
||||
throw new HttpError(`Request timeout`);
|
||||
}
|
||||
if ((error as any).name === 'TimeoutError') {
|
||||
|
||||
// Handle Axios timeout errors
|
||||
if (error instanceof AxiosError && error.code === 'ECONNABORTED') {
|
||||
throw new HttpError(`Request timeout`);
|
||||
}
|
||||
|
||||
throw new HttpError(`Request failed: ${(error as Error).message}`);
|
||||
}
|
||||
}else{
|
||||
} else {
|
||||
throw new HttpError(`Request failed: No proxy configured, use fetch instead`);
|
||||
}
|
||||
}
|
||||
|
|
@ -188,46 +199,34 @@ export class HttpClient {
|
|||
|
||||
// Add proxy (HTTP/HTTPS only) - request level only
|
||||
if (config.proxy && ProxyManager.shouldUseBunFetch(config.proxy)) {
|
||||
(options as any).proxy = ProxyManager.createBunProxyUrl(config.proxy);
|
||||
(options as any).proxy = ProxyManager.createProxyUrl(config.proxy);
|
||||
}
|
||||
|
||||
return options;
|
||||
} /**
|
||||
* Build Got options (extracted for clarity)
|
||||
* Build Axios options (for reference, though we're creating instance in ProxyManager)
|
||||
*/
|
||||
private buildGotOptions(config: RequestConfig, signal: AbortSignal): any {
|
||||
private buildAxiosOptions(config: RequestConfig, signal: AbortSignal): any {
|
||||
const options: any = {
|
||||
method: config.method || 'GET',
|
||||
headers: config.headers || {},
|
||||
signal, // Use AbortSignal instead of Got's timeout
|
||||
retry: {
|
||||
limit: 3,
|
||||
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH']
|
||||
},
|
||||
throwHttpErrors: false,
|
||||
responseType: 'json'
|
||||
signal, // Axios supports AbortSignal
|
||||
timeout: config.timeout || 30000,
|
||||
maxRedirects: 5,
|
||||
validateStatus: () => true // Don't throw on HTTP errors
|
||||
};
|
||||
|
||||
// Add body
|
||||
if (config.body && config.method !== 'GET') {
|
||||
if (typeof config.body === 'object') {
|
||||
options.json = config.body;
|
||||
options.data = config.body;
|
||||
options.headers = { 'Content-Type': 'application/json', ...options.headers };
|
||||
} else {
|
||||
options.body = config.body;
|
||||
options.data = config.body;
|
||||
options.headers = { 'Content-Type': 'text/plain', ...options.headers };
|
||||
}
|
||||
}
|
||||
|
||||
// Add SOCKS proxy via agent - request level only
|
||||
if (config.proxy && !ProxyManager.shouldUseBunFetch(config.proxy)) {
|
||||
ProxyManager.validateConfig(config.proxy);
|
||||
const agent = ProxyManager.createGotAgent(config.proxy);
|
||||
options.agent = {
|
||||
http: agent,
|
||||
https: agent
|
||||
};
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
|
|
@ -248,15 +247,14 @@ export class HttpClient {
|
|||
|
||||
return { data, status: response.status, headers, ok: response.ok };
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse Got response (simplified)
|
||||
* Parse Axios response
|
||||
*/
|
||||
private parseGotResponse<T>(response: any): HttpResponse<T> {
|
||||
private parseAxiosResponse<T>(response: AxiosResponse<T>): HttpResponse<T> {
|
||||
const headers = response.headers as Record<string, string>;
|
||||
const status = response.statusCode;
|
||||
const status = response.status;
|
||||
const ok = status >= 200 && status < 300;
|
||||
const data = response.body as T;
|
||||
const data = response.data;
|
||||
|
||||
if (!ok) {
|
||||
throw new HttpError(
|
||||
|
|
@ -268,7 +266,6 @@ export class HttpClient {
|
|||
|
||||
return { data, status, headers, ok };
|
||||
}
|
||||
|
||||
/**
|
||||
* Unified body parsing (works for fetch response)
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import got from 'got';
|
||||
import axios, { AxiosRequestConfig, type AxiosInstance } from 'axios';
|
||||
import { SocksProxyAgent } from 'socks-proxy-agent';
|
||||
import { HttpsProxyAgent } from 'https-proxy-agent';
|
||||
import { HttpProxyAgent } from 'http-proxy-agent';
|
||||
|
|
@ -6,18 +6,19 @@ import type { ProxyInfo } from './types.js';
|
|||
|
||||
export class ProxyManager {
|
||||
/**
|
||||
* Determine if we should use Bun fetch (HTTP/HTTPS) or Got (SOCKS)
|
||||
* Determine if we should use Bun fetch (HTTP/HTTPS) or Axios (SOCKS)
|
||||
*/
|
||||
static shouldUseBunFetch(proxy: ProxyInfo): boolean {
|
||||
return proxy.protocol === 'http' || proxy.protocol === 'https';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Bun fetch proxy URL for HTTP/HTTPS proxies
|
||||
* Create proxy URL for both Bun fetch and Axios proxy agents
|
||||
*/
|
||||
static createBunProxyUrl(proxy: ProxyInfo): string {
|
||||
static createProxyUrl(proxy: ProxyInfo): string {
|
||||
const { protocol, host, port, username, password } = proxy;
|
||||
|
||||
if(protocol.includes('socks')) {
|
||||
return `${protocol}://${host}:${port}`;
|
||||
}
|
||||
if (username && password) {
|
||||
return `${protocol}://${encodeURIComponent(username)}:${encodeURIComponent(password)}@${host}:${port}`;
|
||||
}
|
||||
|
|
@ -25,13 +26,13 @@ export class ProxyManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* Create appropriate agent for Got based on proxy type
|
||||
* Create appropriate agent for Axios based on proxy type
|
||||
*/
|
||||
static createGotAgent(proxy: ProxyInfo) {
|
||||
static createProxyAgent(proxy: ProxyInfo) {
|
||||
this.validateConfig(proxy);
|
||||
|
||||
const proxyUrl = this.buildProxyUrl(proxy);
|
||||
|
||||
const proxyUrl = this.createProxyUrl(proxy);
|
||||
console.log(`Using proxy url: ${proxyUrl}`);
|
||||
switch (proxy.protocol) {
|
||||
case 'socks4':
|
||||
case 'socks5':
|
||||
|
|
@ -45,42 +46,22 @@ export class ProxyManager {
|
|||
throw new Error(`Unsupported proxy protocol: ${proxy.protocol}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Got instance with proxy configuration
|
||||
* Create Axios instance with proxy configuration
|
||||
*/
|
||||
static async createGotInstance(proxy: ProxyInfo): Promise<typeof got> {
|
||||
const agent = this.createGotAgent(proxy);
|
||||
await new Promise(r => setTimeout(r, 2000));
|
||||
return got.extend({
|
||||
http2: false,
|
||||
agent: {
|
||||
http: agent,
|
||||
https: agent
|
||||
},
|
||||
timeout: {
|
||||
request: 30000,
|
||||
connect: 10000
|
||||
},
|
||||
retry: {
|
||||
limit: 3,
|
||||
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH']
|
||||
},
|
||||
throwHttpErrors: false // We'll handle errors ourselves
|
||||
});
|
||||
static createAxiosConfig(proxy: ProxyInfo): AxiosRequestConfig {
|
||||
const agent = this.createProxyAgent(proxy);
|
||||
return {
|
||||
httpAgent: agent,
|
||||
httpsAgent: agent,
|
||||
// timeout: 30000,
|
||||
// validateStatus: () => true, // Don't throw errors on HTTP status codes
|
||||
// maxRedirects: 5,
|
||||
// headers: {
|
||||
// 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
|
||||
// }
|
||||
};
|
||||
}
|
||||
|
||||
private static buildProxyUrl(proxy: ProxyInfo): string {
|
||||
const { protocol, host, port, username, password } = proxy;
|
||||
if( protocol.includes('socks') ) {
|
||||
return `socks5://${encodeURIComponent('me@bojancode.com')}:${encodeURIComponent('re$Pon7dipR')}@atlanta.us.socks.nordhold.net:1080`;
|
||||
}
|
||||
if (username && password) {
|
||||
return `${protocol}://${encodeURIComponent(username)}:${encodeURIComponent(password)}@${host}:${port}`;
|
||||
}
|
||||
return `${protocol}://${host}:${port}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple proxy config validation
|
||||
*/
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue