Loading kilter/protocol/core.py +5 −16 Original line number Diff line number Diff line Loading @@ -25,7 +25,7 @@ from .buffer import FixedSizeBuffer from .exceptions import InvalidMessage from .exceptions import NeedsMore from .exceptions import UnexpectedMessage from .exceptions import UnimplementedWarning from .exceptions import UnknownMessage from .messages import * EventMessage: TypeAlias = Union[ Loading Loading @@ -92,12 +92,6 @@ All messages that can be sent from a filter to an MTA """ class Unimplemented(messages.BytesMessage, ident=b"\x00"): """ A message that has not been implemented by this package yet """ MTA_EVENT_RESPONSES = { messages.Negotiate.ident: { messages.Negotiate.ident, Loading Loading @@ -181,7 +175,6 @@ MTA_EVENT_RESPONSES = { }, messages.Abort.ident: None, messages.Close.ident: None, Unimplemented.ident: {messages.Continue.ident}, } UPDATE_RESPONSES = { Loading Loading @@ -213,7 +206,7 @@ class FilterProtocol: def read_from( self, buf: FixedSizeBuffer, ) -> Iterable[MTAMessage|Unimplemented]: ) -> Iterable[MTAMessage]: """ Return an iterator yielding each complete message from a buffer Loading @@ -227,13 +220,9 @@ class FilterProtocol: message, size = messages.Message.unpack(buf) except NeedsMore: return except NotImplementedError as exc: if len(exc.args) != 1: raise # pragma: no-cover data = exc.args[0] warn(UnimplementedWarning(f"unimplemented message: {data!r}")) yield Unimplemented(data) del buf[:len(data)] except UnknownMessage as exc: del buf[:len(exc.contents)] raise else: yield self._check_recv(message) message.release() Loading kilter/protocol/exceptions.py +29 −8 Original line number Diff line number Diff line # Copyright 2022 Dominik Sekotill <dom.sekotill@kodo.org.uk> # Copyright 2022-2023 Dominik Sekotill <dom.sekotill@kodo.org.uk> # # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this Loading @@ -8,9 +8,11 @@ Exceptions raised by the package """ from typing import TYPE_CHECKING __all__ = [ "InsufficientSpace", "NeedsMore", "UnexpectedMessage", "InvalidMessage", "UnimplementedWarning", "UnknownMessage", ] Loading @@ -26,6 +28,31 @@ class NeedsMore(Exception): """ class UnknownMessage(ValueError): """ Raised by the message unpacker to indicate that it lacks a class for the message The first (and only) argument to the exception is the message that could not be unpacked. """ if TYPE_CHECKING: def __init__(self, message: bytes): ... def __str__(self) -> str: # pragma: no-cover contents = self.contents message = f"{contents[:50]!r} (trimmed)" if len(contents) > 50 else repr(contents) return f"unknown message: {message}" @property def contents(self) -> bytes: """ The byte string that could not be unpacked """ assert isinstance(self.args[0], bytes) return self.args[0] class UnexpectedMessage(TypeError): """ Raised by a protocol to indicate a message that is not expected in the current state Loading @@ -39,9 +66,3 @@ class InvalidMessage(UnexpectedMessage): """ Raised by a protocol to indicate a message that is unknown to the state machine """ class UnimplementedWarning(Warning): """ Warning of an unknown message type """ kilter/protocol/messages.py +2 −1 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ from typing_extensions import Self from .exceptions import InsufficientSpace from .exceptions import NeedsMore from .exceptions import UnknownMessage if TYPE_CHECKING: from .buffer import FixedSizeBuffer Loading Loading @@ -262,7 +263,7 @@ class Message(metaclass=ABCMeta): try: msg_class = cls._message_classes[ident] except KeyError: raise NotImplementedError(buf[:end].tobytes()) raise UnknownMessage(buf[:end].tobytes()) else: with buf[hdr_size:end] as data: return msg_class.from_buffer(data), end Loading tests/example_filter.py +0 −6 Original line number Diff line number Diff line Loading @@ -12,7 +12,6 @@ from kilter.protocol import messages from kilter.protocol.buffer import SimpleBuffer from kilter.protocol.core import FilterMessage from kilter.protocol.core import FilterProtocol from kilter.protocol.core import Unimplemented async def server() -> None: Loading Loading @@ -63,11 +62,6 @@ async def process_client(client: trio.SocketStream, nursery: trio.Nursery) -> No messages.Unknown() | messages.Header() | messages.EndOfHeaders() | \ messages.Body() | messages.EndOfMessage(): await send_channel.send(messages.Continue()) case Unimplemented(): logging.warning(f"don't know how to respond to {message!r}, send Continue") await send_channel.send(messages.Continue()) case _: logging.warning(f"don't know how to respond to {message!r}") async def client_sender( Loading tests/test_core_filter_protocol.py +7 −7 Original line number Diff line number Diff line Loading @@ -9,11 +9,10 @@ from ipaddress import IPv4Address from kilter.protocol.buffer import SimpleBuffer from kilter.protocol.core import FilterProtocol from kilter.protocol.core import Unimplemented from kilter.protocol.exceptions import InvalidMessage from kilter.protocol.exceptions import NeedsMore from kilter.protocol.exceptions import UnexpectedMessage from kilter.protocol.exceptions import UnimplementedWarning from kilter.protocol.exceptions import UnknownMessage from kilter.protocol.messages import NoDataMessage from kilter.protocol.messages import * Loading Loading @@ -60,18 +59,19 @@ class FilterProtocolTests(unittest.TestCase): def test_read_unimplemented(self) -> None: """ Check that unknown messages cause a warning to be issued, and are yielded raw Check that unknown messages cause UnknownMessage to be raised """ buf = SimpleBuffer(20) buf[:] = b"\x00\x00\x00\x01S" buf[:] = msg = b"\x00\x00\x00\x01S" with self.assertWarns(UnimplementedWarning): for msg in FilterProtocol().read_from(buf): self.assertIsInstance(msg, Unimplemented) with self.assertRaises(UnknownMessage) as exc_cm: for _ in FilterProtocol().read_from(buf): break else: self.fail("No messages read") assert exc_cm.exception.contents == msg def test_read_unexpected(self) -> None: """ Check that reading an available message before a response raises UnexpectedMessage Loading Loading
kilter/protocol/core.py +5 −16 Original line number Diff line number Diff line Loading @@ -25,7 +25,7 @@ from .buffer import FixedSizeBuffer from .exceptions import InvalidMessage from .exceptions import NeedsMore from .exceptions import UnexpectedMessage from .exceptions import UnimplementedWarning from .exceptions import UnknownMessage from .messages import * EventMessage: TypeAlias = Union[ Loading Loading @@ -92,12 +92,6 @@ All messages that can be sent from a filter to an MTA """ class Unimplemented(messages.BytesMessage, ident=b"\x00"): """ A message that has not been implemented by this package yet """ MTA_EVENT_RESPONSES = { messages.Negotiate.ident: { messages.Negotiate.ident, Loading Loading @@ -181,7 +175,6 @@ MTA_EVENT_RESPONSES = { }, messages.Abort.ident: None, messages.Close.ident: None, Unimplemented.ident: {messages.Continue.ident}, } UPDATE_RESPONSES = { Loading Loading @@ -213,7 +206,7 @@ class FilterProtocol: def read_from( self, buf: FixedSizeBuffer, ) -> Iterable[MTAMessage|Unimplemented]: ) -> Iterable[MTAMessage]: """ Return an iterator yielding each complete message from a buffer Loading @@ -227,13 +220,9 @@ class FilterProtocol: message, size = messages.Message.unpack(buf) except NeedsMore: return except NotImplementedError as exc: if len(exc.args) != 1: raise # pragma: no-cover data = exc.args[0] warn(UnimplementedWarning(f"unimplemented message: {data!r}")) yield Unimplemented(data) del buf[:len(data)] except UnknownMessage as exc: del buf[:len(exc.contents)] raise else: yield self._check_recv(message) message.release() Loading
kilter/protocol/exceptions.py +29 −8 Original line number Diff line number Diff line # Copyright 2022 Dominik Sekotill <dom.sekotill@kodo.org.uk> # Copyright 2022-2023 Dominik Sekotill <dom.sekotill@kodo.org.uk> # # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this Loading @@ -8,9 +8,11 @@ Exceptions raised by the package """ from typing import TYPE_CHECKING __all__ = [ "InsufficientSpace", "NeedsMore", "UnexpectedMessage", "InvalidMessage", "UnimplementedWarning", "UnknownMessage", ] Loading @@ -26,6 +28,31 @@ class NeedsMore(Exception): """ class UnknownMessage(ValueError): """ Raised by the message unpacker to indicate that it lacks a class for the message The first (and only) argument to the exception is the message that could not be unpacked. """ if TYPE_CHECKING: def __init__(self, message: bytes): ... def __str__(self) -> str: # pragma: no-cover contents = self.contents message = f"{contents[:50]!r} (trimmed)" if len(contents) > 50 else repr(contents) return f"unknown message: {message}" @property def contents(self) -> bytes: """ The byte string that could not be unpacked """ assert isinstance(self.args[0], bytes) return self.args[0] class UnexpectedMessage(TypeError): """ Raised by a protocol to indicate a message that is not expected in the current state Loading @@ -39,9 +66,3 @@ class InvalidMessage(UnexpectedMessage): """ Raised by a protocol to indicate a message that is unknown to the state machine """ class UnimplementedWarning(Warning): """ Warning of an unknown message type """
kilter/protocol/messages.py +2 −1 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ from typing_extensions import Self from .exceptions import InsufficientSpace from .exceptions import NeedsMore from .exceptions import UnknownMessage if TYPE_CHECKING: from .buffer import FixedSizeBuffer Loading Loading @@ -262,7 +263,7 @@ class Message(metaclass=ABCMeta): try: msg_class = cls._message_classes[ident] except KeyError: raise NotImplementedError(buf[:end].tobytes()) raise UnknownMessage(buf[:end].tobytes()) else: with buf[hdr_size:end] as data: return msg_class.from_buffer(data), end Loading
tests/example_filter.py +0 −6 Original line number Diff line number Diff line Loading @@ -12,7 +12,6 @@ from kilter.protocol import messages from kilter.protocol.buffer import SimpleBuffer from kilter.protocol.core import FilterMessage from kilter.protocol.core import FilterProtocol from kilter.protocol.core import Unimplemented async def server() -> None: Loading Loading @@ -63,11 +62,6 @@ async def process_client(client: trio.SocketStream, nursery: trio.Nursery) -> No messages.Unknown() | messages.Header() | messages.EndOfHeaders() | \ messages.Body() | messages.EndOfMessage(): await send_channel.send(messages.Continue()) case Unimplemented(): logging.warning(f"don't know how to respond to {message!r}, send Continue") await send_channel.send(messages.Continue()) case _: logging.warning(f"don't know how to respond to {message!r}") async def client_sender( Loading
tests/test_core_filter_protocol.py +7 −7 Original line number Diff line number Diff line Loading @@ -9,11 +9,10 @@ from ipaddress import IPv4Address from kilter.protocol.buffer import SimpleBuffer from kilter.protocol.core import FilterProtocol from kilter.protocol.core import Unimplemented from kilter.protocol.exceptions import InvalidMessage from kilter.protocol.exceptions import NeedsMore from kilter.protocol.exceptions import UnexpectedMessage from kilter.protocol.exceptions import UnimplementedWarning from kilter.protocol.exceptions import UnknownMessage from kilter.protocol.messages import NoDataMessage from kilter.protocol.messages import * Loading Loading @@ -60,18 +59,19 @@ class FilterProtocolTests(unittest.TestCase): def test_read_unimplemented(self) -> None: """ Check that unknown messages cause a warning to be issued, and are yielded raw Check that unknown messages cause UnknownMessage to be raised """ buf = SimpleBuffer(20) buf[:] = b"\x00\x00\x00\x01S" buf[:] = msg = b"\x00\x00\x00\x01S" with self.assertWarns(UnimplementedWarning): for msg in FilterProtocol().read_from(buf): self.assertIsInstance(msg, Unimplemented) with self.assertRaises(UnknownMessage) as exc_cm: for _ in FilterProtocol().read_from(buf): break else: self.fail("No messages read") assert exc_cm.exception.contents == msg def test_read_unexpected(self) -> None: """ Check that reading an available message before a response raises UnexpectedMessage Loading