# HG changeset patch # User Goffi # Date 1306326281 -7200 # Node ID 2744dd31e8a50e3be24a291c4c9d4da2d6b92f4b # Parent a7ff1e6f1229214ef8f1e13f2424f8035b215ad4 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 diff -r a7ff1e6f1229 -r 2744dd31e8a5 libervia.tac --- 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)