/gdata/tlslite/SessionCache.py

http://radioappz.googlecode.com/ · Python · 103 lines · 47 code · 18 blank · 38 comment · 11 complexity · 14790609ab341dd37903ffd11bd2bbd9 MD5 · raw file

  1. """Class for caching TLS sessions."""
  2. import thread
  3. import time
  4. class SessionCache:
  5. """This class is used by the server to cache TLS sessions.
  6. Caching sessions allows the client to use TLS session resumption
  7. and avoid the expense of a full handshake. To use this class,
  8. simply pass a SessionCache instance into the server handshake
  9. function.
  10. This class is thread-safe.
  11. """
  12. #References to these instances
  13. #are also held by the caller, who may change the 'resumable'
  14. #flag, so the SessionCache must return the same instances
  15. #it was passed in.
  16. def __init__(self, maxEntries=10000, maxAge=14400):
  17. """Create a new SessionCache.
  18. @type maxEntries: int
  19. @param maxEntries: The maximum size of the cache. When this
  20. limit is reached, the oldest sessions will be deleted as
  21. necessary to make room for new ones. The default is 10000.
  22. @type maxAge: int
  23. @param maxAge: The number of seconds before a session expires
  24. from the cache. The default is 14400 (i.e. 4 hours)."""
  25. self.lock = thread.allocate_lock()
  26. # Maps sessionIDs to sessions
  27. self.entriesDict = {}
  28. #Circular list of (sessionID, timestamp) pairs
  29. self.entriesList = [(None,None)] * maxEntries
  30. self.firstIndex = 0
  31. self.lastIndex = 0
  32. self.maxAge = maxAge
  33. def __getitem__(self, sessionID):
  34. self.lock.acquire()
  35. try:
  36. self._purge() #Delete old items, so we're assured of a new one
  37. session = self.entriesDict[sessionID]
  38. #When we add sessions they're resumable, but it's possible
  39. #for the session to be invalidated later on (if a fatal alert
  40. #is returned), so we have to check for resumability before
  41. #returning the session.
  42. if session.valid():
  43. return session
  44. else:
  45. raise KeyError()
  46. finally:
  47. self.lock.release()
  48. def __setitem__(self, sessionID, session):
  49. self.lock.acquire()
  50. try:
  51. #Add the new element
  52. self.entriesDict[sessionID] = session
  53. self.entriesList[self.lastIndex] = (sessionID, time.time())
  54. self.lastIndex = (self.lastIndex+1) % len(self.entriesList)
  55. #If the cache is full, we delete the oldest element to make an
  56. #empty space
  57. if self.lastIndex == self.firstIndex:
  58. del(self.entriesDict[self.entriesList[self.firstIndex][0]])
  59. self.firstIndex = (self.firstIndex+1) % len(self.entriesList)
  60. finally:
  61. self.lock.release()
  62. #Delete expired items
  63. def _purge(self):
  64. currentTime = time.time()
  65. #Search through the circular list, deleting expired elements until
  66. #we reach a non-expired element. Since elements in list are
  67. #ordered in time, we can break once we reach the first non-expired
  68. #element
  69. index = self.firstIndex
  70. while index != self.lastIndex:
  71. if currentTime - self.entriesList[index][1] > self.maxAge:
  72. del(self.entriesDict[self.entriesList[index][0]])
  73. index = (index+1) % len(self.entriesList)
  74. else:
  75. break
  76. self.firstIndex = index
  77. def _test():
  78. import doctest, SessionCache
  79. return doctest.testmod(SessionCache)
  80. if __name__ == "__main__":
  81. _test()