def initialize(self): _path = msg.join_path(self._path, 'initialize') msg.code_debug( _path, 'New connection established! {0} ' '({0.request.remote_ip})'.format(self)) self.local_pub_sub = OwnerPubSub(name='local_pub_sub') self.ws_pub_sub = OwnerPubSub(name='ws_pub_sub', send_function=self.write_message) self.ws_objects = { ws_class: ws_class(self) for ws_class in self.ws_classes } self.__class__.clients.add(self) self.__class__.client_count += 1 self.clean_closed = False self.ping_timeout_handle = None
def initialize(self): _path = msg.join_path(self._path, "initialize") msg.code_debug(_path, "New connection established! {0} " "({0.request.remote_ip})".format(self)) self.local_pub_sub = OwnerPubSub(name="local_pub_sub") self.ws_pub_sub = OwnerPubSub(name="ws_pub_sub", send_function=self.write_message) self.ws_objects = {ws_class: ws_class(self) for ws_class in self.ws_classes} self.__class__.clients.add(self) self.__class__.client_count += 1 self.clean_closed = False self.ping_timeout_handle = None
class MSGHandler(WebSocketHandler): """Serve the WebSocket clients. An instance of this class is created every time a client connects using WebSocket. The instances of this class deliver messages to a group of objects specialized in attending a group of messages. """ _path = msg.join_path(_path, 'MSGHandler') ws_classes = [] clients = set() client_count = 0 # Total clients that have connected @classmethod @coroutine def stop(cls): for client in cls.clients.copy(): yield client.end() def initialize(self): _path = msg.join_path(self._path, 'initialize') msg.code_debug( _path, 'New connection established! {0} ' '({0.request.remote_ip})'.format(self) ) self.local_pub_sub = OwnerPubSub( name='local_pub_sub') self.ws_pub_sub = OwnerPubSub( name='ws_pub_sub', send_function=self.write_message ) self.ws_objects = { ws_class: ws_class(self) for ws_class in self.ws_classes} self.__class__.clients.add(self) self.__class__.client_count += 1 self.clean_closed = False self.ping_timeout_handle = None def open(self): IOLoop.current().spawn_callback(self.on_pong, b'1') @classmethod def add_class(cls, wsclass): cls.ws_classes.append(wsclass) @classmethod def broadcast(cls, message): for client in cls.clients: client.ws_pub_sub.send_message(message) def on_message(self, message): """Process messages when they arrive. :param str message: The received message. This should be a valid json document. """ try: # Throws ValueError message = json.loads(message) self.ws_pub_sub.execute_actions(message) except NoActionForMsgTypeError: self.send_error( 'noActionForMsgType', message, "The client has sent a message for which " "there is no associated action." ) msg.no_action_for_msg_type(_path, message) except (NotDictError, NoMessageTypeError, ValueError): self.send_malformed_message_error(message) msg.malformed_message(_path, message) @coroutine def on_pong(self, data): """Clear the timeout, sleep, and send a new ping. .. todo:: * Document the times used in this method. The calculations are in my black notebook XD. """ try: if self.ping_timeout_handle is not None: IOLoop.current().remove_timeout( self.ping_timeout_handle) yield sleep(conf.ping_sleep) self.ping(b'1') self.ping_timeout_handle = \ IOLoop.current().call_later( conf.ping_timeout, self.close) except WebSocketClosedError: pass except: raise def send_error(self, critical_type, message, description): self.ws_pub_sub.send_message( {'type': 'critical', 'critical_type': critical_type, 'message': message, 'description': description} ) send_malformed_message_error = partialmethod( send_error, 'malformedMessage', description="The client has sent a message which " "either isn't in JSON format, is not a " "single JSON object, does not have a " "'type' field or at least one " "attribute is not consistent with the " "others." ) def write_message(self, message, binary=False): try: super().write_message(message, binary) except WebSocketClosedError: if not self.clean_closed: raise @coroutine def end(self): """Clean up the associated objects This coroutine calls :meth:`src.wsclass.WSClass.end` for all objects in ``self.ws_objects`` and it removes ``self`` from ``self.__class__.clients``. This coroutine is setup to be called when the WebSocket connection closes or when the program ends. """ try: exceptions = [] for ws_object in self.ws_objects.values(): try: yield ws_object.end() except: exceptions.append( exc_info() ) for exception in exceptions: print_exception(*exception) self.__class__.clients.discard(self) msg.code_debug( msg.join_path( __name__, self.end.__qualname__), 'Connection closed! {0} ' '({0.request.remote_ip})'.format(self) ) except: raise @coroutine def on_close(self): try: yield self.end() except: raise
from .common import db _path = 'src.db.message_broker' _coll_name = 'messages' _coll = db[_coll_name] cursor = None @coroutine def _send_message(message): yield _coll.insert(message) _ps = OwnerPubSub( name='db_pub_sub', send_function=_send_message ) # METHOD ALIASES: register = _ps.register send_message = _ps.send_message remove_owner = _ps.remove_owner @coroutine def _tailable_iteration(function: 'callable'=None, stop: bool=True, sleep: int=0): """Iterates through a tailable cursor. .. todo::
class MSGHandler(WebSocketHandler): """Serve the WebSocket clients. An instance of this class is created every time a client connects using WebSocket. The instances of this class deliver messages to a group of objects specialized in attending a group of messages. """ _path = msg.join_path(_path, 'MSGHandler') ws_classes = [] clients = set() client_count = 0 # Total clients that have connected @classmethod @coroutine def stop(cls): for client in cls.clients.copy(): yield client.end() def initialize(self): _path = msg.join_path(self._path, 'initialize') msg.code_debug( _path, 'New connection established! {0} ' '({0.request.remote_ip})'.format(self)) self.local_pub_sub = OwnerPubSub(name='local_pub_sub') self.ws_pub_sub = OwnerPubSub(name='ws_pub_sub', send_function=self.write_message) self.ws_objects = { ws_class: ws_class(self) for ws_class in self.ws_classes } self.__class__.clients.add(self) self.__class__.client_count += 1 self.clean_closed = False self.ping_timeout_handle = None def open(self): IOLoop.current().spawn_callback(self.on_pong, b'1') @classmethod def add_class(cls, wsclass): cls.ws_classes.append(wsclass) @classmethod def broadcast(cls, message): for client in cls.clients: client.ws_pub_sub.send_message(message) def on_message(self, message): """Process messages when they arrive. :param str message: The received message. This should be a valid json document. """ try: # Throws ValueError message = json.loads(message) self.ws_pub_sub.execute_actions(message) except NoActionForMsgTypeError: self.send_error( 'noActionForMsgType', message, "The client has sent a message for which " "there is no associated action.") msg.no_action_for_msg_type(_path, message) except (NotDictError, NoMessageTypeError, ValueError): self.send_malformed_message_error(message) msg.malformed_message(_path, message) @coroutine def on_pong(self, data): """Clear the timeout, sleep, and send a new ping. .. todo:: * Document the times used in this method. The calculations are in my black notebook XD. """ try: if self.ping_timeout_handle is not None: IOLoop.current().remove_timeout(self.ping_timeout_handle) yield sleep(conf.ping_sleep) self.ping(b'1') self.ping_timeout_handle = \ IOLoop.current().call_later( conf.ping_timeout, self.close) except WebSocketClosedError: pass except: raise def send_error(self, critical_type, message, description): self.ws_pub_sub.send_message({ 'type': 'critical', 'critical_type': critical_type, 'message': message, 'description': description }) send_malformed_message_error = partialmethod( send_error, 'malformedMessage', description="The client has sent a message which " "either isn't in JSON format, is not a " "single JSON object, does not have a " "'type' field or at least one " "attribute is not consistent with the " "others.") def write_message(self, message, binary=False): try: super().write_message(message, binary) except WebSocketClosedError: if not self.clean_closed: raise @coroutine def end(self): """Clean up the associated objects This coroutine calls :meth:`src.wsclass.WSClass.end` for all objects in ``self.ws_objects`` and it removes ``self`` from ``self.__class__.clients``. This coroutine is setup to be called when the WebSocket connection closes or when the program ends. """ try: exceptions = [] for ws_object in self.ws_objects.values(): try: yield ws_object.end() except: exceptions.append(exc_info()) for exception in exceptions: print_exception(*exception) self.__class__.clients.discard(self) msg.code_debug( msg.join_path(__name__, self.end.__qualname__), 'Connection closed! {0} ' '({0.request.remote_ip})'.format(self)) except: raise @coroutine def on_close(self): try: yield self.end() except: raise