Commit | Line | Data |
---|---|---|
1644fdac | 1 | import sys, os, math |
3614ca83 | 2 | from . import dispatch, proto, env |
7bcaef26 | 3 | from .sp import xhtml |
ef04ec87 | 4 | h = xhtml.cons() |
dc7e5d54 FT |
5 | |
6 | __all__ = ["skeleton", "skelfor", "setskel", "usererror"] | |
7 | ||
8 | class skeleton(object): | |
3162cb89 FT |
9 | def page(self, req, title, *content): |
10 | return xhtml.forreq(req, h.html(self.head(req, title), h.body(*content))) | |
dc7e5d54 | 11 | |
3162cb89 | 12 | def head(self, req, title): |
ef04ec87 | 13 | return xhtml.head(title=title) |
dc7e5d54 | 14 | |
3162cb89 FT |
15 | def error(self, req, message, *detail): |
16 | return self.page(req, message, h.h1(message), h.p(*detail)) | |
dc7e5d54 | 17 | |
3162cb89 FT |
18 | def message(self, req, message, *detail): |
19 | return self.page(req, message, h.h1(message), h.p(*detail)) | |
dc7e5d54 | 20 | |
1f61bf31 | 21 | defskel = env.var(skeleton()) |
dc7e5d54 FT |
22 | |
23 | def getskel(req): | |
1f61bf31 | 24 | return [defskel.val] |
dc7e5d54 FT |
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): | |
ef04ec87 | 31 | def __init__(self, message, *detail): |
c33f2d6c | 32 | super().__init__() |
dc7e5d54 FT |
33 | self.message = message |
34 | self.detail = detail | |
35 | ||
36 | def handle(self, req): | |
3162cb89 | 37 | return skelfor(req).error(req, self.message, *self.detail) |
dc7e5d54 FT |
38 | |
39 | class message(dispatch.restart): | |
7bcaef26 | 40 | def __init__(self, message, *detail): |
c33f2d6c | 41 | super().__init__() |
dc7e5d54 FT |
42 | self.message = message |
43 | self.detail = detail | |
44 | ||
45 | def handle(self, req): | |
98cc090c | 46 | return skelfor(req).message(req, self.message, *self.detail) |
dc7e5d54 FT |
47 | |
48 | class httperror(usererror): | |
9bc70dab | 49 | def __init__(self, status, message=None, detail=None): |
dc7e5d54 FT |
50 | if message is None: |
51 | message = proto.statusinfo[status][0] | |
52 | if detail is None: | |
ef04ec87 | 53 | detail = (proto.statusinfo[status][1],) |
7bcaef26 | 54 | super().__init__(message, *detail) |
dc7e5d54 FT |
55 | self.status = status |
56 | ||
57 | def handle(self, req): | |
58 | req.status(self.status, self.message) | |
c33f2d6c | 59 | return super().handle(req) |
dc7e5d54 FT |
60 | |
61 | class notfound(httperror): | |
62 | def __init__(self): | |
c33f2d6c | 63 | return super().__init__(404) |
4bd573f9 FT |
64 | |
65 | class redirect(dispatch.restart): | |
d0034dee FT |
66 | bases = {"url": proto.requrl, |
67 | "script": proto.scripturl, | |
68 | "site": proto.siteurl} | |
69 | ||
9bc70dab | 70 | def __init__(self, url, status=303, base="url"): |
c33f2d6c | 71 | super().__init__() |
4bd573f9 FT |
72 | self.url = url |
73 | self.status = status | |
d0034dee FT |
74 | self.bases[base] |
75 | self.base = base | |
4bd573f9 FT |
76 | |
77 | def handle(self, req): | |
78 | req.status(self.status, "Redirect") | |
d0034dee | 79 | req.ohead["Location"] = proto.appendurl(self.bases[self.base](req), self.url) |
a7b35f84 | 80 | req.ohead["Content-Length"] = 0 |
4bd573f9 | 81 | return [] |
1864be32 FT |
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 [] | |
782b0af5 FT |
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): | |
1644fdac | 112 | def __init__(self, fp, ctype, charset=None, cachable=True): |
782b0af5 FT |
113 | self.fp = fp |
114 | self.ctype = ctype | |
1644fdac FT |
115 | if charset is None and ctype.startswith("text/"): |
116 | charset = sys.getdefaultencoding() | |
117 | self.charset = charset | |
118 | self.cachable = cachable | |
782b0af5 FT |
119 | |
120 | def handle(self, req): | |
1644fdac | 121 | sb = None |
782b0af5 | 122 | if hasattr(self.fp, "fileno"): |
1644fdac FT |
123 | sb = os.fstat(self.fp.fileno()) |
124 | if self.cachable and sb and sb.st_mtime != 0: | |
125 | if "If-Modified-Since" in req.ihead: | |
126 | rtime = proto.phttpdate(req.ihead["If-Modified-Since"]) | |
127 | if rtime is not None and rtime >= math.floor(sb.st_mtime): | |
128 | raise unmodified() | |
129 | req.ohead["Last-Modified"] = proto.httpdate(sb.st_mtime) | |
130 | ctype = self.ctype | |
131 | if self.charset is not None: | |
132 | ctype += "; charset=%s" % (self.charset) | |
133 | req.ohead["Content-Type"] = ctype | |
134 | if sb and sb.st_size > 0: | |
135 | req.ohead["Content-Length"] = str(sb.st_size) | |
782b0af5 | 136 | return fileiter(self.fp) |