python: Added an alternative WSGI server that speaks ashd directly.
[ashd.git] / python / ashd / proto.py
1 import os, socket
2 import htlib
3
4 class protoerr(Exception):
5     pass
6
7 class req(object):
8     def __init__(self, method, url, ver, rest, headers, fd):
9         self.method = method
10         self.url = url
11         self.ver = ver
12         self.rest = rest
13         self.headers = headers
14         self.sk = socket.fromfd(fd, socket.AF_UNIX, socket.SOCK_STREAM).makefile('r+')
15         os.close(fd)
16
17     def close(self):
18         self.sk.close()
19
20     def __getitem__(self, header):
21         header = header.lower()
22         for key, val in self.headers:
23             if key.lower() == header:
24                 return val
25         raise KeyError(header)
26
27     def __contains__(self, header):
28         header = header.lower()
29         for key, val in self.headers:
30             if key.lower() == header:
31                 return True
32         return False
33
34     def dup(self):
35         return req(self.method, self.url, self.ver, self.rest, self.headers, os.dup(self.sk.fileno()))
36
37     def match(self, match):
38         if self.rest[:len(match)] == match:
39             self.rest = self.rest[len(match):]
40             return True
41         return False
42
43     def __str__(self):
44         return "\"%s %s %s\"" % (self.method, self.url, self.ver)
45
46     def __enter__(self):
47         return self
48
49     def __exit__(self, *excinfo):
50         self.sk.close()
51         return False
52
53 def recvreq(sock = 0):
54     data, fd = htlib.recvfd(sock)
55     if fd is None:
56         return None
57     try:
58         print repr(data)
59         parts = data.split('\0')[:-1]
60         if len(parts) < 5:
61             raise protoerr("Truncated request")
62         method, url, ver, rest = parts[:4]
63         headers = []
64         i = 4
65         while True:
66             if parts[i] == "": break
67             if len(parts) - i < 3:
68                 raise protoerr("Truncated request")
69             headers.append((parts[i], parts[i + 1]))
70             i += 2
71         return req(method, url, ver, rest, headers, os.dup(fd))
72     finally:
73         os.close(fd)
74
75 def sendreq(sock, req):
76     data = ""
77     data += req.method + '\0'
78     data += req.url + '\0'
79     data += req.ver + '\0'
80     data += req.rest + '\0'
81     for key, val in req.headers:
82         data += key + '\0'
83         data += val + '\0'
84     data += '\0'
85     htlib.sendfd(sock, req.sk.fileno(), data)
86
87 def serveloop(handler, sock = 0):
88     while True:
89         req = recvreq(sock)
90         if req is None:
91             break
92         try:
93             handler(req)
94         finally:
95             req.close()