From 68ff15e35cf6bfade68d022b68bf196b2b08b2af Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 24 Jun 2026 12:08:15 +1000 Subject: [PATCH] testsuite: daemon-proxy-protocol drop-leg tolerates EPIPE on send The probe() helper sends the @RSYNCD greeting after the PROXY header, then reads the daemon's response. For a `want='drop'` leg (untrusted peer / a daemon with no `proxy protocol hosts`) the daemon closes the connection, which on some CI runners surfaces as EPIPE/ECONNRESET on our sendall() before we ever read -- an uncaught BrokenPipeError that failed the test (seen on AlmaLinux 8 and Ubuntu 22.04, a timing race; other runners closed read-side). Wrap the send/recv in `except OSError` and leave `out` empty: for want='drop' the absent greeting is the expected outcome; want='ok'/'denied' still fail correctly on an absent greeting. --- testsuite/daemon-proxy-protocol_test.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/testsuite/daemon-proxy-protocol_test.py b/testsuite/daemon-proxy-protocol_test.py index 05201f23..450538d8 100644 --- a/testsuite/daemon-proxy-protocol_test.py +++ b/testsuite/daemon-proxy-protocol_test.py @@ -115,6 +115,7 @@ def probe(port, hdr, label, *, want): """ s = socket.create_connection(('127.0.0.1', port), timeout=10) s.settimeout(10) + out = b'' try: if hdr: s.sendall(hdr) @@ -122,7 +123,6 @@ def probe(port, hdr, label, *, want): # list-only request. Protocol 30, no capabilities, no auth. s.sendall(b'@RSYNCD: 30.0\nmod\n') # Slurp everything the daemon writes before it closes. - out = b'' try: s.shutdown(socket.SHUT_WR) except OSError: @@ -135,6 +135,13 @@ def probe(port, hdr, label, *, want): if not chunk: break out += chunk + except OSError: + # A 'drop' daemon closes the connection, which can surface as + # EPIPE/ECONNRESET on our sendall() before we ever read -- a timing + # race seen on some CI runners. Leave `out` empty and let the + # want-check below decide: for want='drop' the absent greeting is the + # expected outcome; for want='ok'/'denied' it still fails correctly. + pass finally: s.close()