1313# limitations under the License.
1414
1515from __future__ import absolute_import # to enable import io from stdlib
16- from collections import defaultdict , deque
16+ from collections import defaultdict , deque , OrderedDict , namedtuple
1717import errno
1818from functools import wraps , partial , total_ordering
1919from heapq import heappush , heappop
@@ -128,14 +128,18 @@ def decompress(byts):
128128frame_header_v3 = struct .Struct ('>BhBi' )
129129
130130
131+ # Named tuple for TLS session cache entries
132+ _SessionCacheEntry = namedtuple ('_SessionCacheEntry' , ['session' , 'timestamp' ])
133+
134+
131135class TLSSessionCache :
132136 """
133137 Thread-safe cache for TLS sessions to enable session resumption.
134138
135139 This cache stores TLS sessions per endpoint (host:port) to allow
136140 quick TLS renegotiation when reconnecting to the same server.
137141 Sessions are automatically expired after a TTL and the cache has
138- a maximum size with LRU eviction.
142+ a maximum size with LRU eviction using OrderedDict .
139143 """
140144
141145 def __init__ (self , max_size = 100 , ttl = 3600 ):
@@ -146,7 +150,7 @@ def __init__(self, max_size=100, ttl=3600):
146150 max_size: Maximum number of sessions to cache (default: 100)
147151 ttl: Time-to-live for cached sessions in seconds (default: 3600)
148152 """
149- self ._sessions = {} # {endpoint_key: (session, timestamp, access_time)}
153+ self ._sessions = OrderedDict () # OrderedDict for O(1) LRU eviction
150154 self ._lock = RLock ()
151155 self ._max_size = max_size
152156 self ._ttl = ttl
@@ -171,16 +175,16 @@ def get_session(self, host, port):
171175 if key not in self ._sessions :
172176 return None
173177
174- session , timestamp , _ = self ._sessions [key ]
178+ entry = self ._sessions [key ]
175179
176180 # Check if session has expired
177- if time .time () - timestamp > self ._ttl :
181+ if time .time () - entry . timestamp > self ._ttl :
178182 del self ._sessions [key ]
179183 return None
180184
181- # Update access time for LRU
182- self ._sessions [ key ] = ( session , timestamp , time . time () )
183- return session
185+ # Move to end to mark as recently used ( LRU)
186+ self ._sessions . move_to_end ( key )
187+ return entry . session
184188
185189 def set_session (self , host , port , session ):
186190 """
@@ -198,23 +202,26 @@ def set_session(self, host, port, session):
198202 current_time = time .time ()
199203
200204 with self ._lock :
201- # If cache is at max size, remove least recently used entry
202- if len (self ._sessions ) >= self ._max_size and key not in self ._sessions :
203- # Find entry with oldest access time
204- oldest_key = min (self ._sessions .keys (),
205- key = lambda k : self ._sessions [k ][2 ])
206- del self ._sessions [oldest_key ]
205+ # If key already exists, just update it
206+ if key in self ._sessions :
207+ self ._sessions [key ] = _SessionCacheEntry (session , current_time )
208+ self ._sessions .move_to_end (key )
209+ return
210+
211+ # If cache is at max size, remove least recently used entry (first item)
212+ if len (self ._sessions ) >= self ._max_size :
213+ self ._sessions .popitem (last = False )
207214
208- # Store session with creation time and access time
209- self ._sessions [key ] = (session , current_time , current_time )
215+ # Store session with creation time
216+ self ._sessions [key ] = _SessionCacheEntry (session , current_time )
210217
211218 def clear_expired (self ):
212219 """Remove all expired sessions from the cache."""
213220 current_time = time .time ()
214221 with self ._lock :
215222 expired_keys = [
216- key for key , ( _ , timestamp , _ ) in self ._sessions .items ()
217- if current_time - timestamp > self ._ttl
223+ key for key , entry in self ._sessions .items ()
224+ if current_time - entry . timestamp > self ._ttl
218225 ]
219226 for key in expired_keys :
220227 del self ._sessions [key ]
0 commit comments