00c7045ffdd03799b62502212c43ec8b569c7dd8
[wrw.git] / wrw / resp.py
1 import os
2 from . import dispatch, proto, env
3 from .sp import xhtml
4 h = xhtml.cons()
5
6 __all__ = ["skeleton", "skelfor", "setskel", "usererror"]
7
8 class skeleton(object):
9     def page(self, req, title, *content):
10         return xhtml.forreq(req, h.html(self.head(req, title), h.body(*content)))
11
12     def head(self, req, title):
13         return xhtml.head(title=title)
14
15     def error(self, req, message, *detail):
16         return self.page(req, message, h.h1(message), h.p(*detail))
17
18     def message(self, req, message, *detail):
19         return self.page(req, message, h.h1(message), h.p(*detail))
20
21 defskel = env.var(skeleton())
22
23 def getskel(req):
24     return [defskel.val]
25 def skelfor(req):
26     return req.item(getskel)[0]
27 def setskel(req, skel):
28     req.item(getskel)[0] = skel
29
30 class usererror(dispatch.restart):
31     def __init__(self, message, *detail):
32         super().__init__()
33         self.message = message
34         self.detail = detail
35
36     def handle(self, req):
37         return skelfor(req).error(req, self.message, *self.detail)
38
39 class message(dispatch.restart):
40     def __init__(self, message, *detail):
41         super().__init__()
42         self.message = message
43         self.detail = detail
44
45     def handle(self, req):
46         return skelfor(req).message(req, self.message, *self.detail)
47
48 class httperror(usererror):
49     def __init__(self, status, message=None, detail=None):
50         if message is None:
51             message = proto.statusinfo[status][0]
52         if detail is None:
53             detail = (proto.statusinfo[status][1],)
54         super().__init__(message, *detail)
55         self.status = status
56
57     def handle(self, req):
58         req.status(self.status, self.message)
59         return super().handle(req)
60
61 class notfound(httperror):
62     def __init__(self):
63         return super().__init__(404)
64
65 class redirect(dispatch.restart):
66     bases = {"url": proto.requrl,
67              "script": proto.scripturl,
68              "site": proto.siteurl}
69
70     def __init__(self, url, status=303, base="url"):
71         super().__init__()
72         self.url = url
73         self.status = status
74         self.bases[base]
75         self.base = base
76
77     def handle(self, req):
78         req.status(self.status, "Redirect")
79         req.ohead["Location"] = proto.appendurl(self.bases[self.base](req), self.url)
80         req.ohead["Content-Length"] = 0
81         return []
82
83 class unmodified(dispatch.restart):
84     def handle(self, req):
85         req.status(304, "Not Modified")
86         req.ohead["Content-Length"] = "0"
87         return []
88
89 class fileiter(object):
90     def __init__(self, fp):
91         self.fp = fp
92
93     def __iter__(self):
94         return self
95
96     def __next__(self):
97         if self.fp is None:
98             raise StopIteration()
99         data = self.fp.read(16384)
100         if data == b"":
101             self.fp.close()
102             self.fp = None
103             raise StopIteration()
104         return data
105
106     def close(self):
107         if self.fp is not None:
108             self.fp.close()
109             self.fp = None
110
111 class fileresp(dispatch.restart):
112     def __init__(self, fp, ctype):
113         self.fp = fp
114         self.ctype = ctype
115
116     def handle(self, req):
117         req.ohead["Content-Type"] = self.ctype
118         if hasattr(self.fp, "fileno"):
119             sz = os.fstat(self.fp.fileno()).st_size
120             if sz > 0:
121                 req.ohead["Content-Length"] = str(sz)
122         return fileiter(self.fp)