Django channels - propagate exceptions to the client

78 Views Asked by At

I was wondering what is the best practice to propagate exceptions to the client using django-channels.
My client is a Swift iOS app using URLSessionWebSocketTask.
I got 3 main scenarios for example where exception is thrown and I want to notify the client.

  1. Authentication, using custom middleware.
from api.jwt_handler import jwt_websocket_decode, get_device_id, get_auth_token
from inbox.exceptions import WebSocketErrors

class WebsocketAuthMiddleware:
    def __init__(self, app):
        self.app = app

    async def __call__(self, scope, receive, send):
        headers = scope["headers"]
        token = get_auth_token(headers)
        device_id = get_device_id(headers)

        if token is None or device_id is None:
            raise WebSocketErrors.invalid_bearer_token <--- Notify client

        user = await jwt_websocket_decode(token, device_id)

        if user is None:
            raise WebSocketErrors.invalid_bearer_token <--- Notify client

        scope['user'] = user

        return await self.app(scope, receive, send)

I've tried wrapping __call__ in try statement, but I'm not sure how to propagate the error to the http transport layer, The client gets 1011 - There was a bad response from the server

  1. Connection rejection - handshake.
class ConversationConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.ws_conversation_id = self.scope["url_route"]["kwargs"]["conversation_id"]
        self.room_group_name = self.ws_conversation_id
        self.user = self.scope["user"]

        if user.can_participate_in_conversation(self.ws_conversation_id) is False:
            raise WebSocketErrors.not_authorized <--- Notify client

        await self.channel_layer.group_add(self.room_group_name, self.channel_name)

        await self.accept()

Here I want to reject connection if user is not part of a given conversation id, to prevent abuse.

  1. In-session data processing - web socket is connected.
    async def receive(self, bytes_data):
        message_json = json.loads(bytes_data)
        type = WebSocketType(message_json["type"])
        
        try:
            web_socket_item = self.get_websocket_item(type, message_json)
            user_id = self.scope["user"].id
            message_body = await web_socket_item.save_and_get_message_body(sender_id=user_id)
        except Exception as error:
            message_body = { 
                "type": WebSocketType.error.value,
                "error": {
                    "code": error.code,
                    "message": error.message
                }
            }

        # Send message to room group
        await self.channel_layer.group_send(
            self.room_group_name, 
            {"type": type.value, "message": message_body}
            )

Here I caught the exception and used the open connection to send message with an error type, But I'm not sure if it is the right practice.

Any unhandled exception causing disconnection, and the client get the same 1011 error. I want to prevent the socket from getting disconnected at some cases (3 for example) and give the client better errors to handle.

0

There are 0 best solutions below