import { describe, it, afterEach } from "node:test"; import assert from "node:assert/strict"; import net from "node:net"; import { fetch } from "undici"; import { createSocksDispatcherWithFamily } from "../../open-sse/utils/socksConnectorWithFamily.ts"; import { clearDispatcherCache } from "../../open-sse/utils/proxyDispatcher.ts"; async function startFakeSocks(opts: { stallAfterGrant: boolean }): Promise<{ port: number; close: () => Promise }> { return new Promise((resolve) => { const serverSockets = new Set(); const server = net.createServer((socket) => { let step = 0; serverSockets.add(socket); socket.on("close", () => serverSockets.delete(socket)); socket.on("data", (_data: Buffer) => { if (step === 0) { socket.write(Buffer.from([0x05, 0x00])); step = 1; return; } if (step === 1) { if (!opts.stallAfterGrant) return; socket.write(Buffer.from([0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])); step = 2; } }); }); server.listen(0, "127.0.0.1", () => { const addr = server.address() as net.AddressInfo; resolve({ port: addr.port, close: () => new Promise((r) => { for (const s of serverSockets) s.destroy(); server.close(() => r()); }), }); }); }); } describe("stub SOCKS e2e", () => { afterEach(() => clearDispatcherCache()); it("pre-grant stall (SOCKS timer) \u2192 error < 1000ms", async () => { const { port, close } = await startFakeSocks({ stallAfterGrant: false }); const dispatcher = createSocksDispatcherWithFamily({ host: "127.0.0.1", port, type: 5 } as any, 4 as any, { connectTimeout: 300, connect: {} } as any); const t0 = Date.now(); await assert.rejects(() => fetch("https://example.invalid/", { dispatcher } as any)); assert.ok(Date.now() - t0 < 1000, `pre-grant stall must error < 1000ms, took ${Date.now() - t0}ms`); await close(); }); it("post-grant stall (TLS timer, https:// only) \u2192 error < 1500ms", async () => { const { port, close } = await startFakeSocks({ stallAfterGrant: true }); const dispatcher = createSocksDispatcherWithFamily({ host: "127.0.0.1", port, type: 5 } as any, 4 as any, { connectTimeout: 300, connect: {} } as any); const t0 = Date.now(); let caught: any = null; await assert.rejects(async () => { try { await fetch("https://example.invalid/", { dispatcher } as any); } catch (e) { caught = e; throw e; } }); const err: any = caught; // The ~1000ms wall time is connectTimeout 300ms + undici immediate/queue overhead, not the 10000ms default. assert.ok(Date.now() - t0 < 1500, `post-grant stall must error < 1500ms, took ${Date.now() - t0}ms (err: ${String((err as any)?.message ?? err).slice(0, 120)})`); await close(); }); });