fix: bound the wait for a silent list server

A server that took the connection and never answered held line mode
93 s, three waits of 30 s for a first byte. A file list now waits 15 s
per answer and asks a silent server twice. Downloads keep their own
timeout between chunks.
This commit is contained in:
Abdessamad Derraz committed 2026-10-23 20:58:19 +02:00
1 parent 812d5d157e
commit d297cc3ff6
4 files changed
+71 -6

No files matched your search

+43
View File
@@ -17,6 +17,7 @@ import subprocess
import sys
import tempfile
import threading
import time
import unittest
from types import SimpleNamespace
import unittest.mock
@@ -2369,6 +2370,48 @@ class FileListsAreRetried(unittest.TestCase):
self.assertIn(f"after {install.MAX_RETRIES} attempts", err.getvalue())
class ASilentServerIsBounded(unittest.TestCase):
"""A server that takes the connection and never answers held line mode
93 s: three waits of 30 s for a first byte that never came."""
def test_two_short_waits_then_the_reason(self):
import socket
import threading
listener = socket.socket()
listener.bind(("127.0.0.1", 0))
listener.listen(8)
self.addCleanup(listener.close)
held = []
def accept():
while True:
try:
conn, _addr = listener.accept()
except OSError:
return
held.append(conn)
threading.Thread(target=accept, daemon=True).start()
self.addCleanup(lambda: [conn.close() for conn in held])
port = listener.getsockname()[1]
err = io.StringIO()
start = time.monotonic()
with unittest.mock.patch.object(install, "LIST_TIMEOUT", 0.5), \
unittest.mock.patch.object(install, "_retry_delay", return_value=0), \
unittest.mock.patch.object(install, "MANIFEST_URL",
f"http://127.0.0.1:{port}/{{platform}}.json"), \
contextlib.redirect_stderr(err):
with self.assertRaises(SystemExit):
install.fetch_manifest("retroarch")
elapsed = time.monotonic() - start
self.assertLess(elapsed, 5)
self.assertEqual(len(held), install.STALLED_ATTEMPTS)
self.assertIn(f"after {install.STALLED_ATTEMPTS} attempts", err.getvalue())
# A download keeps its own, longer wait between chunks.
self.assertGreater(install.LIST_TIMEOUT, 1)
class TargetListFailureIsNotAnAnswer(unittest.TestCase):
"""A timeout is not 'no targets': it sent users to the full pack."""