Exemplo n.º 1
0
 def __init__(self,
              purgeInterval=15,
              deadAfter=60,
              exts=[],
              client_cls=BayeuxSession,
              connection_cls=BayeuxConnection):
     self.deadAfter = deadAfter
     self.timeout = int(self.deadAfter / 2.0)
     self._clientCls = client_cls
     self._connectionCls = connection_cls
     self._root = BayeuxChannel('/')
     self._clients = {}
     self._flushable = set()
     self._ioloop = tornado.ioloop.IOLoop.instance()
     self._purgeGen = None
     if purgeInterval is None:
         self._timer = None
     else:
         self._timer = tornado.ioloop.PeriodicCallback(
             self.purge_clients, purgeInterval * 1000)
         self._timer.start()
     self._exts = exts
     self._willFlush = False
Exemplo n.º 2
0
 def publish(self, msg):
     '''Publishes a message to all clients subscribed to its channel.'''
     # strip off client ID before sending out to other clients
     try:
         senderId = msg['clientId']
         del msg['clientId']
     except KeyError:
         senderId = None
     try:
         del msg['id'] 
     except KeyError:
         pass
     ch = msg.get('channel', None)
     
     # find all subscribed clients
     clients = set()
     self._root.collect_clients(clients, BayeuxChannel.split(ch))
     for cl in clients:
         cl.add_message(msg, senderId)
Exemplo n.º 3
0
 def __init__(self, purgeInterval=15, deadAfter=60, exts=[], 
 client_cls=BayeuxSession, connection_cls=BayeuxConnection):
     self.deadAfter = deadAfter
     self.timeout = int(self.deadAfter/2.0)
     self._clientCls = client_cls
     self._connectionCls = connection_cls
     self._root = BayeuxChannel('/')
     self._clients = {}
     self._flushable = set()
     self._ioloop = tornado.ioloop.IOLoop.instance()
     self._purgeGen = None
     if purgeInterval is None:
         self._timer = None
     else:
         self._timer = tornado.ioloop.PeriodicCallback(self.purge_clients,
             purgeInterval*1000) 
         self._timer.start()
     self._exts = exts
     self._willFlush = False
Exemplo n.º 4
0
    def publish(self, msg):
        '''Publishes a message to all clients subscribed to its channel.'''
        # strip off client ID before sending out to other clients
        try:
            senderId = msg['clientId']
            del msg['clientId']
        except KeyError:
            senderId = None
        try:
            del msg['id']
        except KeyError:
            pass
        ch = msg.get('channel', None)

        # find all subscribed clients
        clients = set()
        self._root.collect_clients(clients, BayeuxChannel.split(ch))
        for cl in clients:
            cl.add_message(msg, senderId)
Exemplo n.º 5
0
class BayeuxManager(object):
    '''Base class for a Bayeux manager tracking known clients.'''
    def __init__(self, purgeInterval=15, deadAfter=60, exts=[], 
    client_cls=BayeuxSession, connection_cls=BayeuxConnection):
        self.deadAfter = deadAfter
        self.timeout = int(self.deadAfter/2.0)
        self._clientCls = client_cls
        self._connectionCls = connection_cls
        self._root = BayeuxChannel('/')
        self._clients = {}
        self._flushable = set()
        self._ioloop = tornado.ioloop.IOLoop.instance()
        self._purgeGen = None
        if purgeInterval is None:
            self._timer = None
        else:
            self._timer = tornado.ioloop.PeriodicCallback(self.purge_clients,
                purgeInterval*1000) 
            self._timer.start()
        self._exts = exts
        self._willFlush = False

    def destroy(self):
        '''Destroyes the Bayeux manager and deletes its known clients'''
        # stop the purge timer
        if self._timer is not None:
            # do after in case we're in the callback to prevent re-registration
            # due to tornado bug
            self._ioloop.add_callback(self._timer.stop)
            # force the callback reference to None, because stop doesn't
            # remove the reference from the ioloop, else gc impeded until
            # next callback occurs, even though stopped
            self._timer.callback = None
            self._timer = None
            self._purgeGen = None
        # purge all clients
        cids = self._clients.keys()
        for cid in cids:
            cl = self.delete_client(cid)
            cl.destroy()
        self._clients = {}

    def flush(self):
        '''Flushes all outgoing client messages immediately.'''
        self._willFlush = False
        leftover = set()
        while self._flushable:
            cl = self._flushable.pop()
            if not cl.on_flush():
                leftover.add(cl)
        # don't reschedule flush now, next connection will do it
        self._flushable = leftover

    def should_flush(self, cl):
        '''
        Marks a client as having outgoing messages that should be flushed.
        '''
        self._flushable.add(cl)
        # schedule the flush if not one already pending
        if not self._willFlush:
            self._ioloop.add_callback(self.flush)
            self._willFlush = True

    def publish(self, msg):
        '''Publishes a message to all clients subscribed to its channel.'''
        # strip off client ID before sending out to other clients
        try:
            senderId = msg['clientId']
            del msg['clientId']
        except KeyError:
            senderId = None
        try:
            del msg['id'] 
        except KeyError:
            pass
        ch = msg.get('channel', None)
        
        # find all subscribed clients
        clients = set()
        self._root.collect_clients(clients, BayeuxChannel.split(ch))
        for cl in clients:
            cl.add_message(msg, senderId)
                
    def get_root_channel(self):
        '''Gets the root / BayeuxChannel instance.'''
        return self._root

    def build_connection(self, handler):
        '''
        Builds a BayeuxConnection instance to represent a single connection
        over a negotiated transport from a client.
        '''
        return self._connectionCls(handler, self)

    def new_client(self):
        '''
        Builds a new BayeuxSession instance to represent a continuing session 
        with a client.'''
        c = self._clientCls(self, exts=self._exts)
        self._clients[c.clientId] = c
        return c
    
    def is_client(self, cid):
        '''Gets if the given client ID is one for a known client.'''
        return self._clients.has_key(cid)
        
    def get_client(self, cid):
        '''Gets the client associated with the given client ID.'''
        return self._clients[cid]
        
    def delete_client(self, cid):
        '''Deletes the client assocaited with the given client ID.'''
        cl = self._clients[cid]
        del self._clients[cid]
        try:
            self.on_purging_client(cid, cl)
        except Exception:
            log.exception('purge delegate')
        return cl
        
    def purge_clients(self):
        '''
        Purges clients that have not performed any action within the 
        configured self.deadAfter interval in seconds.
        '''
        if not self._purgeGen:
            # build a new purge generator
            self._purgeGen = self._purge_clients()
        try:
            # iterate the generator
            self._purgeGen.next()
        except StopIteration:
            # purge complete, toss the generator
            self._purgeGen = None
        else:
            # purge incomplete, schedule for immediately continuation
            self._ioloop.add_callback(self.purge_clients)

    def _purge_clients(self, timeout=2, blockSize=100):
        # snapshot the clients in the dict
        cids = self._clients.keys()
        # get the current time
        now = time.time()
        for i, cid in enumerate(cids):
            cl = self._clients[cid]
            if cl.lastSeen is None:
                self.delete_client(cid)
                cl.destroy()
                continue
            dt = now - cl.lastSeen
            if dt > self.deadAfter:
                cl = self.delete_client(cid)
                cl.destroy()
            if i % blockSize == 0 and time.time() - now > timeout:
                # we're taking too long, yield
                yield
    
    def on_purging_client(self, cid, cl):
        '''
        Called after this manager stops tracking a client but before the 
        BayeuxSession instace for the client is destroyed (i.e., it's 
        final messages flushed, sockets closed, etc.) No expected return 
        value.
        '''
        pass
Exemplo n.º 6
0
 def add_channel(self, ch):
     root = self._manager.get_root_channel()
     root.add_client(self, BayeuxChannel.split(ch))
Exemplo n.º 7
0
 def delete_channel(self, ch):
     root = self._manager.get_root_channel()
     root.remove_client(self, BayeuxChannel.split(ch))
Exemplo n.º 8
0
 def add_channel(self, ch):
     root = self._manager.get_root_channel()
     root.add_client(self, BayeuxChannel.split(ch))
Exemplo n.º 9
0
 def delete_channel(self, ch):
     root = self._manager.get_root_channel()
     root.remove_client(self, BayeuxChannel.split(ch))
Exemplo n.º 10
0
class BayeuxManager(object):
    '''Base class for a Bayeux manager tracking known clients.'''
    def __init__(self,
                 purgeInterval=15,
                 deadAfter=60,
                 exts=[],
                 client_cls=BayeuxSession,
                 connection_cls=BayeuxConnection):
        self.deadAfter = deadAfter
        self.timeout = int(self.deadAfter / 2.0)
        self._clientCls = client_cls
        self._connectionCls = connection_cls
        self._root = BayeuxChannel('/')
        self._clients = {}
        self._flushable = set()
        self._ioloop = tornado.ioloop.IOLoop.instance()
        self._purgeGen = None
        if purgeInterval is None:
            self._timer = None
        else:
            self._timer = tornado.ioloop.PeriodicCallback(
                self.purge_clients, purgeInterval * 1000)
            self._timer.start()
        self._exts = exts
        self._willFlush = False

    def destroy(self):
        '''Destroyes the Bayeux manager and deletes its known clients'''
        # stop the purge timer
        if self._timer is not None:
            # do after in case we're in the callback to prevent re-registration
            # due to tornado bug
            self._ioloop.add_callback(self._timer.stop)
            # force the callback reference to None, because stop doesn't
            # remove the reference from the ioloop, else gc impeded until
            # next callback occurs, even though stopped
            self._timer.callback = None
            self._timer = None
            self._purgeGen = None
        # purge all clients
        cids = self._clients.keys()
        for cid in cids:
            cl = self.delete_client(cid)
            cl.destroy()
        self._clients = {}

    def flush(self):
        '''Flushes all outgoing client messages immediately.'''
        self._willFlush = False
        leftover = set()
        while self._flushable:
            cl = self._flushable.pop()
            if not cl.on_flush():
                leftover.add(cl)
        # don't reschedule flush now, next connection will do it
        self._flushable = leftover

    def should_flush(self, cl):
        '''
        Marks a client as having outgoing messages that should be flushed.
        '''
        self._flushable.add(cl)
        # schedule the flush if not one already pending
        if not self._willFlush:
            self._ioloop.add_callback(self.flush)
            self._willFlush = True

    def publish(self, msg):
        '''Publishes a message to all clients subscribed to its channel.'''
        # strip off client ID before sending out to other clients
        try:
            senderId = msg['clientId']
            del msg['clientId']
        except KeyError:
            senderId = None
        try:
            del msg['id']
        except KeyError:
            pass
        ch = msg.get('channel', None)

        # find all subscribed clients
        clients = set()
        self._root.collect_clients(clients, BayeuxChannel.split(ch))
        for cl in clients:
            cl.add_message(msg, senderId)

    def get_root_channel(self):
        '''Gets the root / BayeuxChannel instance.'''
        return self._root

    def build_connection(self, handler):
        '''
        Builds a BayeuxConnection instance to represent a single connection
        over a negotiated transport from a client.
        '''
        return self._connectionCls(handler, self)

    def new_client(self):
        '''
        Builds a new BayeuxSession instance to represent a continuing session 
        with a client.'''
        c = self._clientCls(self, exts=self._exts)
        self._clients[c.clientId] = c
        return c

    def is_client(self, cid):
        '''Gets if the given client ID is one for a known client.'''
        return self._clients.has_key(cid)

    def get_client(self, cid):
        '''Gets the client associated with the given client ID.'''
        return self._clients[cid]

    def delete_client(self, cid):
        '''Deletes the client assocaited with the given client ID.'''
        cl = self._clients[cid]
        del self._clients[cid]
        try:
            self.on_purging_client(cid, cl)
        except Exception:
            log.exception('purge delegate')
        return cl

    def purge_clients(self):
        '''
        Purges clients that have not performed any action within the 
        configured self.deadAfter interval in seconds.
        '''
        if not self._purgeGen:
            # build a new purge generator
            self._purgeGen = self._purge_clients()
        try:
            # iterate the generator
            self._purgeGen.next()
        except StopIteration:
            # purge complete, toss the generator
            self._purgeGen = None
        else:
            # purge incomplete, schedule for immediately continuation
            self._ioloop.add_callback(self.purge_clients)

    def _purge_clients(self, timeout=2, blockSize=100):
        # snapshot the clients in the dict
        cids = self._clients.keys()
        # get the current time
        now = time.time()
        for i, cid in enumerate(cids):
            cl = self._clients[cid]
            if cl.lastSeen is None:
                self.delete_client(cid)
                cl.destroy()
                continue
            dt = now - cl.lastSeen
            if dt > self.deadAfter:
                cl = self.delete_client(cid)
                cl.destroy()
            if i % blockSize == 0 and time.time() - now > timeout:
                # we're taking too long, yield
                yield

    def on_purging_client(self, cid, cl):
        '''
        Called after this manager stops tracking a client but before the 
        BayeuxSession instace for the client is destroyed (i.e., it's 
        final messages flushed, sockets closed, etc.) No expected return 
        value.
        '''
        pass