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
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)
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
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
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)