1 import threading, time, pickle, random, os
2 from . import cookie, env, proto
4 __all__ = ["db", "get"]
7 return os.urandom(length)
10 def __init__(self, lock, expire=86400 * 7):
11 self.id = proto.enhex(gennonce(16))
14 self.ctime = self.atime = self.mtime = int(time.time())
30 def __getitem__(self, key):
33 def get(self, key, default=None):
34 return self.dict.get(key, default)
36 def __setitem__(self, key, value):
37 self.dict[key] = value
38 if hasattr(value, "sessdirty"):
43 def __delitem__(self, key):
44 old = self.dict.pop(key)
49 def __contains__(self, key):
50 return key in self.dict
52 def __getstate__(self):
54 for k, v in self.__dict__.items():
55 if k == "lock": continue
59 def __setstate__(self, st):
62 # The proper lock is set by the thawer
65 return "<session %s>" % self.id
68 def __init__(self, backdb=None, cookiename="wrwsess", path="/"):
70 self.cookiename = cookiename
72 self.lock = threading.Lock()
74 self.freezetime = 3600
78 now = int(time.time())
80 clist = list(self.live.keys())
84 entry = self.live[sessid]
89 if entry[1] == "retired":
91 elif entry[1] is None:
95 if sess.atime + self.freezetime < now:
100 if sess.atime + sess.expire < now:
107 del self.live[sessid]
114 if len(self.live) == 0:
120 def _fetch(self, sessid):
122 now = int(time.time())
124 if sessid in self.live:
125 entry = self.live[sessid]
127 entry = self.live[sessid] = [threading.RLock(), None]
129 if isinstance(entry[1], session):
132 elif entry[1] == "retired":
134 elif entry[1] is None:
136 thawed = self.thaw(sessid)
137 if thawed.atime + thawed.expire < now:
139 thawed.lock = entry[0]
147 del self.live[sessid]
149 raise Exception("Illegal session entry: " + repr(entry[1]))
151 def checkclean(self):
153 if self.cthread is None:
154 self.cthread = threading.Thread(target = self.cleanloop)
155 self.cthread.setDaemon(True)
158 def mksession(self, req):
159 return session(threading.RLock())
161 def mkcookie(self, req, sess):
162 cookie.add(req, self.cookiename, sess.id,
164 expires=cookie.cdate(time.time() + sess.expire))
166 def fetch(self, req):
167 now = int(time.time())
168 sessid = cookie.get(req, self.cookiename)
173 sess = self._fetch(sessid)
175 sess = self.mksession(req)
181 self.mkcookie(req, sess)
183 self.live[sess.id] = [sess.lock, sess]
189 req.oncommit(ckfreeze)
192 def thaw(self, sessid):
193 if self.backdb is None:
195 data = self.backdb[sessid]
197 return pickle.loads(data)
201 def freeze(self, sess):
202 if self.backdb is None:
205 data = pickle.dumps(sess, -1)
206 self.backdb[sess.id] = data
210 return req.item(self.fetch)
212 class dirback(object):
213 def __init__(self, path):
216 def __getitem__(self, key):
218 with open(os.path.join(self.path, key)) as inf:
223 def __setitem__(self, key, value):
224 if not os.path.exists(self.path):
225 os.makedirs(self.path)
226 with open(os.path.join(self.path, key), "w") as out:
229 default = env.var(db(backdb=dirback(os.path.join("/tmp", "wrwsess-" + str(os.getuid())))))
232 return default.val.get(req)