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)