python: Added a simple, non-threaded handler.
[ashd.git] / python3 / ashd / serve.py
index 6111ec6..ecc8d46 100644 (file)
@@ -75,7 +75,9 @@ class handler(object):
     def handle(self, request):
         raise Exception()
     def ckflush(self, req):
-        raise Exception()
+        while len(req.buffer) > 0:
+            rls, wls, els = select.select([], [req], [req])
+            req.flush()
     def close(self):
         pass
 
@@ -85,35 +87,61 @@ class handler(object):
             raise ValueError("unknown handler argument: " + next(iter(args)))
         return {}
 
+class single(handler):
+    def handle(self, req):
+        try:
+            env = req.mkenv()
+            with perf.request(env) as reqevent:
+                respiter = req.handlewsgi(env, req.startreq)
+                for data in respiter:
+                    req.write(data)
+                if req.status:
+                    reqevent.response([req.status, req.headers])
+                    req.flushreq()
+                self.ckflush(req)
+        except closed:
+            pass
+        except:
+            log.error("exception occurred when handling request", exc_info=True)
+        finally:
+            req.close()
+
 class freethread(handler):
-    def __init__(self, *, max=None, **kw):
+    def __init__(self, *, max=None, timeout=None, **kw):
         super().__init__(**kw)
         self.current = set()
         self.lk = threading.Lock()
         self.tcond = threading.Condition(self.lk)
         self.max = max
+        self.timeout = timeout
 
     @classmethod
-    def parseargs(cls, *, max=None, **args):
+    def parseargs(cls, *, max=None, abort=None, **args):
         ret = super().parseargs(**args)
         if max:
             ret["max"] = int(max)
+        if abort:
+            ret["timeout"] = int(abort)
         return ret
 
     def handle(self, req):
         with self.lk:
-            while self.max is not None and len(self.current) >= self.max:
-                self.tcond.wait()
+            if self.max is not None:
+                if self.timeout is not None:
+                    now = start = time.time()
+                    while len(self.current) >= self.max:
+                        self.tcond.wait(start + self.timeout - now)
+                        now = time.time()
+                        if now - start > self.timeout:
+                            os.abort()
+                else:
+                    while len(self.current) >= self.max:
+                        self.tcond.wait()
             th = reqthread(target=self.run, args=[req])
             th.start()
             while th.is_alive() and th not in self.current:
                 self.tcond.wait()
 
-    def ckflush(self, req):
-        while len(req.buffer) > 0:
-            rls, wls, els = select.select([], [req], [req])
-            req.flush()
-
     def run(self, req):
         try:
             th = threading.current_thread()
@@ -183,11 +211,6 @@ class threadpool(handler):
             while not th in self.current:
                 self.pcond.wait()
 
-    def ckflush(self, req):
-        while len(req.buffer) > 0:
-            rls, wls, els = select.select([], [req], [req])
-            req.flush()
-
     def _handle(self, req):
         try:
             env = req.mkenv()
@@ -260,7 +283,8 @@ class threadpool(handler):
                 self.rcond.notify_all()
                 self.pcond.wait(1)
 
-names = {"free": freethread,
+names = {"single": single,
+         "free": freethread,
          "pool": threadpool}
 
 def parsehspec(spec):