]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - src/allmydata/web/common.py
webish: complete rewrite, break into smaller pieces, auto-create directories, improve...
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / web / common.py
1
2 from twisted.web import http, server
3 from zope.interface import Interface
4 from nevow import loaders, appserver
5 from nevow.inevow import IRequest
6 from nevow.util import resource_filename
7 from allmydata.interfaces import ExistingChildError
8
9 class IClient(Interface):
10     pass
11
12
13 def getxmlfile(name):
14     return loaders.xmlfile(resource_filename('allmydata.web', '%s' % name))
15
16 def boolean_of_arg(arg):
17     # TODO: ""
18     assert arg.lower() in ("true", "t", "1", "false", "f", "0", "on", "off")
19     return arg.lower() in ("true", "t", "1", "on")
20
21 def get_arg(req, argname, default=None, multiple=False):
22     """Extract an argument from either the query args (req.args) or the form
23     body fields (req.fields). If multiple=False, this returns a single value
24     (or the default, which defaults to None), and the query args take
25     precedence. If multiple=True, this returns a tuple of arguments (possibly
26     empty), starting with all those in the query args.
27     """
28     results = []
29     if argname in req.args:
30         results.extend(req.args[argname])
31     if req.fields and argname in req.fields:
32         results.append(req.fields[argname].value)
33     if multiple:
34         return tuple(results)
35     if results:
36         return results[0]
37     return default
38
39 def abbreviate_time(data):
40     # 1.23s, 790ms, 132us
41     if data is None:
42         return ""
43     s = float(data)
44     if s >= 1.0:
45         return "%.2fs" % s
46     if s >= 0.01:
47         return "%dms" % (1000*s)
48     if s >= 0.001:
49         return "%.1fms" % (1000*s)
50     return "%dus" % (1000000*s)
51
52 def abbreviate_rate(data):
53     # 21.8kBps, 554.4kBps 4.37MBps
54     if data is None:
55         return ""
56     r = float(data)
57     if r > 1000000:
58         return "%1.2fMBps" % (r/1000000)
59     if r > 1000:
60         return "%.1fkBps" % (r/1000)
61     return "%dBps" % r
62
63 def abbreviate_size(data):
64     # 21.8kB, 554.4kB 4.37MB
65     if data is None:
66         return ""
67     r = float(data)
68     if r > 1000000000:
69         return "%1.2fGB" % (r/1000000000)
70     if r > 1000000:
71         return "%1.2fMB" % (r/1000000)
72     if r > 1000:
73         return "%.1fkB" % (r/1000)
74     return "%dB" % r
75
76 def text_plain(text, ctx):
77     req = IRequest(ctx)
78     req.setHeader("content-type", "text/plain")
79     req.setHeader("content-length", len(text))
80     return text
81
82 class WebError(Exception):
83     def __init__(self, text, code=http.BAD_REQUEST):
84         self.text = text
85         self.code = code
86
87 # XXX: to make UnsupportedMethod return 501 NOT_IMPLEMENTED instead of 500
88 # Internal Server Error, we either need to do that ICanHandleException trick,
89 # or make sure that childFactory returns a WebErrorResource (and never an
90 # actual exception). The latter is growing increasingly annoying.
91
92 def should_create_intermediate_directories(req):
93     t = get_arg(req, "t", "").strip()
94     return bool(req.method in ("PUT", "POST") and
95                 t not in ("delete", "rename", "rename-form", "check"))
96
97
98 class MyExceptionHandler(appserver.DefaultExceptionHandler):
99     def simple(self, ctx, text, code=http.BAD_REQUEST):
100         req = IRequest(ctx)
101         req.setResponseCode(code)
102         req.setHeader("content-type", "text/plain;charset=utf-8")
103         if isinstance(text, unicode):
104             text = text.encode("utf-8")
105         req.write(text)
106         req.finishRequest(False)
107
108     def renderHTTP_exception(self, ctx, f):
109         if f.check(ExistingChildError):
110             return self.simple(ctx,
111                                "There was already a child by that "
112                                "name, and you asked me to not "
113                                "replace it.",
114                                http.CONFLICT)
115         elif f.check(WebError):
116             return self.simple(ctx, f.value.text, f.value.code)
117         elif f.check(server.UnsupportedMethod):
118             # twisted.web.server.Request.render() has support for transforming
119             # this into an appropriate 501 NOT_IMPLEMENTED or 405 NOT_ALLOWED
120             # return code, but nevow does not.
121             req = IRequest(ctx)
122             method = req.method
123             return self.simple(ctx,
124                                "I don't know how to treat a %s request." % method,
125                                http.NOT_IMPLEMENTED)
126         super = appserver.DefaultExceptionHandler
127         return super.renderHTTP_exception(self, ctx, f)