| 185 | |
| 186 | |
| 187 | class CachingSessionManager(SessionManager): |
| 188 | def __init__(self, parent, num_to_store, skip_paths=None): |
| 189 | self.parent = parent |
| 190 | self.num_to_store = num_to_store |
| 191 | self._cache = OrderedDict() |
| 192 | self.skip_paths = [] if skip_paths is None else skip_paths |
| 193 | |
| 194 | def _normalize(self): |
| 195 | if len(self._cache) > self.num_to_store: |
| 196 | # Flush 20% of the cache |
| 197 | with sess_lock: |
| 198 | while len(self._cache) > (self.num_to_store * 0.8): |
| 199 | self._cache.popitem(False) |
| 200 | |
| 201 | def is_session_ready(self, _session): |
| 202 | if not has_request_context() or _session is None: |
| 203 | return False |
| 204 | |
| 205 | # Session _id returns the str object |
| 206 | # or None if it hasn't been set yet. |
| 207 | try: |
| 208 | return _session['_id'] is not None |
| 209 | except (AssertionError, RuntimeError, KeyError, TypeError): |
| 210 | return False |
| 211 | |
| 212 | def new_session(self): |
| 213 | session = self.parent.new_session() |
| 214 | |
| 215 | # Do not store the session if skip paths |
| 216 | for sp in self.skip_paths: |
| 217 | if request.path.startswith(sp): |
| 218 | return session |
| 219 | |
| 220 | with sess_lock: |
| 221 | self._cache[session.sid] = session |
| 222 | self._normalize() |
| 223 | |
| 224 | return session |
| 225 | |
| 226 | def remove(self, sid): |
| 227 | with sess_lock: |
| 228 | self.parent.remove(sid) |
| 229 | if sid in self._cache: |
| 230 | del self._cache[sid] |
| 231 | |
| 232 | def exists(self, sid): |
| 233 | with sess_lock: |
| 234 | if sid in self._cache: |
| 235 | return True |
| 236 | return self.parent.exists(sid) |
| 237 | |
| 238 | def get(self, sid, digest): |
| 239 | session = None |
| 240 | with (sess_lock): |
| 241 | if sid in self._cache: |
| 242 | session = self._cache[sid] |
| 243 | if self.is_session_ready(session) and\ |
| 244 | session.hmac_digest != digest: |
no outgoing calls
no test coverage detected