<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=us-ascii">
<META NAME="Generator" CONTENT="MS Exchange Server version 5.5.2653.12">
<TITLE>RES: [mod_python] The right way to handle sessions</TITLE>
</HEAD>
<BODY>
<P><FONT SIZE=2>Hi Amir.</FONT>
</P>
<P> <FONT SIZE=2>You can add your code snippet in the mod_python wiki cookbook (<A HREF="http://modpython.coedit.net/CookBook" TARGET="_blank">http://modpython.coedit.net/CookBook</A>). </FONT>
</P>
<P> <FONT SIZE=2>Regards,</FONT>
</P>
<P><FONT SIZE=2>Alexandre Denstone</FONT>
<BR><FONT SIZE=2>BankBoston - Retail Internet Systems</FONT>
<BR><FONT SIZE=2>* ajdenstone@bkb.com.br</FONT>
</P>
<BR>
<P><FONT SIZE=2>-----Mensagem original-----</FONT>
<BR><FONT SIZE=2>De: Amir Salihefendic [<A HREF="mailto:amix@amix.dk">mailto:amix@amix.dk</A>] </FONT>
<BR><FONT SIZE=2>Enviada em: sexta-feira, 13 de agosto de 2004 12:32</FONT>
<BR><FONT SIZE=2>Para: mod_python user mailing list</FONT>
<BR><FONT SIZE=2>Assunto: Re: [mod_python] The right way to handle sessions</FONT>
</P>
<BR>
<P><FONT SIZE=2>> def createSession(req):</FONT>
<BR><FONT SIZE=2>> new_session = Session.Session(req)</FONT>
<BR><FONT SIZE=2>> new_session.unlock()</FONT>
<BR><FONT SIZE=2>></FONT>
<BR><FONT SIZE=2>> #Check id</FONT>
<BR><FONT SIZE=2>> req.write(new_session.id())</FONT>
<BR><FONT SIZE=2>></FONT>
<BR><FONT SIZE=2>> return getSameSession(req, new_session.id())</FONT>
<BR><FONT SIZE=2>></FONT>
<BR><FONT SIZE=2>> def getSameSession(req, SID):</FONT>
<BR><FONT SIZE=2>> same_session = Session.Session(req, SID)</FONT>
<BR><FONT SIZE=2>></FONT>
<BR><FONT SIZE=2>> #Check id</FONT>
<BR><FONT SIZE=2>> req.write("\n%s\n" % same_session.id())</FONT>
<BR><FONT SIZE=2>></FONT>
<BR><FONT SIZE=2>> return "Done"</FONT>
</P>
<P><FONT SIZE=2>Argh forgot something very important (to save the damn session)!: def createSession(req):</FONT>
<BR><FONT SIZE=2> new_session = Session.Session(req)</FONT>
<BR><FONT SIZE=2> new_session.save()</FONT>
<BR><FONT SIZE=2> new_session.unlock()</FONT>
</P>
<P><FONT SIZE=2> #Check id</FONT>
<BR><FONT SIZE=2> req.write(new_session.id())</FONT>
</P>
<P><FONT SIZE=2> return getSameSession(req, new_session.id())</FONT>
</P>
<P><FONT SIZE=2>def getSameSession(req, SID):</FONT>
<BR><FONT SIZE=2> same_session = Session.Session(req, SID)</FONT>
<BR><FONT SIZE=2> same_session.load()</FONT>
</P>
<P><FONT SIZE=2> #Check id</FONT>
<BR><FONT SIZE=2> req.write("\n%s\n" % same_session.id())</FONT>
</P>
<P><FONT SIZE=2> return "Done"</FONT>
</P>
<P><FONT SIZE=2>*Tsk tsk*</FONT>
</P>
<P><FONT SIZE=2>Med venlig hilsen / Kind regards</FONT>
<BR><FONT SIZE=2>Amir Salihefendic</FONT>
<BR><FONT SIZE=2>-----</FONT>
<BR><FONT SIZE=2>What we do in life echoes in eternity</FONT>
<BR><FONT SIZE=2>Den 13/8-2004, kl. 13.30, skrev Amir Salihefendic:</FONT>
</P>
<P><FONT SIZE=2>> Thanks a lot for your time spent on the reply, I appreciate it.</FONT>
<BR><FONT SIZE=2>></FONT>
<BR><FONT SIZE=2>> "Under what circumstances do you call createSession() instead of</FONT>
<BR><FONT SIZE=2>> getSameSession()?"</FONT>
<BR><FONT SIZE=2>></FONT>
<BR><FONT SIZE=2>> Let's take another example:</FONT>
<BR><FONT SIZE=2>> I am making a blog-system where I need to have an admin section. I am</FONT>
<BR><FONT SIZE=2>> using the publisher handler.</FONT>
<BR><FONT SIZE=2>> Simple forms post username and password to admin.py</FONT>
<BR><FONT SIZE=2>> Admin.py has an index function, which has username and password as </FONT>
<BR><FONT SIZE=2>> input.</FONT>
<BR><FONT SIZE=2>> In the index function I check if the username and password match, if </FONT>
<BR><FONT SIZE=2>> they do I create an session and store username and password.</FONT>
<BR><FONT SIZE=2>> Now, I need to redirect to a new function viewPosts (it's also located </FONT>
<BR><FONT SIZE=2>> in the admin module).</FONT>
<BR><FONT SIZE=2>></FONT>
<BR><FONT SIZE=2>> In viewPosts I have to check if the right session is set - else</FONT>
<BR><FONT SIZE=2>> somebody could do admin.py/viewPosts and get access to the admin </FONT>
<BR><FONT SIZE=2>> section.</FONT>
<BR><FONT SIZE=2>></FONT>
<BR><FONT SIZE=2>> Of course, I could solve problem in many other ways:</FONT>
<BR><FONT SIZE=2>> - Send the SID to viewPosts</FONT>
<BR><FONT SIZE=2>> - Place everything in one function (i.e. place everything in</FONT>
<BR><FONT SIZE=2>> admin.py/index)</FONT>
<BR><FONT SIZE=2>> - "Append" the session to the req object</FONT>
<BR><FONT SIZE=2>> - Make viewPosts private (i.e. do _viewPosts) and then I can only </FONT>
<BR><FONT SIZE=2>> access it by going through admin.py/index</FONT>
<BR><FONT SIZE=2>> -etc.</FONT>
<BR><FONT SIZE=2>></FONT>
<BR><FONT SIZE=2>> Anyway, I am interested in knowing: What is the smartest way to</FONT>
<BR><FONT SIZE=2>> "share"/access same session across functions (i.e. when you are </FONT>
<BR><FONT SIZE=2>> internal "redirecting")? Example of what I mean by "redirecting" (not </FONT>
<BR><FONT SIZE=2>> working...):</FONT>
<BR><FONT SIZE=2>> def createSession(req):</FONT>
<BR><FONT SIZE=2>> new_session = Session.Session(req)</FONT>
<BR><FONT SIZE=2>> return getSameSession(req)</FONT>
<BR><FONT SIZE=2>></FONT>
<BR><FONT SIZE=2>> def getSameSession(req):</FONT>
<BR><FONT SIZE=2>> same_session = Session.Session(req)</FONT>
<BR><FONT SIZE=2>> return same_session.id()</FONT>
<BR><FONT SIZE=2>></FONT>
<BR><FONT SIZE=2>></FONT>
<BR><FONT SIZE=2>> My guess is to send the SID (working). This seems to be a very light</FONT>
<BR><FONT SIZE=2>> and easy approach:</FONT>
<BR><FONT SIZE=2>> def createSession(req):</FONT>
<BR><FONT SIZE=2>> new_session = Session.Session(req)</FONT>
<BR><FONT SIZE=2>> new_session.unlock()</FONT>
<BR><FONT SIZE=2>></FONT>
<BR><FONT SIZE=2>> #Check id</FONT>
<BR><FONT SIZE=2>> req.write(new_session.id())</FONT>
<BR><FONT SIZE=2>></FONT>
<BR><FONT SIZE=2>> return getSameSession(req, new_session.id())</FONT>
<BR><FONT SIZE=2>></FONT>
<BR><FONT SIZE=2>> def getSameSession(req, SID):</FONT>
<BR><FONT SIZE=2>> same_session = Session.Session(req, SID)</FONT>
<BR><FONT SIZE=2>></FONT>
<BR><FONT SIZE=2>> #Check id</FONT>
<BR><FONT SIZE=2>> req.write("\n%s\n" % same_session.id())</FONT>
<BR><FONT SIZE=2>></FONT>
<BR><FONT SIZE=2>> return "Done"</FONT>
<BR><FONT SIZE=2>></FONT>
<BR><FONT SIZE=2>> (This is a solution I use now.) Of course this is very simplified</FONT>
<BR><FONT SIZE=2>> example...</FONT>
<BR><FONT SIZE=2>></FONT>
<BR><FONT SIZE=2>> Med venlig hilsen / Kind regards</FONT>
<BR><FONT SIZE=2>> Amir Salihefendic</FONT>
<BR><FONT SIZE=2>> -----</FONT>
<BR><FONT SIZE=2>> What we do in life echoes in eternity</FONT>
<BR><FONT SIZE=2>> Den 13/8-2004, kl. 1.17, skrev Byron Ellacott:</FONT>
<BR><FONT SIZE=2>></FONT>
<BR><FONT SIZE=2>>> Amir Salihefendic wrote:</FONT>
<BR><FONT SIZE=2>>>> I welcome myself to this list ;-)</FONT>
<BR><FONT SIZE=2>>></FONT>
<BR><FONT SIZE=2>>> Welcome!</FONT>
<BR><FONT SIZE=2>>></FONT>
<BR><FONT SIZE=2>>> [working example]</FONT>
<BR><FONT SIZE=2>>>> def createSession(req):</FONT>
<BR><FONT SIZE=2>>>> def getSameSession(req):</FONT>
<BR><FONT SIZE=2>>></FONT>
<BR><FONT SIZE=2>>> The important thing to note here is that the code is identical in</FONT>
<BR><FONT SIZE=2>>> both calls. That is, if you call Session.Session(req) with no </FONT>
<BR><FONT SIZE=2>>> session ID in cookies, or the existing session ID has no matching </FONT>
<BR><FONT SIZE=2>>> session stored on disk, a new session ID is generated.</FONT>
<BR><FONT SIZE=2>>></FONT>
<BR><FONT SIZE=2>>> But, the session is /not/ written to disk.</FONT>
<BR><FONT SIZE=2>>></FONT>
<BR><FONT SIZE=2>>>> def createSession(req):</FONT>
<BR><FONT SIZE=2>>>> return getSameSession(req)</FONT>
<BR><FONT SIZE=2>>>> def getSameSession(req):</FONT>
<BR><FONT SIZE=2>>>> return same_session.id()</FONT>
<BR><FONT SIZE=2>>></FONT>
<BR><FONT SIZE=2>>> So when you change to calling getSameSession() from createSession()</FONT>
<BR><FONT SIZE=2>>> you are in fact creating /two/ sessions. My guess would be that your </FONT>
<BR><FONT SIZE=2>>> apparent infinite loop is in fact a deadlock, caused by the fact that </FONT>
<BR><FONT SIZE=2>>> your second created session will be reusing the session ID created by </FONT>
<BR><FONT SIZE=2>>> the first one, and then attempting to establish a global lock on that </FONT>
<BR><FONT SIZE=2>>> session ID. Bad.</FONT>
<BR><FONT SIZE=2>>></FONT>
<BR><FONT SIZE=2>>>> This does not work - it makes an infinitive loop..! Now to fix this</FONT>
<BR><FONT SIZE=2>>>> you need to unlock the session in createSession (.. I have no clue </FONT>
<BR><FONT SIZE=2>>>> why you have to do this..?). And then you need to restart apache... </FONT>
<BR><FONT SIZE=2>>>> i.e.:</FONT>
<BR><FONT SIZE=2>>></FONT>
<BR><FONT SIZE=2>>> This bears up my analysis well. The locking done by the Session</FONT>
<BR><FONT SIZE=2>>> module is an Apache-wide lock on the session ID. So, when </FONT>
<BR><FONT SIZE=2>>> createSession() first creates a session, it locks that sid. Then, it </FONT>
<BR><FONT SIZE=2>>> calls getSameSession() which attempts to lock the same sid. The lock </FONT>
<BR><FONT SIZE=2>>> call blocks here, waiting for the first lock to be released, but that </FONT>
<BR><FONT SIZE=2>>> release will never come. If you cause the first created session to </FONT>
<BR><FONT SIZE=2>>> unlock itself, you will not create a deadlock, but you will need to </FONT>
<BR><FONT SIZE=2>>> restart apache to break an existing deadlock.</FONT>
<BR><FONT SIZE=2>>></FONT>
<BR><FONT SIZE=2>>>> But this does not work quite well.. If you delete the Session: And </FONT>
<BR><FONT SIZE=2>>>> then you do this: First time the id's aren't same - after that they </FONT>
<BR><FONT SIZE=2>>>> are the same.</FONT>
<BR><FONT SIZE=2>>></FONT>
<BR><FONT SIZE=2>>> When a session has been deleted, it is marked as invalid and calling</FONT>
<BR><FONT SIZE=2>>> .load() on that sid will fail. .__init__() will create a new sid if </FONT>
<BR><FONT SIZE=2>>> .load() fails for an existing one, so I suspect you are seeing this </FONT>
<BR><FONT SIZE=2>>> behaviour.</FONT>
<BR><FONT SIZE=2>>></FONT>
<BR><FONT SIZE=2>>> Overall, it seems you are making the session management process far</FONT>
<BR><FONT SIZE=2>>> more complicated than it needs to be. Under what circumstances do </FONT>
<BR><FONT SIZE=2>>> you call createSession() instead of getSameSession() ?</FONT>
<BR><FONT SIZE=2>>></FONT>
<BR><FONT SIZE=2>>> I am successfully using the Session module to handle an HTML forms</FONT>
<BR><FONT SIZE=2>>> based login system:</FONT>
<BR><FONT SIZE=2>>></FONT>
<BR><FONT SIZE=2>>> session = Session.Session(req)</FONT>
<BR><FONT SIZE=2>>> if NeedAuth and not session.has_key('username'):</FONT>
<BR><FONT SIZE=2>>> redirect(AuthPage)</FONT>
<BR><FONT SIZE=2>>> ServePage(req)</FONT>
<BR><FONT SIZE=2>>> session.save()</FONT>
<BR><FONT SIZE=2>>> return apache.OK</FONT>
<BR><FONT SIZE=2>>></FONT>
<BR><FONT SIZE=2>>> This is a simplification, since I use an extensible application</FONT>
<BR><FONT SIZE=2>>> object that allows you to change how sessions are generated and how </FONT>
<BR><FONT SIZE=2>>> users are authenticated, but it boils down to the above. I create a </FONT>
<BR><FONT SIZE=2>>> new session or get an existing session. I do not care which. If the </FONT>
<BR><FONT SIZE=2>>> page being requested needs auth, but the session doesn't contain a </FONT>
<BR><FONT SIZE=2>>> username key, I redirect[1] to the login page, which stores the </FONT>
<BR><FONT SIZE=2>>> username value into the session if the user presents the right </FONT>
<BR><FONT SIZE=2>>> credentials. Otherwise, I serve the requested page, then save the </FONT>
<BR><FONT SIZE=2>>> session, and Bob's a builder.</FONT>
<BR><FONT SIZE=2>>></FONT>
<BR><FONT SIZE=2>>> Note that I cannot distinguish between an expired session and no</FONT>
<BR><FONT SIZE=2>>> session at all, but this is a limitation of the Session module. I </FONT>
<BR><FONT SIZE=2>>> can distinguish between a new session and an existing session by </FONT>
<BR><FONT SIZE=2>>> calling session.is_new(), but that distinction turns out not to be </FONT>
<BR><FONT SIZE=2>>> very important.</FONT>
<BR><FONT SIZE=2>>></FONT>
<BR><FONT SIZE=2>>> At some point, I will need to extract the sid from the cookie set</FONT>
<BR><FONT SIZE=2>>> myself, to be able to distinguish between a timed out session, an </FONT>
<BR><FONT SIZE=2>>> invalidated session, a missing session and a brand new session. (Or </FONT>
<BR><FONT SIZE=2>>> else I'll have to patch the Session module, which I'm not real keen </FONT>
<BR><FONT SIZE=2>>> to do.)</FONT>
<BR><FONT SIZE=2>>></FONT>
<BR><FONT SIZE=2>>> --</FONT>
<BR><FONT SIZE=2>>> bje</FONT>
<BR><FONT SIZE=2>>></FONT>
<BR><FONT SIZE=2>>> [1] The 'redirects' I do are simply changing what view my code</FONT>
<BR><FONT SIZE=2>>> believes it is processing, so execution continues down the line, and </FONT>
<BR><FONT SIZE=2>>> session.save() is called in due order. If you're using an Apache </FONT>
<BR><FONT SIZE=2>>> internal redirect or a 302/303 redirect, you should ensure new </FONT>
<BR><FONT SIZE=2>>> sessions are saved as soon as created with "if session.is_new(): </FONT>
<BR><FONT SIZE=2>>> session.save()".</FONT>
<BR><FONT SIZE=2>>> _______________________________________________</FONT>
<BR><FONT SIZE=2>>> Mod_python mailing list</FONT>
<BR><FONT SIZE=2>>> Mod_python@modpython.org</FONT>
<BR><FONT SIZE=2>>> <A HREF="http://mailman.modpython.org/mailman/listinfo/mod_python" TARGET="_blank">http://mailman.modpython.org/mailman/listinfo/mod_python</A></FONT>
<BR><FONT SIZE=2>>></FONT>
<BR><FONT SIZE=2>></FONT>
<BR><FONT SIZE=2>> _______________________________________________</FONT>
<BR><FONT SIZE=2>> Mod_python mailing list</FONT>
<BR><FONT SIZE=2>> Mod_python@modpython.org </FONT>
<BR><FONT SIZE=2>> <A HREF="http://mailman.modpython.org/mailman/listinfo/mod_python" TARGET="_blank">http://mailman.modpython.org/mailman/listinfo/mod_python</A></FONT>
<BR><FONT SIZE=2>></FONT>
</P>
<P><FONT SIZE=2>_______________________________________________</FONT>
<BR><FONT SIZE=2>Mod_python mailing list</FONT>
<BR><FONT SIZE=2>Mod_python@modpython.org <A HREF="http://mailman.modpython.org/mailman/listinfo/mod_python" TARGET="_blank">http://mailman.modpython.org/mailman/listinfo/mod_python</A></FONT>
</P>
<BR>
<P><FONT SIZE=1>Esta mensagem, incluindo seus anexos, pode conter informação confidencial e/ou privilegiada. Se você recebeu este e-mail por engano, não utilize, copie ou divulgue as informações nele contidas. E, por favor, avise imediatamente o remetente, respondendo ao e-mail, e em seguida apague-o. Este e-mail possui conteúdo informativo e não transacional. Caso necessite de atendimento imediato, recomendamos utilizar um dos canais disponíveis: </FONT><A HREF="http://www.bankboston.com.br"><U><FONT SIZE=1>Internet Banking</FONT></U></A><FONT SIZE=1> , </FONT><A HREF="http://www.bankboston.com.br/bpt"><U><FONT SIZE=1>BankBoston por telefone</FONT></U></A><FONT SIZE=1> ou agência/representante de atendimento de sua conveniência. Agradecemos sua colaboração.</FONT></P>
<P><FONT SIZE=1>This message, including its attachments, may contain confidential and/or privileged information. If you received this email by mistake, do not use, copy or disseminate any information herein contained. Please notify us immediately by replying to the sender and then delete it. This email is for information purposes only, not for transactions. In case you need immediate assistance, please use one of the following channels: </FONT><A HREF="http://www.bankboston.com.br"><U><FONT SIZE=1>Internet Banking</FONT></U></A><FONT SIZE=1> , </FONT><A HREF="http://www.bankboston.com.br/bpt"><U><FONT SIZE=1>BankBoston by phone</FONT></U></A><FONT SIZE=1> or branch/relationship manager at your convenience. Thank you for your cooperation.</FONT></P>
</BODY>
</HTML>