source file: /home/buildslave/tahoe/edgy/build/src/allmydata/webish.py
file stats: 83 lines, 80 executed: 96.4% covered
1.
2. import time
3. from twisted.application import service, strports, internet
4. from twisted.web import http
5. from twisted.internet import defer
6. from nevow import appserver, inevow
7. from allmydata.util import log
8.
9. from allmydata.web import introweb, root
10. from allmydata.web.common import IClient, MyExceptionHandler
11.
12. # we must override twisted.web.http.Request.requestReceived with a version
13. # that doesn't use cgi.parse_multipart() . Since we actually use Nevow, we
14. # override the nevow-specific subclass, nevow.appserver.NevowRequest . This
15. # is an exact copy of twisted.web.http.Request (from SVN HEAD on 10-Aug-2007)
16. # that modifies the way form arguments are parsed. Note that this sort of
17. # surgery may induce a dependency upon a particular version of twisted.web
18.
19. parse_qs = http.parse_qs
20. class MyRequest(appserver.NevowRequest):
21. fields = None
22. def requestReceived(self, command, path, version):
23. """Called by channel when all data has been received.
24.
25. This method is not intended for users.
26. """
27. self.content.seek(0,0)
28. self.args = {}
29. self.stack = []
30.
31. self.method, self.uri = command, path
32. self.clientproto = version
33. x = self.uri.split('?', 1)
34.
35. if len(x) == 1:
36. self.path = self.uri
37. else:
38. self.path, argstring = x
39. self.args = parse_qs(argstring, 1)
40.
41. # cache the client and server information, we'll need this later to be
42. # serialized and sent with the request so CGIs will work remotely
43. self.client = self.channel.transport.getPeer()
44. self.host = self.channel.transport.getHost()
45.
46. # Argument processing.
47.
48. ## The original twisted.web.http.Request.requestReceived code parsed the
49. ## content and added the form fields it found there to self.args . It
50. ## did this with cgi.parse_multipart, which holds the arguments in RAM
51. ## and is thus unsuitable for large file uploads. The Nevow subclass
52. ## (nevow.appserver.NevowRequest) uses cgi.FieldStorage instead (putting
53. ## the results in self.fields), which is much more memory-efficient.
54. ## Since we know we're using Nevow, we can anticipate these arguments
55. ## appearing in self.fields instead of self.args, and thus skip the
56. ## parse-content-into-self.args step.
57.
58. ## args = self.args
59. ## ctype = self.getHeader('content-type')
60. ## if self.method == "POST" and ctype:
61. ## mfd = 'multipart/form-data'
62. ## key, pdict = cgi.parse_header(ctype)
63. ## if key == 'application/x-www-form-urlencoded':
64. ## args.update(parse_qs(self.content.read(), 1))
65. ## elif key == mfd:
66. ## try:
67. ## args.update(cgi.parse_multipart(self.content, pdict))
68. ## except KeyError, e:
69. ## if e.args[0] == 'content-disposition':
70. ## # Parse_multipart can't cope with missing
71. ## # content-dispostion headers in multipart/form-data
72. ## # parts, so we catch the exception and tell the client
73. ## # it was a bad request.
74. ## self.channel.transport.write(
75. ## "HTTP/1.1 400 Bad Request\r\n\r\n")
76. ## self.channel.transport.loseConnection()
77. ## return
78. ## raise
79. self.processing_started_timestamp = time.time()
80. self.process()
81.
82. def _logger(self):
83. # we build up a log string that hides most of the cap, to preserve
84. # user privacy. We retain the query args so we can identify things
85. # like t=json. Then we send it to the flog. We make no attempt to
86. # match apache formatting. TODO: when we move to DSA dirnodes and
87. # shorter caps, consider exposing a few characters of the cap, or
88. # maybe a few characters of its hash.
89. x = self.uri.split("?", 1)
90. if len(x) == 1:
91. # no query args
92. path = self.uri
93. queryargs = ""
94. else:
95. path, queryargs = x
96. # there is a form handler which redirects POST /uri?uri=FOO into
97. # GET /uri/FOO so folks can paste in non-HTTP-prefixed uris. Make
98. # sure we censor these too.
99. if queryargs.startswith("uri="):
100. queryargs = "[uri=CENSORED]"
101. queryargs = "?" + queryargs
102. if path.startswith("/uri"):
103. path = "/uri/[CENSORED].."
104. elif path.startswith("/file"):
105. path = "/file/[CENSORED].."
106. elif path.startswith("/named"):
107. path = "/named/[CENSORED].."
108.
109. uri = path + queryargs
110.
111. log.msg(format="web: %(clientip)s %(method)s %(uri)s %(code)s %(length)s",
112. clientip=self.getClientIP(),
113. method=self.method,
114. uri=uri,
115. code=self.code,
116. length=(self.sentLength or "-"),
117. facility="tahoe.webish",
118. level=log.OPERATIONAL,
119. )
120.
121.
122.
123. class WebishServer(service.MultiService):
124. name = "webish"
125. root_class = root.Root
126.
127. def __init__(self, webport, nodeurl_path=None):
128. service.MultiService.__init__(self)
129. self.webport = webport
130. self.root = self.root_class()
131. self.site = site = appserver.NevowSite(self.root)
132. self.site.requestFactory = MyRequest
133. s = strports.service(webport, site)
134. s.setServiceParent(self)
135. self.listener = s # stash it so the tests can query for the portnum
136. self._started = defer.Deferred()
137. if nodeurl_path:
138. self._started.addCallback(self._write_nodeurl_file, nodeurl_path)
139.
140. def startService(self):
141. service.MultiService.startService(self)
142. # to make various services available to render_* methods, we stash a
143. # reference to the client on the NevowSite. This will be available by
144. # adapting the 'context' argument to a special marker interface named
145. # IClient.
146. self.site.remember(self.parent, IClient)
147. # I thought you could do the same with an existing interface, but
148. # apparently 'ISite' does not exist
149. #self.site._client = self.parent
150. self.site.remember(MyExceptionHandler(), inevow.ICanHandleException)
151. self._started.callback(None)
152.
153. def _write_nodeurl_file(self, junk, nodeurl_path):
154. # what is our webport?
155. s = self.listener
156. if isinstance(s, internet.TCPServer):
157. base_url = "http://127.0.0.1:%d/" % s._port.getHost().port
158. elif isinstance(s, internet.SSLServer):
159. base_url = "https://127.0.0.1:%d/" % s._port.getHost().port
160. else:
161. base_url = None
162. if base_url:
163. f = open(nodeurl_path, 'wb')
164. # this file is world-readable
165. f.write(base_url + "\n")
166. f.close()
167.
168. class IntroducerWebishServer(WebishServer):
169. root_class = introweb.IntroducerRoot