mirror of
https://github.com/DIYgod/RSSHub.git
synced 2026-10-18 22:32:10 +08:00
e0b3b98401
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.
140 lines
4.9 KiB
TypeScript
140 lines
4.9 KiB
TypeScript
import { PacProxyAgent } from 'pac-proxy-agent';
|
|
import { SocksProxyAgent } from 'socks-proxy-agent';
|
|
import { ProxyAgent } from 'undici';
|
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
const loadProxy = async (env: Record<string, string>) => {
|
|
vi.resetModules();
|
|
for (const [key, value] of Object.entries(env)) {
|
|
vi.stubEnv(key, value);
|
|
}
|
|
return (await import('@/utils/proxy')).default;
|
|
};
|
|
|
|
const requestTlsOf = (dispatcher: ProxyAgent) => dispatcher[Object.getOwnPropertySymbols(dispatcher).find((s) => s.description === 'request tls settings')!];
|
|
|
|
describe('proxy', () => {
|
|
afterEach(() => {
|
|
vi.clearAllTimers();
|
|
vi.useRealTimers();
|
|
vi.unstubAllEnvs();
|
|
});
|
|
|
|
it('uses PAC proxy when PAC_URI is set', async () => {
|
|
const proxy = await loadProxy({
|
|
PAC_URI: 'http://example.com/proxy.pac',
|
|
PROXY_URIS: '',
|
|
PROXY_URI: '',
|
|
});
|
|
|
|
expect(proxy.agent).toBeInstanceOf(PacProxyAgent);
|
|
expect(proxy.dispatcher).toBeNull();
|
|
expect(proxy.proxyUri).toBe('http://example.com/proxy.pac');
|
|
|
|
const current = proxy.getCurrentProxy();
|
|
expect(current?.uri).toBe('http://example.com/proxy.pac');
|
|
});
|
|
|
|
it('handles multi-proxy selection and updates after failures', async () => {
|
|
const proxy = await loadProxy({
|
|
PROXY_URIS: 'http://proxy1.local:8080,http://proxy2.local:8081',
|
|
PAC_URI: '',
|
|
PROXY_URI: '',
|
|
});
|
|
|
|
expect(proxy.multiProxy).toBeDefined();
|
|
const current = proxy.getCurrentProxy();
|
|
expect(current).not.toBeNull();
|
|
expect(proxy.getDispatcherForProxy(current!)).toBeInstanceOf(ProxyAgent);
|
|
expect(proxy.getAgentForProxy({ uri: 'socks5://proxy.local:1080', isActive: true, failureCount: 0 })).toBeInstanceOf(SocksProxyAgent);
|
|
|
|
proxy.markProxyFailed(current!.uri);
|
|
const next = proxy.getCurrentProxy();
|
|
expect(next).not.toBeNull();
|
|
});
|
|
|
|
it('clears proxy when multi-proxy has no valid entries', async () => {
|
|
const proxy = await loadProxy({
|
|
PROXY_URIS: 'http://inv lid.test',
|
|
PAC_URI: '',
|
|
PROXY_URI: '',
|
|
});
|
|
|
|
expect(proxy.getCurrentProxy()).toBeNull();
|
|
proxy.markProxyFailed('http://inv lid.test');
|
|
expect(proxy.agent).toBeNull();
|
|
expect(proxy.dispatcher).toBeNull();
|
|
expect(proxy.proxyUri).toBeUndefined();
|
|
});
|
|
|
|
it('creates a socks proxy agent for single proxy settings', async () => {
|
|
const proxy = await loadProxy({
|
|
PROXY_URI: 'socks5://proxy.local:1080',
|
|
PROXY_URIS: '',
|
|
PAC_URI: '',
|
|
});
|
|
|
|
expect(proxy.agent).toBeInstanceOf(SocksProxyAgent);
|
|
expect(proxy.dispatcher).toBeInstanceOf(ProxyAgent);
|
|
expect(proxy.getCurrentProxy()?.uri).toBe('socks5://proxy.local:1080');
|
|
expect(requestTlsOf(proxy.dispatcher!).ALPNProtocols).toEqual(['h2', 'http/1.1']);
|
|
});
|
|
|
|
it('creates a socks5 dispatcher for socks5h proxies', async () => {
|
|
const proxy = await loadProxy({
|
|
PROXY_URI: 'socks5h://proxy.local:1080',
|
|
PROXY_URIS: '',
|
|
PAC_URI: '',
|
|
});
|
|
|
|
expect(proxy.agent).toBeInstanceOf(SocksProxyAgent);
|
|
expect(proxy.dispatcher).toBeInstanceOf(ProxyAgent);
|
|
expect(proxy.getCurrentProxy()?.uri).toBe('socks5h://proxy.local:1080');
|
|
});
|
|
|
|
it('disables socks4 proxies instead of failing to start', async () => {
|
|
const proxy = await loadProxy({
|
|
PROXY_URI: 'socks4://proxy.local:1080',
|
|
PROXY_URIS: '',
|
|
PAC_URI: '',
|
|
});
|
|
|
|
expect(proxy.agent).toBeNull();
|
|
expect(proxy.dispatcher).toBeNull();
|
|
expect(proxy.getCurrentProxy()).toBeNull();
|
|
});
|
|
|
|
it('prefers h2 on the http proxy dispatcher', async () => {
|
|
const proxy = await loadProxy({
|
|
PROXY_URI: 'http://proxy.local:8080',
|
|
PROXY_URIS: '',
|
|
PAC_URI: '',
|
|
});
|
|
|
|
expect(proxy.dispatcher).toBeInstanceOf(ProxyAgent);
|
|
expect(requestTlsOf(proxy.dispatcher!).preferH2).toBe(true);
|
|
});
|
|
|
|
it('applies minVersion to the proxy dispatcher request tls', async () => {
|
|
const proxy = await loadProxy({
|
|
PROXY_URI: 'http://proxy.local:8080',
|
|
PROXY_URIS: '',
|
|
PAC_URI: '',
|
|
});
|
|
|
|
const dispatcher = proxy.getDispatcherForProxy(proxy.getCurrentProxy()!, 'TLSv1.3')!;
|
|
expect(requestTlsOf(dispatcher).minVersion).toBe('TLSv1.3');
|
|
expect(requestTlsOf(proxy.dispatcher!).minVersion).toBeUndefined();
|
|
});
|
|
|
|
it('returns null agent for unsupported proxy protocol', async () => {
|
|
const proxy = await loadProxy({
|
|
PROXY_URI: '',
|
|
PROXY_URIS: '',
|
|
PAC_URI: '',
|
|
});
|
|
|
|
expect(proxy.getAgentForProxy({ uri: 'ftp://proxy.local:21', isActive: true, failureCount: 0 })).toBeNull();
|
|
});
|
|
});
|