| 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 |