diff --git a/ext_http_server.py b/ext_http_server.py index 58d1cdb..1545207 100644 --- a/ext_http_server.py +++ b/ext_http_server.py @@ -259,6 +259,10 @@ def __init__( class MyServer(socketserver.ThreadingMixIn, SecureHTTPServer): """A threaded SecureHTTPServer with basic error filtering.""" + # Run connection threads as daemons so a lingering HTTP/1.1 keep-alive + # connection cannot block interpreter shutdown on Ctrl-C (see issue #1). + daemon_threads = True + def handle_error(self, request: socket | tuple[bytes, socket], client_address: Any) -> None: # noqa: ANN401 """Disable tracebacks on connection close errors.""" _, exc_value, _ = sys.exc_info() @@ -317,6 +321,8 @@ def main() -> int: server.serve_forever() except KeyboardInterrupt: print("\nGoodbye") + finally: + server.server_close() return 0 diff --git a/tests/test_ext_http_server.py b/tests/test_ext_http_server.py index f5b0599..1cbee40 100644 --- a/tests/test_ext_http_server.py +++ b/tests/test_ext_http_server.py @@ -156,6 +156,12 @@ def test_server_serves_range_request(secure_server): assert body == b"56789ABCDEFGHIJ" +def test_server_uses_daemon_threads(): + # Daemon connection threads keep lingering keep-alive connections from + # blocking interpreter shutdown on Ctrl-C (see issue #1). + assert MyServer.daemon_threads is True + + def test_set_rate_limit_computes_block_size(): RateLimitWriter.set_rate_limit(128) assert RateLimitWriter.block_size == int(1024 * 128 * RateLimitWriter.INTERVAL_LEN)