source file: /home/buildslave/tahoe/edgy/build/src/allmydata/web/common.py
file stats: 101 lines, 98 executed: 97.0% covered
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, FileTooLargeError
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(FileTooLargeError):
118. return self.simple(ctx, str(f.value), http.REQUEST_ENTITY_TOO_LARGE)
119. elif f.check(server.UnsupportedMethod):
120. # twisted.web.server.Request.render() has support for transforming
121. # this into an appropriate 501 NOT_IMPLEMENTED or 405 NOT_ALLOWED
122. # return code, but nevow does not.
123. req = IRequest(ctx)
124. method = req.method
125. return self.simple(ctx,
126. "I don't know how to treat a %s request." % method,
127. http.NOT_IMPLEMENTED)
128. super = appserver.DefaultExceptionHandler
129. return super.renderHTTP_exception(self, ctx, f)
130.
131. class RenderMixin:
132.
133. def renderHTTP(self, ctx):
134. request = IRequest(ctx)
135.
136. # if we were using regular twisted.web Resources (and the regular
137. # twisted.web.server.Request object) then we could implement
138. # render_PUT and render_GET. But Nevow's request handler
139. # (NevowRequest.gotPageContext) goes directly to renderHTTP. Copy
140. # some code from the Resource.render method that Nevow bypasses, to
141. # do the same thing.
142. m = getattr(self, 'render_' + request.method, None)
143. if not m:
144. from twisted.web.server import UnsupportedMethod
145. raise UnsupportedMethod(getattr(self, 'allowedMethods', ()))
146. return m(ctx)