mirror of
https://github.com/DIYgod/RSSHub.git
synced 2026-10-16 16:44:53 +08:00
Since #22401 every socks* PROXY_URI is handed to undici's ProxyAgent, which only accepts socks5: and socks:. A socks5h, socks4 or socks4a URI fell through to its HTTP proxy path and threw "Invalid URL protocol" while the proxy module loaded, so the server never started, even though unify-proxy listed all of them as supported. Pass socks5h to undici as socks5: its SOCKS5 client sends host names to the proxy, so DNS still resolves there, as playwright.ts already assumes. undici has no SOCKS4 support, so drop socks4 and socks4a from the accepted protocols, which logs the supported list and disables the proxy like any other unsupported protocol.
160 lines
5.1 KiB
TypeScript
160 lines
5.1 KiB
TypeScript
import type { SecureVersion } from 'node:tls';
|
|
|
|
import { HttpsProxyAgent } from 'https-proxy-agent';
|
|
import { PacProxyAgent } from 'pac-proxy-agent';
|
|
import { SocksProxyAgent } from 'socks-proxy-agent';
|
|
import { ProxyAgent } from 'undici';
|
|
|
|
import type { Config } from '@/config';
|
|
import { config } from '@/config';
|
|
import logger from '@/utils/logger';
|
|
|
|
import type { MultiProxyResult, ProxyState } from './multi-proxy';
|
|
import createMultiProxy from './multi-proxy';
|
|
import pacProxy from './pac-proxy';
|
|
import unifyProxy from './unify-proxy';
|
|
|
|
const proxyIsPAC = config.pacUri || config.pacScript;
|
|
|
|
interface ProxyExport {
|
|
agent: PacProxyAgent<string> | HttpsProxyAgent<string> | SocksProxyAgent | null;
|
|
dispatcher: ProxyAgent | null;
|
|
proxyUri?: string;
|
|
proxyObj: Config['proxy'];
|
|
proxyUrlHandler?: URL | null;
|
|
multiProxy?: MultiProxyResult;
|
|
getCurrentProxy: () => ProxyState | null;
|
|
markProxyFailed: (proxyUri: string) => void;
|
|
getAgentForProxy: (proxyState: ProxyState) => any;
|
|
getDispatcherForProxy: (proxyState: ProxyState, minVersion?: SecureVersion) => ProxyAgent | null;
|
|
}
|
|
|
|
let proxyUri: string | undefined;
|
|
let proxyObj: Config['proxy'];
|
|
let proxyUrlHandler: URL | null = null;
|
|
let multiProxy: MultiProxyResult | undefined;
|
|
|
|
const createAgentForProxy = (uri: string, proxyObj: Config['proxy']): any => {
|
|
if (uri.startsWith('http')) {
|
|
return new HttpsProxyAgent(uri, {
|
|
headers: {
|
|
'proxy-authorization': proxyObj?.auth ? `Basic ${proxyObj.auth}` : undefined,
|
|
},
|
|
});
|
|
}
|
|
if (uri.startsWith('socks')) {
|
|
return new SocksProxyAgent(uri);
|
|
}
|
|
return null;
|
|
};
|
|
|
|
const createDispatcherForProxy = (uri: string, proxyObj: Config['proxy'], minVersion?: SecureVersion): ProxyAgent | null => {
|
|
if (uri.startsWith('http')) {
|
|
return new ProxyAgent({
|
|
uri,
|
|
token: proxyObj?.auth ? `Basic ${proxyObj.auth}` : undefined,
|
|
requestTls: {
|
|
rejectUnauthorized: process.env.NODE_TLS_REJECT_UNAUTHORIZED !== '0',
|
|
preferH2: true,
|
|
minVersion,
|
|
},
|
|
});
|
|
}
|
|
if (uri.startsWith('socks')) {
|
|
return new ProxyAgent({
|
|
// undici only accepts socks5: and socks:, and its SOCKS5 client already lets the proxy resolve host names
|
|
uri: uri.replace(/^socks5h:/, 'socks5:'),
|
|
requestTls: {
|
|
ALPNProtocols: ['h2', 'http/1.1'],
|
|
minVersion,
|
|
},
|
|
});
|
|
}
|
|
return null;
|
|
};
|
|
|
|
if (proxyIsPAC) {
|
|
const proxy = pacProxy(config.pacUri, config.pacScript, config.proxy);
|
|
proxyUri = proxy.proxyUri;
|
|
proxyObj = proxy.proxyObj;
|
|
proxyUrlHandler = proxy.proxyUrlHandler;
|
|
} else if (config.proxyUris && config.proxyUris.length > 0) {
|
|
multiProxy = createMultiProxy(config.proxyUris, config.proxy);
|
|
proxyObj = multiProxy.proxyObj;
|
|
const currentProxy = multiProxy.getNextProxy();
|
|
if (currentProxy) {
|
|
proxyUri = currentProxy.uri;
|
|
proxyUrlHandler = currentProxy.urlHandler ?? null;
|
|
}
|
|
logger.info(`Multi-proxy initialized with ${config.proxyUris.length} proxies`);
|
|
} else {
|
|
const proxy = unifyProxy(config.proxyUri, config.proxy);
|
|
proxyUri = proxy.proxyUri;
|
|
proxyObj = proxy.proxyObj;
|
|
proxyUrlHandler = proxy.proxyUrlHandler;
|
|
}
|
|
|
|
let agent: PacProxyAgent<string> | HttpsProxyAgent<string> | SocksProxyAgent | null = null;
|
|
let dispatcher: ProxyAgent | null = null;
|
|
|
|
if (proxyIsPAC && proxyUri) {
|
|
agent = new PacProxyAgent(`pac+${proxyUri}`);
|
|
} else if (proxyUri) {
|
|
agent = createAgentForProxy(proxyUri, proxyObj);
|
|
dispatcher = createDispatcherForProxy(proxyUri, proxyObj);
|
|
}
|
|
|
|
const getCurrentProxy = (): ProxyState | null => {
|
|
if (multiProxy) {
|
|
return multiProxy.getNextProxy();
|
|
}
|
|
if (proxyUri) {
|
|
return {
|
|
uri: proxyUri,
|
|
isActive: true,
|
|
failureCount: 0,
|
|
urlHandler: proxyUrlHandler,
|
|
};
|
|
}
|
|
return null;
|
|
};
|
|
|
|
const markProxyFailed = (failedProxyUri: string) => {
|
|
if (!multiProxy) {
|
|
return;
|
|
}
|
|
multiProxy.markProxyFailed(failedProxyUri);
|
|
const nextProxy = multiProxy.getNextProxy();
|
|
if (nextProxy) {
|
|
proxyUri = nextProxy.uri;
|
|
proxyUrlHandler = nextProxy.urlHandler || null;
|
|
agent = createAgentForProxy(nextProxy.uri, proxyObj);
|
|
dispatcher = createDispatcherForProxy(nextProxy.uri, proxyObj);
|
|
logger.info(`Switched to proxy: ${nextProxy.uri}`);
|
|
} else {
|
|
logger.warn('No available proxies remaining');
|
|
agent = null;
|
|
dispatcher = null;
|
|
proxyUri = undefined;
|
|
}
|
|
};
|
|
|
|
const getAgentForProxy = (proxyState: ProxyState) => createAgentForProxy(proxyState.uri, proxyObj);
|
|
|
|
const getDispatcherForProxy = (proxyState: ProxyState, minVersion?: SecureVersion) => createDispatcherForProxy(proxyState.uri, proxyObj, minVersion);
|
|
|
|
const proxyExport: ProxyExport = {
|
|
agent,
|
|
dispatcher,
|
|
proxyUri,
|
|
proxyObj,
|
|
proxyUrlHandler,
|
|
multiProxy,
|
|
getCurrentProxy,
|
|
markProxyFailed,
|
|
getAgentForProxy,
|
|
getDispatcherForProxy,
|
|
};
|
|
|
|
export default proxyExport;
|