1 """High-level utility module for ashd(7)
3 This module implements a rather convenient interface for writing ashd
4 handlers, wrapping the low-level ashd.proto module.
7 import os, socket, collections
10 __all__ = ["stdfork", "pchild", "respond", "serveloop"]
12 def stdfork(argv, chinit = None):
13 """Fork a persistent handler process using the `argv' argument
14 list, as per the standard ashd(7) calling convention. For an
15 easier-to-use interface, see the `pchild' class.
17 If a callable object of no arguments is provided in the `chinit'
18 argument, it will be called in the child process before exec()'ing
19 the handler program, and can be used to set parameters for the new
20 process, such as working directory, nice level or ulimits.
22 Returns the file descriptor of the socket for sending requests to
25 csk, psk = socket.socketpair(socket.AF_UNIX, socket.SOCK_SEQPACKET)
29 os.dup2(csk.fileno(), 0)
30 for fd in range(3, 1024):
35 if chinit is not None:
37 os.execvp(argv[0], argv)
41 fd = os.dup(psk.fileno())
46 """class pchild(argv, autorespawn=False, chinit=None)
48 Represents a persistent child handler process, started as per the
49 standard ashd(7) calling convention. It will be called with the
50 `argv' argument lest, which should be a list (or other iterable)
53 If `autorespawn' is specified as True, the child process will be
54 automatically restarted if a request cannot be successfully sent
57 For a description of the `chinit' argument, see `stdfork'.
59 When this child handler should be disposed of, care should be
60 taken to call the close() method to release its socket and let it
61 exit. This class also implements the resource-manager interface,
62 so that it can be used in `with' statements.
65 def __init__(self, argv, autorespawn = False, chinit = None):
69 self.respawn = autorespawn
73 """Start the child handler, or restart it if it is already
74 running. You should not have to call this method manually
75 unless you explicitly want to manage the process' lifecycle.
78 self.fd = stdfork(self.argv, self.chinit)
81 """Close this child handler's socket. For normal child
82 handlers, this will make the program terminate normally.
91 def passreq(self, req):
92 """Pass the specified request (which should be an instance of
93 the ashd.proto.req class) to this child handler. If the child
94 handler fails for some reason, and `autorespawn' was specified
95 as True when creating this handler, one attempt will be made
98 Note: You still need to close the request normally.
100 This method may raise an OSError if the request fails and
101 autorespawning was either not requested, or if the
102 autorespawning fails.
105 proto.sendreq(self.fd, req)
109 proto.sendreq(self.fd, req)
114 def __exit__(self, *excinfo):
118 def respond(req, body, status = ("200 OK"), ctype = "text/html"):
119 """Simple function for conveniently responding to a request.
121 Sends the specified body text to the request's response socket,
122 prepending an HTTP header with the appropriate Content-Type and
123 Content-Length headers, and then closes the response socket.
125 The `status' argument can be used to specify a non-200 response,
126 and the `ctype' argument can be used to specify a non-HTML
129 If `body' is not a byte string, its string representation will be
133 respond(req, "Not found", status = "404 Not Found", ctype = "text/plain")
135 if isinstance(body, collections.ByteString):
139 body = body.encode("utf-8")
140 if ctype[:5] == "text/" and ctype.find(';') < 0:
141 ctype = ctype + "; charset=utf-8"
144 head += "HTTP/1.1 %s\n" % status
145 head += "Content-Type: %s\n" % ctype
146 head += "Content-Length: %i\n" % len(body)
148 req.sk.write(head.encode("ascii"))
153 def serveloop(handler, sock = 0):
154 """Implements a simple loop for serving requests sequentially, by
155 receiving requests from standard input (or the specified socket),
156 passing them to the specified handler function, and finally making
157 sure to close them. Returns when end-of-file is received on the
160 The handler function should be a callable object of one argument,
161 and is called once for each received request.
164 req = proto.recvreq(sock)