Merge branch 'master' into python3
authorFredrik Tolf <fredrik@dolda2000.com>
Wed, 14 Mar 2012 20:34:22 +0000 (21:34 +0100)
committerFredrik Tolf <fredrik@dolda2000.com>
Wed, 14 Mar 2012 20:34:22 +0000 (21:34 +0100)
Conflicts:
wrw/__init__.py

wrw/__init__.py
wrw/form.py
wrw/proto.py
wrw/req.py

index d4b0a63..6458f64 100644 (file)
@@ -1,5 +1,6 @@
 __all__ = ["wsgiwrap", "restart", "cookie", "formdata"]
 
+from . import proto
 from .util import wsgiwrap, formparams, persession, sessiondata, autodirty, manudirty
 from .dispatch import restart
 from . import cookie
index dd1d5e9..d089fd7 100644 (file)
@@ -1,4 +1,5 @@
 import cgi
+from . import proto
 
 __all__ = ["formdata"]
 
@@ -36,5 +37,143 @@ class formwrap(object):
     def values(self):
         return [val for key, val in self.items()]
 
+class badmultipart(Exception):
+    pass
+
+class formpart(object):
+    def __init__(self, form):
+        self.form = form
+        self.buf = ""
+        self.eof = False
+        self.head = {}
+
+    def parsehead(self):
+        pass
+
+    def fillbuf(self, sz):
+        req = self.form.req
+        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:
+                self.buf += self.form.buf[:p]
+                self.form.buf = self.form.buf[p + len(mboundary):]
+                self.eof = True
+                break
+            p = self.form.buf.find(lboundary)
+            if p >= 0:
+                self.buf += self.form.buf[:p]
+                self.form.buf = self.form.buf[p + len(lboundary):]
+                self.eof = True
+                self.form.eof = True
+                break
+            self.buf += self.form.buf[:-len(lboundary)]
+            self.form.buf = self.form.buf[-len(lboundary):]
+            if sz >= 0 and len(self.buf) >= sz:
+                break
+            while len(self.form.buf) <= len(lboundary):
+                ret = req.env["wsgi.input"].read(8192)
+                if ret == "":
+                    raise badmultipart("Missing last multipart boundary")
+                self.form.buf += ret
+
+    def read(self, limit = -1):
+        self.fillbuf(limit)
+        if limit >= 0:
+            ret = self.buf[:limit]
+            self.buf = self.buf[limit:]
+        else:
+            ret = self.buf
+            self.buf = ""
+        return ret
+
+    def readline(self, limit = -1):
+        last = 0
+        while True:
+            p = self.buf.find('\n', last)
+            if p < 0:
+                if self.eof:
+                    ret = self.buf
+                    self.buf = ""
+                    return ret
+                last = len(self.buf)
+                self.fillbuf(last + 128)
+            else:
+                ret = self.buf[:p + 1]
+                self.buf = self.buf[p + 1:]
+                return ret
+
+    def close(self):
+        self.fillbuf(-1)
+
+    def __enter__(self):
+        return self
+
+    def __exit__(self, *excinfo):
+        return False
+
+    def parsehead(self):
+        def headline():
+            ln = self.readline(256)
+            if ln[-1] != '\n':
+                raise badmultipart("Too long header line in part")
+            return ln.rstrip()
+
+        ln = headline()
+        while True:
+            if ln == "":
+                break
+            buf = ln
+            while True:
+                ln = headline()
+                if not ln[1:].isspace():
+                    break
+                buf += ln.lstrip()
+            p = buf.find(':')
+            if p < 0:
+                raise badmultipart("Malformed multipart header line")
+            self.head[buf[:p].strip().lower()] = buf[p + 1:].lstrip()
+
+        val, par = proto.pmimehead(self.head.get("content-disposition", ""))
+        if val != "form-data":
+            raise badmultipart("Unexpected Content-Disposition in form part: %r" % val)
+        if not "name" in par:
+            raise badmultipart("Missing name in form part")
+        self.name = par["name"]
+        self.filename = par.get("filename")
+        val, par = proto.pmimehead(self.head.get("content-type", ""))
+        self.ctype = val
+        self.charset = par.get("charset")
+        encoding = self.head.get("content-transfer-encoding", "binary")
+        if encoding != "binary":
+            raise badmultipart("Form part uses unexpected transfer encoding: %r" % encoding)
+
+class multipart(object):
+    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")
+        self.boundary = par["boundary"]
+        self.req = req
+        self.buf = "\r\n"
+        self.eof = False
+        self.lastpart = formpart(self)
+        self.lastpart.close()
+
+    def __iter__(self):
+        return 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()
+        return self.lastpart
+
 def formdata(req):
     return req.item(formwrap)
index 6b43396..7df66ff 100644 (file)
@@ -17,6 +17,55 @@ def phttpdate(dstr):
     tz = (((tz / 100) * 60) + (tz % 100)) * 60
     return time.mktime(time.strptime(dstr, "%a, %d %b %Y %H:%M:%S")) - tz - time.altzone
 
+def pmimehead(hstr):
+    def pws(p):
+        while p < len(hstr) and hstr[p].isspace():
+            p += 1
+        return p
+    def token(p, sep):
+        buf = ""
+        p = pws(p)
+        if p >= len(hstr):
+            return "", p
+        if hstr[p] == '"':
+            p += 1
+            while p < len(hstr):
+                if hstr[p] == '\\':
+                    p += 1
+                    if p < len(hstr):
+                        buf += hstr[p]
+                        p += 1
+                    else:
+                        break
+                elif hstr[p] == '"':
+                    p += 1
+                    break
+                else:
+                    buf += hstr[p]
+                    p += 1
+            return buf, pws(p)
+        else:
+            while p < len(hstr):
+                if hstr[p] in sep:
+                    break
+                buf += hstr[p]
+                p += 1
+            return buf.strip(), pws(p)
+    p = 0
+    val, p = token(p, ";")
+    pars = {}
+    while p < len(hstr):
+        if hstr[p] != ';':
+            break
+        p += 1
+        k, p = token(p, "=")
+        if k == "" or hstr[p:p + 1] != '=':
+            break
+        p += 1
+        v, p = token(p, ';')
+        pars[k.lower()] = v
+    return val, pars
+
 def htmlq(html):
     ret = ""
     for c in html:
index 0689cbd..82a1f84 100644 (file)
@@ -62,6 +62,7 @@ class request(object):
 class origrequest(request):
     def __init__(self, env):
         self.env = env
+        self.method = env["REQUEST_METHOD"].upper()
         self.uriname = env["SCRIPT_NAME"]
         self.filename = env.get("SCRIPT_FILENAME")
         self.uri = env["REQUEST_URI"]
@@ -136,6 +137,7 @@ class copyrequest(request):
         self.parent = p
         self.top = p.topreq()
         self.env = p.env
+        self.method = p.method
         self.uriname = p.uriname
         self.filename = p.filename
         self.uri = p.uri