miguelgrinberg / python-engineio

Python Engine.IO server and client
MIT License
232 stars 147 forks source link

KeyError when calling sio.disconnect() #179

Closed ysde closed 4 years ago

ysde commented 4 years ago

I got KeyError when I manually calling sio.disconnect to disconnect another sid.

KeyError: '88747820ac304e5d8e1d8a8d17d2ffea'

https://github.com/miguelgrinberg/python-engineio/blob/d6ae500a9c2a33bafdddc27a5ff30e663064a1bd/engineio/asyncio_server.py#L396

miguelgrinberg commented 4 years ago

Can you show me some code? How do I reproduce this crash?

ysde commented 4 years ago

Hi @miguelgrinberg

This is the server setting, using SocketIO with AsyncRedis and AsyncServer.

mgr = socketio.AsyncRedisManager(f'redis://{redis_conf.host}:{redis_conf.port}')
sio = socketio.AsyncServer(async_mode='asgi',
                           cors_allowed_origins=[],
                           logger=True,
                           engineio_logger=True,
                           client_manager=mgr)

First the client connect to the server and join a room, then sleep like 5 seconds, then emit a close_room event to call close_room function to close the room.

async def close_room(room, sio):
    for p in sio.manager.get_participants(room=room, namespace='/'):
        await sio.disconnect(p)

Would it be the case that because I call sio.disconnect, then in server.py it delete the sid in self.sockets https://github.com/miguelgrinberg/python-engineio/blob/d6ae500a9c2a33bafdddc27a5ff30e663064a1bd/engineio/server.py#L294

But the connection is not closed yet, and it sends a message to the server maybe ping pong message. And then in asyncio_server.py https://github.com/miguelgrinberg/python-engineio/blob/d6ae500a9c2a33bafdddc27a5ff30e663064a1bd/engineio/asyncio_server.py#L396

it try to delete the sid in the self.sockets agagin, but the key's already deleted, then KeyError occurs.

This is the trace log:

[2020-06-17 10:31:43,284] [9956] [4687670720] [uvicorn.error] [ERROR] Exception in ASGI application Traceback (most recent call last): File "/Users/arthur/.local/share/virtualenvs/test_room-5qPkBY6Q/lib/python3.7/site-packages/uvicorn/protocols/websockets/websockets_impl.py", line 153, in run_asgi result = await self.app(self.scope, self.asgi_receive, self.asgi_send) File "/Users/arthur/.local/share/virtualenvs/test_room-5qPkBY6Q/lib/python3.7/site-packages/uvicorn/middleware/proxy_headers.py", line 45, in call return await self.app(scope, receive, send) File "/Users/arthur/.local/share/virtualenvs/test_room-5qPkBY6Q/lib/python3.7/site-packages/fastapi/applications.py", line 146, in call await super().call(scope, receive, send) File "/Users/arthur/.local/share/virtualenvs/test_room-5qPkBY6Q/lib/python3.7/site-packages/starlette/applications.py", line 102, in call await self.middleware_stack(scope, receive, send) File "/Users/arthur/.local/share/virtualenvs/test_room-5qPkBY6Q/lib/python3.7/site-packages/starlette/middleware/errors.py", line 146, in call await self.app(scope, receive, send) File "/Users/arthur/.local/share/virtualenvs/test_room-5qPkBY6Q/lib/python3.7/site-packages/starlette/middleware/base.py", line 21, in call await self.app(scope, receive, send) File "/Users/arthur/.local/share/virtualenvs/test_room-5qPkBY6Q/lib/python3.7/site-packages/starlette/middleware/cors.py", line 68, in call await self.app(scope, receive, send) File "/Users/arthur/.local/share/virtualenvs/test_room-5qPkBY6Q/lib/python3.7/site-packages/starlette/exceptions.py", line 58, in call await self.app(scope, receive, send) File "/Users/arthur/.local/share/virtualenvs/test_room-5qPkBY6Q/lib/python3.7/site-packages/starlette/routing.py", line 550, in call await route.handle(scope, receive, send) File "/Users/arthur/.local/share/virtualenvs/test_room-5qPkBY6Q/lib/python3.7/site-packages/starlette/routing.py", line 376, in handle await self.app(scope, receive, send) File "/Users/arthur/.local/share/virtualenvs/test_room-5qPkBY6Q/lib/python3.7/site-packages/engineio/async_drivers/asgi.py", line 54, in call await self.engineio_server.handle_request(scope, receive, send) File "/Users/arthur/.local/share/virtualenvs/test_room-5qPkBY6Q/lib/python3.7/site-packages/socketio/asyncio_server.py", line 353, in handle_request return await self.eio.handle_request(*args, **kwargs) File "/Users/arthur/.local/share/virtualenvs/test_room-5qPkBY6Q/lib/python3.7/site-packages/engineio/asyncio_server.py", line 243, in handle_request b64, jsonp_index) File "/Users/arthur/.local/share/virtualenvs/test_room-5qPkBY6Q/lib/python3.7/site-packages/engineio/asyncio_server.py", line 396, in _handle_connect del self.sockets[sid] KeyError: '6f21cf98b85749188a8d2ae7fb4161e0'

Thank you

ysde commented 4 years ago

Might related to this one https://github.com/miguelgrinberg/python-socketio/issues/366

miguelgrinberg commented 4 years ago

This is insufficient information, I still can't reproduce the error.

I connected two clients to a room, then from one of them sent an event that uses the for-loop that you have to disconnect all the people in that room. I don't see any crashes. Can you send a more detailed code example that I can use to reproduce the crash please?

ysde commented 4 years ago

Hi @miguelgrinberg

I made a simple program and I can reproduce it every time.

Hope you can reproduce it too.

Thanks for the help.

Server:

import socketio
import uvicorn
from fastapi import FastAPI
from socketio import ASGIApp

app = FastAPI()

mgr = socketio.AsyncRedisManager(f'redis://localhost:6379')
sio = socketio.AsyncServer(async_mode='asgi',
                           allow_upgrades=False,
                           cors_allowed_origins="*",
                           engineio_logger=True,
                           client_manager=mgr
                           )
sio_app = ASGIApp(sio)

@sio.event
async def connect(sid, environ):
    pass

@sio.event
async def create_room(sid, data):
    await sio.disconnect(sid)

app.mount('/ws', sio_app)

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=80)

Client

sio = socketio.Client()

sio.connect('http://localhost/',
            transports='websocket',
            socketio_path="/ws/socket.io/"
            )

sio.emit('create_room', {})
ysde commented 4 years ago

Hi @miguelgrinberg

Thanks for the fix, may I know when will you have next release on PyPi ?

Thank you

miguelgrinberg commented 4 years ago

@ysde 3.13.1 with this fix is out.