Changed tweets, added eca
This commit is contained in:
128
eca/sessions.py
Normal file
128
eca/sessions.py
Normal file
@@ -0,0 +1,128 @@
|
||||
from http.cookies import SimpleCookie
|
||||
from collections import namedtuple
|
||||
from collections.abc import Mapping
|
||||
from itertools import product, chain
|
||||
import time
|
||||
import random
|
||||
|
||||
from . import httpd
|
||||
from . import Context, context_activate
|
||||
|
||||
|
||||
# Name generation for contexts and sessions
|
||||
def name_parts():
|
||||
"""
|
||||
This generator will create an endless list of steadily increasing
|
||||
name part lists.
|
||||
"""
|
||||
# name parts
|
||||
letters = ['alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta', 'eta',
|
||||
'theta', 'iota', 'kappa', 'lambda', 'mu', 'nu', 'xi', 'omicron',
|
||||
'pi', 'rho', 'sigma', 'tau', 'upsilon', 'phi', 'chi', 'psi',
|
||||
'omega']
|
||||
colours = ['red', 'orange', 'yellow', 'green', 'blue', 'violet']
|
||||
|
||||
# randomize order
|
||||
random.shuffle(letters)
|
||||
random.shuffle(colours)
|
||||
|
||||
# yield initial sequence (letter-colour)
|
||||
parts = [letters, colours]
|
||||
yield parts
|
||||
|
||||
# forever generate longer sequences by appending the letter list
|
||||
# over and over. Note that this is the *same* letter list, so it will have
|
||||
# the exact order.
|
||||
while True:
|
||||
random.shuffle(letters)
|
||||
random.shuffle(colours)
|
||||
parts.append(letters)
|
||||
yield parts
|
||||
|
||||
# construct an iterator that will endlessly generate names:
|
||||
# 1) for each parts list p in name_parts() we take the cartesian product
|
||||
# 2) the product iterators are generated by the for...in generator
|
||||
# 3) we chain these iterators so that when the first is exhausted, we can
|
||||
# continue with the second, etc.
|
||||
# 4) we map the function '-'.join over the list of parts from the chain
|
||||
names = map('-'.join, chain.from_iterable((product(*p) for p in name_parts())))
|
||||
|
||||
|
||||
class SessionCookie(httpd.Filter):
|
||||
"""
|
||||
The actual HTTP filter that will apply the cookie handling logic to each
|
||||
request. This filter defers to the SessionManager with respect to the
|
||||
cookie name to use and the activation of sessions.
|
||||
"""
|
||||
def bind(self, manager):
|
||||
"""Post constructor configuration of filter."""
|
||||
self.manager = manager
|
||||
|
||||
def handle(self):
|
||||
"""
|
||||
Determine if a cookie needs to be set and let the session manager
|
||||
handle activation.
|
||||
"""
|
||||
cookies = self.request.cookies
|
||||
morsel = cookies.get(self.manager.cookie)
|
||||
|
||||
if not morsel:
|
||||
# Determine new cookie
|
||||
value = self.manager.generate_name()
|
||||
|
||||
# Set new cookie
|
||||
cookies[self.manager.cookie] = value
|
||||
cookies[self.manager.cookie]['path'] = '/'
|
||||
|
||||
# Send the new cookie as header
|
||||
self.request.send_header('Set-Cookie', cookies[self.manager.cookie].OutputString())
|
||||
else:
|
||||
value = morsel.value
|
||||
|
||||
self.manager.activate(value)
|
||||
|
||||
|
||||
class Session:
|
||||
"""
|
||||
The Session bookkeeping data.
|
||||
"""
|
||||
def __init__(self, context, seen):
|
||||
self.context = context
|
||||
self.seen = seen
|
||||
|
||||
def activate(self):
|
||||
"""Activate the session. Updates last seen time."""
|
||||
self.seen = time.time()
|
||||
context_activate(self.context)
|
||||
|
||||
|
||||
class SessionManager:
|
||||
"""
|
||||
The SessionManager class. This class is callable so it can be used in place
|
||||
of a constructor in the configuration.
|
||||
"""
|
||||
def __init__(self, cookie_name):
|
||||
self.sessions = {}
|
||||
self.cookie = cookie_name
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
handler = SessionCookie(*args, **kwargs)
|
||||
handler.bind(self)
|
||||
return handler
|
||||
|
||||
def generate_name(self):
|
||||
result = next(names)
|
||||
while result in self.sessions:
|
||||
result = next(names)
|
||||
return result
|
||||
|
||||
def _new_session(self, name):
|
||||
result = Session(Context(name=name, init_data={'name': name}), time.time())
|
||||
result.context.start()
|
||||
return result
|
||||
|
||||
def activate(self, name):
|
||||
if name not in self.sessions:
|
||||
self.sessions[name] = self._new_session(name)
|
||||
self.sessions[name].activate()
|
||||
|
||||
Reference in New Issue
Block a user