miguelgrinberg / simple-websocket

Simple WebSocket server and client for Python.
MIT License
79 stars 17 forks source link

Raise ConnectionClosed when a Ping/Pong-related timeout occurs #33

Closed lkedziora closed 10 months ago

lkedziora commented 11 months ago

Hi!

This PR fixes an issue where blocking ws.receive() calls are not informed of the connection closing when a Ping/Pong related timeout occurs.

Reproducing:

server.py

from flask import Flask, request
from simple_websocket import Server, ConnectionClosed

app = Flask(__name__)

@app.route('/command', websocket=True)
def command():
    ws = Server.accept(request.environ, ping_interval=25)
    try:
        while True:
            # blocking receive() call
            # pings used to detect disconnections
            data = ws.receive()
            # .. do stuff with data ..
    except ConnectionClosed:
        print("Connection closed, cleaning up..", flush=True)
        # .. other cleanup ..
    return ''

if __name__ == '__main__':
    app.run()

client.py

from simple_websocket import Client, ConnectionClosed

def main():
    ws = Client.connect('ws://localhost:5000/command')
    try:
        while True:
            data = input('> ')
            ws.send(data)
            data = ws.receive()
            print(f'< {data}')
    except (KeyboardInterrupt, EOFError, ConnectionClosed):
        ws.close()

if __name__ == '__main__':
    main()
  1. Run server.py, run client.py, and Ctrl-C the client. Observe that ConnectionClosed is properly thrown
  2. Run client.py again and Ctrl-Z to simulate a connection dropout. Wait until the Ping/Pong timeout occurs: sleep 50
  3. Restore the client. The connection was already dropped and the simple-websocket server thread was already closed, but blocking readers on the server are not informed of the condition, causing them to be stuck forever.