From 65bd931889503be111ec2f22780d9b204473b84c Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Wed, 13 Nov 2024 23:00:22 +0100 Subject: [PATCH] Reintroduce InvalidMessage. This improves compatibility with the legacy implementation and clarifies error reporting. Fix #1548. --- docs/reference/exceptions.rst | 4 ++-- src/websockets/__init__.py | 4 +++- src/websockets/client.py | 6 +++++- src/websockets/exceptions.py | 11 +++++++++-- src/websockets/legacy/client.py | 3 ++- src/websockets/legacy/exceptions.py | 7 ------- src/websockets/legacy/server.py | 3 ++- src/websockets/server.py | 6 +++++- tests/legacy/test_exceptions.py | 4 ---- tests/test_exceptions.py | 4 ++++ 10 files changed, 32 insertions(+), 20 deletions(-) diff --git a/docs/reference/exceptions.rst b/docs/reference/exceptions.rst index 75934ef9..d6b7f0f5 100644 --- a/docs/reference/exceptions.rst +++ b/docs/reference/exceptions.rst @@ -30,6 +30,8 @@ also reported by :func:`~websockets.asyncio.server.serve` in logs. .. autoexception:: InvalidHandshake +.. autoexception:: InvalidMessage + .. autoexception:: SecurityError .. autoexception:: InvalidStatus @@ -74,8 +76,6 @@ Legacy exceptions These exceptions are only used by the legacy :mod:`asyncio` implementation. -.. autoexception:: InvalidMessage - .. autoexception:: InvalidStatusCode .. autoexception:: AbortHandshake diff --git a/src/websockets/__init__.py b/src/websockets/__init__.py index 0c7e9b4c..c278b21d 100644 --- a/src/websockets/__init__.py +++ b/src/websockets/__init__.py @@ -31,6 +31,7 @@ "InvalidHeader", "InvalidHeaderFormat", "InvalidHeaderValue", + "InvalidMessage", "InvalidOrigin", "InvalidParameterName", "InvalidParameterValue", @@ -71,6 +72,7 @@ InvalidHeader, InvalidHeaderFormat, InvalidHeaderValue, + InvalidMessage, InvalidOrigin, InvalidParameterName, InvalidParameterValue, @@ -122,6 +124,7 @@ "InvalidHeader": ".exceptions", "InvalidHeaderFormat": ".exceptions", "InvalidHeaderValue": ".exceptions", + "InvalidMessage": ".exceptions", "InvalidOrigin": ".exceptions", "InvalidParameterName": ".exceptions", "InvalidParameterValue": ".exceptions", @@ -159,7 +162,6 @@ "WebSocketClientProtocol": ".legacy.client", # .legacy.exceptions "AbortHandshake": ".legacy.exceptions", - "InvalidMessage": ".legacy.exceptions", "InvalidStatusCode": ".legacy.exceptions", "RedirectHandshake": ".legacy.exceptions", "WebSocketProtocolError": ".legacy.exceptions", diff --git a/src/websockets/client.py b/src/websockets/client.py index f6cbc9f6..5ced05c2 100644 --- a/src/websockets/client.py +++ b/src/websockets/client.py @@ -11,6 +11,7 @@ InvalidHandshake, InvalidHeader, InvalidHeaderValue, + InvalidMessage, InvalidStatus, InvalidUpgrade, NegotiationError, @@ -318,7 +319,10 @@ def parse(self) -> Generator[None]: self.reader.read_to_eof, ) except Exception as exc: - self.handshake_exc = exc + self.handshake_exc = InvalidMessage( + "did not receive a valid HTTP response" + ) + self.handshake_exc.__cause__ = exc self.send_eof() self.parser = self.discard() next(self.parser) # start coroutine diff --git a/src/websockets/exceptions.py b/src/websockets/exceptions.py index f3e75197..81fbb1ef 100644 --- a/src/websockets/exceptions.py +++ b/src/websockets/exceptions.py @@ -8,7 +8,7 @@ * :exc:`InvalidURI` * :exc:`InvalidHandshake` * :exc:`SecurityError` - * :exc:`InvalidMessage` (legacy) + * :exc:`InvalidMessage` * :exc:`InvalidStatus` * :exc:`InvalidStatusCode` (legacy) * :exc:`InvalidHeader` @@ -48,6 +48,7 @@ "InvalidHeader", "InvalidHeaderFormat", "InvalidHeaderValue", + "InvalidMessage", "InvalidOrigin", "InvalidUpgrade", "NegotiationError", @@ -185,6 +186,13 @@ class SecurityError(InvalidHandshake): """ +class InvalidMessage(InvalidHandshake): + """ + Raised when a handshake request or response is malformed. + + """ + + class InvalidStatus(InvalidHandshake): """ Raised when a handshake response rejects the WebSocket upgrade. @@ -410,7 +418,6 @@ class ConcurrencyError(WebSocketException, RuntimeError): deprecated_aliases={ # deprecated in 14.0 - 2024-11-09 "AbortHandshake": ".legacy.exceptions", - "InvalidMessage": ".legacy.exceptions", "InvalidStatusCode": ".legacy.exceptions", "RedirectHandshake": ".legacy.exceptions", "WebSocketProtocolError": ".legacy.exceptions", diff --git a/src/websockets/legacy/client.py b/src/websockets/legacy/client.py index a3856b47..29141f39 100644 --- a/src/websockets/legacy/client.py +++ b/src/websockets/legacy/client.py @@ -17,6 +17,7 @@ from ..exceptions import ( InvalidHeader, InvalidHeaderValue, + InvalidMessage, NegotiationError, SecurityError, ) @@ -34,7 +35,7 @@ from ..http11 import USER_AGENT from ..typing import ExtensionHeader, LoggerLike, Origin, Subprotocol from ..uri import WebSocketURI, parse_uri -from .exceptions import InvalidMessage, InvalidStatusCode, RedirectHandshake +from .exceptions import InvalidStatusCode, RedirectHandshake from .handshake import build_request, check_response from .http import read_response from .protocol import WebSocketCommonProtocol diff --git a/src/websockets/legacy/exceptions.py b/src/websockets/legacy/exceptions.py index e2279c82..784e7d13 100644 --- a/src/websockets/legacy/exceptions.py +++ b/src/websockets/legacy/exceptions.py @@ -8,13 +8,6 @@ from ..typing import StatusLike -class InvalidMessage(InvalidHandshake): - """ - Raised when a handshake request or response is malformed. - - """ - - class InvalidStatusCode(InvalidHandshake): """ Raised when a handshake response status code is invalid. diff --git a/src/websockets/legacy/server.py b/src/websockets/legacy/server.py index 9326b610..f9d57cb9 100644 --- a/src/websockets/legacy/server.py +++ b/src/websockets/legacy/server.py @@ -17,6 +17,7 @@ from ..exceptions import ( InvalidHandshake, InvalidHeader, + InvalidMessage, InvalidOrigin, InvalidUpgrade, NegotiationError, @@ -32,7 +33,7 @@ from ..http11 import SERVER from ..protocol import State from ..typing import ExtensionHeader, LoggerLike, Origin, StatusLike, Subprotocol -from .exceptions import AbortHandshake, InvalidMessage +from .exceptions import AbortHandshake from .handshake import build_response, check_request from .http import read_request from .protocol import WebSocketCommonProtocol, broadcast diff --git a/src/websockets/server.py b/src/websockets/server.py index 607cc306..1b663a13 100644 --- a/src/websockets/server.py +++ b/src/websockets/server.py @@ -13,6 +13,7 @@ InvalidHandshake, InvalidHeader, InvalidHeaderValue, + InvalidMessage, InvalidOrigin, InvalidUpgrade, NegotiationError, @@ -552,7 +553,10 @@ def parse(self) -> Generator[None]: self.reader.read_line, ) except Exception as exc: - self.handshake_exc = exc + self.handshake_exc = InvalidMessage( + "did not receive a valid HTTP request" + ) + self.handshake_exc.__cause__ = exc self.send_eof() self.parser = self.discard() next(self.parser) # start coroutine diff --git a/tests/legacy/test_exceptions.py b/tests/legacy/test_exceptions.py index e5d22a91..4e6ff952 100644 --- a/tests/legacy/test_exceptions.py +++ b/tests/legacy/test_exceptions.py @@ -7,10 +7,6 @@ class ExceptionsTests(unittest.TestCase): def test_str(self): for exception, exception_str in [ - ( - InvalidMessage("malformed HTTP message"), - "malformed HTTP message", - ), ( InvalidStatusCode(403, Headers()), "server rejected WebSocket connection: HTTP 403", diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py index fef41d13..e0518b0e 100644 --- a/tests/test_exceptions.py +++ b/tests/test_exceptions.py @@ -91,6 +91,10 @@ def test_str(self): SecurityError("redirect from WSS to WS"), "redirect from WSS to WS", ), + ( + InvalidMessage("malformed HTTP message"), + "malformed HTTP message", + ), ( InvalidStatus(Response(401, "Unauthorized", Headers())), "server rejected WebSocket connection: HTTP 401",