Wednesday, November 18, 2009

Twisted Web in 60 seconds: session basics

Welcome to the 15th installment of "Twisted Web in 60 seconds". As promised, I'll be covering sessions in this installment. Or, more accurately, I'll be covering a tiny bit of sessions. As this is the most complicated topic I've covered so far, I'm going to take a few installments to cover all the different aspects.



In this installment, you can expect to learn the very basics of the Twisted Web session API: how to get the session object for the current request and how to prematurely expire a session.



Before I get into the APIs, though, I should explain the big picture of sessions in Twisted Web. Sessions are represented by instances of Session. The Site creates a new instance of Session the first time an application asks for it for a particular session. Session instances are kept on the Site instance until they expire (due to inactivity or because they are explicitly expired). Each time after the first that a particular session's Session object is requested, it is retrieved from the Site.



With the conceptual underpinnings of the upcoming API in place, here comes the example. This will be a very simple rpy script which tells a user what their unique session identifier is and lets them prematurely expire it.



First, I'll import Resource so I can define a couple subclasses of it:



  from twisted.web.resource import Resource


Next I'll define the resource which tells the client what its session identifier is. This is done easily by first getting the session object using Request.getSession and then getting the session object's uid attribute.



  class ShowSession(Resource):
     def render_GET(self, request):
         return 'Your session id is: ' + request.getSession().uid


To let the client expire their own session before it times out, I'll define another resource which expires whatever session it is requested with. This is done using the Session.expire method.



  class ExpireSession(Resource):
     def render_GET(self, request):
         request.getSession().expire()
         return 'Your session has been expired.'


Finally, to make the example an rpy script, I'll make an instance of ShowSession and give it an instance of ExpireSession as a child using Resource.putChild (covered earlier).



  resource = ShowSession()
  resource.putChild("expire", ExpireSession())


And that is the complete example. You can fire this up and load the top page. You'll see a (rather opaque) session identifier that remains the same across reloads (at least until you flush the TWISTED_SESSION cookie from your browser or enough time passes). You can then visit the expire child and go back to the top page and see that you have a new session.



Here's the complete source for the example.



from twisted.web.resource import Resource

class ShowSession(Resource):
   def render_GET(self, request):
       return 'Your session id is: ' + request.getSession().uid

class ExpireSession(Resource):
   def render_GET(self, request):
       request.getSession().expire()
       return 'Your session has been expired.'

resource = ShowSession()
resource.putChild("expire", ExpireSession())


Next time I'll talk about how you can persist information in the session object.

9 comments:

  1. I haven't been following lately, but this series is AWESOME.

    ReplyDelete
  2. Thank you for the installments. :)

    I have some questions regarding sessions.

    How can I replace session cookie name? I guess I can't because it's hard-coded in Request.getSession(), or did I miss something?

    And how can I change generation of session id? Override Site._mkuid()?

    ReplyDelete
  3. I think you mostly figured out the answers to both your questions. :) One other thing I'll point out, though, is that the "_" prefix indicates something private to Twisted, so using or overriding it is a bad plan for future compatibility. You may want to file tickets in Twisted's tracker for each of these. Make sure you describe your use-cases. :)

    ReplyDelete
  4. I am really to stupid for mod_rewrite and some such. I wondered if it is possible and makes sense to use twisted as a common web-front-end for trac, nagios and other stuff.

    Will you cover the usage of twisted as a proxy ?

    Awesome series btw. Since years I have the feeling that twisted is very useful but never managed to wrap my head around it. This series helps a lot.

    ReplyDelete
  5. This is a great series JP. One general question I have about using Twisted for web development though... Twisted seems to play both sides of the web server/web framework split. It does the web server's job (actually binding to sockets and understanding HTTP/1.1) and the web framework's job (building responses to GET and POST requests). This is certainly handy, but what if in the future I want to use a dedicated web server like Apache or nginx? Is all my twisted.web code useless at this point?

    ReplyDelete
  6. Twisted Web does have some proxy functionality built in. It's a lot less featureful than almost any other proxy you'll find, but it can sometimes be a useful starting point for custom proxies. I'll cover that at some point.

    Thanks. :)

    ReplyDelete
  7. Hopefully not useless! At the very least, you can put your Twisted Web app behind Apache or nginx (mod_proxy or the like). Depending on your motivation for using Apache or nginx, this may or may not be a sensible thing to do.

    What there really is no ready-made way to do is actually put your Twisted Web code *into* Apache (using either mod_python or anything else). The Apache environment violates too many important assumptions Twisted Web makes about its environment for this to be generally doable. You may find solutions which work for particular bits of code. That's all beyond my experience, though.

    ReplyDelete
  8. Maybe I'm daft, but I haven't been able to figure out the right way to actually store info in the Session from googling for online docs. Eagerly awaiting the next entry!

    ReplyDelete
  9. How would you store stuff in a database instead of in memory? We have three web servers that need to be synched. Currently, we keep all our session data in the database so that a request made to any of the webservers maintains session state.

    ReplyDelete