Exemple #1
0
    def _translate_webserver_error(self, ex):
        s = str(ex)

        # NOTE(kgriffs): uvicorn or any other server using the "websockets"
        #   package that allows exceptions to bubble up
        if 'code = 1000 (OK)' in s:
            return errors.WebSocketDisconnected(WSCloseCode.NORMAL)

        # NOTE(kgriffs): Autobahn (used by Daphne) raises a generic exception
        #   with this message
        if 'protocol accepted must be from the list' in s:
            return ValueError('WebSocket subprotocol must be from the list sent by the client')

        return None
Exemple #2
0
    def _require_accepted(self):
        if self._state == _WebSocketState.ACCEPTED:
            return

        if self._state in {_WebSocketState.CONNECT, _WebSocketState.HANDSHAKE}:
            raise falcon_errors.OperationNotAllowed(
                'WebSocket connection has not yet been accepted'
            )
        elif self._state == _WebSocketState.CLOSED:
            raise falcon_errors.WebSocketDisconnected(self._close_code)

        assert self._state == _WebSocketState.DENIED

        if self._close_code == WSCloseCode.PATH_NOT_FOUND:
            raise falcon_errors.WebSocketPathNotFound(WSCloseCode.PATH_NOT_FOUND)

        if self._close_code == WSCloseCode.SERVER_ERROR:
            raise falcon_errors.WebSocketServerError(WSCloseCode.SERVER_ERROR)

        if self._close_code == WSCloseCode.HANDLER_NOT_FOUND:
            raise falcon_errors.WebSocketHandlerNotFound(WSCloseCode.HANDLER_NOT_FOUND)

        raise falcon_errors.WebSocketDisconnected(self._close_code)
Exemple #3
0
    async def _receive(self) -> dict:
        event = await self._asgi_receive()

        event_type = event['type']

        if event_type != EventType.WS_RECEIVE:
            # NOTE(kgriffs): Based on the ASGI spec, there are no other
            #   event types that should be emitted by the protocol server,
            #   but we sanity-check it here just in case.
            assert event_type == EventType.WS_DISCONNECT

            self._state = _WebSocketState.CLOSED
            self._close_code = event.get('code', WSCloseCode.NORMAL)
            raise errors.WebSocketDisconnected(self._close_code)

        return event
Exemple #4
0
    async def _send(self, msg: dict):
        if self._buffered_receiver.client_disconnected:
            self._state = _WebSocketState.CLOSED
            self._close_code = self._buffered_receiver.client_disconnected_code
            raise errors.WebSocketDisconnected(self._close_code)

        try:
            await self._asgi_send(msg)
        except Exception as ex:
            # NOTE(kgriffs): If uvicorn (or any other server that uses the
            #   the "websockets" library) allows exceptions to bubble up,
            #   we will have an error raised on client disconnect.
            #
            #   Daphne, on the other hand, does not raise an error but just
            #   eats the message. This approach is actually more in keeping
            #   with the ASGI spec, but poses its own challenges.

            translated_ex = self._translate_webserver_error(ex)
            if translated_ex:
                raise translated_ex

            # NOTE(kgriffs): Re-raise other errors directly so that we don't
            #   obscure the traceback.
            raise
Exemple #5
0
 def _require_accepted(self):
     if self._state == _WebSocketState.HANDSHAKE:
         raise errors.OperationNotAllowed(
             'WebSocket connection has not yet been accepted')
     elif self._state == _WebSocketState.CLOSED:
         raise errors.WebSocketDisconnected(self._close_code)