class SessionMiddleware(Unit, Middleware): """A session middleware.""" configuration = Configuration({ 'enabled': Boolean(default=True, required=True), 'cookie': Structure( { 'name': Text(nonnull=True, min_length=1, default='sessionid'), 'max_age': Integer(minimum=0), 'secure': Boolean(default=False), }, generate_default=True, required=True), 'store': Structure( structure={ FilesystemSessionStore: { 'path': Text(default=None), }, }, polymorphic_on=ObjectReference(name='implementation', nonnull=True), default={'implementation': FilesystemSessionStore}, required=True, ) }) enabled = configured_property('enabled') def __init__(self, store): self.store = store['implementation'](session_class=Session, **pruned(store, 'implementation')) def dispatch(self, application, environ, start_response): session = None if self.enabled: session = self._get_session(environ) environ['request.session'] = session if session is None: return application(environ, start_response) def injecting_start_response(status, headers, exc_info=None): if session.expired: headers.append( ('Set-Cookie', self._construct_cookie(session, True))) self.store.delete(session) elif session.should_save: self.store.save(session) headers.append(('Set-Cookie', self._construct_cookie(session))) return start_response(status, headers, exc_info) return ClosingIterator(application(environ, injecting_start_response), lambda: self.store.save_if_modified(session)) def _construct_cookie(self, session, unset=False): params = self.configuration['cookie'] expires = (LONG_AGO if unset else params.get('expires')) return dump_cookie(params['name'], session.sid, params.get('max_age'), expires, params.get('path', '/'), params.get('domain'), params.get('secure'), params.get('httponly', True)) def _get_session(self, environ): cookie = parse_cookie(environ.get('HTTP_COOKIE', '')) id = cookie.get(self.configuration['cookie']['name'], None) if id is not None: return self.store.get(id) else: return self.store.new()