mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-26 17:12:27 +03:00
fix(sse): prevent client-abort (aborted/ECONNRESET) from crashing the server (#11556)
Merged via /merge-batch (lote 2026-08-26, v3.8.51). Boarded no worktree combinado junto com outras ~30 PRs; validação única: typecheck/complexity/cognitive-complexity/changelog-integrity verdes, file-size rebaseado onde necessário (crescimento legítimo), lint com os mesmos 228 achados pré-existentes confirmados via sonda contra o tip puro (não introduzidos por este lote), e ~370 testes focados (unit + vitest) passando. Obrigado pela contribuição.
This commit is contained in:
17
scripts/dev/httpClientAbortGuard.mjs
Normal file
17
scripts/dev/httpClientAbortGuard.mjs
Normal file
@@ -0,0 +1,17 @@
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Re-export of the shared client-abort guard so the Node-only dev server
|
||||
* (`run-next.mjs`) keeps importing from its original relative path. The real
|
||||
* implementation lives in `src/shared/utils/httpClientAbortGuard.mjs` (importable
|
||||
* from both `.mjs` and the TypeScript servers under `src/`, tsconfig allowJs).
|
||||
*
|
||||
* @module
|
||||
*/
|
||||
|
||||
export {
|
||||
isClientAbortError,
|
||||
shouldSwallowUncaught,
|
||||
attachRequestStreamGuards,
|
||||
installProcessCrashGuard,
|
||||
} from "../../src/shared/utils/httpClientAbortGuard.mjs";
|
||||
@@ -16,6 +16,10 @@ import { isTurbopackCacheCorruption, purgeAllTurbopackCaches } from "./turbopack
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { getMainServerTimeoutConfig } from "./main-server-timeouts.mjs";
|
||||
import { createSystemdNotifier } from "./systemd-notify.mjs";
|
||||
import {
|
||||
attachRequestStreamGuards,
|
||||
installProcessCrashGuard,
|
||||
} from "./httpClientAbortGuard.mjs";
|
||||
|
||||
const { maybeHandleDisallowedMethod } = methodGuard;
|
||||
const { wrapRequestListenerWithHeadResponseGuard } = headResponseGuard;
|
||||
@@ -162,6 +166,13 @@ async function prepareWithHeal() {
|
||||
}
|
||||
|
||||
async function start() {
|
||||
// Safety net: a client aborting a connection (browser navigation, HMR reconnect,
|
||||
// Back/Forward cache) can emit `Error: aborted`/`ECONNRESET` on the request
|
||||
// stream. Without this the single missed listener becomes an uncaughtException
|
||||
// that takes the whole server down — surfacing as a wall of ERR_CONNECTION_REFUSED
|
||||
// after login. Benign aborts are swallowed; genuine errors still crash loudly.
|
||||
installProcessCrashGuard();
|
||||
|
||||
await prepareWithHeal();
|
||||
|
||||
const requestHandler = nextApp.getRequestHandler();
|
||||
@@ -176,6 +187,10 @@ async function start() {
|
||||
|
||||
const server = http.createServer(
|
||||
wrapRequestListenerWithHeadResponseGuard((req, res) => {
|
||||
// Absorb client-abort errors (browser closes the socket during
|
||||
// navigation/HMR/bfcache) on the request/response streams so they never
|
||||
// surface as an uncaughtException that kills the whole server (#fix-dev-server-aborted).
|
||||
attachRequestStreamGuards(req, res);
|
||||
if (maybeHandleDisallowedMethod(req, res)) return;
|
||||
// Stamp the real TCP peer IP before Next sees the request, so the authz
|
||||
// middleware can decide LOCAL_ONLY locality without trusting the Host header.
|
||||
|
||||
Reference in New Issue
Block a user