From b96ac9809be0a461bd7d6635b65ec0e7c0655840 Mon Sep 17 00:00:00 2001 From: Kent Bull Date: Sat, 15 Aug 2026 21:01:45 -0600 Subject: [PATCH 1/2] fix: restore idle TCP connection cleanup Propagate the server's configured idle tymeout to accepted TCP and TLS Remoters. Without that value, supervisors treat idle expiration as disabled and leave stale connections active. Add regression coverage that advances virtual time and exercises the normal idle cleanup gate. Fixes #167 --- src/hio/core/tcp/serving.py | 4 ++-- tests/core/tcp/test_tcp.py | 43 ++++++++++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/hio/core/tcp/serving.py b/src/hio/core/tcp/serving.py index d69256a4..8b83c20f 100644 --- a/src/hio/core/tcp/serving.py +++ b/src/hio/core/tcp/serving.py @@ -278,7 +278,7 @@ def serviceAxes(self): cs=cs, bs=self.bs, wl=self.wl, - timeout=self.tymeout) + tymeout=self.tymeout) if ca in self.ixes and self.ixes[ca] is not remoter: self.shutdownIx(ca) self.ixes[ca] = remoter @@ -556,7 +556,7 @@ def serviceAxes(self): bs=self.bs, cs=cs, wl=self.wl, - timeout=self.tymeout, + tymeout=self.tymeout, context=self.context, version=self.version, certify=self.certify, diff --git a/tests/core/tcp/test_tcp.py b/tests/core/tcp/test_tcp.py index 19890c6e..bb953066 100644 --- a/tests/core/tcp/test_tcp.py +++ b/tests/core/tcp/test_tcp.py @@ -98,7 +98,7 @@ def test_tcp_basic(): assert server.opened == False tymist = tyming.Tymist() - with tcp.openServer(tymth=tymist.tymen(), ha=("", 6101)) as server, \ + with tcp.openServer(tymth=tymist.tymen(), ha=("", 6101), tymeout=1.5) as server, \ tcp.openClient(tymth=tymist.tymen(), ha=("127.0.0.1", 6101)) as beta, \ tcp.openClient(tymth=tymist.tymen(), ha=("127.0.0.1", 6101)) as gamma: @@ -136,6 +136,8 @@ def test_tcp_basic(): assert ixBeta.cs.getpeername() == beta.cs.getsockname() # ixBeta remote beta local assert ixBeta.ca == beta.ca == ixBeta.cs.getpeername() assert ixBeta.ha == beta.ha == ixBeta.cs.getsockname() + assert ixBeta.tymeout == server.tymeout == 1.5 + assert ixBeta.tymer.duration == ixBeta.tymeout msgOut = b"Beta sends to Server" count = beta.send(msgOut) @@ -472,6 +474,42 @@ def test_tcp_basic(): """Done Test""" + +def test_server_idle_tymeout_enables_remoter_cleanup(): + """ + Test a supervisor can remove an idle Remoter at the Server's tymeout. + """ + tymist = tyming.Tymist() + with tcp.openServer(tymth=tymist.tymen(), + ha=("127.0.0.1", 0), + tymeout=1.0) as server: + # Acceptor does not refresh .eha after binding an ephemeral port. + server.eha = server.ha + + with tcp.openClient(tymth=tymist.tymen(), ha=server.ha) as client: + for _ in range(10): + client.serviceConnect() + server.serviceConnects() + if client.connected and client.ca in server.ixes: + break + time.sleep(0.05) + + assert client.connected == True + assert client.ca in server.ixes + + remoter = server.ixes[client.ca] + assert server.tymeout == 1.0 + + tymist.tick(tock=server.tymeout) + + # HIO HTTP and KERIpy supervisors treat zero tymeout as disabled. + if remoter.tymeout > 0.0 and remoter.tymer.expired: + server.removeIx(client.ca) + + assert client.ca not in server.ixes + assert remoter.cs is None + + def test_tcp_service(): """ Test Classes tcp service methods @@ -691,6 +729,7 @@ def test_tcp_tls_default_context(): with tcp.openServer(cls=tcp.ServerTls, tymth=tymist.tymen(), ha=("", 6101), + tymeout=1.5, bs=16192, keypath=serverKeyPath, certpath=serverCertPath, @@ -730,6 +769,8 @@ def test_tcp_tls_default_context(): assert ixBeta.cs.getpeername() == beta.cs.getsockname() assert ixBeta.ca == beta.ca assert ixBeta.ha == beta.ha + assert ixBeta.tymeout == server.tymeout == 1.5 + assert ixBeta.tymer.duration == ixBeta.tymeout msgOut = b"Beta sends to Server\n" beta.tx(msgOut) From a237db642d767130b373a7772aaadd4bc125de75 Mon Sep 17 00:00:00 2001 From: Kent Bull Date: Fri, 28 Aug 2026 13:57:40 -0600 Subject: [PATCH 2/2] docs: clarify WASM backend isolation assertion Document that the WASM smoke test verifies native-only persistence backends are not imported eagerly. This is a test-contract clarification and keeps the assertion intent clear while refreshing CI for the pull request. --- tests/wasm/test_webduring_wasm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/wasm/test_webduring_wasm.py b/tests/wasm/test_webduring_wasm.py index 3151203f..c6a6d21e 100644 --- a/tests/wasm/test_webduring_wasm.py +++ b/tests/wasm/test_webduring_wasm.py @@ -48,6 +48,7 @@ async def test_webduror_wasm_contract(selenium): import hio.base from hio.base import WebDuror + # WASM must not eagerly import native-only persistence backends. assert "hio.base.during" not in sys.modules assert "pysodium" not in sys.modules assert "pychloride" not in sys.modules