trevorblades/countries

400 Bad Request for websockets endpoint

leszekhanusz opened this issue · 5 comments

Hi,

I am the maintainer of the graphql-python/gql client and we make a great use of your countries website for our examples in our documentation. A big thanks for your website which provides a stable backend when we need to do some simple tests sometimes.

It has been brought to my attention recently that all the examples involving the websockets protocol are not working anymore (the http protocol continues to work fine).

Now we always receive a 400 Bad Request response when trying to connect to the websockets endpoint.

If I try to run the following code with gql version 3.1.0:

import asyncio                                                                                                          
import logging                                                                                                          
                                                                                                                        
from gql import Client, gql                                                                                             
from gql.transport.websockets import WebsocketsTransport                                                                
                                                                                                                        
logging.basicConfig(level=logging.DEBUG)                                                                                
                                                                                                                        
                                                                                                                        
async def main():                                                                                                       
                                                                                                                        
    transport = WebsocketsTransport(                                                                                    
        url="wss://countries.trevorblades.com/graphql",                                                                 
        subprotocols=[WebsocketsTransport.APOLLO_SUBPROTOCOL],                                                          
    )                                                                                                                   
                                                                                                                        
    async with Client(                                                                                                  
        transport=transport, fetch_schema_from_transport=True,                                                          
    ) as session:                                                                                                       
                                                                                                                        
        # Execute single query                                                                                          
        query = gql(                                                                                                    
            """                                                                                                         
            query getContinents {                                                                                       
              continents {                                                                                              
                code                                                                                                    
                name                                                                                                    
              }                                                                                                         
            }                                                                                                           
        """                                                                                                             
        )                                                                                                               
        result = await session.execute(query)                                                                           
        print(result)                                                                                                   
                                                                                                                        
                                                                                                                        
asyncio.run(main())

Then I receive the following result:

DEBUG:asyncio:Using selector: EpollSelector
DEBUG:gql.transport.websockets:connect: starting
DEBUG:websockets.client:= connection is CONNECTING
DEBUG:websockets.client:> GET /graphql HTTP/1.1
DEBUG:websockets.client:> Host: countries.trevorblades.com
DEBUG:websockets.client:> Upgrade: websocket
DEBUG:websockets.client:> Connection: Upgrade
DEBUG:websockets.client:> Sec-WebSocket-Key: ZK9W8vXCkiwnBMzyG0UnSA==
DEBUG:websockets.client:> Sec-WebSocket-Version: 13
DEBUG:websockets.client:> Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
DEBUG:websockets.client:> Sec-WebSocket-Protocol: graphql-ws
DEBUG:websockets.client:> User-Agent: Python/3.8 websockets/10.2
DEBUG:websockets.client:< HTTP/1.1 400 Bad Request
DEBUG:websockets.client:< access-control-allow-origin: *
DEBUG:websockets.client:< content-type: text/html; charset=utf-8
DEBUG:websockets.client:< content-length: 18
DEBUG:websockets.client:< etag: W/"12-7JEJwpG8g89ii7CR/6hhfN27Q+k"
DEBUG:websockets.client:< date: Mon, 11 Apr 2022 19:33:55 GMT
DEBUG:websockets.client:< connection: keep-alive
DEBUG:websockets.client:< keep-alive: timeout=5
DEBUG:websockets.client:< server: Fly/523fc696 (2022-04-06)
DEBUG:websockets.client:< via: 1.1 fly.io
DEBUG:websockets.client:< fly-request-id: 01G0D1X9FAAFXFHY58RRYJ6WGX-fra
DEBUG:websockets.client:! failing connection with code 1006
DEBUG:websockets.client:x closing TCP connection
DEBUG:websockets.client:= connection is CLOSED
Traceback (most recent call last):
  File "docs/code_examples/websockets_async.py", line 36, in <module>
    asyncio.run(main())
  File "/home/leszek/miniconda3/envs/gql-dev/lib/python3.8/asyncio/runners.py", line 43, in run
    return loop.run_until_complete(main)
  File "/home/leszek/miniconda3/envs/gql-dev/lib/python3.8/asyncio/base_events.py", line 616, in run_until_complete
    return future.result()
  File "docs/code_examples/websockets_async.py", line 17, in main
    async with Client(
  File "/home/leszek/miniconda3/envs/gql-dev/lib/python3.8/site-packages/gql/client.py", line 580, in __aenter__
    await self.transport.connect()
  File "/home/leszek/miniconda3/envs/gql-dev/lib/python3.8/site-packages/gql/transport/websockets_base.py", line 491, in connect
    self.websocket = await asyncio.wait_for(
  File "/home/leszek/miniconda3/envs/gql-dev/lib/python3.8/asyncio/tasks.py", line 483, in wait_for
    return fut.result()
  File "/home/leszek/miniconda3/envs/gql-dev/lib/python3.8/asyncio/tasks.py", line 684, in _wrap_awaitable
    return (yield from awaitable.__await__())
  File "/home/leszek/miniconda3/envs/gql-dev/lib/python3.8/site-packages/websockets/legacy/client.py", line 650, in __await_impl_timeout__
    return await asyncio.wait_for(self.__await_impl__(), self.open_timeout)
  File "/home/leszek/miniconda3/envs/gql-dev/lib/python3.8/asyncio/tasks.py", line 483, in wait_for
    return fut.result()
  File "/home/leszek/miniconda3/envs/gql-dev/lib/python3.8/site-packages/websockets/legacy/client.py", line 658, in __await_impl__
    await protocol.handshake(
  File "/home/leszek/miniconda3/envs/gql-dev/lib/python3.8/site-packages/websockets/legacy/client.py", line 328, in handshake
    raise InvalidStatusCode(status_code, response_headers)
websockets.exceptions.InvalidStatusCode: server rejected WebSocket connection: HTTP 400

It has been a while that I tested this so this could have been the case for a long time already.

This API doesn't currently support GraphQL subscriptions, and there's no plan for any. What do you need to connect via websockets for?

I know you don't have any subscriptions. It was used to test the apollo websockets transport for the client. Queries and mutations were working on the websockets endpoint, check this test file which used to work perfectly fine.

But if you don't plan to bring back the websockets support back, no worries, I'll try to find another always-on backend for our websockets examples.

I just tested a local version of countries and the websockets endpoint is working, so it should be a configuration issue in the hosting environment.

$ echo 'query { continent(code:"AF") { name } }' | gql-cli ws://localhost:4000/graphql
{"continent": {"name": "Africa"}}

Hey @leszekhanusz, oh my goodness I'm replying so late, I'm sorry. I just wanted to follow up on this and let you know that due to some recent changes in the way this API is hosted (I'm using Cloudflare Workers now), it won't support WS connections.

Hope you found another API to use an example! Out of curiosity, what API did you go with?