Danny Tuppeny
Danny Tuppeny

Reputation: 42343

Why does this simple websocket code throw when the client disconnects?

I'm writing some simple web socket code based on these docs. The server is expected to listen for web sockets and just respond to ping messages with pong.

I'm running on Python3 and the server code looks like this:

import asyncio
import websockets
from jsonrpcserver.aio import methods
from jsonrpcserver.response import NotificationResponse

@methods.add
async def ping():
    return 'pong'

async def accept_connection(websocket, path):
    async for request in websocket:
        response = await methods.dispatch(request)
        if not response.is_notification:
            await websocket.send(str(response))

start_server = websockets.serve(accept_connection, 'localhost', 5000)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

The client is in Dart and just calls ping twice then closes the connection. The output from the server gets this error when the client disconnects:

Dannys-MacBook:pythonws danny$ python3 server.py 
--> {"jsonrpc":"2.0","method":"ping","id":0}
<-- {"jsonrpc": "2.0", "result": "pong", "id": 0}
--> {"jsonrpc":"2.0","method":"ping","id":1}
<-- {"jsonrpc": "2.0", "result": "pong", "id": 1}
Error in connection handler
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/websockets/server.py", line 152, in handler
    yield from self.ws_handler(self, path)
  File "server.py", line 11, in accept_connection
    async for request in websocket:
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/websockets/py36/protocol.py", line 15, in __aiter__
    yield await self.recv()
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/websockets/protocol.py", line 350, in recv
    yield from self.ensure_open()
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/websockets/protocol.py", line 512, in ensure_open
    self.close_code, self.close_reason) from self.transfer_data_exc
websockets.exceptions.ConnectionClosed: WebSocket connection is closed: code = 1005 (no status code [internal]), no reason

According to the docs linked above:

Iteration terminates when the client disconnects.

This leads me to believe it should just exit the async for loop and not throw?

Upvotes: 4

Views: 4613

Answers (1)

Danny Tuppeny
Danny Tuppeny

Reputation: 42343

https://www.pydoc.io/pypi/websockets-6.0/autoapi/protocol/index.html

The iterator yields incoming messages. It exits normally when the connection is closed with the status code 1000 (OK) or 1001 (going away). It raises a ConnectionClosed exception when the connection is closed with any other status code.

Closing with a 1000 status from the client prevents the exception.

Upvotes: 2

Related Questions