Skip to content

Commit 52d6c4c

Browse files
fix: validate handshake version in MessageEncoder constructor
MessageEncoder now rejects unsupported protocol versions at construction time rather than silently producing invalid handshake bytes that cause the server to drop the connection with no useful error. Closes #102 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 48f362e commit 52d6c4c

2 files changed

Lines changed: 21 additions & 1 deletion

File tree

src/dqlitewire/codec.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,11 @@ def __init__(self, version: int = PROTOCOL_VERSION) -> None:
119119
PROTOCOL_VERSION (1). Use PROTOCOL_VERSION_LEGACY
120120
(0x86104dd760433fe5) for pre-1.0 dqlite servers.
121121
"""
122+
if version not in _SUPPORTED_VERSIONS:
123+
raise ProtocolError(
124+
f"Unsupported protocol version: {version:#x}. "
125+
f"Supported: {', '.join(f'{v:#x}' for v in sorted(_SUPPORTED_VERSIONS))}"
126+
)
122127
self._version = version
123128

124129
def encode(self, message: Message) -> bytes:

tests/test_codec.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
encode_message,
1010
)
1111
from dqlitewire.constants import PROTOCOL_VERSION, PROTOCOL_VERSION_LEGACY
12-
from dqlitewire.exceptions import DecodeError
12+
from dqlitewire.exceptions import DecodeError, ProtocolError
1313
from dqlitewire.messages import (
1414
ClientRequest,
1515
DbResponse,
@@ -50,6 +50,21 @@ def test_encoder_has_no_buffer_attribute(self) -> None:
5050
encoder = MessageEncoder()
5151
assert not hasattr(encoder, "_buffer")
5252

53+
def test_encoder_rejects_invalid_version(self) -> None:
54+
"""102: invalid handshake version should raise at construction time."""
55+
with pytest.raises(ProtocolError, match="Unsupported protocol version"):
56+
MessageEncoder(version=0xDEADBEEF)
57+
58+
def test_encoder_accepts_legacy_version(self) -> None:
59+
"""102: PROTOCOL_VERSION_LEGACY must be accepted."""
60+
encoder = MessageEncoder(version=PROTOCOL_VERSION_LEGACY)
61+
assert encoder.encode_handshake() is not None
62+
63+
def test_encoder_accepts_default_version(self) -> None:
64+
"""102: default PROTOCOL_VERSION must be accepted."""
65+
encoder = MessageEncoder(version=PROTOCOL_VERSION)
66+
assert encoder.encode_handshake() is not None
67+
5368
def test_encode_message(self) -> None:
5469
from dqlitewire.constants import RequestType
5570
from dqlitewire.messages.base import Header

0 commit comments

Comments
 (0)