diff --git a/src/calibre/srv/handler.py b/src/calibre/srv/handler.py index 0b248e59c5..adb80f8a4a 100644 --- a/src/calibre/srv/handler.py +++ b/src/calibre/srv/handler.py @@ -15,6 +15,7 @@ from threading import Lock from calibre.db.cache import Cache from calibre.db.legacy import create_backend, LibraryDatabase from calibre.srv.routes import Router +from calibre.srv.session import Sessions from calibre.utils.date import utcnow def init_library(library_path): @@ -71,18 +72,21 @@ class Context(object): url_for = None CATEGORY_CACHE_SIZE = 25 SEARCH_CACHE_SIZE = 100 + SESSION_COOKIE = 'calibre_session' def __init__(self, libraries, opts, testing=False): self.opts = opts self.library_broker = LibraryBroker(libraries) self.testing = testing self.lock = Lock() + self.sessions = Sessions() def init_session(self, endpoint, data): - pass + data.session = self.sessions.get_or_create(key=data.cookies.get(self.SESSION_COOKIE), username=data.username) def finalize_session(self, endpoint, data, output): - pass + data.outcookie[self.SESSION_COOKIE] = data.session.key + data.outcookie[self.SESSION_COOKIE]['path'] = self.url_for(None) def get_library(self, library_id=None): return self.library_broker.get(library_id) diff --git a/src/calibre/srv/session.py b/src/calibre/srv/session.py new file mode 100644 index 0000000000..c101f8c9f2 --- /dev/null +++ b/src/calibre/srv/session.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python2 +# vim:fileencoding=utf-8 +# License: GPLv3 Copyright: 2015, Kovid Goyal + +from __future__ import (unicode_literals, division, absolute_import, + print_function) +from copy import deepcopy +from uuid import uuid4 +from threading import Lock + +from calibre.utils.lru_cache import lru_cache + +defaults = { + 'sort': 'date:asc', + 'library_id': None, +} + +class Session(object): + + def __init__(self): + self._data = deepcopy(defaults) + + def __getitem__(self, key): + return self._data[key] + + def __setitem__(self, key, val): + self._data[key] = val + + +class SessionProxy(object): + + ''' Prevent the creation of a long-lived session object for every new + request without a session cookie. Instead, this object lives only as long + an individual request, and unless some setting is changed from the default + simply returns values from the global defaults object. ''' + + def __init__(self, sessions, key): + self.sessions = sessions + self.key = key + self.actual_session = None + + def __getitem__(self, key): + if self.actual_session is None: + return defaults[key] + return self.actual_session[key] + + def __setitem__(self, key, val): + with self.sessions.lock: + if self.actual_session is None: + self.actual_session = self.sessions.cache[self.key] = Session() + self.actual_session[key] = val + +class Sessions(object): + + def __init__(self): + self.cache = lru_cache(size=2000) + self.lock = Lock() + + def get_or_create(self, key=None, username=None): + key = key or str(uuid4()).replace('-', '') + try: + with self.lock: + return self.cache[key] + except KeyError: + return SessionProxy(self, key)