well seems like no socks on bun

This commit is contained in:
Bojan Kucera 2025-06-08 00:01:27 -04:00
parent 93578bd030
commit 6380165a94
6 changed files with 116 additions and 137 deletions

View file

@ -11,45 +11,46 @@ export class ProxyService {
private readonly CACHE_KEY = 'proxy';
private readonly CACHE_TTL = 86400; // 24 hours
private readonly CHECK_TIMEOUT = 7000;
private readonly CHECK_IP = '99.246.102.205'
private readonly CHECK_URL = 'https://proxy-detection.stare.gg/?api_key=bd406bf53ddc6abe1d9de5907830a955';
private readonly PROXY_SOURCES = [
{url: 'https://raw.githubusercontent.com/vakhov/fresh-proxy-list/refs/heads/master/http.txt',protocol: 'http', },
{url: 'https://raw.githubusercontent.com/vakhov/fresh-proxy-list/refs/heads/master/http.txt',protocol: 'http', },
{url: 'https://raw.githubusercontent.com/casa-ls/proxy-list/refs/heads/main/http',protocol: 'http', },
{url: 'https://raw.githubusercontent.com/databay-labs/free-proxy-list/refs/heads/master/http.txt',protocol: 'http', },
{url: 'https://raw.githubusercontent.com/BreakingTechFr/Proxy_Free/refs/heads/main/proxies/http.txt', protocol: 'http' },
{url: 'https://raw.githubusercontent.com/TheSpeedX/PROXY-List/master/http.txt',protocol: 'http', },
{url: 'https://raw.githubusercontent.com/ErcinDedeoglu/proxies/main/proxies/http.txt',protocol: 'http', },
{url: 'https://raw.githubusercontent.com/monosans/proxy-list/main/proxies/http.txt',protocol: 'http', },
{url: 'https://raw.githubusercontent.com/TuanMinPay/live-proxy/master/http.txt',protocol: 'http', },
{url: 'https://raw.githubusercontent.com/casals-ar/proxy-list/main/http',protocol: 'http', },
{url: 'https://raw.githubusercontent.com/prxchk/proxy-list/main/http.txt',protocol: 'http', },
{url: 'https://raw.githubusercontent.com/MuRongPIG/Proxy-Master/main/http.txt',protocol: 'http', },
{url: 'https://raw.githubusercontent.com/vakhov/fresh-proxy-list/master/http.txt',protocol: 'http', },
{url: 'https://raw.githubusercontent.com/sunny9577/proxy-scraper/master/proxies.txt',protocol: 'http', },
// {url: 'https://raw.githubusercontent.com/vakhov/fresh-proxy-list/refs/heads/master/http.txt',protocol: 'http', },
// {url: 'https://raw.githubusercontent.com/vakhov/fresh-proxy-list/refs/heads/master/http.txt',protocol: 'http', },
// {url: 'https://raw.githubusercontent.com/casa-ls/proxy-list/refs/heads/main/http',protocol: 'http', },
// {url: 'https://raw.githubusercontent.com/databay-labs/free-proxy-list/refs/heads/master/http.txt',protocol: 'http', },
// {url: 'https://raw.githubusercontent.com/BreakingTechFr/Proxy_Free/refs/heads/main/proxies/http.txt', protocol: 'http' },
// {url: 'https://raw.githubusercontent.com/TheSpeedX/PROXY-List/master/http.txt',protocol: 'http', },
// {url: 'https://raw.githubusercontent.com/ErcinDedeoglu/proxies/main/proxies/http.txt',protocol: 'http', },
// {url: 'https://raw.githubusercontent.com/monosans/proxy-list/main/proxies/http.txt',protocol: 'http', },
// {url: 'https://raw.githubusercontent.com/TuanMinPay/live-proxy/master/http.txt',protocol: 'http', },
// {url: 'https://raw.githubusercontent.com/casals-ar/proxy-list/main/http',protocol: 'http', },
// {url: 'https://raw.githubusercontent.com/prxchk/proxy-list/main/http.txt',protocol: 'http', },
// {url: 'https://raw.githubusercontent.com/MuRongPIG/Proxy-Master/main/http.txt',protocol: 'http', },
// {url: 'https://raw.githubusercontent.com/vakhov/fresh-proxy-list/master/http.txt',protocol: 'http', },
// {url: 'https://raw.githubusercontent.com/sunny9577/proxy-scraper/master/proxies.txt',protocol: 'http', },
{url: 'https://raw.githubusercontent.com/r00tee/Proxy-List/refs/heads/main/Https.txt',protocol: 'https', },
{url: 'https://raw.githubusercontent.com/ErcinDedeoglu/proxies/main/proxies/https.txt',protocol: 'https', },
{url: 'https://github.com/vakhov/fresh-proxy-list/blob/master/https.txt', protocol: 'https' },
{url: 'https://raw.githubusercontent.com/databay-labs/free-proxy-list/refs/heads/master/https.txt',protocol: 'https', },
// {url: 'https://raw.githubusercontent.com/MuRongPIG/Proxy-Master/main/socks4.txt',protocol: 'socks4', },
// {url: 'https://raw.githubusercontent.com/TheSpeedX/PROXY-List/master/socks4.txt',protocol: 'socks4', },
// {url: 'https://raw.githubusercontent.com/TuanMinPay/live-proxy/master/socks4.txt',protocol: 'socks4', },
// {url: 'https://raw.githubusercontent.com/casals-ar/proxy-list/main/socks4',protocol: 'socks4', },
// {url: 'https://raw.githubusercontent.com/prxchk/proxy-list/main/socks4.txt',protocol: 'socks4', },
// {url: 'https://raw.githubusercontent.com/BreakingTechFr/Proxy_Free/refs/heads/main/proxies/socks4.txt',protocol: 'socks4', },
// {url: 'https://raw.githubusercontent.com/ErcinDedeoglu/proxies/main/proxies/socks4.txt',protocol: 'socks4', },
// {url: 'https://raw.githubusercontent.com/monosans/proxy-list/main/proxies/socks4.txt',protocol: 'socks4', },
// {url: 'https://raw.githubusercontent.com/TheSpeedX/PROXY-List/master/socks5.txt',protocol: 'socks5', },
// {url: 'https://raw.githubusercontent.com/hookzof/socks5_list/master/proxy.txt',protocol: 'socks5', },
// {url: 'https://raw.githubusercontent.com/ErcinDedeoglu/proxies/main/proxies/socks5.txt',protocol: 'socks5', },
// {url: 'https://raw.githubusercontent.com/monosans/proxy-list/main/proxies/socks5.txt',protocol: 'socks5', },
// {url: 'https://raw.githubusercontent.com/TuanMinPay/live-proxy/master/socks5.txt',protocol: 'socks5', },
// {url: 'https://raw.githubusercontent.com/casals-ar/proxy-list/main/socks5',protocol: 'socks5', },
// {url: 'https://raw.githubusercontent.com/prxchk/proxy-list/main/socks5.txt',protocol: 'socks5', },
// {url: 'https://raw.githubusercontent.com/hookzof/socks5_list/master/proxy.txt',protocol: 'socks5', },
// {url: 'https://raw.githubusercontent.com/MuRongPIG/Proxy-Master/main/socks5.txt',protocol: 'socks5', },
// {url: 'https://raw.githubusercontent.com/BreakingTechFr/Proxy_Free/refs/heads/main/proxies/socks5.txt',protocol: 'socks5', },
// {url: 'https://raw.githubusercontent.com/r00tee/Proxy-List/refs/heads/main/Https.txt',protocol: 'https', },
// {url: 'https://raw.githubusercontent.com/ErcinDedeoglu/proxies/main/proxies/https.txt',protocol: 'https', },
// {url: 'https://github.com/vakhov/fresh-proxy-list/blob/master/https.txt', protocol: 'https' },
// {url: 'https://raw.githubusercontent.com/databay-labs/free-proxy-list/refs/heads/master/https.txt',protocol: 'https', },
{url: 'https://raw.githubusercontent.com/MuRongPIG/Proxy-Master/main/socks4.txt',protocol: 'socks4', },
{url: 'https://raw.githubusercontent.com/TheSpeedX/PROXY-List/master/socks4.txt',protocol: 'socks4', },
{url: 'https://raw.githubusercontent.com/TuanMinPay/live-proxy/master/socks4.txt',protocol: 'socks4', },
{url: 'https://raw.githubusercontent.com/casals-ar/proxy-list/main/socks4',protocol: 'socks4', },
{url: 'https://raw.githubusercontent.com/prxchk/proxy-list/main/socks4.txt',protocol: 'socks4', },
{url: 'https://raw.githubusercontent.com/BreakingTechFr/Proxy_Free/refs/heads/main/proxies/socks4.txt',protocol: 'socks4', },
{url: 'https://raw.githubusercontent.com/ErcinDedeoglu/proxies/main/proxies/socks4.txt',protocol: 'socks4', },
{url: 'https://raw.githubusercontent.com/monosans/proxy-list/main/proxies/socks4.txt',protocol: 'socks4', },
{url: 'https://raw.githubusercontent.com/TheSpeedX/PROXY-List/master/socks5.txt',protocol: 'socks5', },
{url: 'https://raw.githubusercontent.com/hookzof/socks5_list/master/proxy.txt',protocol: 'socks5', },
{url: 'https://raw.githubusercontent.com/ErcinDedeoglu/proxies/main/proxies/socks5.txt',protocol: 'socks5', },
{url: 'https://raw.githubusercontent.com/monosans/proxy-list/main/proxies/socks5.txt',protocol: 'socks5', },
{url: 'https://raw.githubusercontent.com/TuanMinPay/live-proxy/master/socks5.txt',protocol: 'socks5', },
{url: 'https://raw.githubusercontent.com/casals-ar/proxy-list/main/socks5',protocol: 'socks5', },
{url: 'https://raw.githubusercontent.com/prxchk/proxy-list/main/socks5.txt',protocol: 'socks5', },
{url: 'https://raw.githubusercontent.com/hookzof/socks5_list/master/proxy.txt',protocol: 'socks5', },
{url: 'https://raw.githubusercontent.com/MuRongPIG/Proxy-Master/main/socks5.txt',protocol: 'socks5', },
{url: 'https://raw.githubusercontent.com/BreakingTechFr/Proxy_Free/refs/heads/main/proxies/socks5.txt',protocol: 'socks5', },
]
constructor() {
@ -157,7 +158,7 @@ export class ProxyService {
responseTime: response.responseTime,
};
if (isWorking) {
if (isWorking && !response.data.includes('CHECK_IP')) {
await this.cache.set(`${this.CACHE_KEY}:${proxy.protocol}://${proxy.host}:${proxy.port}`, result, this.CACHE_TTL);
} else {
await this.cache.del(`${this.CACHE_KEY}:${proxy.protocol}://${proxy.host}:${proxy.port}`);

View file

@ -213,7 +213,7 @@
"dependencies": {
"@stock-bot/logger": "*",
"@stock-bot/types": "*",
"got": "^14.4.7",
"axios": "^1.9.0",
"http-proxy-agent": "^7.0.2",
"https-proxy-agent": "^7.0.6",
"proxy-agent": "^6.5.0",
@ -952,6 +952,8 @@
"autoprefixer": ["autoprefixer@10.4.21", "", { "dependencies": { "browserslist": "^4.24.4", "caniuse-lite": "^1.0.30001702", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ=="],
"axios": ["axios@1.9.0", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } }, "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg=="],
"b4a": ["b4a@1.6.7", "", {}, "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg=="],
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],

View file

@ -18,7 +18,7 @@
"dependencies": {
"@stock-bot/logger": "*",
"@stock-bot/types": "*",
"got": "^14.4.7",
"axios": "^1.9.0",
"http-proxy-agent": "^7.0.2",
"https-proxy-agent": "^7.0.6",
"proxy-agent": "^6.5.0",

View file

@ -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)
*/

View file

@ -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
*/

View file

@ -141,10 +141,9 @@ describe('ProxyManager', () => {
host: 'proxy.example.com',
port: 8080,
username: 'user',
password: 'pass'
};
password: 'pass' };
const proxyUrl = ProxyManager.createBunProxyUrl(proxy);
const proxyUrl = ProxyManager.createProxyUrl(proxy);
expect(proxyUrl).toBe('http://user:pass@proxy.example.com:8080');
});
@ -152,10 +151,9 @@ describe('ProxyManager', () => {
const proxy: ProxyInfo = {
protocol: 'https',
host: 'proxy.example.com',
port: 8080
};
port: 8080 };
const proxyUrl = ProxyManager.createBunProxyUrl(proxy);
const proxyUrl = ProxyManager.createProxyUrl(proxy);
expect(proxyUrl).toBe('https://proxy.example.com:8080');
});
});