mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-10-30 09:16:55 +00:00
* fix(cli): a finished capture exits even when a site's DNS never answers Capture resolved every page request with the system lookup and gave up after 10 s, but a system lookup cannot be cancelled: the abandoned one kept the process alive until the OS resolver timed out, so a capture that had printed its result could run past the app's 5-minute cap and lose its folder. Resolve through c-ares instead, as hostAnswers does, and cancel it at the same 10 s deadline. * test(cli): a capture lookup that answers must exit right after too Dropping the timer cleanup kept every successful lookup alive 10 s and no test noticed. * fix(cli): capture falls back to the system lookup and exits once its result is out c-ares answers first. When it has no answer, the system lookup (hosts file, OS-only DNS) runs in a child process killed at the 10 s deadline, cached per host, because Node waits for an in-process system lookup even in process.exit. Capture then asks the CLI to exit 100 ms after printing its result, as render does. * fix(cli): capture reads the hosts file first and kills its lookup child on exit The hosts file answers before DNS, as the system lookup does (overrides and 0.0.0.0 blocks keep working). A host whose system answer is cached skips c-ares, a caller that joins a running lookup keeps its own deadline, and a pending lookup child is killed when the CLI exits. * fix(cli): capture's forced exit waits until a slow reader has the whole result The root exit now waits for stdout and stderr to hand over what is already written before it calls process.exit, so a large JSON result on a slow pipe arrives complete. A hostname listed several times in the hosts file now has every address checked, not only the first. * fix(cli): keep process exit in the CLI root and let test fixtures exit The output wait is now a helper that only waits; cli.ts still owns the process.exit. The process-ownership check treats test fixtures like tests, since each one runs as its own process. * chore(cli): run the process-ownership check before every commit A process.exit outside a process root used to pass the local hooks and fail only in CI's Preflight.
38 lines
1.3 KiB
TypeScript
38 lines
1.3 KiB
TypeScript
import { createSocket } from "node:dgram";
|
|
|
|
export interface TestDnsServer {
|
|
/** `host:port`, for a Resolver's setServers. */
|
|
address: string;
|
|
close(): void;
|
|
}
|
|
|
|
/** A DNS server: "silent" never replies, "answer" answers A with `ipv4`, "no-aaaa" answers A and drops AAAA. */
|
|
export async function startTestDnsServer(
|
|
mode: "silent" | "answer" | "no-aaaa",
|
|
ipv4 = "127.0.0.1",
|
|
): Promise<TestDnsServer> {
|
|
const socket = createSocket("udp4");
|
|
socket.on("message", (msg, peer) => {
|
|
if (mode === "silent") return;
|
|
let end = 12;
|
|
while (msg[end] !== 0) end += (msg[end] ?? 0) + 1;
|
|
end += 5;
|
|
const isA = msg.readUInt16BE(end - 4) === 1;
|
|
if (mode === "no-aaaa" && !isA) return;
|
|
const header = Buffer.alloc(12);
|
|
msg.copy(header, 0, 0, 2);
|
|
header.writeUInt16BE(0x8180, 2);
|
|
header.writeUInt16BE(1, 4);
|
|
header.writeUInt16BE(isA ? 1 : 0, 6);
|
|
const answer = Buffer.from([0xc0, 0x0c, 0, 1, 0, 1, 0, 0, 0, 60, 0, 4]);
|
|
const record = Buffer.concat([answer, Buffer.from(ipv4.split(".").map(Number))]);
|
|
socket.send(
|
|
Buffer.concat([header, msg.subarray(12, end), ...(isA ? [record] : [])]),
|
|
peer.port,
|
|
peer.address,
|
|
);
|
|
});
|
|
await new Promise<void>((resolve) => socket.bind(0, "127.0.0.1", resolve));
|
|
return { address: `127.0.0.1:${socket.address().port}`, close: () => socket.close() };
|
|
}
|