summaryrefslogtreecommitdiffstats
path: root/src/lib/tlslite/SessionCache.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/tlslite/SessionCache.py')
-rwxr-xr-xsrc/lib/tlslite/SessionCache.py103
1 files changed, 103 insertions, 0 deletions
diff --git a/src/lib/tlslite/SessionCache.py b/src/lib/tlslite/SessionCache.py
new file mode 100755
index 000000000..34cf0b0ec
--- /dev/null
+++ b/src/lib/tlslite/SessionCache.py
@@ -0,0 +1,103 @@
+"""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()