From dc290fb418aa71f4a5c1bd3fbba8df84d414c4b1 Mon Sep 17 00:00:00 2001 From: cacosandon Date: Wed, 1 May 2024 18:04:13 -0700 Subject: [PATCH 1/4] fix(channels/generic): ensure text message exists before deciding to handle --- channels/generic/websocket.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/channels/generic/websocket.py b/channels/generic/websocket.py index 6d41c8ee..42e5c54e 100644 --- a/channels/generic/websocket.py +++ b/channels/generic/websocket.py @@ -60,7 +60,7 @@ def websocket_receive(self, message): Called when a WebSocket frame is received. Decodes it and passes it to receive(). """ - if "text" in message: + if "text" in message and message["text"] is not None: self.receive(text_data=message["text"]) else: self.receive(bytes_data=message["bytes"]) @@ -200,7 +200,7 @@ async def websocket_receive(self, message): Called when a WebSocket frame is received. Decodes it and passes it to receive(). """ - if "text" in message: + if "text" in message and message["text"] is not None: await self.receive(text_data=message["text"]) else: await self.receive(bytes_data=message["bytes"]) From 26fdd8151bb3c59ae5545239f784ab76840b9616 Mon Sep 17 00:00:00 2001 From: cacosandon Date: Mon, 2 Sep 2024 23:47:58 -0400 Subject: [PATCH 2/4] tests(channels/generic): regression test for double check of text message None --- tests/test_generic_websocket.py | 41 +++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/tests/test_generic_websocket.py b/tests/test_generic_websocket.py index c553eb84..c3e7022d 100644 --- a/tests/test_generic_websocket.py +++ b/tests/test_generic_websocket.py @@ -485,3 +485,44 @@ async def connect(self): assert msg["type"] == "websocket.close" assert msg["code"] == 4007 assert msg["reason"] == "test reason" + +@pytest.mark.django_db +@pytest.mark.asyncio +async def test_websocket_receive_with_none_text(): + """ + Tests that the receive method handles messages with None text data correctly. + """ + + class TestConsumer(WebsocketConsumer): + def receive(self, text_data=None, bytes_data=None): + if text_data: + self.send(text_data="Received text: " + text_data) + elif bytes_data: + self.send(text_data=f"Received bytes of length: {len(bytes_data)}") + + app = TestConsumer() + + # Open a connection + communicator = WebsocketCommunicator(app, "/testws/") + connected, _ = await communicator.connect() + assert connected + + # Simulate Hypercorn behavior (both 'text' and 'bytes' keys present, but 'text' is None) + await communicator.send_input({"type": "websocket.receive", "text": None, "bytes": b"test data"}) + response = await communicator.receive_output() + assert response["type"] == "websocket.send" + assert response["text"] == "Received bytes of length: 9" + + # Test with only 'bytes' key (simulating uvicorn/daphne behavior) + await communicator.send_input({"type": "websocket.receive", "bytes": b"more data"}) + response = await communicator.receive_output() + assert response["type"] == "websocket.send" + assert response["text"] == "Received bytes of length: 9" + + # Test with valid text data + await communicator.send_input({"type": "websocket.receive", "text": "Hello, world!"}) + response = await communicator.receive_output() + assert response["type"] == "websocket.send" + assert response["text"] == "Received text: Hello, world!" + + await communicator.disconnect() From 43a83c6c0bf6468d551a3ab22b3e07b2d6c07cdf Mon Sep 17 00:00:00 2001 From: cacosandon Date: Tue, 3 Sep 2024 13:48:15 -0400 Subject: [PATCH 3/4] refactor(channels/generic): short condition --- channels/generic/websocket.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/channels/generic/websocket.py b/channels/generic/websocket.py index 42e5c54e..b4d99119 100644 --- a/channels/generic/websocket.py +++ b/channels/generic/websocket.py @@ -60,7 +60,7 @@ def websocket_receive(self, message): Called when a WebSocket frame is received. Decodes it and passes it to receive(). """ - if "text" in message and message["text"] is not None: + if message.get("text") is not None: self.receive(text_data=message["text"]) else: self.receive(bytes_data=message["bytes"]) @@ -200,7 +200,7 @@ async def websocket_receive(self, message): Called when a WebSocket frame is received. Decodes it and passes it to receive(). """ - if "text" in message and message["text"] is not None: + if message.get("text") is not None: await self.receive(text_data=message["text"]) else: await self.receive(bytes_data=message["bytes"]) From f9fceba8392478c0d5e90ec916cf108d04b274cc Mon Sep 17 00:00:00 2001 From: cacosandon Date: Wed, 4 Sep 2024 08:31:59 -0400 Subject: [PATCH 4/4] lint: fix flake8 errors --- tests/test_generic_websocket.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/tests/test_generic_websocket.py b/tests/test_generic_websocket.py index c3e7022d..0ade1a02 100644 --- a/tests/test_generic_websocket.py +++ b/tests/test_generic_websocket.py @@ -486,6 +486,7 @@ async def connect(self): assert msg["code"] == 4007 assert msg["reason"] == "test reason" + @pytest.mark.django_db @pytest.mark.asyncio async def test_websocket_receive_with_none_text(): @@ -507,8 +508,15 @@ def receive(self, text_data=None, bytes_data=None): connected, _ = await communicator.connect() assert connected - # Simulate Hypercorn behavior (both 'text' and 'bytes' keys present, but 'text' is None) - await communicator.send_input({"type": "websocket.receive", "text": None, "bytes": b"test data"}) + # Simulate Hypercorn behavior + # (both 'text' and 'bytes' keys present, but 'text' is None) + await communicator.send_input( + { + "type": "websocket.receive", + "text": None, + "bytes": b"test data", + } + ) response = await communicator.receive_output() assert response["type"] == "websocket.send" assert response["text"] == "Received bytes of length: 9" @@ -520,7 +528,9 @@ def receive(self, text_data=None, bytes_data=None): assert response["text"] == "Received bytes of length: 9" # Test with valid text data - await communicator.send_input({"type": "websocket.receive", "text": "Hello, world!"}) + await communicator.send_input( + {"type": "websocket.receive", "text": "Hello, world!"} + ) response = await communicator.receive_output() assert response["type"] == "websocket.send" assert response["text"] == "Received text: Hello, world!"