From: Fredrik Tolf Date: Sun, 8 Nov 2015 03:01:08 +0000 (+0100) Subject: Merge branch 'master' into python2 X-Git-Url: http://dolda2000.com/gitweb/?a=commitdiff_plain;ds=sidebyside;h=b6f62b898efdc272130d3dbdd6742c5b785e7c7d;hp=-c;p=wrw.git Merge branch 'master' into python2 Conflicts: wrw/auth.py wrw/form.py wrw/proto.py wrw/session.py --- b6f62b898efdc272130d3dbdd6742c5b785e7c7d diff --combined wrw/auth.py index 4ae292d,27e2a98..bb0e0cd --- a/wrw/auth.py +++ b/wrw/auth.py @@@ -1,9 -1,9 +1,9 @@@ import binascii, hashlib, threading, time - import resp -from . import resp, proto ++import resp, proto class unauthorized(resp.httperror): def __init__(self, challenge, message=None, detail=None): - super().__init__(401, message, detail) + super(unauthorized, self).__init__(401, message, detail) if isinstance(challenge, str): challenge = [challenge] self.challenge = challenge @@@ -11,11 -11,11 +11,11 @@@ def handle(self, req): for challenge in self.challenge: req.ohead.add("WWW-Authenticate", challenge) - return super().handle(req) + return super(unauthorized, self).handle(req) class forbidden(resp.httperror): def __init__(self, message=None, detail=None): - super().__init__(403, message, detail) + super(forbidden, self).__init__(403, message, detail) def parsemech(req): h = req.ihead.get("Authorization", None) @@@ -31,9 -31,13 +31,9 @@@ def parsebasic(req) if mech != "basic": return None, None try: - raw = binascii.a2b_base64(data) + raw = proto.unb64(data) except binascii.Error: return None, None - try: - raw = raw.decode("utf-8") - except UnicodeError: - raw = raw.decode("latin1") p = raw.find(":") if p < 0: return None, None @@@ -51,9 -55,9 +51,9 @@@ class basiccache(object) def _obscure(self, nm, pw): dig = hashlib.sha256() - dig.update(self.realm.encode("utf-8")) - dig.update(nm.encode("utf-8")) - dig.update(pw.encode("utf-8")) + dig.update(self.realm) + dig.update(nm) + dig.update(pw) return dig.digest() def check(self, req): @@@ -76,7 -80,7 +76,7 @@@ with lock: try: ret = self.auth(req, nm, pw) - except forbidden as exc: + except forbidden, exc: with self._lock: self._cache[nm, pwh] = (lock, now, "f", exc) raise diff --combined wrw/form.py index da13a4a,7905dc6..76abab6 --- a/wrw/form.py +++ b/wrw/form.py @@@ -1,16 -1,19 +1,19 @@@ -import urllib.parse -from . import proto +import urlparse +import proto __all__ = ["formdata"] def formparse(req): buf = {} - buf.update(urllib.parse.parse_qsl(req.query)) + buf.update(urlparse.parse_qsl(req.query)) if req.ihead.get("Content-Type") == "application/x-www-form-urlencoded": - rbody = req.input.read(2 ** 20) + try: + rbody = req.input.read(2 ** 20) + except IOError as exc: + return exc if len(rbody) >= 2 ** 20: - raise ValueError("x-www-form-urlencoded data is absurdly long") + return ValueError("x-www-form-urlencoded data is absurdly long") - buf.update(urllib.parse.parse_qsl(rbody.decode("latin1"))) + buf.update(urlparse.parse_qsl(rbody)) return buf class badmultipart(Exception): @@@ -19,7 -22,7 +22,7 @@@ class formpart(object): def __init__(self, form): self.form = form - self.buf = b"" + self.buf = "" self.eof = False self.head = {} @@@ -28,8 -31,8 +31,8 @@@ def fillbuf(self, sz): req = self.form.req - mboundary = b"\r\n--" + self.form.boundary + b"\r\n" - lboundary = b"\r\n--" + self.form.boundary + b"--\r\n" + mboundary = "\r\n--" + self.form.boundary + "\r\n" + lboundary = "\r\n--" + self.form.boundary + "--\r\n" while not self.eof: p = self.form.buf.find(mboundary) if p >= 0: @@@ -50,7 -53,7 +53,7 @@@ break while len(self.form.buf) <= len(lboundary): ret = req.input.read(8192) - if ret == b"": + if ret == "": raise badmultipart("Missing last multipart boundary") self.form.buf += ret @@@ -61,17 -64,17 +64,17 @@@ self.buf = self.buf[limit:] else: ret = self.buf - self.buf = b"" + self.buf = "" return ret def readline(self, limit=-1): last = 0 while True: - p = self.buf.find(b'\n', last) + p = self.buf.find('\n', last) if p < 0: if self.eof: ret = self.buf - self.buf = b"" + self.buf = "" return ret last = len(self.buf) self.fillbuf(last + 128) @@@ -81,7 -84,9 +84,9 @@@ return ret def close(self): - self.fillbuf(-1) + while True: - if self.read(8192) == b"": ++ if self.read(8192) == "": + break def __enter__(self): return self @@@ -90,12 -95,15 +95,12 @@@ self.close() return False - def parsehead(self, charset): + def parsehead(self): def headline(): ln = self.readline(256) - if ln[-1] != ord(b'\n'): + if ln[-1] != '\n': raise badmultipart("Too long header line in part") - try: - return ln.decode(charset).rstrip() - except UnicodeError: - raise badmultipart("Form part header is not in assumed charset") + return ln.rstrip() ln = headline() while True: @@@ -127,30 -135,39 +132,35 @@@ raise badmultipart("Form part uses unexpected transfer encoding: %r" % encoding) class multipart(object): - def __init__(self, req, charset): + def __init__(self, req): val, par = proto.pmimehead(req.ihead.get("Content-Type", "")) if req.method != "POST" or val != "multipart/form-data": raise badmultipart("Request is not a multipart form") if "boundary" not in par: raise badmultipart("Multipart form lacks boundary") - try: - self.boundary = par["boundary"].encode("us-ascii") - except UnicodeError: - raise badmultipart("Multipart boundary must be ASCII string") + self.boundary = par["boundary"] self.req = req - self.buf = b"\r\n" + self.buf = "\r\n" self.eof = False - self.headcs = charset self.lastpart = formpart(self) self.lastpart.close() def __iter__(self): return self - def __next__(self): + def next(self): if not self.lastpart.eof: raise RuntimeError("All form parts must be read entirely") if self.eof: raise StopIteration() self.lastpart = formpart(self) - self.lastpart.parsehead(self.headcs) + self.lastpart.parsehead() return self.lastpart - def formdata(req): - return req.item(formparse) + def formdata(req, onerror=Exception): + data = req.item(formparse) + if isinstance(data, Exception): + if onerror is Exception: + raise data + return onerror + return data diff --combined wrw/proto.py index 3dcf0d7,ad1775a..a67fa19 --- a/wrw/proto.py +++ b/wrw/proto.py @@@ -1,4 -1,4 +1,4 @@@ - import time, calendar + import time, calendar, collections, binascii, base64 statusinfo = { 400: ("Bad Request", "Invalid HTTP request."), @@@ -98,19 -98,20 +98,19 @@@ def simpleerror(env, startreq, code, ti """ % (title, title, htmlq(msg)) - buf = buf.encode("us-ascii") startreq("%i %s" % (code, title), [("Content-Type", "text/html"), ("Content-Length", str(len(buf)))]) return [buf] def urlq(url): - if isinstance(url, str): + if isinstance(url, unicode): url = url.encode("utf-8") ret = "" - invalid = "&=#?/\"'" - invalid = b";&=#?/\"'" ++ invalid = ";&=#?/\"'" for c in url: - if c in invalid or (c <= 32) or (c >= 128): - ret += "%%%02X" % c + if c in invalid or (ord(c) <= 32) or (ord(c) >= 128): + ret += "%%%02X" % ord(c) else: - ret += chr(c) + ret += c return ret class urlerror(ValueError): @@@ -194,3 -195,37 +194,22 @@@ def parurl(url, pars={}, **augment) return url + "?" + qs else: return url + + # Wrap these, since binascii is a bit funky. :P + def enhex(bs): - return base64.b16encode(bs).decode("us-ascii") ++ return base64.b16encode(bs) + def unhex(es): - if not isinstance(es, collections.ByteString): - try: - es = es.encode("us-ascii") - except UnicodeError: - raise binascii.Error("non-ascii character in hex-string") + return base64.b16decode(es) + def enb32(bs): - return base64.b32encode(bs).decode("us-ascii") ++ return base64.b32encode(bs) + def unb32(es): - if not isinstance(es, collections.ByteString): - try: - es = es.encode("us-ascii") - except UnicodeError: - raise binascii.Error("non-ascii character in base32-string") + if (len(es) % 8) != 0: + es += b"=" * (8 - (len(es) % 8)) + es = es.upper() # The whole point of Base32 is that it's case-insensitive :P + return base64.b32decode(es) + def enb64(bs): - return base64.b64encode(bs).decode("us-ascii") ++ return base64.b64encode(bs) + def unb64(es): - if not isinstance(es, collections.ByteString): - try: - es = es.encode("us-ascii") - except UnicodeError: - raise binascii.Error("non-ascii character in base64-string") + if (len(es) % 4) != 0: + es += b"=" * (4 - (len(es) % 4)) + return base64.b64decode(es) diff --combined wrw/session.py index 0c585a3,8141827..476113a --- a/wrw/session.py +++ b/wrw/session.py @@@ -1,5 -1,5 +1,9 @@@ import threading, time, pickle, random, os ++<<<<<<< HEAD +import cookie, env ++======= + from . import cookie, env, proto ++>>>>>>> master __all__ = ["db", "get"] @@@ -8,7 -8,7 +12,7 @@@ def gennonce(length) class session(object): def __init__(self, lock, expire=86400 * 7): - self.id = gennonce(16).encode("hex") + self.id = proto.enhex(gennonce(16)) self.dict = {} self.lock = lock self.ctime = self.atime = self.mtime = int(time.time()) @@@ -77,7 -77,7 +81,7 @@@ class db(object) def clean(self): now = int(time.time()) with self.lock: - clist = list(self.live.keys()) + clist = self.live.keys() for sessid in clist: with self.lock: try: @@@ -195,7 -195,7 +199,7 @@@ data = self.backdb[sessid] try: return pickle.loads(data) - except: + except Exception, e: raise KeyError() def freeze(self, sess): diff --combined wrw/util.py index 92b441c,a1dcc6c..22bce9d --- a/wrw/util.py +++ b/wrw/util.py @@@ -1,5 -1,5 +1,5 @@@ import inspect, math -from . import req, dispatch, session, form, resp, proto +import req, dispatch, session, form, resp, proto def wsgiwrap(callable): def wrapper(env, startreq): @@@ -10,14 -10,17 +10,17 @@@ def formparams(callable): spec = inspect.getargspec(callable) def wrapper(req): - data = form.formdata(req) + try: + data = form.formdata(req) + except IOError: + raise resp.httperror(400, "Invalid request", "Form data was incomplete") args = dict(data.items()) args["req"] = req if not spec.keywords: for arg in list(args): if arg not in spec.args: del args[arg] - for i in range(len(spec.args) - (len(spec.defaults) if spec.defaults else 0)): + for i in xrange(len(spec.args) - (len(spec.defaults) if spec.defaults else 0)): if spec.args[i] not in args: raise resp.httperror(400, "Missing parameter", ("The query parameter `", resp.h.code(spec.args[i]), "' is required but not supplied.")) return callable(**args) @@@ -87,12 -90,12 +90,12 @@@ class preiter(object) self.bk = real self.bki = iter(real) self._next = None - self.__next__() + self.next() def __iter__(self): return self - def __next__(self): + def next(self): if self._next is self.end: raise StopIteration() ret = self._next @@@ -112,6 -115,16 +115,6 @@@ def pregen(callable) wrapper.__wrapped__ = callable return wrapper -def stringwrap(charset): - def dec(callable): - @pregen - def wrapper(*args, **kwargs): - for string in callable(*args, **kwargs): - yield string.encode(charset) - wrapper.__wrapped__ = callable - return wrapper - return dec - class sessiondata(object): @classmethod def get(cls, req, create=True): @@@ -133,7 -146,7 +136,7 @@@ class autodirty(sessiondata): @classmethod def get(cls, req): - ret = super().get(req) + ret = super(autodirty, cls).get(req) if "_is_dirty" not in ret.__dict__: ret.__dict__["_is_dirty"] = False return ret @@@ -145,18 -158,18 +148,18 @@@ return self._is_dirty def __setattr__(self, name, value): - super().__setattr__(name, value) + super(autodirty, self).__setattr__(name, value) if "_is_dirty" in self.__dict__: self.__dict__["_is_dirty"] = True def __delattr__(self, name): - super().__delattr__(name, value) + super(autodirty, self).__delattr__(name, value) if "_is_dirty" in self.__dict__: self.__dict__["_is_dirty"] = True class manudirty(object): def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + super(manudirty, self).__init__(*args, **kwargs) self.__dirty = False def sessfrozen(self): @@@ -199,7 -212,7 +202,7 @@@ class specslot(object) class specclass(type): def __init__(self, name, bases, tdict): - super().__init__(name, bases, tdict) + super(specclass, self).__init__(name, bases, tdict) sslots = set() dslots = set() for cls in self.__mro__: @@@ -211,8 -224,7 +214,8 @@@ for i, slot in enumerate(self.__sslots_a__): setattr(self, slot, specslot(slot, i, slot in dslots)) -class specdirty(sessiondata, metaclass=specclass): +class specdirty(sessiondata): + __metaclass__ = specclass __slots__ = ["session", "__sslots__", "_is_dirty"] def __specinit__(self): @@@ -220,7 -232,7 +223,7 @@@ @staticmethod def __new__(cls, req, sess): - self = super().__new__(cls) + self = super(specdirty, cls).__new__(cls) self.session = sess self.__sslots__ = [specslot.unbound] * len(cls.__sslots_a__) self.__specinit__()