Validate handshake completion on initial connect#975
Conversation
The initial-connect path only checked for -ERR after CONNECT+PING; a server that closed the socket or answered with an unexpected frame fell through silently and produced a CONNECTED client on a dead connection. The reconnect path already validated all three outcomes. Move the shared CONNECT+PING+PONG sequence into complete_handshake next to establish_connection so both paths get the same checks, and both now special-case authorization failures in the error message.
|
Claude finished @caspervonb's task in 3m 35s —— View job PR Review
The extraction is clean and the core goal is achieved: initial connect now validates
Reconnect now gets auth-aware error messages — The old reconnect path raised
Tests — The two new tests cover exactly the previously-missing scenarios. |
| msg = f"Failed to verify connection: {e}" | ||
| raise ConnectionError(msg) from e | ||
|
|
||
| if response is None: |
There was a problem hiding this comment.
Both except handlers unconditionally re-raise, so response is always assigned when execution reaches here — this is correct at runtime. However, a static type checker that doesn't track that both handlers always raise (e.g. mypy without control-flow narrowing) may flag response as potentially unbound here.
Initialising before the loop makes the intent explicit and silences that class of warning:
| if response is None: | |
| response: Message | None = None | |
| try: | |
| while True: | |
| response = await asyncio.wait_for(parse(connection), timeout=timeout) | |
| if not isinstance(response, Ok): | |
| break | |
| except TimeoutError: | |
| await connection.close() | |
| msg = "Server did not respond to PING" | |
| raise ConnectionError(msg) | |
| except Exception as e: | |
| await connection.close() | |
| msg = f"Failed to verify connection: {e}" | |
| raise ConnectionError(msg) from e | |
| if response is None: |
Initial connect only checked for
-ERRafter sending CONNECT+PING; a server that closed the socket mid-handshake or answered with an unexpected frame fell through silently andconnect()returned a CONNECTED client wrapping a dead connection. The reconnect path already validated all three outcomes — this closes the asymmetry flagged in review on #949 by extracting the shared sequence intocomplete_handshakealongsideestablish_connection(#948), so the two paths cannot drift again.Stacked on #949; will retarget to main once that merges.