Cannot send events from 'disconnect' event handler of Flask-SocketIO server

22 Views Asked by At

I'm building a simple presence announcement system using Flask-SocketIO for my server.
My idea was to detect every 'connect'/'disconnect' event on the server and broadcast to all online users a 'presence' event that correctly indicates whether User A has just connected or disconnected.
The 'connect' handler, i.e. the handler that dispatches the 'User A has come online' presence works well; the problem has to do with the 'disconnect' one, since trying to send/emit an event from that handler gets me this error:

 Traceback (most recent call last):  
  File "...\.venv\Lib\site-packages\werkzeug\serving.py", line 362, in run_wsgi
    execute(self.server.app)  
  File "...\.venv\Lib\site-packages\werkzeug\serving.py", line 328, in execute
    write(b"")  
  File "...\.venv\Lib\site-packages\werkzeug\serving.py", line 253, in write
    assert status_set is not None, "write() before start_response"  
AssertionError: write() before start_response

Here is my code:

app = Flask(__name__)
sio = SocketIO(
    app,
    kwargs={'logger': True, 'engineio_logger': True}
)
logging.basicConfig(level=logging.INFO)


class _User:
    def __init__(self, username):
        self.username = username

    def with_sid(self, sid):
        return User(self.username, sid)

    def __hash__(self):
        return hash(self.username)

    def __eq__(self, other):
        return self.username == other.username

class User(_User):
    def __init__(self, username, sid):
        super().__init__(username)
        self.sid = sid

    @staticmethod
    def get_user(username, sid):
        for user in userStorage:
            if user.username == username:
                return user.with_sid(sid)

        user = User(username, sid)
        print(f'New user {username} created')
        userStorage.add(user)
        return user


userStorage: Set[_User] = set()
onlineUsers: Set[User] = set()


@sio.on('connect', namespace='/chat')
def user_online():
    username = request.headers['username']
    sid = request.sid
    user = User.get_user(username, sid)
    onlineUsers.add(user)
    print(f'User {username} connected')

    # presence broadcating
    sio.emit(
        event='presence',
        data="""{
        "header": "presence", 
        "type": "online", 
        "user_ref": "%s"
        }""" % username,
        namespace='/chat',
        include_self=True,
        to=[u.sid for u in onlineUsers]
    )


@sio.on('disconnect', namespace='/chat')
def user_offline():
    username = request.headers['username']
    sid = request.sid
    user = User.get_user(username, sid)
    onlineUsers.remove(user)
    print(f'User {username} disconnected')
    sio.send(
        event='presence',
        data="""{
        "header": "presence", 
        "type": "offline", 
        "user_ref": "%s"
        }""" % username,
        namespace='/chat',
        include_self=True,
        to=[u.sid for u in onlineUsers]
    )


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

0

There are 0 best solutions below