-
-import time
+import re, time
from twisted.application import service, strports, internet
from twisted.web import http
from twisted.internet import defer
-from nevow import appserver, inevow
-from allmydata.util import log
+from nevow import appserver, inevow, static
+from allmydata.util import log, fileutil
from allmydata.web import introweb, root
-from allmydata.web.common import IClient, MyExceptionHandler
+from allmydata.web.common import IOpHandleTable, MyExceptionHandler
# we must override twisted.web.http.Request.requestReceived with a version
# that doesn't use cgi.parse_multipart() . Since we actually use Nevow, we
parse_qs = http.parse_qs
class MyRequest(appserver.NevowRequest):
fields = None
+ _tahoe_request_had_error = None
+
def requestReceived(self, command, path, version):
"""Called by channel when all data has been received.
uri = path + queryargs
- log.msg(format="web: %(clientip)s %(method)s %(uri)s %(code)s %(length)s",
+ error = ""
+ if self._tahoe_request_had_error:
+ error = " [ERROR]"
+
+ log.msg(format="web: %(clientip)s %(method)s %(uri)s %(code)s %(length)s%(error)s",
clientip=self.getClientIP(),
method=self.method,
uri=uri,
code=self.code,
length=(self.sentLength or "-"),
+ error=error,
facility="tahoe.webish",
level=log.OPERATIONAL,
)
-
class WebishServer(service.MultiService):
name = "webish"
- root_class = root.Root
- def __init__(self, webport, nodeurl_path=None):
+ def __init__(self, client, webport, nodeurl_path=None, staticdir=None,
+ clock=None):
service.MultiService.__init__(self)
+ # the 'data' argument to all render() methods default to the Client
+ # the 'clock' argument to root.Root is, if set, a
+ # twisted.internet.task.Clock that is provided by the unit tests
+ # so that they can test features that involve the passage of
+ # time in a deterministic manner.
+ self.root = root.Root(client, clock)
+ self.buildServer(webport, nodeurl_path, staticdir)
+ if self.root.child_operations:
+ self.site.remember(self.root.child_operations, IOpHandleTable)
+ self.root.child_operations.setServiceParent(self)
+
+ def buildServer(self, webport, nodeurl_path, staticdir):
self.webport = webport
- self.root = self.root_class()
self.site = site = appserver.NevowSite(self.root)
self.site.requestFactory = MyRequest
+ self.site.remember(MyExceptionHandler(), inevow.ICanHandleException)
+ self.staticdir = staticdir # so tests can check
+ if staticdir:
+ self.root.putChild("static", static.File(staticdir))
+ if re.search(r'^\d', webport):
+ webport = "tcp:"+webport # twisted warns about bare "0" or "3456"
s = strports.service(webport, site)
s.setServiceParent(self)
- self.listener = s # stash it so the tests can query for the portnum
+
+ self._scheme = None
+ self._portnum = None
+ self._url = None
+ self._listener = s # stash it so we can query for the portnum
+
self._started = defer.Deferred()
if nodeurl_path:
- self._started.addCallback(self._write_nodeurl_file, nodeurl_path)
+ def _write_nodeurl_file(ign):
+ # this file will be created with default permissions
+ line = self.getURL() + "\n"
+ fileutil.write_atomically(nodeurl_path, line, mode="")
+ self._started.addCallback(_write_nodeurl_file)
+
+ def getURL(self):
+ assert self._url
+ return self._url
+
+ def getPortnum(self):
+ assert self._portnum
+ return self._portnum
def startService(self):
- service.MultiService.startService(self)
- # to make various services available to render_* methods, we stash a
- # reference to the client on the NevowSite. This will be available by
- # adapting the 'context' argument to a special marker interface named
- # IClient.
- self.site.remember(self.parent, IClient)
- # I thought you could do the same with an existing interface, but
- # apparently 'ISite' does not exist
- #self.site._client = self.parent
- self.site.remember(MyExceptionHandler(), inevow.ICanHandleException)
- self._started.callback(None)
+ def _got_port(lp):
+ self._portnum = lp.getHost().port
+ # what is our webport?
+ assert self._scheme
+ self._url = "%s://127.0.0.1:%d/" % (self._scheme, self._portnum)
+ self._started.callback(None)
+ return lp
+ def _fail(f):
+ self._started.errback(f)
+ return f
- def _write_nodeurl_file(self, junk, nodeurl_path):
- # what is our webport?
- s = self.listener
- if isinstance(s, internet.TCPServer):
- base_url = "http://127.0.0.1:%d/" % s._port.getHost().port
+ service.MultiService.startService(self)
+ s = self._listener
+ if hasattr(s, 'endpoint') and hasattr(s, '_waitingForPort'):
+ # Twisted 10.2 gives us a StreamServerEndpointService. This is
+ # ugly but should do for now.
+ classname = s.endpoint.__class__.__name__
+ if classname.startswith('SSL'):
+ self._scheme = 'https'
+ else:
+ self._scheme = 'http'
+ s._waitingForPort.addCallbacks(_got_port, _fail)
+ elif isinstance(s, internet.TCPServer):
+ # Twisted <= 10.1
+ self._scheme = 'http'
+ _got_port(s._port)
elif isinstance(s, internet.SSLServer):
- base_url = "https://127.0.0.1:%d/" % s._port.getHost().port
+ # Twisted <= 10.1
+ self._scheme = 'https'
+ _got_port(s._port)
else:
- base_url = None
- if base_url:
- f = open(nodeurl_path, 'wb')
- # this file is world-readable
- f.write(base_url + "\n")
- f.close()
+ # who knows, probably some weirdo future version of Twisted
+ self._started.errback(AssertionError("couldn't find out the scheme or port for the web-API server"))
+
class IntroducerWebishServer(WebishServer):
- root_class = introweb.IntroducerRoot
+ def __init__(self, introducer, webport, nodeurl_path=None, staticdir=None):
+ service.MultiService.__init__(self)
+ self.root = introweb.IntroducerRoot(introducer)
+ self.buildServer(webport, nodeurl_path, staticdir)
+