test: ensure HTTP server timeout is not caused by a delayed response
What changed, and why it matters
This commit only adds a new automated test. It checks that Bitcoin Core's HTTP server does not wrongly close a long-running RPC connection while the server is still actively working on a delayed response. There is no change to production code, no bug fix, and no security patch.
No action required; this is a test-only addition. Reviewers may optionally run the functional test suite to confirm the new test passes.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff adds a single test method check_server_busy_idle_timeout() to test/functional/interface_http.py. It verifies that the -rpcservertimeout idle timeout does not terminate a connection that is waiting for waitforblockheight to complete, even after the timeout period passes. After the block is generated, the response is delivered and the socket remains open; only a subsequent true idle period triggers the timeout. No C++ or Python server logic is modified.
Changed components
test/functional/interface_http.pyInspect captured patch +21 / −0
diff --git a/test/functional/interface_http.py b/test/functional/interface_http.py
index fbdf2fbe..cfe8973a 100755
--- a/test/functional/interface_http.py
+++ b/test/functional/interface_http.py
@@ -103,6 +103,7 @@ class HTTPBasicsTest (BitcoinTestFramework):
self.check_pipelining()
self.check_chunked_transfer()
self.check_idle_timeout()
+ self.check_server_busy_idle_timeout()
def check_default_connection(self):
@@ -262,5 +263,25 @@ class HTTPBasicsTest (BitcoinTestFramework):
assert not conn.sock_closed()
+ def check_server_busy_idle_timeout(self):
+ self.log.info("Check that -rpcservertimeout won't close on a delayed response")
+ tip_height = self.nodes[2].getblockcount()
+ conn = BitcoinHTTPConnection(self.nodes[2])
+ conn.post_raw('/', f'{{"method": "waitforblockheight", "params": [{tip_height + 1}]}}')
+
+ # Wait until after the timeout, then generate a block with a second HTTP connection
+ time.sleep(RPCSERVERTIMEOUT + 1)
+ generated_block = self.generate(self.nodes[2], 1, sync_fun=self.no_op)[0]
+
+ # The first connection gets the response it is patiently waiting for
+ response1 = conn.recv_raw().decode()
+ assert generated_block in response1
+ # The connection is still open
+ assert not conn.sock_closed()
+
+ # Now it will actually close due to idle timeout
+ conn.expect_timeout(RPCSERVERTIMEOUT)
+
+
if __name__ == '__main__':
HTTPBasicsTest(__file__).main()
Why this scored 12/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.