Skip to content

Commit 43159ae

Browse files
authored
Check server version on connection open (#74)
* Check server version when opening the connection * Add internal function to check if server is >=4.2.0 This will be useful when we implement SQL filter support.
1 parent bc9a942 commit 43159ae

File tree

6 files changed

+692
-159
lines changed

6 files changed

+692
-159
lines changed

poetry.lock

Lines changed: 164 additions & 158 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ readme = "README.md"
1010
python = "^3.9"
1111
python-qpid-proton = "^0.39.0"
1212
typing-extensions = "^4.13.0"
13+
packaging = "^23.0"
1314

1415

1516
[tool.poetry.group.dev.dependencies]

rabbitmq_amqp_python_client/connection.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
)
1212

1313
import typing_extensions
14+
from packaging import version
1415

1516
from .address_helper import validate_address
1617
from .consumer import Consumer
@@ -166,6 +167,82 @@ def _create_connection(self) -> None:
166167
password=self._password,
167168
)
168169

170+
self._validate_server_properties()
171+
172+
def _validate_server_properties(self) -> None:
173+
"""
174+
Validate the server properties returned in the connection handshake.
175+
176+
Checks that the server is RabbitMQ and the version is >= 4.0.0.
177+
178+
Raises:
179+
ValidationCodeException: If server is not RabbitMQ or version < 4.0.0
180+
"""
181+
if self._conn is None or self._conn.conn is None:
182+
raise ValidationCodeException("Connection not established")
183+
184+
remote_props = self._conn.conn.remote_properties
185+
if remote_props is None:
186+
raise ValidationCodeException("No remote properties received from server")
187+
188+
# Check if server is RabbitMQ
189+
product = remote_props.get("product")
190+
if product != "RabbitMQ":
191+
raise ValidationCodeException(
192+
f"Connection to non-RabbitMQ server detected. "
193+
f"Expected 'RabbitMQ', got '{product}'"
194+
)
195+
196+
# Check server version is >= 4.0.0
197+
server_version = remote_props.get("version")
198+
if server_version is None:
199+
raise ValidationCodeException("Server version not provided")
200+
201+
try:
202+
if version.parse(str(server_version)) < version.parse("4.0.0"):
203+
raise ValidationCodeException(
204+
f"The AMQP client library requires RabbitMQ 4.0.0 or higher. "
205+
f"Server version: {server_version}"
206+
)
207+
except Exception as e:
208+
raise ValidationCodeException(
209+
f"Failed to parse server version '{server_version}': {e}"
210+
)
211+
212+
logger.debug(f"Connected to RabbitMQ server version {server_version}")
213+
214+
def _is_server_version_gte_4_2_0(self) -> bool:
215+
"""
216+
Check if the server version is greater than or equal to 4.2.0.
217+
218+
This is an internal method that can be used to conditionally enable
219+
features that require RabbitMQ 4.2.0 or higher.
220+
221+
Returns:
222+
bool: True if server version >= 4.2.0, False otherwise
223+
224+
Raises:
225+
ValidationCodeException: If connection is not established or
226+
remote properties are not available
227+
"""
228+
if self._conn is None or self._conn.conn is None:
229+
raise ValidationCodeException("Connection not established")
230+
231+
remote_props = self._conn.conn.remote_properties
232+
if remote_props is None:
233+
raise ValidationCodeException("No remote properties received from server")
234+
235+
server_version = remote_props.get("version")
236+
if server_version is None:
237+
raise ValidationCodeException("Server version not provided")
238+
239+
try:
240+
return version.parse(str(server_version)) >= version.parse("4.2.0")
241+
except Exception as e:
242+
raise ValidationCodeException(
243+
f"Failed to parse server version '{server_version}': {e}"
244+
)
245+
169246
def dial(self) -> None:
170247
"""
171248
Establish a connection to the AMQP server.

rabbitmq_amqp_python_client/qpid/proton/_endpoints.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,7 @@ def remote_desired_capabilities(self):
482482
return c and SymbolList(c)
483483

484484
@property
485-
def remote_properties(self):
485+
def remote_properties(self) -> Optional[Data]:
486486
"""
487487
The properties specified by the remote peer for this connection.
488488

rabbitmq_amqp_python_client/qpid/proton/_utils.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,11 @@ def __init__(
465465
lambda: not (self.conn.state & Endpoint.REMOTE_UNINIT),
466466
msg="Opening connection",
467467
)
468+
self.wait(
469+
lambda: (self.conn.state & Endpoint.REMOTE_ACTIVE),
470+
timeout=10,
471+
msg="Connection opened",
472+
)
468473

469474
except ConnectionException:
470475
if self.conn is not None:

0 commit comments

Comments
 (0)