Repository navigation
dsh web: HTTP keep-alive connections killed at node-default 5s cause browser requests to hang forever (panels "always loading") #10106
Replies: 1 comment
|
The missing configuration is real, but one link in the causal chain does not hold, and it changes what the fix should be. What is true
What does not hold: the client is not blind Node advertises the idle timeout on every keep-alive response. Measured just now on Node v22.22.3 with a bare So a browser pooling that socket is told, in the response, that the server will drop it after 5 s. Chrome/Firefox/Safari all use that header to age pooled idle sockets with a grace margin. Your own reproduction shows That matters because the unexplained symptom in your report is "the promise never settles, spinner forever". A socket the client already knows is expiring cannot produce that on its own. The mechanism that does produce it is the close/send race: the client writes a request onto a socket at the exact moment the server's timer fires. The request bytes are accepted by a socket that is being torn down, no response is generated, and the client sees either a reset it may not retry (non-idempotent requests, plugin RPCs) or a stall until its own timeout. This is a well-known Node behaviour, and it is why servers are usually configured with an idle timeout comfortably longer than a browser's own idle threshold. So the fix is still "set the timeouts", but with one constraint If you raise Two side notes on the diagnosis
I would keep the report, reword the impact paragraph to name the close/send race rather than "client unaware", and add the |
Uh oh!
There was an error while loading. Please reload this page.
Summary
dsh-host-webservercreates the HTTP server (createServer(listener)) but never setskeepAliveTimeout/headersTimeout. Node's defaults apply: keepAliveTimeout = 5000ms. Any browser page that stays open >5s without requests has its pooled keep-alive sockets closed by the server; reusing one of these half-dead sockets makes the request hang forever (no response, no reset to the client until timeout). This makes web-UI plugin panels ("always loading") break after the page idles for a few seconds.Reproduction (dsh 0.2.1-rc.2, dsh web on 127.0.0.1:3080)
Also visible in
lsof -nP -iTCP:3080: the browser holdsCLOSE_WAITsockets (server closed, client unaware).Impact
fetchcalls write into dead sockets → promise never settles → spinner forever. (curl / fresh connections always work, which makes it look like "server fine, UI broken".)Suggested fix
In
dsh-host-webserveraftercreateServer/ beforelisten:(Or expose both as webserver config options.)
Side note
While debugging we also noticed the web login (
?token=…) guards pages but plugin-registered/api/...routes respond to unauthenticated requests that carry a custom header convention — worth a look as a separate hardening item.Environment
dsh web(port 3080), ChromeAll reactions