From ece3e67aabbb4ae233a5b6830628479c66b79c68 Mon Sep 17 00:00:00 2001 From: linhongkuan Date: Thu, 25 Jun 2026 01:09:58 +0800 Subject: [PATCH 1/2] Exit runserver when Daphne startup aborts --- daphne/management/commands/runserver.py | 19 +++++++++++--- tests/test_runserver_startup.py | 34 +++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 tests/test_runserver_startup.py diff --git a/daphne/management/commands/runserver.py b/daphne/management/commands/runserver.py index d505f335..3af7125e 100644 --- a/daphne/management/commands/runserver.py +++ b/daphne/management/commands/runserver.py @@ -136,16 +136,27 @@ def inner_run(self, *args, **options): # build the endpoint description string from host/port options endpoints = build_endpoint_description_strings(host=self.addr, port=self.port) + self.run_daphne( + application=self.get_application(options), + endpoints=endpoints, + options=options, + root_path=getattr(settings, "FORCE_SCRIPT_NAME", "") or "", + ) + + def run_daphne(self, application, endpoints, options, root_path): try: - self.server_cls( - application=self.get_application(options), + self.server = self.server_cls( + application=application, endpoints=endpoints, signal_handlers=not options["use_reloader"], action_logger=self.log_action, http_timeout=self.http_timeout, - root_path=getattr(settings, "FORCE_SCRIPT_NAME", "") or "", + root_path=root_path, websocket_handshake_timeout=self.websocket_handshake_timeout, - ).run() + ) + self.server.run() + if self.server.abort_start: + raise CommandError("Daphne failed to start.") logger.debug("Daphne exited") except KeyboardInterrupt: shutdown_message = options.get("shutdown_message", "") diff --git a/tests/test_runserver_startup.py b/tests/test_runserver_startup.py new file mode 100644 index 00000000..49d2cc58 --- /dev/null +++ b/tests/test_runserver_startup.py @@ -0,0 +1,34 @@ +from unittest import TestCase + +from django.core.management import CommandError + +from daphne.management.commands.runserver import Command + + +class TestRunserverCommand(TestCase): + class AbortedServer: + abort_start = True + + def __init__(self, **kwargs): + self.init_kwargs = kwargs + self.ran = False + + def run(self): + self.ran = True + + def test_run_daphne_raises_command_error_when_start_aborts(self): + command = Command() + command.server_cls = self.AbortedServer + command.http_timeout = None + command.websocket_handshake_timeout = 5 + + with self.assertRaisesRegex(CommandError, "Daphne failed to start."): + command.run_daphne( + application=object(), + endpoints=["tcp:port=8000:interface=127.0.0.1"], + options={"use_reloader": False}, + root_path="", + ) + + self.assertTrue(command.server.ran) + self.assertTrue(command.server.init_kwargs["signal_handlers"]) From 32abb45f229951977a1e003d9e240d94841465db Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 25 Jun 2026 03:28:48 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- daphne/http_protocol.py | 4 +--- tests/test_websocket.py | 8 ++------ 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/daphne/http_protocol.py b/daphne/http_protocol.py index 1319f462..fdd470f1 100755 --- a/daphne/http_protocol.py +++ b/daphne/http_protocol.py @@ -77,9 +77,7 @@ def process(self): return for value in values: if INVALID_HEADER_VALUE_BYTES.intersection(value): - self.basic_error( - 400, b"Bad Request", "Invalid header value" - ) + self.basic_error(400, b"Bad Request", "Invalid header value") return # Get upgrade header diff --git a/tests/test_websocket.py b/tests/test_websocket.py index 844b14ee..bf385143 100644 --- a/tests/test_websocket.py +++ b/tests/test_websocket.py @@ -330,9 +330,7 @@ def test_websocket_max_message_size_allows_under_limit(self): sock, _ = self.websocket_handshake(test_app) _, messages = test_app.get_received() self.assert_valid_websocket_connect_message(messages[0]) - test_app.add_send_messages( - [{"type": "websocket.send", "text": "ack"}] - ) + test_app.add_send_messages([{"type": "websocket.send", "text": "ack"}]) self.websocket_send_frame(sock, "x" * 16) assert self.websocket_receive_frame(sock) == "ack" @@ -418,9 +416,7 @@ def test_websocket_upgrade_rejects_smuggled_headers(self): for byte in self.INVALID_BYTES: with self.subTest(byte=byte): value = b"innocent" + byte + b"X-Secret-Auth: admin-token" - response = self.run_daphne_raw( - self._websocket_upgrade_request(value) - ) + response = self.run_daphne_raw(self._websocket_upgrade_request(value)) self.assertTrue( response.startswith(b"HTTP/1.1 400"), f"expected 400 for byte {byte!r}, got {response[:80]!r}",