--- /dev/null
+__all__ = ["request", "wsgiwrap", "restart", "cookie", "formdata"]
+
+from req import request
+from util import wsgiwrap
+from dispatch import restart
+import cookie
+from form import formdata
--- /dev/null
+import Cookie
+
+__all__ = ["cookies", "get", "add"]
+
+def addcookies(req):
+ ck = cookies(req)
+ for nm in ck.codec:
+ req.ohead.add("Set-Cookie", ck.codec[nm].OutputString())
+
+class cookiedict(object):
+ def __init__(self, req):
+ self.bk = Cookie.SimpleCookie(req.ihead.get("Cookie"))
+ self.codec = Cookie.SimpleCookie()
+ req.oncommit(addcookies)
+
+ def __getitem__(self, name):
+ return self.bk[name].value
+
+ def __contains__(self, name):
+ return name in self.bk
+
+ def get(self, name, default = None):
+ if name not in self.bk:
+ return default
+ return self.bk[name].value
+
+ def add(self, name, value, path = None):
+ self.codec[name] = value
+ if path is not None: self.codec[name]["path"] = path
+
+ def __setitem__(self, name, value):
+ self.add(name, value)
+
+def cookies(req):
+ return req.item(cookiedict)
+
+def get(req, name, default = None):
+ return cookies(req).get(name, default)
+
+def add(req, name, value, path = None):
+ cookies(req).add(name, value, path)
--- /dev/null
+__all__ = ["restart"]
+
+class restart(Exception):
+ def handle(self, req):
+ pass
+
+def mangle(result):
+ try:
+ iter(result)
+ except TypeError:
+ pass
+ else:
+ return result
+ return [str(result)]
+
+def handle(req, startreq, handler):
+ try:
+ resp = [""]
+ while True:
+ try:
+ resp = handler(req)
+ break
+ except restart, i:
+ handler = i
+ req.commit(startreq)
+ return resp
+ finally:
+ req.cleanup()
--- /dev/null
+import cgi
+
+__all__ = ["formdata"]
+
+class formwrap(object):
+ def __init__(self, req):
+ if req.ihead["Content-Type"] == "application/x-www-form-urlencoded":
+ self.cf = cgi.parse(environ = req.env, fp = req.env["wsgi.input"])
+ else:
+ self.cf = cgi.parse(environ = req.env)
+
+ def __getitem__(self, key):
+ return self.cf[key][0]
+
+ def get(self, key, default = ""):
+ if key in self:
+ return self.cf[key][0]
+ return default
+
+ def __contains__(self, key):
+ return key in self.cf and len(self.cf[key]) > 0
+
+ def __iter__(self):
+ return iter(self.cf)
+
+ def items(self):
+ def iter():
+ for key, list in self.cf.items():
+ for val in list:
+ yield key, val
+ return list(iter())
+
+ def keys(self):
+ return self.cf.keys()
+
+ def values(self):
+ return [val for key, val in self.items()]
+
+def formdata(req):
+ return req.item(formwrap)
--- /dev/null
+__all__ = ["request"]
+
+class headdict(object):
+ def __init__(self):
+ self.dict = {}
+
+ def __getitem__(self, key):
+ return self.dict[key.lower()][1]
+
+ def __setitem__(self, key, val):
+ self.dict[key.lower()] = [key, val]
+
+ def __contains__(self, key):
+ return key.lower() in self.dict
+
+ def __delitem__(self, key):
+ del self.dict[key.lower()]
+
+ def __iter__(self):
+ return iter((list[0] for list in self.dict.itervalues()))
+
+ def get(self, key, default = ""):
+ if key.lower() in self.dict:
+ return self.dict[key.lower()][1]
+ return default
+
+ def getlist(self, key):
+ return self.dict.setdefault(key.lower(), [key])[1:]
+
+ def add(self, key, val):
+ self.dict.setdefault(key.lower(), [key]).append(val)
+
+ def __repr__(self):
+ return repr(self.dict)
+
+ def __str__(self):
+ return str(self.dict)
+
+def fixcase(str):
+ str = str.lower()
+ i = 0
+ b = True
+ while i < len(str):
+ if b:
+ str = str[:i] + str[i].upper() + str[i + 1:]
+ b = False
+ if str[i] == '-':
+ b = True
+ i += 1
+ return str
+
+class request(object):
+ def __init__(self, env):
+ self.env = env
+ self.uriname = env["SCRIPT_NAME"]
+ self.filename = env.get("SCRIPT_FILENAME")
+ self.uri = env["REQUEST_URI"]
+ self.pathinfo = env["PATH_INFO"]
+ self.query = env["QUERY_STRING"]
+ self.remoteaddr = env["REMOTE_ADDR"]
+ self.serverport = env["SERVER_PORT"]
+ self.https = "HTTPS" in env
+ self.ihead = headdict()
+ self.ohead = headdict()
+ for k, v in env.items():
+ if k[:5] == "HTTP_":
+ self.ihead.add(fixcase(k[5:].replace("_", "-")), v)
+ self.items = {}
+ self.statuscode = (200, "OK")
+ self.ohead["Content-Type"] = "text/html"
+ self.resources = set()
+ self.clean = set()
+ self.commitfuns = []
+
+ def status(self, code, msg):
+ self.statuscode = code, msg
+
+ def item(self, id):
+ if id in self.items:
+ return self.items[id]
+ self.items[id] = new = id(self)
+ if hasattr(new, "__enter__") and hasattr(new, "__exit__"):
+ self.withres(new)
+ return new
+
+ def withres(self, res):
+ if res not in self.resources:
+ done = False
+ res.__enter__()
+ try:
+ self.resources.add(res)
+ self.clean.add(res.__exit__)
+ done = True
+ finally:
+ if not done:
+ res.__exit__(None, None, None)
+ self.resources.discard(res)
+
+ def cleanup(self):
+ def clean1(list):
+ if len(list) > 0:
+ try:
+ list[0]()
+ finally:
+ clean1(list[1:])
+ clean1(list(self.clean))
+
+ def oncommit(self, fn):
+ if fn not in self.commitfuns:
+ self.commitfuns.append(fn)
+
+ def commit(self, startreq):
+ for fun in reversed(self.commitfuns):
+ fun(self)
+ hdrs = []
+ for nm in self.ohead:
+ for val in self.ohead.getlist(nm):
+ hdrs.append((nm, val))
+ startreq("%s %s" % self.statuscode, hdrs)
--- /dev/null
+import threading, time, pickle, random, os
+import cookie
+
+__all__ = ["db", "get"]
+
+def hexencode(str):
+ ret = ""
+ for byte in str:
+ ret += "%02X" % (ord(byte),)
+ return ret
+
+def gennonce(length):
+ nonce = ""
+ for i in xrange(length):
+ nonce += chr(random.randint(0, 255))
+ return nonce
+
+class session(object):
+ def __init__(self, expire = 86400 * 7):
+ self.id = hexencode(gennonce(16))
+ self.dict = {}
+ self.lock = threading.Lock()
+ self.ctime = self.atime = self.mtime = int(time.time())
+ self.expire = expire
+ self.dctl = set()
+ self.dirtyp = False
+
+ def dirty(self):
+ for d in self.dctl:
+ if d.sessdirty():
+ return True
+ return self.dirtyp
+
+ def frozen(self):
+ for d in self.dctl:
+ d.sessfrozen()
+ self.dirtyp = False
+
+ def __getitem__(self, key):
+ return self.dict[key]
+
+ def get(self, key, default = None):
+ return self.dict.get(key, default)
+
+ def __setitem__(self, key, value):
+ self.dict[key] = value
+ if hasattr(value, "sessdirty"):
+ self.dctl.add(value)
+ else:
+ self.dirtyp = True
+
+ def __delitem__(self, key):
+ old = self.dict.pop(key)
+ if old in self.dctl:
+ self.dctl.remove(old)
+ self.dirtyp = True
+
+ def __contains__(self, key):
+ return key in self.dict
+
+ def __getstate__(self):
+ ret = []
+ for k, v in self.__dict__.items():
+ if k == "lock": continue
+ ret.append((k, v))
+ return ret
+
+ def __setstate__(self, st):
+ for k, v in st:
+ self.__dict__[k] = v
+ self.lock = threading.Lock()
+
+class db(object):
+ def __init__(self, cookiename = "wrwsess", path = "/"):
+ self.live = {}
+ self.cookiename = cookiename
+ self.path = path
+ self.lock = threading.Lock()
+ self.lastuse = 0
+ self.cthread = None
+ self.freezetime = 3600
+
+ def clean(self):
+ now = int(time.time())
+ with self.lock:
+ dlist = []
+ for sess in self.live.itervalues():
+ if sess.atime + self.freezetime < now:
+ try:
+ if sess.dirty():
+ self.freeze(sess)
+ except:
+ if sess.atime + sess.expire < now:
+ dlist.append(sess)
+ else:
+ dlist.append(sess)
+ for sess in dlist:
+ del self.live[sess.id]
+
+ def cleanloop(self):
+ try:
+ lastuse = self.lastuse
+ while self.lastuse >= lastuse:
+ lastuse = self.lastuse
+ time.sleep(300)
+ self.clean()
+ finally:
+ with self.lock:
+ self.cthread = None
+
+ def fetch(self, req):
+ now = int(time.time())
+ self.lastuse = now
+ sessid = cookie.get(req, self.cookiename)
+ with self.lock:
+ if self.cthread is None:
+ self.cthread = threading.Thread(target = self.cleanloop)
+ self.cthread.setDaemon(True)
+ self.cthread.start()
+ try:
+ if sessid is None:
+ raise KeyError()
+ elif sessid in self.live:
+ sess = self.live[sessid]
+ else:
+ sess = self.thaw(sessid)
+ self.live[sessid] = sess
+ if sess.atime + sess.expire < now:
+ raise KeyError()
+ sess.atime = now
+ except KeyError:
+ sess = session()
+ self.live[sess.id] = sess
+ req.oncommit(self.addcookie)
+ req.oncommit(self.ckfreeze)
+ return sess
+
+ def addcookie(self, req):
+ sess = req.item(self.fetch)
+ cookie.add(req, self.cookiename, sess.id, self.path)
+
+ def ckfreeze(self, req):
+ sess = req.item(self.fetch)
+ if sess.dirty():
+ try:
+ self.freeze(sess)
+ except:
+ pass
+
+ def thaw(self, sessid):
+ raise KeyError()
+
+ def freeze(self, sess):
+ raise TypeError()
+
+class backeddb(db):
+ def __init__(self, backdb, *args, **kw):
+ super(backeddb, self).__init__(*args, **kw)
+ self.backdb = backdb
+
+ def thaw(self, sessid):
+ data = self.backdb[sessid]
+ try:
+ return pickle.loads(data)
+ except Exception, e:
+ raise KeyError()
+
+ def freeze(self, sess):
+ self.backdb[sess.id] = pickle.dumps(sess)
+ sess.frozen()
+
+class dirback(object):
+ def __init__(self, path):
+ self.path = path
+
+ def __getitem__(self, key):
+ try:
+ with open(os.path.join(self.path, key)) as inf:
+ return inf.read()
+ except IOError:
+ raise KeyError(key)
+
+ def __setitem__(self, key, value):
+ if not os.path.exists(self.path):
+ os.makedirs(self.path)
+ with open(os.path.join(self.path, key), "w") as out:
+ out.write(value)
+
+default = backeddb(dirback(os.path.join("/tmp", "wrwsess-" + str(os.getuid()))))
+
+def get(req):
+ return req.item(default.fetch)
--- /dev/null
+import req, dispatch
+
+def wsgiwrap(callable):
+ def wrapper(env, startreq):
+ return dispatch.handle(req.request(env), startreq, callable)
+ return wrapper