1# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
2# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
3
4"""
5Creates a session object.
6
7In your application, use::
8
9    environ['paste.flup_session_service'].session
10
11This will return a dictionary.  The contents of this dictionary will
12be saved to disk when the request is completed.  The session will be
13created when you first fetch the session dictionary, and a cookie will
14be sent in that case.  There's current no way to use sessions without
15cookies, and there's no way to delete a session except to clear its
16data.
17"""
18
19from paste import httpexceptions
20from paste import wsgilib
21import flup.middleware.session
22flup_session = flup.middleware.session
23
24# This is a dictionary of existing stores, keyed by a tuple of
25# store type and parameters
26store_cache = {}
27
28class NoDefault(object):
29    pass
30
31class SessionMiddleware(object):
32
33    session_classes = {
34        'memory': (flup_session.MemorySessionStore,
35                   [('session_timeout', 'timeout', int, 60)]),
36        'disk': (flup_session.DiskSessionStore,
37                 [('session_timeout', 'timeout', int, 60),
38                  ('session_dir', 'storeDir', str, '/tmp/sessions')]),
39        'shelve': (flup_session.ShelveSessionStore,
40                   [('session_timeout', 'timeout', int, 60),
41                    ('session_file', 'storeFile', str,
42                     '/tmp/session.shelve')]),
43        }
44
45
46    def __init__(self, app,
47                 global_conf=None,
48                 session_type=NoDefault,
49                 cookie_name=NoDefault,
50                 **store_config
51                 ):
52        self.application = app
53        if session_type is NoDefault:
54            session_type = global_conf.get('session_type', 'disk')
55        self.session_type = session_type
56        try:
57            self.store_class, self.store_args = self.session_classes[self.session_type]
58        except KeyError:
59            raise KeyError(
60                "The session_type %s is unknown (I know about %s)"
61                % (self.session_type,
62                   ', '.join(self.session_classes.keys())))
63        kw = {}
64        for config_name, kw_name, coercer, default in self.store_args:
65            value = coercer(store_config.get(config_name, default))
66            kw[kw_name] = value
67        self.store = self.store_class(**kw)
68        if cookie_name is NoDefault:
69            cookie_name = global_conf.get('session_cookie', '_SID_')
70        self.cookie_name = cookie_name
71
72    def __call__(self, environ, start_response):
73        service = flup_session.SessionService(
74            self.store, environ, cookieName=self.cookie_name,
75            fieldName=self.cookie_name)
76        environ['paste.flup_session_service'] = service
77
78        def cookie_start_response(status, headers, exc_info=None):
79            service.addCookie(headers)
80            return start_response(status, headers, exc_info)
81
82        try:
83            app_iter = self.application(environ, cookie_start_response)
84        except httpexceptions.HTTPException as e:
85            headers = (e.headers or {}).items()
86            service.addCookie(headers)
87            e.headers = dict(headers)
88            service.close()
89            raise
90        except:
91            service.close()
92            raise
93
94        return wsgilib.add_close(app_iter, service.close)
95
96def make_session_middleware(app, global_conf,
97                            session_type=NoDefault,
98                            cookie_name=NoDefault,
99                            **store_config):
100    """
101    Wraps the application in a session-managing middleware.
102    The session service can then be found in
103    ``environ['paste.flup_session_service']``
104    """
105    return SessionMiddleware(
106        app, global_conf=global_conf,
107        session_type=session_type, cookie_name=cookie_name,
108        **store_config)
109