/** * Serving an application (or a fixture page) to the browser under test, the way a deployment * would. */ import * as http from "node:http"; import type { Socket } from "node:net"; /** * Serve `handler` on an ephemeral port, and hand back a close that CLOSES. * * `server.close()` alone stops the listener and then waits for every keep-alive connection * to drain on its own — a browser that is still attached keeps the server half-alive long * after the harness believes it gone. These suites close a server while a browser is still * pointed at it (the wallet export does exactly that), so the sockets are tracked and * destroyed: "closed" has to mean closed, or the next thing to go wrong gets blamed on the * suite instead of on the connection nobody hung up. */ export function serveOnEphemeralPort( handler: (req: http.IncomingMessage, res: http.ServerResponse) => void, ): Promise<{ url: string; close: () => void }> { const server = http.createServer(handler); const open = new Set(); server.on("connection", (socket) => { open.add(socket); socket.on("close", () => open.delete(socket)); }); return new Promise((resolve) => { server.listen(0, "127.0.0.1", () => { const port = (server.address() as { port: number }).port; resolve({ url: `http://127.0.0.1:${port}`, close: () => { server.close(); for (const socket of open) socket.destroy(); open.clear(); }, }); }); }); }