changeset 44:2744dd31e8a5

server side: Session management refactoring - Session is now managed in a more twisted-like way - Session time out managed - Session can now be locked, preventing for expiration - Session is locked when the getSignal request is active, preventing from expiration when the user has the page open but do nothing
author Goffi <goffi@goffi.org>
date Wed, 25 May 2011 14:24:41 +0200
parents a7ff1e6f1229
children 7f106052326f
files libervia.tac
diffstat 1 files changed, 76 insertions(+), 40 deletions(-) [+]
line wrap: on
line diff
--- a/libervia.tac	Wed May 25 14:21:48 2011 +0200
+++ b/libervia.tac	Wed May 25 14:24:41 2011 +0200
@@ -28,6 +28,7 @@
 from twisted.web import error as weberror
 from twisted.web.static import File
 from twisted.web.resource import Resource
+from twisted.python.components import registerAdapter
 from twisted.words.protocols.jabber.jid import JID
 from txjsonrpc.web import jsonrpc
 from txjsonrpc import jsonrpclib
@@ -35,12 +36,46 @@
 import re
 import glob
 import os.path
+import sys
 from server_side.blog import MicroBlog
+from zope.interface import Interface, Attribute, implements
 
-TIMEOUT = 120 #Session's time out, after that the user will be disconnected
+TIMEOUT = 10 #Session's time out, after that the user will be disconnected
 LIBERVIA_DIR = "output/"
 CARDS_DIR = "cards/"
 
+class ISATSession(Interface):
+    profile = Attribute("Sat profile")
+    jid = Attribute("JID associated with the profile")
+
+class SATSession(object):
+    implements(ISATSession)
+    def __init__(self, session):
+        self.profile = None
+        self.jid = None
+
+class LiberviaSession(server.Session):
+    sessionTimeout = TIMEOUT
+
+    def __init__(self, *args, **kwargs):
+        self.__lock = False
+        server.Session.__init__(self, *args, **kwargs)
+
+    def lock(self):
+        """Prevent session from expiring"""
+        self.__lock = True
+        self._expireCall.reset(sys.maxint)
+
+    def unlock(self):
+        """Allow session to expire again, and touch it"""
+        self.__lock = False
+        self.touch()
+
+    def touch(self):
+        if not self.__lock:
+            server.Session.touch(self)
+
+
 class MethodHandler(jsonrpc.JSONRPC):
 
     def __init__(self, sat_host):
@@ -49,9 +84,8 @@
 
     def render(self, request):
         self.session = request.getSession()
-        try:
-            profile = self.session.sat_profile
-        except AttributeError:
+        profile = ISATSession(self.session).profile
+        if not profile:
             #user is not identified, we return a jsonrpc fault
             parsed = jsonrpclib.loads(request.content.read())
             fault = jsonrpclib.Fault(0, "Not allowed") #FIXME: define some standard error codes for libervia
@@ -60,30 +94,31 @@
 
     def jsonrpc_getProfileJid(self):
         """Return the jid of the profile"""
-        profile = self.session.sat_profile
-        self.session.sat_jid = self.sat_host.bridge.getParamA("JabberID", "Connection", profile_key=profile)
-        return self.session.sat_jid
+        sat_session = ISATSession(self.session)
+        profile = sat_session.profile
+        sat_session.sat_jid = JID(self.sat_host.bridge.getParamA("JabberID", "Connection", profile_key=profile))
+        return sat_session.sat_jid.full()
         
     def jsonrpc_getContacts(self):
         """Return all passed args."""
-        profile = self.session.sat_profile
+        profile = ISATSession(self.session).profile
         return self.sat_host.bridge.getContacts(profile)
 
     def jsonrpc_setStatus(self, status):
         """Change the status"""
-        profile = self.session.sat_profile
+        profile = ISATSession(self.session).profile
         print "new status received:", status
         self.sat_host.bridge.setPresence('', '', 0, {'':status}, profile)
 
     
     def jsonrpc_sendMessage(self, to_jid, msg, subject, type):
         """send message"""
-        profile = self.session.sat_profile
+        profile = ISATSession(self.session).profile
         return self.sat_host.bridge.sendMessage(to_jid, msg, subject, type, profile)
 
     def jsonrpc_sendMblog(self, raw_text):
         """Parse raw_text of the microblog box, and send message consequently"""
-        profile = self.session.sat_profile
+        profile = ISATSession(self.session).profile
         match = re.match(r'@(.+?): *(.*$)', raw_text)
         if match:
             recip = match.group(1)
@@ -97,31 +132,31 @@
 
     def jsonrpc_getPresenceStatus(self):
         """Get Presence information for connected contacts"""
-        profile = self.session.sat_profile
+        profile = ISATSession(self.session).profile
         return self.sat_host.bridge.getPresenceStatus(profile) 
 
     def jsonrpc_getHistory(self, from_jid, to_jid, size):
         """Return history for the from_jid/to_jid couple"""
         #FIXME: this method should definitely be asynchrone, need to fix it !!!
-        profile = self.session.sat_profile
-        try:
-            _jid = JID(self.session.sat_jid)
-        except:
+        sat_session = ISATSession(self.session)
+        profile = sat_session.profile
+        sat_jid = sat_session.jid
+        if not sat_jid:
             error("No jid saved for this profile")
             return {}
-        if JID(from_jid).userhost() != _jid.userhost() and JID(to_jid) != _jid.userhost():
+        if JID(from_jid).userhost() != sat_jid.userhost() and JID(to_jid).userhost() != sat_jid.userhost():
             error("Trying to get history from a different jid, maybe a hack attempt ?")
             return {}
         return self.sat_host.bridge.getHistory(from_jid, to_jid, size)
 
     def jsonrpc_getRoomJoined(self):
         """Return list of room already joined by user"""
-        profile = self.session.sat_profile
+        profile = ISATSession(self.session).profile
         return self.sat_host.bridge.getRoomJoined(profile) 
 
     def jsonrpc_launchTarotGame(self, other_players):
         """Create a room, invite the other players and start a Tarot game"""
-        profile = self.session.sat_profile
+        profile = ISATSession(self.session).profile
         self.sat_host.bridge.tarotGameLaunch(other_players, profile)
 
     def jsonrpc_getTarotCardsPaths(self):
@@ -130,17 +165,17 @@
 
     def jsonrpc_tarotGameReady(self, player, referee):
         """Tell to the server that we are ready to start the game"""
-        profile = self.session.sat_profile
+        profile = ISATSession(self.session).profile
         self.sat_host.bridge.tarotGameReady(player, referee)
 
     def jsonrpc_tarotGameContratChoosed(self, player_nick, referee, contrat):
         """Tell to the server that we are ready to start the game"""
-        profile = self.session.sat_profile
+        profile = ISATSession(self.session).profile
         self.sat_host.bridge.tarotGameContratChoosed(player_nick, referee, contrat, profile)
     
     def jsonrpc_tarotGamePlayCards(self, player_nick, referee, cards):
         """Tell to the server that we are ready to start the game"""
-        profile = self.session.sat_profile
+        profile = ISATSession(self.session).profile
         self.sat_host.bridge.tarotGamePlayCards(player_nick, referee, cards, profile)
 
 class Register(jsonrpc.JSONRPC):
@@ -162,7 +197,6 @@
 
     def _fillMblogNodes(self, result, session):
         """Fill the microblog nodes association for this session"""
-        print "Filling session for %s with %s" % (session.sat_profile, result)
         session.sat_mblog_nodes = dict(result)
 
     def render(self, request):
@@ -178,9 +212,8 @@
         parsed = jsonrpclib.loads(request.content.read())
         if parsed.get("method")!="isRegistered":
             #if we don't call login or isRegistered, we need to be identified
-            try:
-                profile = _session.sat_profile
-            except AttributeError:
+            profile = ISATSession(_session).profile
+            if not profile:
                 #user is not identified, we return a jsonrpc fault
                 fault = jsonrpclib.Fault(0, "Not allowed") #FIXME: define some standard error codes for libervia
                 return jsonrpc.JSONRPC._cbRender(self, fault, request, parsed.get('id'), parsed.get('jsonrpc'))
@@ -235,7 +268,10 @@
         and return "LOGGED" to the requester"""
         self.__cleanWaiting(profile)
         _session = request.getSession()
-        _session.sat_profile = profile
+        sat_session = ISATSession(_session)
+        if sat_session.profile:
+            error (_('/!\\ Session has already a profile, this should NEVER happen !'))
+        sat_session.profile = profile
         self.sat_host.prof_connected.add(profile)
         d = defer.Deferred()
         self.sat_host.bridge.getMblogNodes(profile, d.callback, d.errback)
@@ -253,12 +289,12 @@
 
     def jsonrpc_isConnected(self):
         _session = self.request.getSession()
-        profile = _session.sat_profile
+        profile = ISATSession(_session).profile
         return self.sat_host.bridge.isConnected(profile)
     
     def jsonrpc_connect(self):
         _session = self.request.getSession()
-        profile = _session.sat_profile
+        profile = ISATSession(_session).profile
         if self.profiles_waiting.has_key(profile):
             raise jsonrpclib.Fault('1','Already waiting') #FIXME: define some standard error codes for libervia
         self.profiles_waiting[profile] = self.request
@@ -268,11 +304,8 @@
     def jsonrpc_isRegistered(self):
         """Tell if the user is already registered"""
         _session = self.request.getSession()
-        try:
-            profile = _session.sat_profile
-        except AttributeError:
-            return False
-        return True
+        profile = ISATSession(_session).profile
+        return bool(profile)
        
 class SignalHandler(jsonrpc.JSONRPC):
     
@@ -290,14 +323,18 @@
         """Keep the connection alive until a signal is received, then send it
         @return: (signal, *signal_args)"""
         _session = self.request.getSession()
-        profile = _session.sat_profile
+        profile = ISATSession(_session).profile
         if profile in self.queue: #if we have signals to send in queue
             if self.queue[profile]:
                 return self.queue[profile].pop(0)
             else:
                 #the queue is empty, we delete the profile from queue
                 del self.queue[profile]
+        _session.lock() #we don't want the session to expire as long as this connection is active
+        def unlock(ignore):
+            _session.unlock()
         self.signalDeferred[profile] = defer.Deferred()
+        self.request.notifyFinish().addBoth(unlock)
         return self.signalDeferred[profile]
     
     def getGenericCb(self, function_name):
@@ -338,9 +375,8 @@
         """
         _session = request.getSession()
         parsed = jsonrpclib.loads(request.content.read())
-        try:
-            profile = _session.sat_profile
-        except AttributeError:
+        profile = ISATSession(_session).profile
+        if not profile:
             #user is not identified, we return a jsonrpc fault
             fault = jsonrpclib.Fault(0, "Not allowed") #FIXME: define some standard error codes for libervia
             return jsonrpc.JSONRPC._cbRender(self, fault, request, parsed.get('id'), parsed.get('jsonrpc'))
@@ -362,7 +398,6 @@
             self.bridge=DBusBridgeFrontend()
         except BridgeExceptionNoService:
             print(u"Can't connect to SàT backend, are you sure it's launched ?")
-            import sys
             sys.exit(1)
         self.bridge.register("connected", self.signal_handler.connected)
         self.bridge.register("connectionError", self.signal_handler.connectionError)
@@ -375,6 +410,7 @@
         root.putChild('blog', MicroBlog(self))
         root.putChild('css', File("server_css/"))
         self.site = server.Site(root)
+        self.site.sessionFactory = LiberviaSession
 
     def startService(self):
         reactor.listenTCP(8080, self.site)
@@ -386,7 +422,7 @@
         reactor.stop()
 
 
-
+registerAdapter(SATSession, server.Session, ISATSession)
 application = service.Application('Libervia')
 service = Libervia()
 service.setServiceParent(application)