Merge branch 'master' into timeheap
[ashd.git] / python3 / scgi-wsgi3
CommitLineData
55fa3f63 1#!/usr/bin/python3
c06db49a 2
79662f8c 3import sys, os, getopt, logging, collections
c06db49a 4import socket
19ff507b 5import ashd.scgi, ashd.serve
0cd97ae2
FT
6try:
7 import pdm.srv
8except:
9 pdm = None
c06db49a
FT
10
11def usage(out):
8db41888 12 out.write("usage: scgi-wsgi3 [-hAL] [-m PDM-SPEC] [-p MODPATH] [-t REQUEST-HANDLER[:PAR[=VAL](,PAR[=VAL])...]] [-T [HOST:]PORT] HANDLER-MODULE [ARGS...]\n")
c06db49a
FT
13
14sk = None
8db41888 15hspec = "free", {}
c06db49a 16modwsgi_compat = False
78c8462c 17setlog = True
8db41888 18opts, args = getopt.getopt(sys.argv[1:], "+hALp:t:T:m:")
c06db49a
FT
19for o, a in opts:
20 if o == "-h":
21 usage(sys.stdout)
22 sys.exit(0)
23 elif o == "-p":
e4769c65 24 sys.path.insert(0, a)
78c8462c
FT
25 elif o == "-L":
26 setlog = False
c06db49a
FT
27 elif o == "-T":
28 sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
78c8462c 29 sk.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
c06db49a
FT
30 p = a.rfind(":")
31 if p < 0:
075a379e 32 bindhost = "localhost"
c06db49a
FT
33 bindport = int(a)
34 else:
35 bindhost = a[:p]
36 bindport = int(a[p + 1:])
37 sk.bind((bindhost, bindport))
38 sk.listen(32)
39 elif o == "-A":
40 modwsgi_compat = True
0cd97ae2
FT
41 elif o == "-m":
42 if pdm is not None:
43 pdm.srv.listen(a)
8db41888
FT
44 elif o == "-t":
45 hspec = ashd.serve.parsehspec(a)
c06db49a
FT
46if len(args) < 1:
47 usage(sys.stderr)
48 sys.exit(1)
78c8462c 49if setlog:
b327e4c1 50 logging.basicConfig(format="scgi-wsgi3(%(name)s): %(levelname)s: %(message)s")
c06db49a
FT
51
52if sk is None:
53 # This is suboptimal, since the socket on stdin is not necessarily
54 # AF_UNIX, but Python does not seem to offer any way around it,
55 # that I can find.
56 sk = socket.fromfd(0, socket.AF_UNIX, socket.SOCK_STREAM)
57
58try:
59 handlermod = __import__(args[0], fromlist = ["dummy"])
55fa3f63 60except ImportError as exc:
1f3d7aa3 61 sys.stderr.write("scgi-wsgi3: handler %s not found: %s\n" % (args[0], exc.args[0]))
c06db49a
FT
62 sys.exit(1)
63if not modwsgi_compat:
64 if not hasattr(handlermod, "wmain"):
1f3d7aa3 65 sys.stderr.write("scgi-wsgi3: handler %s has no `wmain' function\n" % args[0])
c06db49a 66 sys.exit(1)
adb11d5f 67 handler = handlermod.wmain(*args[1:])
c06db49a
FT
68else:
69 if not hasattr(handlermod, "application"):
1f3d7aa3 70 sys.stderr.write("scgi-wsgi3: handler %s has no `application' object\n" % args[0])
c06db49a
FT
71 sys.exit(1)
72 handler = handlermod.application
73
79662f8c
FT
74def mkenv(head, sk):
75 try:
76 env = ashd.scgi.decodehead(head, "utf-8")
77 env["wsgi.uri_encoding"] = "utf-8"
78 except UnicodeError:
79 env = ashd.scgi.decodehead(head, "latin-1")
80 env["wsgi.uri_encoding"] = "latin-1"
81 env["wsgi.version"] = 1, 0
82 if "HTTP_X_ASH_PROTOCOL" in env:
83 env["wsgi.url_scheme"] = env["HTTP_X_ASH_PROTOCOL"]
84 elif "HTTPS" in env:
85 env["wsgi.url_scheme"] = "https"
86 else:
87 env["wsgi.url_scheme"] = "http"
88 env["wsgi.input"] = sk
89 env["wsgi.errors"] = sys.stderr
90 env["wsgi.multithread"] = True
91 env["wsgi.multiprocess"] = False
92 env["wsgi.run_once"] = False
93 return env
94
95def recode(thing):
96 if isinstance(thing, collections.ByteString):
97 return thing
98 else:
99 return str(thing).encode("latin-1")
100
c9aa6b28
FT
101class request(ashd.serve.wsgirequest):
102 def __init__(self, *, sk, **kw):
103 super().__init__(**kw)
fbcb2f86
FT
104 self.bsk = sk.dup()
105 self.sk = self.bsk.makefile("rwb")
c9aa6b28
FT
106
107 def mkenv(self):
fbcb2f86 108 return mkenv(ashd.scgi.readhead(self.sk), self.sk)
c9aa6b28
FT
109
110 def handlewsgi(self, env, startreq):
111 return handler(env, startreq)
112
113 def fileno(self):
fbcb2f86 114 return self.bsk.fileno()
79662f8c
FT
115
116 def writehead(self, status, headers):
c9aa6b28
FT
117 w = self.buffer.extend
118 w(b"Status: " + recode(status) + b"\n")
79662f8c 119 for nm, val in headers:
c9aa6b28
FT
120 w(recode(nm) + b": " + recode(val) + b"\n")
121 w(b"\n")
79662f8c 122
c9aa6b28 123 def flush(self):
79662f8c 124 try:
fbcb2f86 125 ret = self.bsk.send(self.buffer, socket.MSG_DONTWAIT)
c9aa6b28 126 self.buffer[:ret] = b""
79662f8c
FT
127 except IOError:
128 raise ashd.serve.closed()
129
c9aa6b28
FT
130 def close(self):
131 self.sk.close()
fbcb2f86 132 self.bsk.close()
79662f8c 133
8db41888 134if hspec[0] not in ashd.serve.names:
19ff507b 135 sys.stderr.write("scgi-wsgi3: no such request handler: %s\n" % hspec[0])
8db41888
FT
136 sys.exit(1)
137hclass = ashd.serve.names[hspec[0]]
138try:
139 hargs = hclass.parseargs(**hspec[1])
140except ValueError as exc:
19ff507b 141 sys.stderr.write("scgi-wsgi3: %s\n" % exc)
8db41888
FT
142 sys.exit(1)
143
144reqhandler = hclass(**hargs)
c67f9f8b
FT
145try:
146 while True:
147 nsk, addr = sk.accept()
148 try:
149 reqhandler.handle(request(sk=nsk, handler=reqhandler))
150 finally:
151 nsk.close()
152finally:
153 reqhandler.close()