Release v0.51.239 — Release HG (stage-q10) (#3494)
Some checks failed
Release & Docker / release (push) Has been cancelled
Some checks failed
Release & Docker / release (push) Has been cancelled
## Release v0.51.239 — Release HG (stage-q10) Phase 3 MEDIUM-ring **salvage** from #3407. The source PR bundled a universal reliability fix with debug scaffolding + Android-specific work; this release ships only the clean, universal nugget. ### Fixed | Salvaged from | Author | Fix | |---|---|---| | #3407 | @PatrickNoFilter | `server.py` ignores `SIGPIPE` (`SIG_IGN`) at import time so a client dropping the connection mid-response (tab close mid-stream, network drop, mobile backgrounding, dropped long-poll, `/api/updates/check` timeout) can't silently `Term` the whole process. The broken write now surfaces as a catchable `BrokenPipeError`; the server keeps serving. | ### Why salvage, not merge whole #3407 (585L, 11 commits) bundles three groups: (1) the SIGPIPE fix + a 271-line `diag_shim.py` debug module, (2) an Android-cgroup-specific `os.fork`/`setsid` restart rewrite in `updates.py`, (3) personal deploy scripts (`start-webui.sh`/`watchdog-loop.sh`, which the author notes are "user-side infra, not in the server tree"). Only the SIGPIPE fix is universal, low-risk, and ship-ready — the rest is investigation tooling for a now-solved mystery or platform-specific. The source PR is held with a detailed split explanation. ### Added safety over the source PR The original used a bare `signal.signal(signal.SIGPIPE, ...)` which would `AttributeError` on Windows (no `SIGPIPE`). The salvaged version is `getattr`-guarded so it's a no-op on Windows, preserving the native-Windows support shipped in #1952 (HD). ### Gate results - **Full pytest suite**: 7498 passed, 9 skipped, 3 xpassed, **0 failed** - **ruff**: CLEAN · **browser-smoke**: CLEAN - **Codex (regression)**: SAFE TO SHIP — verified the getattr Windows-guard, that the ignore disposition lands correctly across the `os.execv` self-restart, and that subprocess children use `restore_signals=True` so the ignore doesn't leak to git/shell/editor children. Regression test `tests/test_issue3407_sigpipe_ignore.py` pins SIG_IGN on POSIX, no-raise import, and the getattr guard. Co-authored-by: PatrickNoFilter <PatrickNoFilter@users.noreply.github.com>
This commit is contained in:
21
server.py
21
server.py
@@ -6,6 +6,7 @@ All business logic lives in api/*.
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import signal
|
||||
import socket
|
||||
import sys
|
||||
import threading
|
||||
@@ -13,6 +14,26 @@ import time
|
||||
import traceback
|
||||
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
||||
|
||||
# ── SIGPIPE handling ────────────────────────────────────────────────────────
|
||||
# Ignore SIGPIPE so a client closing the connection mid-response (browser tab
|
||||
# close, network drop, mobile backgrounding, a dropped long-poll, an
|
||||
# `/api/updates/check` timeout, etc.) does not terminate the whole server
|
||||
# process. Python's default action for SIGPIPE is `Term`, so a single dropped
|
||||
# `socket.send()` in any request thread could kill the entire WebUI silently —
|
||||
# no exception, no log, no `/health` response. With SIG_IGN the kernel still
|
||||
# returns EPIPE to the offending write (surfaced as `BrokenPipeError`); the
|
||||
# per-request handler unwinds and the connection just closes, while the server
|
||||
# keeps serving. Set at import time so it is in effect before any
|
||||
# ThreadingHTTPServer worker thread writes its first response. (Salvaged from
|
||||
# #3407 @PatrickNoFilter — reproduced in production 2026-06-02.)
|
||||
#
|
||||
# SIGPIPE is POSIX-only; it does not exist on Windows (where there is no
|
||||
# broken-pipe signal and writes to a dead socket raise an OSError directly), so
|
||||
# guard with getattr to keep native-Windows support (#1952) working.
|
||||
_SIGPIPE = getattr(signal, "SIGPIPE", None)
|
||||
if _SIGPIPE is not None:
|
||||
signal.signal(_SIGPIPE, signal.SIG_IGN)
|
||||
|
||||
# ── Test-mode network isolation ─────────────────────────────────────────────
|
||||
# When `HERMES_WEBUI_TEST_NETWORK_BLOCK=1` is set in the environment, refuse
|
||||
# outbound socket connections to anything that is not loopback / RFC1918 /
|
||||
|
||||
Reference in New Issue
Block a user