#36204 http: disconnect clients that never finish a request
| Reviewability | Reviews | Agreement | Size | ||||
|---|---|---|---|---|---|---|---|
| #36204 http: disconnect clients that never finish a request | Needs rebase | 0 | Mild | 67 + 183 tests | |||
|
|
|
|
| |||
Reviewability: ReadyStalePaused Agreement: StrongPositivePositive w/ caveatsNeutralMildDisputedBlockedCrickets Reviews: current code-review ACKs, then (+stale ACKs) and -NACKs; greener = more ACKs. Size: lines added or modified outside tests, then in tests; greener = smaller. | |||||||
Goal
Adds a completion deadline to the internal HTTP server to disconnect clients that take too long to finish sending a request. The deadline timer starts on the first byte of a request, expires after the duration set by -rpcservertimeout, and is not reset by subsequent reads. The deadline is cleared once request parsing completes, deferred while worker threads process a request, restarted while response bytes are sent, and disabled when -rpcservertimeout is zero.
Problem: The existing -rpcservertimeout only measures inactivity, resetting every time bytes are read from a socket. A client trickling bytes without ever sending a complete HTTP request can hold a connection slot indefinitely without triggering authentication. A small number of such connections can exhaust all available slots (default 16, or -rpcmaxconnections), preventing legitimate RPC clients from connecting.
Evidence of importance:
- PR description explains the vulnerability: "The -rpcservertimeout timer only measures inactivity. Every read from a client resets it, so a client that keeps sending bytes without ever completing a request can hold the connection for ever. Connection slots are capped by default 16 or by -rpcmaxconnections. Once those slots are taken, the server stops accepting new connections."
- A new functional test (check_trickled_request_timeout in test/functional/interface_http.py) demonstrates that with -rpcmaxconnections=1, trickling a request byte-by-byte blocks subsequent RPC calls until the new request deadline closes the stalled socket.
- Author notes the scope and impact: "The impact is low because RPC port binds to localhost by default, so this is a robustness fix rather than a remotely exploitable security issue."
Reviewability
Ready · Needs rebase
Conflicts with master; review the logic now, the rebase is mechanical.
Waiting on (from the thread read):
- author: response to hodlinator's objection regarding the test assertion duration 2026-09-18
Agreement
Mild
objection that needs an answer has none yet (hodlinator)
Thread notes: Added Concept ACK from pinheadmz; the PR remains waiting on the author to respond to hodlinator's objection in rc:4046818492.
Objections, suggestions, and questions:
| Reviewer | Type | Harm, request, or question | Status | Must be | Author replied | Quote |
|---|---|---|---|---|---|---|
| hodlinator (member) | suggestion, approach | Order condition checks so !m_req_busy comes first as the cheapest check | accepted · reviewer agreed with author's rationale | answered | yes | 2026-09-11: 'nanonit: Feels like `!m_req_busy` would be the cheapest check and be the first one (same for `is_idle`).' Status from: 2026-09-17: hodlinator: 'Agree on this.' |
| hodlinator (member) | suggestion, approach | Consider adopting mockable steady clock and FakeSteadyClock from #36159 in httpserver_tests | accepted · reviewer left it to author's preference after author asked if #36159 must land first | answered | yes | 2026-09-11: 'Not sure time drift is an issue with these tests, but #36159's 9afe15897e8dbee9429722d0db7859d895919bf4 switches to the mockable steady clock and freezes time in 784ac4fb9b4b3b9a5614e4ea54c108e1246ce5' Status from: 2026-09-17: hodlinator: 'I'm happy however the changes get in.' |
| hodlinator (member) | suggestion, approach | Send the first byte in the thread instead of sending it before thread launch | fixed · fix pushed · author confirmed and reworked the test | answered | yes | 2026-09-11: 'Seems to work fine without send the first byte here and instead sending it in the thread?' Status from: 2026-09-14: janb84: 'Yes and I reworked the test, thanks' |
| hodlinator (member) | question, approach | Whether tricklers need to be cleaned up if the test fails | answered · author confirmed test cleanup is handled | answered | yes | 2026-09-11: 'If the test fails for some reason we don't need to clean up the `tricklers` right? See 659671ac3db7e5157178efe4d9f8bce7d92ea237' Status from: 2026-09-14: janb84: 'correct, thanks, see above' |
| hodlinator (member) | objection, correctness | Test assertion allowing duration less than RPCSERVERTIMEOUT could allow premature socket reclamation to pass unnoticed | open | answered | no | 2026-09-18: 'Why would we allow *less* than `RPCSERVERTIMEOUT` (which is only `2`)?' |
Support:
- hodlinator (member) (Concept ACK, 2026-09-11): (no reason given) [not substantive]
- hodlinator (member) (2026-09-18) [read]: Nice how you cleaned up the test, makes it easy to grasp.
- pinheadmz (member) (Concept ACK, 2026-10-30): this mitigates slow-loris and perhaps most importantly, mitigates the LLM "findings" reports we keep getting
Participants:
- hodlinator (member, objection, 13 statements 2026-09-11 to 2026-09-18): Concept ACK, but objected to test assertion allowing socket reclamation duration less than RPCSERVERTIMEOUT
- pinheadmz (member, support, 1 statements 2026-10-30 to 2026-10-30): Concept ACK, noting it mitigates slow-loris and LLM report churn
Files
193 lines under test/bench/ci.
- src/test/httpserver_tests.cpp +120/-10
- test/functional/interface_http.py +63/-0
- src/httpserver.cpp +42/-2
- src/httpserver.h +25/-0
Card
The short text the ranking pass compares across PRs.
Adds a completion deadline timer to the HTTP server to disconnect clients that do not finish sending a complete request within the timeout window. Currently, -rpcservertimeout only tracks idle time between reads, so a slowloris-style client trickling bytes can hold a connection slot indefinitely; occupying all connection slots (default 16) blocks legitimate RPC clients before authentication is checked. The author notes that because the RPC port binds to localhost by default, this serves primarily as a local robustness fix against connection exhaustion.
Data
thread read through 2026-10-30 (23 statements), head 99e880356b
labels: RPC/REST/ZMQ, Needs rebase
Categories
RPC / REST / ZMQ
P3 · DoS protection
Prevents slow-loris connection slot exhaustion by disconnecting HTTP clients that fail to complete requests within -rpcservertimeout. As noted by the author, impact is moderate because RPC binds to localhost by default, making this a deferred robustness fix rather than an urgent remote vulnerability.
Membership: Matches RPC/REST/ZMQ label and modifies HTTP server connection timeout logic in src/httpserver.cpp and src/httpserver.h.
Factors: security/stability 1, bug 1, performance 0, user value 1, leverage 0