From 9204d323f854df12132d6fa758106ef12bcb7fc2 Mon Sep 17 00:00:00 2001 From: James Aylett Date: Tue, 24 Feb 2026 20:47:50 +0000 Subject: [PATCH 1/4] Don't fall back to Apollo when not in subprotocols If the server doesn't return Sec-WebSocket-Protocol, we can't default to graphql-ws (ie Apollo) if the transport was configured without it as a subprotocol. In that situation, default to graphql-transport-ws (GRAPHQLWS). --- gql/transport/websockets_protocol.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/gql/transport/websockets_protocol.py b/gql/transport/websockets_protocol.py index 3b66a0cb..df47c6cc 100644 --- a/gql/transport/websockets_protocol.py +++ b/gql/transport/websockets_protocol.py @@ -479,12 +479,15 @@ async def _after_connect(self): # Find the backend subprotocol returned in the response headers try: self.subprotocol = self.response_headers["Sec-WebSocket-Protocol"] + log.debug(f"backend subprotocol returned: {self.subprotocol!r}") except KeyError: - # If the server does not send the subprotocol header, using - # the apollo subprotocol by default - self.subprotocol = self.APOLLO_SUBPROTOCOL - - log.debug(f"backend subprotocol returned: {self.subprotocol!r}") + # If the server does not send the subprotocol header, use + # the apollo subprotocol by default unless we didn't ask for it + if self.APOLLO_SUBPROTOCOL in self.adapter.subprotocols: + self.subprotocol = self.APOLLO_SUBPROTOCOL + else: + self.subprotocol = self.GRAPHQLWS_SUBPROTOCOL + log.debug(f"backend returned no subprotocol, using: {self.subprotocol!r}") async def _after_initialize(self): From a1320002781e8e69a23ac6c41202718c5d33a097 Mon Sep 17 00:00:00 2001 From: James Aylett Date: Tue, 24 Feb 2026 21:10:34 +0000 Subject: [PATCH 2/4] fixup! Don't fall back to Apollo when not in subprotocols --- gql/transport/websockets_protocol.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gql/transport/websockets_protocol.py b/gql/transport/websockets_protocol.py index df47c6cc..c7759e3f 100644 --- a/gql/transport/websockets_protocol.py +++ b/gql/transport/websockets_protocol.py @@ -483,7 +483,7 @@ async def _after_connect(self): except KeyError: # If the server does not send the subprotocol header, use # the apollo subprotocol by default unless we didn't ask for it - if self.APOLLO_SUBPROTOCOL in self.adapter.subprotocols: + if self.adapter.subprotocols is None or self.APOLLO_SUBPROTOCOL in self.adapter.subprotocols: self.subprotocol = self.APOLLO_SUBPROTOCOL else: self.subprotocol = self.GRAPHQLWS_SUBPROTOCOL From ed18026d604c4dd527aff116a7be96cb12d14cc1 Mon Sep 17 00:00:00 2001 From: James Aylett Date: Tue, 24 Feb 2026 21:49:31 +0000 Subject: [PATCH 3/4] fixup! fixup! Don't fall back to Apollo when not in subprotocols --- gql/transport/websockets_protocol.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gql/transport/websockets_protocol.py b/gql/transport/websockets_protocol.py index c7759e3f..1ccf744e 100644 --- a/gql/transport/websockets_protocol.py +++ b/gql/transport/websockets_protocol.py @@ -483,7 +483,10 @@ async def _after_connect(self): except KeyError: # If the server does not send the subprotocol header, use # the apollo subprotocol by default unless we didn't ask for it - if self.adapter.subprotocols is None or self.APOLLO_SUBPROTOCOL in self.adapter.subprotocols: + if ( + self.adapter.subprotocols is None + or self.APOLLO_SUBPROTOCOL in self.adapter.subprotocols + ): self.subprotocol = self.APOLLO_SUBPROTOCOL else: self.subprotocol = self.GRAPHQLWS_SUBPROTOCOL From 35938a64307d7c74f7c0aa83a0096d00ac721532 Mon Sep 17 00:00:00 2001 From: Leszek Hanusz Date: Sat, 28 Feb 2026 14:55:24 +0100 Subject: [PATCH 4/4] Adding test --- tests/test_graphqlws_subscription.py | 38 ++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tests/test_graphqlws_subscription.py b/tests/test_graphqlws_subscription.py index 416726aa..a65e4895 100644 --- a/tests/test_graphqlws_subscription.py +++ b/tests/test_graphqlws_subscription.py @@ -899,3 +899,41 @@ async def test_graphqlws_subscription_reconnecting_session( break assert transport._connected is False + + +@pytest.mark.asyncio +@pytest.mark.parametrize("server", [server_countdown], indirect=True) +@pytest.mark.parametrize("subscription_str", [countdown_subscription_str]) +async def test_graphqlws_subscription_no_server_protocol(server, subscription_str): + """The goal of this test is to verify that if the client requests only the + graphqlws subprotocol AND the server is not returning its subprotocol + in its header, then the client will assume that the protocol used is + the graphqlws subprotocol (See PR #586). + """ + + from gql.transport.websockets import WebsocketsTransport + + url = f"ws://{server.hostname}:{server.port}/graphql" + print(f"url = {url}") + + transport = WebsocketsTransport( + url=url, + subprotocols=[WebsocketsTransport.GRAPHQLWS_SUBPROTOCOL], + keep_alive_timeout=3, + ) + + client = Client(transport=transport) + + count = 10 + subscription = gql(subscription_str.format(count=count)) + + async with client as session: + async for result in session.subscribe(subscription): + + number = result["number"] + print(f"Number received: {number}") + + assert number == count + count -= 1 + + assert count == -1