Files
diegosouzapw--omniroute/tests/unit/inspector-http-proxy.test.ts
2026-07-13 13:39:12 +08:00

143 lines
4.6 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
import http from "node:http";
import net from "node:net";
import { startHttpProxyServer } from "../../src/mitm/inspector/httpProxyServer.ts";
import { globalTrafficBuffer } from "../../src/mitm/inspector/buffer.ts";
async function withUpstream(
handler: (req: http.IncomingMessage, res: http.ServerResponse) => void
): Promise<{ port: number; close: () => Promise<void> }> {
const server = http.createServer(handler);
await new Promise<void>((resolve) => server.listen(0, "127.0.0.1", () => resolve()));
const addr = server.address();
const port = typeof addr === "object" && addr ? addr.port : 0;
return {
port,
close: () => new Promise<void>((res) => server.close(() => res())),
};
}
async function withTcpServer(): Promise<{ port: number; close: () => Promise<void> }> {
const server = net.createServer((socket) => {
socket.on("data", () => {
socket.end("ok");
});
});
await new Promise<void>((resolve) => server.listen(0, "127.0.0.1", () => resolve()));
const addr = server.address();
const port = typeof addr === "object" && addr ? addr.port : 0;
return {
port,
close: () => new Promise<void>((res) => server.close(() => res())),
};
}
function sendThroughProxy(
proxyPort: number,
upstreamPort: number,
method = "GET"
): Promise<{ status: number; body: string }> {
return new Promise((resolve, reject) => {
const req = http.request(
{
host: "127.0.0.1",
port: proxyPort,
method,
path: `http://127.0.0.1:${upstreamPort}/test`,
headers: { host: `127.0.0.1:${upstreamPort}` },
},
(res) => {
const chunks: Buffer[] = [];
res.on("data", (c) => chunks.push(c));
res.on("end", () =>
resolve({ status: res.statusCode ?? 0, body: Buffer.concat(chunks).toString("utf8") })
);
}
);
req.once("error", reject);
req.end();
});
}
function sendConnect(proxyPort: number, target: string): Promise<number> {
return new Promise((resolve, reject) => {
const socket = net.connect(proxyPort, "127.0.0.1");
socket.once("error", reject);
socket.once("connect", () => {
socket.write(`CONNECT ${target} HTTP/1.1\r\nHost: ${target}\r\n\r\n`);
});
socket.once("data", (chunk) => {
const line = chunk.toString("utf8").split("\r\n")[0];
const m = line.match(/HTTP\/1\.1\s+(\d+)/);
socket.end();
resolve(m ? Number(m[1]) : 0);
});
});
}
test("HTTP direct passes through and records buffer entry", async () => {
globalTrafficBuffer.clear();
const upstream = await withUpstream((_req, res) => {
res.writeHead(200, { "content-type": "text/plain" });
res.end("hello");
});
const proxy = await startHttpProxyServer(0);
try {
const sizeBefore = globalTrafficBuffer.size();
const { status, body } = await sendThroughProxy(proxy.port, upstream.port);
assert.equal(status, 200);
assert.equal(body, "hello");
// give buffer.update a tick (it runs inside async path)
await new Promise((r) => setTimeout(r, 30));
assert.ok(globalTrafficBuffer.size() > sizeBefore);
const entry = globalTrafficBuffer.list().at(-1);
assert.ok(entry);
assert.equal(entry.source, "http-proxy");
assert.equal(entry.method, "GET");
assert.equal(entry.status, 200);
assert.match(entry.responseBody ?? "", /hello/);
} finally {
await proxy.stop();
await upstream.close();
}
});
test("CONNECT tunnel returns 200 and records metadata-only entry", async () => {
globalTrafficBuffer.clear();
const tcp = await withTcpServer();
const proxy = await startHttpProxyServer(0);
try {
const sizeBefore = globalTrafficBuffer.size();
const status = await sendConnect(proxy.port, `127.0.0.1:${tcp.port}`);
assert.equal(status, 200);
await new Promise((r) => setTimeout(r, 30));
assert.ok(globalTrafficBuffer.size() > sizeBefore);
const entry = globalTrafficBuffer.list().at(-1);
assert.ok(entry);
assert.equal(entry.method, "CONNECT");
assert.equal(entry.source, "http-proxy");
assert.equal(entry.responseBody, null);
assert.match(entry.note ?? "", /TLS tunnel/);
} finally {
await proxy.stop();
await tcp.close();
}
});
test("EADDRINUSE rejects with code", async () => {
const first = await startHttpProxyServer(0);
try {
await assert.rejects(
() => startHttpProxyServer(first.port),
(err: NodeJS.ErrnoException) => {
assert.ok(err);
assert.equal(err.code, "EADDRINUSE");
return true;
}
);
} finally {
await first.stop();
}
});