From dd489e65aa72e82b5d179927beb870531b615608 Mon Sep 17 00:00:00 2001 From: Slawomir Koszewski Date: Fri, 30 Oct 2026 02:01:27 +0100 Subject: [PATCH] fix: the web service, the metrics service and the bootstrap server log a failure in one line instead of a traceback, and nothing when a client goes away. --- debian-package/debian/changelog | 10 ++++++++++ .../src/usr/lib/vpn-router/bootstrap | 13 ++++++++++++- .../src/usr/lib/vpn-router/vpnrouter_serve.py | 18 ++++++++++++++++-- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/debian-package/debian/changelog b/debian-package/debian/changelog index fae9a3c..35741b8 100644 --- a/debian-package/debian/changelog +++ b/debian-package/debian/changelog @@ -1,3 +1,13 @@ +vpn-router (0.37.2-1) unstable; urgency=medium + + fix: the web service, the metrics service and the bootstrap server log a failure in one line instead of a traceback, and nothing when a client goes away. + + * A request whose client closed the connection is no longer logged. + * Any other failure of a request, a thread or the service is logged in + one line with the exception's type and message. + + -- Slawomir Koszewski Fri, 30 Oct 2026 01:59:19 +0100 + vpn-router (0.37.1-1) unstable; urgency=medium fix: /config answers the OpenID Provider's state and issuer. diff --git a/debian-package/src/usr/lib/vpn-router/bootstrap b/debian-package/src/usr/lib/vpn-router/bootstrap index 7e8fbe2..107bae9 100755 --- a/debian-package/src/usr/lib/vpn-router/bootstrap +++ b/debian-package/src/usr/lib/vpn-router/bootstrap @@ -8,6 +8,7 @@ the logs. """ import subprocess +import sys from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer PORT = 17081 @@ -51,5 +52,15 @@ class Handler(BaseHTTPRequestHandler): self.wfile.write(body) +def failed(request, client_address): + """Log a failed request in one line, and nothing when the client went away.""" + failure = sys.exc_info()[1] + if not isinstance(failure, ConnectionError): + print(f'a request from {client_address[0]} failed: {type(failure).__name__}: {failure}', file=sys.stderr, flush=True) + + if __name__ == '__main__': - ThreadingHTTPServer(('', PORT), Handler).serve_forever() + sys.excepthook = lambda kind, value, _: print(f'failed: {kind.__name__}: {value}', file=sys.stderr, flush=True) + server = ThreadingHTTPServer(('', PORT), Handler) + server.handle_error = failed + server.serve_forever() diff --git a/debian-package/src/usr/lib/vpn-router/vpnrouter_serve.py b/debian-package/src/usr/lib/vpn-router/vpnrouter_serve.py index 22c23cd..494a50f 100644 --- a/debian-package/src/usr/lib/vpn-router/vpnrouter_serve.py +++ b/debian-package/src/usr/lib/vpn-router/vpnrouter_serve.py @@ -4,6 +4,7 @@ Only the Python standard library is used, so the bootstrap server, which must ru is broken, can use it. """ +import sys import threading import time from http.server import ThreadingHTTPServer @@ -14,9 +15,21 @@ def serve(wanted, handler, log, interval): wanted answers ([(address, port), ...], '') or ([], the reason not to listen). A server whose place is no longer wanted is stopped, and a new one listens at each new place; a place that - cannot be bound is tried again the next round. Every new message is logged with log. Does - not return. + cannot be bound is tried again the next round. Every new message is logged with log. A request + whose client went away is not logged; any other exception a request, a thread or the process + does not handle is logged with log in one line, without a traceback. Does not return. """ + def failed(request, client_address): + failure = sys.exc_info()[1] + if not isinstance(failure, ConnectionError): + log(f'a request from {client_address[0]} failed: {type(failure).__name__}: {failure}') + + def thread_failed(hook): + if not issubclass(hook.exc_type, (SystemExit, ConnectionError)): + log(f'{hook.thread.name if hook.thread else "a thread"} failed: {hook.exc_type.__name__}: {hook.exc_value}') + + threading.excepthook = thread_failed + sys.excepthook = lambda kind, value, _: log(f'failed: {kind.__name__}: {value}') servers, reported = {}, None while True: targets, reason = wanted() @@ -34,6 +47,7 @@ def serve(wanted, handler, log, interval): except OSError as exc: messages.append(f'cannot listen on {target[0]}:{target[1]}: {exc}') continue + server.handle_error = failed thread = threading.Thread(target=server.serve_forever, name=f'server {target[0]}', daemon=True) thread.start() servers[target] = (server, thread)