"""What a deferred accept does to connections that never send anything. Those connections never reach opentracker, so its own client timeout cannot clean them up: whatever bound exists is the kernel's. This measures it, and then asks the question that follows from it -- whether enough of them keep the tracker from answering anybody else. make opentracker python3 tests/accept_filter_probe.py On FreeBSD the filter does nothing unless accf_data is loaded, so run it both ways to see whether the numbers differ: kldload accf_data """ import os import socket import signal import subprocess import sys import tempfile import time import urllib.error import urllib.request TRACKER = os.environ.get("OPENTRACKER", "./opentracker") IDLE = int(os.environ.get("IDLE", "300")) PATIENCE = float(os.environ.get("PATIENCE", "90")) HASH = "".join("%%%02x" % b for b in range(1, 21)) def start(port): directory = tempfile.mkdtemp(prefix="ot-accf-") with open(os.path.join(directory, "conf"), "w") as handle: handle.write("listen.tcp 127.0.0.1:%d\nlisten.udp 127.0.0.1:%d\n" % (port, port)) log = open(os.path.join(directory, "log"), "w+b") tracker = subprocess.Popen([TRACKER, "-f", os.path.join(directory, "conf")], stdout=log, stderr=log) for _ in range(300): try: socket.create_connection(("127.0.0.1", port), timeout=0.2).close() return tracker, log except OSError: time.sleep(0.02) tracker.kill() raise SystemExit("tracker never listened on %d" % port) def announce(port, timeout=5): """One ordinary request, so we can see whether the tracker still serves.""" started = time.time() try: urllib.request.urlopen( "http://127.0.0.1:%d/announce?info_hash=%s&peer_id=-OT0001-probe0000001&port=6881&left=0" % (port, HASH), timeout=timeout, ).read() return time.time() - started, None except Exception as problem: # noqa: BLE001 - reported, not swallowed return time.time() - started, problem def main(): port = int(os.environ.get("PORT", "28000")) tracker, log = start(port) try: print(" how long a connection that says nothing is held") quiet = socket.create_connection(("127.0.0.1", port), timeout=5) quiet.settimeout(PATIENCE) started = time.time() try: held = "dropped after %.1f s" % (time.time() - started) if quiet.recv(1) == b"" else "sent us something" except socket.timeout: held = "still open after %.0f s" % PATIENCE except OSError as problem: held = "ended after %.1f s: %s" % (time.time() - started, problem) quiet.close() print(" %s" % held) print(" and whether %d of them keep anybody else out" % IDLE) before, problem = announce(port) print(" a request before: %.0f ms%s" % (before * 1000, "" if problem is None else " (%s)" % problem)) idlers = [] for _ in range(IDLE): try: idlers.append(socket.create_connection(("127.0.0.1", port), timeout=2)) except OSError as problem: print(" could only open %d of them: %s" % (len(idlers), problem)) break print(" holding %d connections that have sent nothing" % len(idlers)) during, problem = announce(port, timeout=10) print(" a request during: %.0f ms%s" % (during * 1000, "" if problem is None else " REFUSED: %s" % problem)) for idler in idlers: idler.close() after, problem = announce(port) print(" a request after: %.0f ms%s" % (after * 1000, "" if problem is None else " (%s)" % problem)) finally: tracker.send_signal(signal.SIGINT) try: tracker.wait(timeout=10) except subprocess.TimeoutExpired: tracker.kill() tracker.wait() log.close() if __name__ == "__main__": main()