-
Notifications
You must be signed in to change notification settings - Fork 17
Abrupt error on closing the websocket connection #111
Description
Description
Using django channels for the websocket server, I have a custom middleware for authentication.
I followed django channels recommendation to set up such custom middleware:
# /ws/authenticate.py
from utils.auth import authenticate
from asgiref.sync import sync_to_async
class QueryAuthMiddleware:
"""
Custom middleware (insecure) that takes user IDs from the query string.
"""
def __init__(self, app):
# Store the ASGI application we were passed
self.app = app
async def __call__(self, scope, receive, send):
# Look up user from query string (you should also do things like
# checking if it is a valid user ID, or if scope["user"] is already
# populated).
(_, token) = scope["query_string"].split(b"=")
try:
user, kc_user = await sync_to_async(authenticate, thread_sensitive=True)(token=token.decode("utf-8"))
except:
user = None
scope["user"] = user
# print("QUERY_STRING", scope["query_string"])
# scope['user'] = await get_user(int(scope["query_string"]))
return await self.app(scope, receive, send)
# asgi.py
application = ProtocolTypeRouter({
"http": django_asgi_app,
# Just HTTP for now. (We can add other protocols later.)
"websocket": AllowedHostsOriginValidator(
QueryAuthMiddleware(
URLRouter(urlpatterns_ws))
),
})On the client side I have a react application in which I am initializing a WebsockerProvider using yjs library.
When I call wsProvider.close() the connection get abruptly closed on the server side which results in an error:
Traceback (most recent call last):
middleman | File "/usr/local/lib/python3.10/site-packages/uvicorn/protocols/websockets/websockets_impl.py", line 243, in run_asgi
middleman | result = await self.app(self.scope, self.asgi_receive, self.asgi_send) # type: ignore[func-returns-value]
middleman | File "/usr/local/lib/python3.10/site-packages/uvicorn/middleware/proxy_headers.py", line 60, in __call__
middleman | return await self.app(scope, receive, send)
middleman | File "/usr/local/lib/python3.10/site-packages/channels/routing.py", line 48, in __call__
middleman | return await application(scope, receive, send)
middleman | File "/usr/local/lib/python3.10/site-packages/channels/security/websocket.py", line 37, in __call__
middleman | return await self.application(scope, receive, send)
middleman | File "/opt/app/ws/authentication.py", line 30, in __call__
middleman | return await self.app(scope, receive, send)
middleman | File "/usr/local/lib/python3.10/site-packages/channels/routing.py", line 118, in __call__
middleman | return await application(
middleman | File "/usr/local/lib/python3.10/site-packages/channels/consumer.py", line 95, in app
middleman | return await consumer(scope, receive, send)
middleman | File "/usr/local/lib/python3.10/site-packages/channels/consumer.py", line 58, in __call__
middleman | await await_many_dispatch(
middleman | File "/usr/local/lib/python3.10/site-packages/channels/utils.py", line 50, in await_many_dispatch
middleman | await dispatch(result)
middleman | File "/usr/local/lib/python3.10/site-packages/channels/consumer.py", line 74, in dispatch
middleman | await handler(message)
middleman | File "/usr/local/lib/python3.10/site-packages/pycrdt_websocket/django_channels_consumer.py", line 209, in send_message
middleman | await self.send(bytes_data=message_wrapper["message"])
middleman | File "/usr/local/lib/python3.10/site-packages/channels/generic/websocket.py", line 221, in send
middleman | await super().send({"type": "websocket.send", "bytes": bytes_data})
middleman | File "/usr/local/lib/python3.10/site-packages/channels/consumer.py", line 82, in send
middleman | await self.base_send(message)
middleman | File "/usr/local/lib/python3.10/site-packages/uvicorn/protocols/websockets/websockets_impl.py", line 359, in asgi_send
middleman | raise RuntimeError(msg % message_type)
middleman | RuntimeError: Unexpected ASGI message 'websocket.send', after sending 'websocket.close' or response already completed.It seems that the Authentication middleware is causing the error, and I have the impression that it crashed because pycrdt-websocket is still trying to update the awareness or the yDoc on the server side
Reproduce
To reproduce the behaviour create a simple django application,
install django channels configured with daphne and uvicorn as specified in their documentation.
Create a websocket provider on the client side, and it will results in an abrupt closing of the websocket
Expected behavior
I expect that the websocket on server side close smootly without causing any error. When I call wsProvider.close() on the client side the websocket should not try to do another authentication call on the Django Auth middleware.
Context
- Operating System and version: Ubuntu 22.04
- Browser and version: Chromium latest version
- Jupyter Server version: pycrdt-websocket latest
Browser Output
Paste the output from your browser Javascript console here, if applicable.