Repository navigation
Conversation
The server can reject a connection by closing it with code 4403 (reason "Forbidden") shortly after connection_init. Only the reason "Unauthenticated" stopped the monitor, so a 4403 was reconnected forever and ws_unauthenticated stayed False. Record the close code and treat 4403 as unauthenticated too, so the monitor stops and the caller can reauthenticate. test_unauthenticated_stops_monitor now has a subscription, without which the monitor stops for having nothing to resume and the test passes regardless.
4403 has only been seen right after connection_init, before the ack. On an acknowledged connection it may be transient, so reconnect rather than prompt for reauth. Read the code from aiohttp's close_code instead of recording it from the CLOSE message.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The web socket monitor only stops reconnecting when the server's close reason is
"Unauthenticated". It never reads the close code. When the server rejects a connection with code 4403 (reason"Forbidden"), which arrives about 0.5 s afterconnection_init, the monitor reconnects indefinitely and gets the same rejection every time.ws_unauthenticatedstays False, so the integration's reauth path (_subscribe_retry_delayincoordinator.py, which raisesConfigEntryAuthFailedonws_unauthenticated) never fires.Changes
ws_monitor.py: aFORBIDDEN = 4403constant and a_forbiddenflag, reset innew_connection. When the connection closes,_receivesets the flag if aiohttp'sclose_codeis 4403 and the connection had not been acknowledged yet.unauthenticatedis true for the reason"Unauthenticated"or when that flag is set, and the monitor stops wheneverunauthenticatedis true (it used to compare the reason string only).Connection TTL expired) is already handled quietly. 4401 is graphql-transport-ws's response to a message sent beforeconnection_ack, so logging in again would not fix it.tests/test_ws_monitor.py:FakeServer.close_code(default 4000, the previous hard-coded value) andFakeServer.ack_before_close, plus two tests:test_forbidden_stops_monitorcloses with 4403/"Forbidden"before the ack while the monitor has a subscription, then checks that there was exactly one connection and thatws_unauthenticatedis set.test_forbidden_after_ack_reconnectsacknowledges the connection and then closes with 4403. It checks that the monitor reconnects and thatws_unauthenticatedstays False.Testing
234 passed (232 at 67ba96f, plus 2 new). Ruff and mypy are clean. Against the unmodified
ws_monitor.py,test_forbidden_stops_monitorfails (it times out while the monitor keeps reconnecting). Without the before-ack check,test_forbidden_after_ack_reconnectsfails (it times out because the monitor stops).Notes
Forbiddenabout 0.5 s afterconnection_init, and our notes attribute it to a malformedu-sess. That interpretation is claim C6 in our fork'sdocs/development/WS_CONTENTION.md, and it was never tested live, because provoking 4403 on purpose was ruled out. If 4403 before the ack can also mean something a new login would not fix, this change turns a reconnect loop into a reauth prompt for that case. A 4403 after the ack is left as a reconnect for the same reason.test_unauthenticated_stops_monitorpassed vacuously. With no subscription, the monitor stops because it has nothing to resume, whatever the close reason. With the"Unauthenticated"check disabled, it still passed. This PR adds the missingadd_subscription(monitor)(one line), and with that line the test fails when the check is disabled.ws_unauthenticatedis checked, which is the same as an"Unauthenticated"close today.