Open vsemionov opened 7 months ago
I think this is probably related to https://github.com/miguelgrinberg/flask-sock/issues/64. It appears the Gevent worker in Gunicorn does not implement the same mechanism as the threaded worker to exit out of a WebSocket connection cleanly. The Gunicorn support in this package is designed to work with the threaded worker, and it so happens that the eventlet worker also implements similar logic. I'm not sure why the gevent worker does not follow the same pattern, but my suggestion is that if you want to use gevent then you drop Gunicorn and use gevent's own WSGI server.
Using simple-websocket from git commit 32ec52 and Gunicorn 21.2.0. When a client closes the websocket, the following is logged:
I believe it happens because the IO thread closes the underlying OS socket before terminating:
Base._thread() in ws.py
I believe calling this and closing the OS socket by this package is wrong and should not be done, simply because the socket is owned and managed by the web server.
The reason it only happens when the connection is closed by the client, is that when the server initiates the close, this only sets the
connected
attribute, but the IO thread is sleeping and only notices that after a while.Also, I had the problem that after a server-initiated close, the browser (Chrome 121.0.6167.139) complained about receiving an invalid frame. It was because the web server sends the http headers after the websocket view is finished. To prevent this, after closing the WebSocket from the Server class, I do:
In the above,
ws
is a Server instance, andsocket
is the standard python module. Doing this also removes the need for this inflask-sock
. While the code in the link does prevent exceptions in logs, it also removes the websocket requests from the web server's access logs. And the exceptions are still raised and get reported in Sentry. So I recommend you do something similar to the above quoted line. Unlike calling close() on the OS socket, calling shutdown() does not cause later exceptions in werkzeug or gunicorn (I have not tested with eventlet).