1 import threading, time, pickle, random, os
5 from . import cookie, env, proto
8 __all__ = ["db", "get"]
11 return os.urandom(length)
13 class session(object):
14 def __init__(self, lock, expire=86400 * 7):
15 self.id = proto.enhex(gennonce(16))
18 self.ctime = self.atime = self.mtime = int(time.time())
34 def __getitem__(self, key):
37 def get(self, key, default=None):
38 return self.dict.get(key, default)
40 def __setitem__(self, key, value):
41 self.dict[key] = value
42 if hasattr(value, "sessdirty"):
47 def __delitem__(self, key):
48 old = self.dict.pop(key)
53 def __contains__(self, key):
54 return key in self.dict
56 def __getstate__(self):
58 for k, v in self.__dict__.items():
59 if k == "lock": continue
63 def __setstate__(self, st):
66 # The proper lock is set by the thawer
69 return "<session %s>" % self.id
72 def __init__(self, backdb=None, cookiename="wrwsess", path="/"):
74 self.cookiename = cookiename
76 self.lock = threading.Lock()
78 self.freezetime = 3600
82 now = int(time.time())
84 clist = self.live.keys()
88 entry = self.live[sessid]
93 if entry[1] == "retired":
95 elif entry[1] is None:
99 if sess.atime + self.freezetime < now:
104 if sess.atime + sess.expire < now:
111 del self.live[sessid]
118 if len(self.live) == 0:
124 def _fetch(self, sessid):
126 now = int(time.time())
128 if sessid in self.live:
129 entry = self.live[sessid]
131 entry = self.live[sessid] = [threading.RLock(), None]
133 if isinstance(entry[1], session):
136 elif entry[1] == "retired":
138 elif entry[1] is None:
140 thawed = self.thaw(sessid)
141 if thawed.atime + thawed.expire < now:
143 thawed.lock = entry[0]
151 del self.live[sessid]
153 raise Exception("Illegal session entry: " + repr(entry[1]))
155 def checkclean(self):
157 if self.cthread is None:
158 self.cthread = threading.Thread(target = self.cleanloop)
159 self.cthread.setDaemon(True)
162 def mksession(self, req):
163 return session(threading.RLock())
165 def mkcookie(self, req, sess):
166 cookie.add(req, self.cookiename, sess.id,
168 expires=cookie.cdate(time.time() + sess.expire))
170 def fetch(self, req):
171 now = int(time.time())
172 sessid = cookie.get(req, self.cookiename)
177 sess = self._fetch(sessid)
179 sess = self.mksession(req)
185 self.mkcookie(req, sess)
187 self.live[sess.id] = [sess.lock, sess]
193 req.oncommit(ckfreeze)
196 def thaw(self, sessid):
197 if self.backdb is None:
199 data = self.backdb[sessid]
201 return pickle.loads(data)
205 def freeze(self, sess):
206 if self.backdb is None:
209 data = pickle.dumps(sess, -1)
210 self.backdb[sess.id] = data
214 return req.item(self.fetch)
216 class dirback(object):
217 def __init__(self, path):
220 def __getitem__(self, key):
222 with open(os.path.join(self.path, key)) as inf:
227 def __setitem__(self, key, value):
228 if not os.path.exists(self.path):
229 os.makedirs(self.path)
230 with open(os.path.join(self.path, key), "w") as out:
233 default = env.var(db(backdb=dirback(os.path.join("/tmp", "wrwsess-" + str(os.getuid())))))
236 return default.val.get(req)