python: Added an alternative WSGI server that speaks ashd directly.
[ashd.git] / python / ashd / proto.py
diff --git a/python/ashd/proto.py b/python/ashd/proto.py
new file mode 100644 (file)
index 0000000..2c040c1
--- /dev/null
@@ -0,0 +1,95 @@
+import os, socket
+import htlib
+
+class protoerr(Exception):
+    pass
+
+class req(object):
+    def __init__(self, method, url, ver, rest, headers, fd):
+        self.method = method
+        self.url = url
+        self.ver = ver
+        self.rest = rest
+        self.headers = headers
+        self.sk = socket.fromfd(fd, socket.AF_UNIX, socket.SOCK_STREAM).makefile('r+')
+        os.close(fd)
+
+    def close(self):
+        self.sk.close()
+
+    def __getitem__(self, header):
+        header = header.lower()
+        for key, val in self.headers:
+            if key.lower() == header:
+                return val
+        raise KeyError(header)
+
+    def __contains__(self, header):
+        header = header.lower()
+        for key, val in self.headers:
+            if key.lower() == header:
+                return True
+        return False
+
+    def dup(self):
+        return req(self.method, self.url, self.ver, self.rest, self.headers, os.dup(self.sk.fileno()))
+
+    def match(self, match):
+        if self.rest[:len(match)] == match:
+            self.rest = self.rest[len(match):]
+            return True
+        return False
+
+    def __str__(self):
+        return "\"%s %s %s\"" % (self.method, self.url, self.ver)
+
+    def __enter__(self):
+        return self
+
+    def __exit__(self, *excinfo):
+        self.sk.close()
+        return False
+
+def recvreq(sock = 0):
+    data, fd = htlib.recvfd(sock)
+    if fd is None:
+        return None
+    try:
+        print repr(data)
+        parts = data.split('\0')[:-1]
+        if len(parts) < 5:
+            raise protoerr("Truncated request")
+        method, url, ver, rest = parts[:4]
+        headers = []
+        i = 4
+        while True:
+            if parts[i] == "": break
+            if len(parts) - i < 3:
+                raise protoerr("Truncated request")
+            headers.append((parts[i], parts[i + 1]))
+            i += 2
+        return req(method, url, ver, rest, headers, os.dup(fd))
+    finally:
+        os.close(fd)
+
+def sendreq(sock, req):
+    data = ""
+    data += req.method + '\0'
+    data += req.url + '\0'
+    data += req.ver + '\0'
+    data += req.rest + '\0'
+    for key, val in req.headers:
+        data += key + '\0'
+        data += val + '\0'
+    data += '\0'
+    htlib.sendfd(sock, req.sk.fileno(), data)
+
+def serveloop(handler, sock = 0):
+    while True:
+        req = recvreq(sock)
+        if req is None:
+            break
+        try:
+            handler(req)
+        finally:
+            req.close()