def handle(self, request): """Handle the websocket connection.""" wsock = self.wsock = web.WebSocketResponse() yield from wsock.prepare(request) self.debug("Connected") # Get a reference to current task so we can cancel our connection self._handle_task = asyncio.Task.current_task(loop=self.hass.loop) @callback def handle_hass_stop(event): """Cancel this connection.""" self.cancel() unsub_stop = self.hass.bus.async_listen( EVENT_HOMEASSISTANT_STOP, handle_hass_stop) self._writer_task = self.hass.async_add_job(self._writer()) final_message = None msg = None authenticated = False try: if request[KEY_AUTHENTICATED]: authenticated = True else: yield from self.wsock.send_json(auth_required_message()) msg = yield from wsock.receive_json() msg = AUTH_MESSAGE_SCHEMA(msg) if validate_password(request, msg['api_password']): authenticated = True else: self.debug("Invalid password") yield from self.wsock.send_json( auth_invalid_message('Invalid password')) if not authenticated: yield from process_wrong_login(request) return wsock yield from self.wsock.send_json(auth_ok_message()) # ---------- AUTH PHASE OVER ---------- msg = yield from wsock.receive_json() last_id = 0 while msg: self.debug("Received", msg) msg = BASE_COMMAND_MESSAGE_SCHEMA(msg) cur_id = msg['id'] if cur_id <= last_id: self.to_write.put_nowait(error_message( cur_id, ERR_ID_REUSE, 'Identifier values have to increase.')) else: handler_name = 'handle_{}'.format(msg['type']) getattr(self, handler_name)(msg) last_id = cur_id msg = yield from wsock.receive_json() except vol.Invalid as err: error_msg = "Message incorrectly formatted: " if msg: error_msg += humanize_error(msg, err) else: error_msg += str(err) self.log_error(error_msg) if not authenticated: final_message = auth_invalid_message(error_msg) else: if isinstance(msg, dict): iden = msg.get('id') else: iden = None final_message = error_message( iden, ERR_INVALID_FORMAT, error_msg) except TypeError as err: if wsock.closed: self.debug("Connection closed by client") else: self.log_error("Unexpected TypeError", msg) except ValueError as err: msg = "Received invalid JSON" value = getattr(err, 'doc', None) # Py3.5+ only if value: msg += ': {}'.format(value) self.log_error(msg) self._writer_task.cancel() except asyncio.CancelledError: self.debug("Connection cancelled by server") except asyncio.QueueFull: self.log_error("Client exceeded max pending messages [1]:", MAX_PENDING_MSG) self._writer_task.cancel() except Exception: # pylint: disable=broad-except error = "Unexpected error inside websocket API. " if msg is not None: error += str(msg) _LOGGER.exception(error) finally: unsub_stop() for unsub in self.event_listeners.values(): unsub() try: if final_message is not None: self.to_write.put_nowait(final_message) self.to_write.put_nowait(None) # Make sure all error messages are written before closing yield from self._writer_task except asyncio.QueueFull: self._writer_task.cancel() yield from wsock.close() self.debug("Closed connection") return wsock
def handle(self): """Handle the websocket connection.""" request = self.request wsock = self.wsock = web.WebSocketResponse(heartbeat=55) yield from wsock.prepare(request) self.debug("Connected") # Get a reference to current task so we can cancel our connection self._handle_task = asyncio.Task.current_task(loop=self.hass.loop) @callback def handle_hass_stop(event): """Cancel this connection.""" self.cancel() unsub_stop = self.hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP, handle_hass_stop) self._writer_task = self.hass.async_add_job(self._writer()) final_message = None msg = None authenticated = False try: if request[KEY_AUTHENTICATED]: authenticated = True else: yield from self.wsock.send_json(auth_required_message()) msg = yield from wsock.receive_json() msg = AUTH_MESSAGE_SCHEMA(msg) if validate_password(request, msg['api_password']): authenticated = True else: self.debug("Invalid password") yield from self.wsock.send_json( auth_invalid_message('Invalid password')) if not authenticated: yield from process_wrong_login(request) return wsock yield from self.wsock.send_json(auth_ok_message()) # ---------- AUTH PHASE OVER ---------- msg = yield from wsock.receive_json() last_id = 0 while msg: self.debug("Received", msg) msg = BASE_COMMAND_MESSAGE_SCHEMA(msg) cur_id = msg['id'] if cur_id <= last_id: self.to_write.put_nowait( error_message(cur_id, ERR_ID_REUSE, 'Identifier values have to increase.')) else: handler_name = 'handle_{}'.format(msg['type']) getattr(self, handler_name)(msg) last_id = cur_id msg = yield from wsock.receive_json() except vol.Invalid as err: error_msg = "Message incorrectly formatted: " if msg: error_msg += humanize_error(msg, err) else: error_msg += str(err) self.log_error(error_msg) if not authenticated: final_message = auth_invalid_message(error_msg) else: if isinstance(msg, dict): iden = msg.get('id') else: iden = None final_message = error_message(iden, ERR_INVALID_FORMAT, error_msg) except TypeError as err: if wsock.closed: self.debug("Connection closed by client") else: _LOGGER.exception("Unexpected TypeError: %s", msg) except ValueError as err: msg = "Received invalid JSON" value = getattr(err, 'doc', None) # Py3.5+ only if value: msg += ': {}'.format(value) self.log_error(msg) self._writer_task.cancel() except CANCELLATION_ERRORS: self.debug("Connection cancelled by server") except asyncio.QueueFull: self.log_error("Client exceeded max pending messages [1]:", MAX_PENDING_MSG) self._writer_task.cancel() except Exception: # pylint: disable=broad-except error = "Unexpected error inside websocket API. " if msg is not None: error += str(msg) _LOGGER.exception(error) finally: unsub_stop() for unsub in self.event_listeners.values(): unsub() try: if final_message is not None: self.to_write.put_nowait(final_message) self.to_write.put_nowait(None) # Make sure all error messages are written before closing yield from self._writer_task except asyncio.QueueFull: self._writer_task.cancel() yield from wsock.close() self.debug("Closed connection") return wsock
def handle(self): """Handle the websocket connection.""" wsock = self.wsock = web.WebSocketResponse() yield from wsock.prepare(self.request) # Set up to cancel this connection when Home Assistant shuts down socket_task = asyncio.Task.current_task(loop=self.hass.loop) @callback def cancel_connection(event): """Cancel this connection.""" socket_task.cancel() unsub_stop = self.hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP, cancel_connection) self.debug('Connected') msg = None authenticated = False try: if self.request[KEY_AUTHENTICATED]: authenticated = True else: self.send_message(auth_required_message()) msg = yield from wsock.receive_json() msg = AUTH_MESSAGE_SCHEMA(msg) if validate_password(self.request, msg['api_password']): authenticated = True else: self.debug('Invalid password') self.send_message(auth_invalid_message('Invalid password')) if not authenticated: yield from process_wrong_login(self.request) return wsock self.send_message(auth_ok_message()) msg = yield from wsock.receive_json() last_id = 0 while msg: self.debug('Received', msg) msg = BASE_COMMAND_MESSAGE_SCHEMA(msg) cur_id = msg['id'] if cur_id <= last_id: self.send_message(error_message( cur_id, ERR_ID_REUSE, 'Identifier values have to increase.')) else: handler_name = 'handle_{}'.format(msg['type']) getattr(self, handler_name)(msg) last_id = cur_id msg = yield from wsock.receive_json() except vol.Invalid as err: error_msg = 'Message incorrectly formatted: ' if msg: error_msg += humanize_error(msg, err) else: error_msg += str(err) self.log_error(error_msg) if not authenticated: self.send_message(auth_invalid_message(error_msg)) else: if isinstance(msg, dict): iden = msg.get('id') else: iden = None self.send_message(error_message(iden, ERR_INVALID_FORMAT, error_msg)) except TypeError as err: if wsock.closed: self.debug('Connection closed by client') else: self.log_error('Unexpected TypeError', msg) except ValueError as err: msg = 'Received invalid JSON' value = getattr(err, 'doc', None) # Py3.5+ only if value: msg += ': {}'.format(value) self.log_error(msg) except asyncio.CancelledError: self.debug('Connection cancelled by server') except Exception: # pylint: disable=broad-except error = 'Unexpected error inside websocket API. ' if msg is not None: error += str(msg) _LOGGER.exception(error) finally: unsub_stop() for unsub in self.event_listeners.values(): unsub() yield from wsock.close() self.debug('Closed connection') return wsock
def handle(self): """Handle the websocket connection.""" wsock = self.wsock = web.WebSocketResponse() yield from wsock.prepare(self.request) # Set up to cancel this connection when Home Assistant shuts down socket_task = asyncio.Task.current_task(loop=self.hass.loop) @callback def cancel_connection(event): """Cancel this connection.""" socket_task.cancel() unsub_stop = self.hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP, cancel_connection) self.debug('Connected') msg = None authenticated = False try: if self.request[KEY_AUTHENTICATED]: authenticated = True else: self.send_message(auth_required_message()) msg = yield from wsock.receive_json() msg = AUTH_MESSAGE_SCHEMA(msg) if validate_password(self.request, msg['api_password']): authenticated = True else: self.debug('Invalid password') self.send_message(auth_invalid_message('Invalid password')) if not authenticated: yield from process_wrong_login(self.request) return wsock self.send_message(auth_ok_message()) msg = yield from wsock.receive_json() last_id = 0 while msg: self.debug('Received', msg) msg = BASE_COMMAND_MESSAGE_SCHEMA(msg) cur_id = msg['id'] if cur_id <= last_id: self.send_message( error_message(cur_id, ERR_ID_REUSE, 'Identifier values have to increase.')) else: handler_name = 'handle_{}'.format(msg['type']) getattr(self, handler_name)(msg) last_id = cur_id msg = yield from wsock.receive_json() except vol.Invalid as err: error_msg = 'Message incorrectly formatted: ' if msg: error_msg += humanize_error(msg, err) else: error_msg += str(err) self.log_error(error_msg) if not authenticated: self.send_message(auth_invalid_message(error_msg)) else: if isinstance(msg, dict): iden = msg.get('id') else: iden = None self.send_message( error_message(iden, ERR_INVALID_FORMAT, error_msg)) except TypeError as err: if wsock.closed: self.debug('Connection closed by client') else: self.log_error('Unexpected TypeError', msg) except ValueError as err: msg = 'Received invalid JSON' value = getattr(err, 'doc', None) # Py3.5+ only if value: msg += ': {}'.format(value) self.log_error(msg) except asyncio.CancelledError: self.debug('Connection cancelled by server') except Exception: # pylint: disable=broad-except error = 'Unexpected error inside websocket API. ' if msg is not None: error += str(msg) _LOGGER.exception(error) finally: unsub_stop() for unsub in self.event_listeners.values(): unsub() yield from wsock.close() self.debug('Closed connection') return wsock