1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
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()
|