Client state is incorrect
tamir-laminar opened this issue · comments
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:
- This behaviour changed in version
11.0
, in version10.4
we would get error in that case. - When I'm debugging the code I can see that we are changing the state to
CLOSING
, but other routine changes the state back toOPEN
although it is still close.
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.