Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions daphne/http_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
29 changes: 29 additions & 0 deletions daphne/management/commands/runserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,22 @@ def add_arguments(self, parser):
"seconds (default: 5)"
),
)
parser.add_argument(
"--websocket-max-message-size",
type=int,
help="Maximum size, in bytes, of an incoming WebSocket message. "
"0 disables the limit (not recommended; allows unauthenticated "
"memory exhaustion).",
default=None,
)
parser.add_argument(
"--websocket-max-frame-size",
type=int,
help="Maximum size, in bytes, of a single incoming WebSocket frame. "
"0 disables the limit (not recommended; allows unauthenticated "
"memory exhaustion).",
default=None,
)
if apps.is_installed("django.contrib.staticfiles"):
parser.add_argument(
"--nostatic",
Expand All @@ -95,6 +111,17 @@ def handle(self, *args, **options):
raise CommandError(
"You have not set ASGI_APPLICATION, which is needed to run the server."
)
self.websocket_max_message_size = options.get("websocket_max_message_size")
if self.websocket_max_message_size is None:
self.websocket_max_message_size = getattr(
settings, "DAPHNE_WEBSOCKET_MAX_MESSAGE_SIZE", 1024 * 1024
)
self.websocket_max_frame_size = options.get("websocket_max_frame_size")
if self.websocket_max_frame_size is None:
self.websocket_max_frame_size = getattr(
settings, "DAPHNE_WEBSOCKET_MAX_FRAME_SIZE", 1024 * 1024
)

# Dispatch upward
super().handle(*args, **options)

Expand Down Expand Up @@ -145,6 +172,8 @@ def inner_run(self, *args, **options):
http_timeout=self.http_timeout,
root_path=getattr(settings, "FORCE_SCRIPT_NAME", "") or "",
websocket_handshake_timeout=self.websocket_handshake_timeout,
websocket_max_message_size=self.websocket_max_message_size,
websocket_max_frame_size=self.websocket_max_frame_size,
).run()
logger.debug("Daphne exited")
except KeyboardInterrupt:
Expand Down
8 changes: 2 additions & 6 deletions tests/test_websocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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}",
Expand Down
Loading