python-websockets / websockets

Library for building WebSocket servers and clients in Python
https://websockets.readthedocs.io/
BSD 3-Clause "New" or "Revised" License
5.06k stars 505 forks source link

Client state is incorrect #1463

Closed tamir-laminar closed 1 month ago

tamir-laminar commented 1 month ago

There is a problem with the client state when sending messaged over a closed socket.

look at the example below:

import asyncio
import websockets

PORT = 22334

async def handle_connection(ws):
    print(f"Got connection request on path: {ws.path}")
    # this code is in comment on propose to see the behaviour of `send` when connection is closed
    # async for message in ws:
    #     print(f"Received message: {message}")
    return

async def main():
    print(f"Starting server on port {PORT}")
    async with websockets.serve(ws_handler=handle_connection, host="localhost", port=PORT):
        print(f"Starting client")
        async with websockets.connect(f"ws://localhost:{PORT}/connect") as client:
            print(f"Sending messages")
            await client.send("Hello, world!")  # expected to fail
            await client.send("Hello, world!")  # expected to fail
            await client.send("Hello, world!")  # expected to fail

            print("Client state is:", client.state.name)  # this should be `CLOSING`, instead we are getting `OPEN`

            try:
                await client.recv()  # this fails correctly because the connection is closed
            except websockets.exceptions.ConnectionClosedOK:
                print("Connection is closed")

if __name__ == "__main__":
    asyncio.run(main())

output:

Starting server on port 22334
Starting client
Got connection request on path: /connect
Sending messages
Client state is: OPEN
Connection is closed

In this example the server gets a connection request from the client and just drop the connection. The client can still sends messages without getting ConnectionClosedOK exception.

Notes:

aaugustin commented 1 month ago

This is essentially https://websockets.readthedocs.io/en/stable/faq/asyncio.html#why-does-my-program-never-receive-any-messages

This code doesn't actually yield control to the event loop until await client.recv(). Only then does the event loop get a chance to run and to notice that the connection has been close.

If you add await asyncio.sleep(0.1) right after connect(), you should get the behavior that you expect.