OLD | NEW |
| 1 # Authors: |
| 2 # Trevor Perrin |
| 3 # Martin von Loewis - python 3 port |
| 4 # |
| 5 # See the LICENSE file for legal information regarding use of this file. |
| 6 |
1 """Class for caching TLS sessions.""" | 7 """Class for caching TLS sessions.""" |
2 | 8 |
3 import thread | 9 import threading |
4 import time | 10 import time |
5 | 11 |
6 class SessionCache: | 12 class SessionCache(object): |
7 """This class is used by the server to cache TLS sessions. | 13 """This class is used by the server to cache TLS sessions. |
8 | 14 |
9 Caching sessions allows the client to use TLS session resumption | 15 Caching sessions allows the client to use TLS session resumption |
10 and avoid the expense of a full handshake. To use this class, | 16 and avoid the expense of a full handshake. To use this class, |
11 simply pass a SessionCache instance into the server handshake | 17 simply pass a SessionCache instance into the server handshake |
12 function. | 18 function. |
13 | 19 |
14 This class is thread-safe. | 20 This class is thread-safe. |
15 """ | 21 """ |
16 | 22 |
17 #References to these instances | 23 #References to these instances |
18 #are also held by the caller, who may change the 'resumable' | 24 #are also held by the caller, who may change the 'resumable' |
19 #flag, so the SessionCache must return the same instances | 25 #flag, so the SessionCache must return the same instances |
20 #it was passed in. | 26 #it was passed in. |
21 | 27 |
22 def __init__(self, maxEntries=10000, maxAge=14400): | 28 def __init__(self, maxEntries=10000, maxAge=14400): |
23 """Create a new SessionCache. | 29 """Create a new SessionCache. |
24 | 30 |
25 @type maxEntries: int | 31 @type maxEntries: int |
26 @param maxEntries: The maximum size of the cache. When this | 32 @param maxEntries: The maximum size of the cache. When this |
27 limit is reached, the oldest sessions will be deleted as | 33 limit is reached, the oldest sessions will be deleted as |
28 necessary to make room for new ones. The default is 10000. | 34 necessary to make room for new ones. The default is 10000. |
29 | 35 |
30 @type maxAge: int | 36 @type maxAge: int |
31 @param maxAge: The number of seconds before a session expires | 37 @param maxAge: The number of seconds before a session expires |
32 from the cache. The default is 14400 (i.e. 4 hours).""" | 38 from the cache. The default is 14400 (i.e. 4 hours).""" |
33 | 39 |
34 self.lock = thread.allocate_lock() | 40 self.lock = threading.Lock() |
35 | 41 |
36 # Maps sessionIDs to sessions | 42 # Maps sessionIDs to sessions |
37 self.entriesDict = {} | 43 self.entriesDict = {} |
38 | 44 |
39 #Circular list of (sessionID, timestamp) pairs | 45 #Circular list of (sessionID, timestamp) pairs |
40 self.entriesList = [(None,None)] * maxEntries | 46 self.entriesList = [(None,None)] * maxEntries |
41 | 47 |
42 self.firstIndex = 0 | 48 self.firstIndex = 0 |
43 self.lastIndex = 0 | 49 self.lastIndex = 0 |
44 self.maxAge = maxAge | 50 self.maxAge = maxAge |
45 | 51 |
46 def __getitem__(self, sessionID): | 52 def __getitem__(self, sessionID): |
47 self.lock.acquire() | 53 self.lock.acquire() |
48 try: | 54 try: |
49 self._purge() #Delete old items, so we're assured of a new one | 55 self._purge() #Delete old items, so we're assured of a new one |
50 session = self.entriesDict[sessionID] | 56 session = self.entriesDict[bytes(sessionID)] |
51 | 57 |
52 #When we add sessions they're resumable, but it's possible | 58 #When we add sessions they're resumable, but it's possible |
53 #for the session to be invalidated later on (if a fatal alert | 59 #for the session to be invalidated later on (if a fatal alert |
54 #is returned), so we have to check for resumability before | 60 #is returned), so we have to check for resumability before |
55 #returning the session. | 61 #returning the session. |
56 | 62 |
57 if session.valid(): | 63 if session.valid(): |
58 return session | 64 return session |
59 else: | 65 else: |
60 raise KeyError() | 66 raise KeyError() |
61 finally: | 67 finally: |
62 self.lock.release() | 68 self.lock.release() |
63 | 69 |
64 | 70 |
65 def __setitem__(self, sessionID, session): | 71 def __setitem__(self, sessionID, session): |
66 self.lock.acquire() | 72 self.lock.acquire() |
67 try: | 73 try: |
68 #Add the new element | 74 #Add the new element |
69 self.entriesDict[sessionID] = session | 75 self.entriesDict[bytes(sessionID)] = session |
70 self.entriesList[self.lastIndex] = (sessionID, time.time()) | 76 self.entriesList[self.lastIndex] = (sessionID, time.time()) |
71 self.lastIndex = (self.lastIndex+1) % len(self.entriesList) | 77 self.lastIndex = (self.lastIndex+1) % len(self.entriesList) |
72 | 78 |
73 #If the cache is full, we delete the oldest element to make an | 79 #If the cache is full, we delete the oldest element to make an |
74 #empty space | 80 #empty space |
75 if self.lastIndex == self.firstIndex: | 81 if self.lastIndex == self.firstIndex: |
76 del(self.entriesDict[self.entriesList[self.firstIndex][0]]) | 82 del(self.entriesDict[self.entriesList[self.firstIndex][0]]) |
77 self.firstIndex = (self.firstIndex+1) % len(self.entriesList) | 83 self.firstIndex = (self.firstIndex+1) % len(self.entriesList) |
78 finally: | 84 finally: |
79 self.lock.release() | 85 self.lock.release() |
(...skipping 14 matching lines...) Expand all Loading... |
94 else: | 100 else: |
95 break | 101 break |
96 self.firstIndex = index | 102 self.firstIndex = index |
97 | 103 |
98 def _test(): | 104 def _test(): |
99 import doctest, SessionCache | 105 import doctest, SessionCache |
100 return doctest.testmod(SessionCache) | 106 return doctest.testmod(SessionCache) |
101 | 107 |
102 if __name__ == "__main__": | 108 if __name__ == "__main__": |
103 _test() | 109 _test() |
OLD | NEW |