async def __connection_handler(self, websocket_client_connection: websockets.WebSocketClientProtocol, path: str) -> None: if self.__stop_server.is_set(): return # check auth and stuff TODO origin: Optional[str] success: Optional[bool] (origin, success) = await self.__authenticate_connection(websocket_client_connection) if success is False: # failed auth, stop connection await self.__close_websocket_client_connection(origin, websocket_client_connection) return origin_logger = get_origin_logger(logger, origin=origin) origin_logger.info("New connection from {}", websocket_client_connection.remote_address) async with self.__users_connecting_mutex: if origin in self.__users_connecting: origin_logger.info("Client is already connecting") return else: self.__users_connecting.add(origin) continue_register = True async with self.__current_users_mutex: origin_logger.debug("Checking if an entry is already present") entry = self.__current_users.get(origin, None) if entry is None: origin_logger.info("Need to start a new worker thread") entry = WebsocketConnectedClientEntry(origin=origin, websocket_client_connection=websocket_client_connection, worker_instance=None, worker_thread=None, loop_running=self.__loop) if not await self.__add_worker_and_thread_to_entry(entry, origin): continue_register = False else: origin_logger.info("There is a worker thread entry present, handling accordingly") if entry.websocket_client_connection.open: origin_logger.error("Old connection open while a new one is attempted to be established, " "aborting handling of connection") continue_register = False entry.websocket_client_connection = websocket_client_connection # TODO: also change the worker's Communicator? idk yet if entry.worker_thread.is_alive() and not entry.worker_instance.is_stopping(): origin_logger.info("Worker thread still alive, continue as usual") # TODO: does this need more handling? probably update communicator or whatever? elif not entry.worker_thread.is_alive(): origin_logger.info("Old thread is dead, trying to start a new one") if not await self.__add_worker_and_thread_to_entry(entry, origin): continue_register = False else: origin_logger.info("Old thread is about to stop. Wait a little and reconnect") # random sleep to not have clients try again in sync continue_register = False if continue_register: self.__current_users[origin] = entry if not continue_register: await asyncio.sleep(rand.uniform(3, 15)) async with self.__users_connecting_mutex: origin_logger.debug("Removing from users_connecting") self.__users_connecting.remove(origin) return try: if not entry.worker_thread.is_alive(): entry.worker_thread.start() # TODO: we need to somehow check threads and synchronize connection status with worker status? async with self.__users_connecting_mutex: self.__users_connecting.remove(origin) receiver_task = asyncio.ensure_future( self.__client_message_receiver(origin, entry)) await receiver_task except Exception as e: origin_logger.opt(exception=True).error("Other unhandled exception during registration: {}", e) # also check if thread is already running to not start it again. If it is not alive, we need to create it.. finally: origin_logger.info("Awaiting unregister") # TODO: cleanup thread is not really desired, I'd prefer to only restart a worker if the route changes :( self.__worker_shutdown_queue.put(entry.worker_thread) origin_logger.info("Done with connection ({})", websocket_client_connection.remote_address)
async def __connection_handler(self, websocket_client_connection: websockets.WebSocketClientProtocol, path: str) -> None: if self.__stop_server.is_set(): return # check auth and stuff TODO origin: Optional[str] success: Optional[bool] (origin, success) = await self.__authenticate_connection(websocket_client_connection) if success is False: # failed auth, stop connection await self.__close_websocket_client_connection(origin, websocket_client_connection) return origin_logger = get_origin_logger(logger, origin=origin) remote_address = websocket_client_connection.remote_address origin_logger.info("New connection from {}", remote_address) if self.__enable_configmode: origin_logger.warning('Connected in ConfigMode. No mapping will occur in the current mode') async with self.__users_connecting_mutex: if origin in self.__users_connecting: origin_logger.info("Done with connection ({}) -- Client is already connecting", remote_address) return else: self.__users_connecting.add(origin) continue_register = True async with self.__current_users_mutex: origin_logger.debug("Checking if an entry is already present") entry = self.__current_users.get(origin, None) device = None use_configmode = self.__enable_configmode if not self.__enable_configmode: for _, dev in self.__data_manager.search('device', params={'origin': origin}).items(): if dev['origin'] == origin: device = dev break if not self.__data_manager.is_device_active(device.identifier): origin_logger.warning('Origin is currently paused. Unpause through MADmin to begin working') use_configmode = True if entry is None or use_configmode: origin_logger.info("Need to start a new worker thread") entry = WebsocketConnectedClientEntry(origin=origin, websocket_client_connection=websocket_client_connection, worker_instance=None, worker_thread=None, loop_running=self.__loop) if not await self.__add_worker_and_thread_to_entry(entry, origin, use_configmode=use_configmode): continue_register = False else: origin_logger.info("There is a worker thread entry present, handling accordingly") if entry.websocket_client_connection.open: origin_logger.error("Old connection open while a new one is attempted to be established, " "aborting handling of connection") continue_register = False elif entry.worker_thread.is_alive() and not entry.worker_instance.is_stopping(): # Ideally we just set the new connection in the entry and the worker starts using it. # The problem is that the above check is racey. The worker could be stopping right # now (because it shut itself down, etc). It's best to just disallow a new connection # until the old connection is detected dead and the worker completely stops. origin_logger.info("Worker thread still alive, rejecting new conn ({}) because thread " "should be stopped when old connection is found dead", remote_address) continue_register = False elif not entry.worker_thread.is_alive(): origin_logger.info("Old thread is dead, trying to start a new one ({})", remote_address) if not await self.__add_worker_and_thread_to_entry(entry, origin, use_configmode=use_configmode): continue_register = False else: origin_logger.info("Old thread is about to stop. Wait a little and reconnect ({})", remote_address) # random sleep to not have clients try again in sync continue_register = False if continue_register: entry.websocket_client_connection = websocket_client_connection if continue_register: self.__current_users[origin] = entry if not continue_register: await asyncio.sleep(rand.uniform(3, 15)) async with self.__users_connecting_mutex: origin_logger.debug("Removing from users_connecting") self.__users_connecting.remove(origin) origin_logger.info("Done with connection ({}) (not allowing register)", remote_address) return worker_instance = entry.worker_instance worker_thread = entry.worker_thread try: if not worker_thread.is_alive(): origin_logger.debug('starting worker thread ({})', remote_address) worker_thread.start() # TODO: we need to somehow check threads and synchronize connection status with worker status? async with self.__users_connecting_mutex: self.__users_connecting.remove(origin) receiver_task = asyncio.ensure_future( self.__client_message_receiver(origin, entry)) origin_logger.debug('awaiting __client_message_receiver for connection {}', remote_address) await receiver_task except Exception as e: origin_logger.opt(exception=True).error("Other unhandled exception during registration: {}", e) # also check if thread is already running to not start it again. If it is not alive, we need to create it.. finally: # TODO: cleanup thread is not really desired, I'd prefer to only restart a worker if the route changes :( cur_connection = entry.websocket_client_connection if cur_connection == websocket_client_connection: origin_logger.debug('stopping worker (connection {} done)', remote_address) worker_instance.stop_worker() self.__worker_shutdown_queue.put(worker_thread) else: origin_logger.warning('Not stopping worker (connection {} done): we raced (connection {} active)', remote_address, cur_connection.remote_address) origin_logger.info("Done with connection ({})", remote_address)