Sign in

Google Cookbook - Google App Engine

Session handling with Memcache
Posted by phil.totheworld on Fri 24 Apr 2009 in Memcache API
User ratings:
I've been trying to teach myself Python by witting a basic Twitter client in Google AppEngine.

One of the things which I found lacking from the Webapp and Django frameworks was anything to manage sessions well. So, realising that the Memcache API has a timeout I figured that I could use that as the basis for session management.

I ended up creating 2 classes, a SessionManager class to create, retrieve and delete sessions ans a Session object which would act as a simple dictionary to store arbitrary session values in.

I would then be able to use the Session and SessionManager classes like this in my Controller class:
class Controller(object):
def __init__(self, request, response):
self.request = request
self.response = response
self.sessionManager = SessionManager(self.request, self.response)
self.session = self.sessionManager.current()
self.initialize()


The SessionManager constructor needs both Request and Response objects because it needs to read and write Cookies.

Here's the SessionManager class:
from google.appengine.api import memcache
import datetime
import random
class SessionManager(object):
def __init__(self, request, response, timeout=1200):
self.timeout = timeout
self.request = request
self.response = response
self.cookieName = 'SID'

def current(self):
cookievalue = self.request.cookies.get(self.cookieName)
if ((cookievalue is not None) and (memcache.get(cookievalue) is not None)):
return Session(cookievalue, self.timeout, False)
else:
return self._createSession()

def _createSession(self):
newId = self.createNewId()
memcache.set(key=newId, value=True, time=self.timeout,)
now = datetime.datetime.now()
inc = datetime.timedelta(seconds=self.timeout)
now += inc
self._setCookie(key=self.cookieName, value=newId, expires=now)

return Session(newId, self.timeout, True)

def destroySession(self):
for key in self.current().keys():
memcache.delete(key=key)

self._clearCookie(self.cookieName)

def createNewId(self):
newHash = str(hash(datetime.datetime.utcnow().strftime('%Y%m%d%H%M%S%f'))) + str(random.random())
while memcache.get(newHash) is not None:
newHash = self.CreateNewId()
return newHash

def _setCookie(self, key, value, expires, path='/'):
self.response.headers.add_header('Set-Cookie', key + '=' + value + ' path=' + path + '; expires ' + expires.strftime('%a, %d-%b-%Y %H:%M:00 %Z'))

def _clearCookie(self, key):
self._setCookie(key=key, value='', expires=datetime.datetime.now())


I'm creating a unique sessionId by concatenating the current date/time with a random number.

Here's the Session class:
class Session(object):
def __init__(self, id, timeout, isNew=False):
self.id = id
self.timeout = timeout
self.isNew = isNew
self._keysKey = 'SID-' + self.id

if self.isNew:
self.items = dict([('id', self.id)])
else:
self.items = memcache.get(key=self._keysKey)
self._update()

def get(self, key):
return self.__getitem__(key=key)

def set(self, key, value):
return self.__setitem__(key=key, value=value)

def __getitem__(self, key):
if self.hasKey(key=key):
val = self.items[key]
return val
else:
logging.debug('session.get(' + key + ') = None')
return None

def __setitem__(self, key, value):
self.items[key] = value
self._update()
return value

def hasKey(self, key):
hk = (key in self.items.keys())
return hk

def keys(self):
return self.items.keys()

def _update(self):
memcache.set(key=self._keysKey, value=self.items, time=self.timeout))


That's kind of it really. It's working well at the moment but I've not used it with very large numbers of concurrent users.
Attached files
SessionManager.pyViewDownload

Comments (0)

Sign in to add comment.