Comply better with the CGI specification by unquoting PATH_INFO.
[ashd.git] / python / ashd-wsgi
index d334765..7462492 100755 (executable)
@@ -1,7 +1,7 @@
 #!/usr/bin/python
 
 import sys, os, getopt, threading
-import ashd.proto
+import ashd.proto, ashd.util
 
 def usage(out):
     out.write("usage: ashd-wsgi [-hA] [-p MODPATH] HANDLER-MODULE [ARGS...]\n")
@@ -29,13 +29,52 @@ if not modwsgi_compat:
     if not hasattr(handlermod, "wmain"):
         sys.stderr.write("ashd-wsgi: handler %s has no `wmain' function\n" % args[0])
         sys.exit(1)
-    handler = handlermod.wmain(args[1:])
+    handler = handlermod.wmain(*args[1:])
 else:
     if not hasattr(handlermod, "application"):
         sys.stderr.write("ashd-wsgi: handler %s has no `application' object\n" % args[0])
         sys.exit(1)
     handler = handlermod.application
 
+cwd = os.getcwd()
+def absolutify(path):
+    if path[0] != '/':
+        return os.path.join(cwd, path)
+    return path
+
+def unquoteurl(url):
+    buf = ""
+    i = 0
+    while i < len(url):
+        c = url[i]
+        i += 1
+        if c == '%':
+            if len(url) > i + 2:
+                c = 0
+                if '0' <= url[i] <= '9':
+                    c |= (ord(url[i]) - ord('0')) << 4
+                elif 'a' <= url[i] <= 'f':
+                    c |= (ord(url[i]) - ord('a')) << 4
+                elif 'A' <= url[i] <= 'F':
+                    c |= (ord(url[i]) - ord('A')) << 4
+                else:
+                    raise ValueError("Illegal URL escape character")
+                if '0' <= url[i + 1] <= '9':
+                    c |= ord(url[i + 1]) - ord('0')
+                elif 'a' <= url[i + 1] <= 'f':
+                    c |= ord(url[i + 1]) - ord('a')
+                elif 'A' <= url[i + 1] <= 'F':
+                    c |= ord(url[i + 1]) - ord('A')
+                else:
+                    raise ValueError("Illegal URL escape character")
+                buf += chr(c)
+                i += 2
+            else:
+                raise ValueError("Incomplete URL escape character")
+        else:
+            buf += c
+    return buf
+
 def dowsgi(req):
     env = {}
     env["wsgi.version"] = 1, 0
@@ -46,12 +85,15 @@ def dowsgi(req):
     env["SERVER_PROTOCOL"] = req.ver
     env["REQUEST_METHOD"] = req.method
     env["REQUEST_URI"] = req.url
-    env["PATH_INFO"] = req.rest
+    try:
+        env["PATH_INFO"] = unquoteurl(req.rest)
+    except:
+        env["PATH_INFO"] = req.rest
     name = req.url
     p = name.find('?')
     if p >= 0:
-        name = name[:p]
         env["QUERY_STRING"] = name[p + 1:]
+        name = name[:p]
     else:
         env["QUERY_STRING"] = ""
     if name[-len(req.rest):] == req.rest:
@@ -63,7 +105,7 @@ def dowsgi(req):
     if "X-Ash-Address" in req: env["REMOTE_ADDR"] = req["X-Ash-Address"]
     if "Content-Type" in req: env["CONTENT_TYPE"] = req["Content-Type"]
     if "Content-Length" in req: env["CONTENT_LENGTH"] = req["Content-Length"]
-    if "X-Ash-File" in req: env["SCRIPT_FILENAME"] = req["X-Ash-File"]
+    if "X-Ash-File" in req: env["SCRIPT_FILENAME"] = absolutify(req["X-Ash-File"])
     if "X-Ash-Protocol" in req: env["wsgi.url_scheme"] = req["X-Ash-Protocol"]
     env["wsgi.input"] = req.sk
     env["wsgi.errors"] = sys.stderr
@@ -74,9 +116,7 @@ def dowsgi(req):
     resp = []
     respsent = []
 
-    def write(data):
-        if not data:
-            return
+    def flushreq():
         if not respsent:
             if not resp:
                 raise Exception, "Trying to write data before starting response."
@@ -86,6 +126,11 @@ def dowsgi(req):
             for nm, val in headers:
                 req.sk.write("%s: %s\n" % (nm, val))
             req.sk.write("\n")
+
+    def write(data):
+        if not data:
+            return
+        flushreq()
         req.sk.write(data)
         req.sk.flush()
 
@@ -106,7 +151,8 @@ def dowsgi(req):
     try:
         for data in respiter:
             write(data)
-        write("")
+        if resp:
+            flushreq()
     finally:
         if hasattr(respiter, "close"):
             respiter.close()
@@ -125,4 +171,4 @@ class reqthread(threading.Thread):
 def handle(req):
     reqthread(req).start()
 
-ashd.proto.serveloop(handle)
+ashd.util.serveloop(handle)