fix(dev): allow Ctrl+C to promptly kill dev server by closing active connections (#13020)

* fix(dev): allow Ctrl+C to promptly kill dev server by closing active connections and adding force-exit timeout

* test(dev): add regression assertions for prompt dev server exit on Ctrl+C

* docs(changelog): add fragment for #13020
This commit is contained in:
Tuan Dinh
2026-09-18 22:23:15 +07:00
committed by GitHub
parent 4587c7ec3a
commit e188da6399
3 changed files with 35 additions and 0 deletions

View File

@@ -0,0 +1 @@
- **fix(dev):** allow Ctrl+C to promptly kill dev server by closing active connections ([#13020](https://github.com/diegosouzapw/OmniRoute/pull/13020)) — thanks @tuandinh0801

View File

@@ -235,15 +235,31 @@ async function start() {
process.exit(1);
});
let isShuttingDown = false;
const shutdown = async (signal) => {
if (isShuttingDown) {
// Second Ctrl+C / signal forces immediate exit
process.exit(1);
}
isShuttingDown = true;
// Safety net: force exit after 2s if keep-alive sockets or Next.js app close hangs
const forceExitTimer = setTimeout(() => {
process.exit(0);
}, 2000);
forceExitTimer.unref?.();
systemdNotifier.stopping();
try {
server.closeIdleConnections?.();
server.closeAllConnections?.();
await new Promise((resolve) => server.close(resolve));
await globalThis.__omnirouteRequestShutdown?.(signal);
await nextApp.close();
} catch (error) {
console.error("[SHUTDOWN] Failed during signal:", signal, error);
} finally {
clearTimeout(forceExitTimer);
process.exit(0);
}
};

View File

@@ -27,3 +27,21 @@ test("the custom Next runner owns exit and awaits application cleanup before clo
assert.ok(applicationCleanup < nextClose, "application cleanup must finish before Next closes");
assert.ok(nextClose < processExit, "process exit must remain the final shutdown action");
});
test("custom Next runner closes existing connections and supports force exit on Ctrl+C", () => {
const closeAll = runNextSource.indexOf("server.closeAllConnections?.()");
const serverClose = runNextSource.indexOf("server.close(resolve)");
assert.ok(closeAll >= 0, "must close active/keep-alive connections to prevent hanging on Ctrl+C");
assert.ok(closeAll < serverClose, "connections must be terminated before server.close wait");
assert.match(
runNextSource,
/if\s*\(\s*isShuttingDown\s*\)\s*\{\s*\/\/[^\n]*\n\s*process\.exit\(1\);?\s*\}/,
"second signal / Ctrl+C must immediately exit"
);
assert.match(
runNextSource,
/setTimeout\(\(\)\s*=>\s*\{\s*process\.exit\(0\);?\s*\},\s*\d+\)/,
"must have fallback force exit timer"
);
});