Viewing file: SessionCache.py (3.39 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
"""Class for caching TLS sessions."""
import thread import time
class SessionCache: """This class is used by the server to cache TLS sessions.
Caching sessions allows the client to use TLS session resumption and avoid the expense of a full handshake. To use this class, simply pass a SessionCache instance into the server handshake function.
This class is thread-safe. """
#References to these instances #are also held by the caller, who may change the 'resumable' #flag, so the SessionCache must return the same instances #it was passed in.
def __init__(self, maxEntries=10000, maxAge=14400): """Create a new SessionCache.
@type maxEntries: int @param maxEntries: The maximum size of the cache. When this limit is reached, the oldest sessions will be deleted as necessary to make room for new ones. The default is 10000.
@type maxAge: int @param maxAge: The number of seconds before a session expires from the cache. The default is 14400 (i.e. 4 hours)."""
self.lock = thread.allocate_lock()
# Maps sessionIDs to sessions self.entriesDict = {}
#Circular list of (sessionID, timestamp) pairs self.entriesList = [(None,None)] * maxEntries
self.firstIndex = 0 self.lastIndex = 0 self.maxAge = maxAge
def __getitem__(self, sessionID): self.lock.acquire() try: self._purge() #Delete old items, so we're assured of a new one session = self.entriesDict[sessionID]
#When we add sessions they're resumable, but it's possible #for the session to be invalidated later on (if a fatal alert #is returned), so we have to check for resumability before #returning the session.
if session.valid(): return session else: raise KeyError() finally: self.lock.release()
def __setitem__(self, sessionID, session): self.lock.acquire() try: #Add the new element self.entriesDict[sessionID] = session self.entriesList[self.lastIndex] = (sessionID, time.time()) self.lastIndex = (self.lastIndex+1) % len(self.entriesList)
#If the cache is full, we delete the oldest element to make an #empty space if self.lastIndex == self.firstIndex: del(self.entriesDict[self.entriesList[self.firstIndex][0]]) self.firstIndex = (self.firstIndex+1) % len(self.entriesList) finally: self.lock.release()
#Delete expired items def _purge(self): currentTime = time.time()
#Search through the circular list, deleting expired elements until #we reach a non-expired element. Since elements in list are #ordered in time, we can break once we reach the first non-expired #element index = self.firstIndex while index != self.lastIndex: if currentTime - self.entriesList[index][1] > self.maxAge: del(self.entriesDict[self.entriesList[index][0]]) index = (index+1) % len(self.entriesList) else: break self.firstIndex = index
def _test(): import doctest, SessionCache return doctest.testmod(SessionCache)
if __name__ == "__main__": _test()
|