faset over fra Z3950 til google books
This commit is contained in:
103
python/gdata/tlslite/SessionCache.py
Normal file
103
python/gdata/tlslite/SessionCache.py
Normal file
@@ -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()
|
Reference in New Issue
Block a user