1// @effect-diagnostics nodeBuiltinImport:off
2import type * as NodeHttp from "node:http";
3
4/**
5 * Node surfaces late socket write failures (EPIPE, ECONNRESET,
6 * ERR_STREAM_DESTROYED) as "error" events. An "error" event without a
7 * listener escalates into an uncaught exception and terminates the whole
8 * server process, taking every other client and all in-flight provider
9 * work with it.
10 *
11 * Two emitters need coverage:
12 *
13 * - Upgrade sockets. Once a connection upgrades (the websocket RPC path,
14 * including its auth rejection responses), Node's http server detaches
15 * its own socket error handling, so the raw socket has no listener at
16 * all until the websocket server adopts it.
17 * - Server responses. Response streams have no default error listener
18 * either.
19 *
20 * A disconnected client only affects its own request: the request fiber is
21 * already interrupted through the response "close" event, so the write
22 * failure needs no handling beyond being observed.
23 */
24export function guardHttpResponseWriteErrors<T extends NodeHttp.Server>(
25 server: T,
26 onError?: (error: unknown) => void,
27): T {
28 server.on("request", (_request, response) => {
29 response.on("error", (error) => {
30 onError?.(error);
31 });
32 });
33 server.on("upgrade", (_request, socket) => {
34 socket.on("error", (error) => {
35 onError?.(error);
36 });
37 });
38 return server;
39}