]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blobdiff - src/allmydata/webish.py
Implementation, tests and docs for blacklists. This version allows listing directorie...
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / webish.py
index e3f560709bcad33468c0f6196d8af1921d79c0ba..a8e0bff8e252cc917cc4d82630038c279d46c0eb 100644 (file)
@@ -1,12 +1,12 @@
-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, static
-from allmydata.util import log
+from allmydata.util import log, fileutil
 
 from allmydata.web import introweb, root
-from allmydata.web.common import IClient, IOpHandleTable, 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
@@ -18,6 +18,8 @@ from allmydata.web.common import IClient, IOpHandleTable, MyExceptionHandler
 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.
 
@@ -107,12 +109,17 @@ class MyRequest(appserver.NevowRequest):
 
         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,
                 )
@@ -120,53 +127,92 @@ class MyRequest(appserver.NevowRequest):
 
 class WebishServer(service.MultiService):
     name = "webish"
-    root_class = root.Root
 
-    def __init__(self, webport, nodeurl_path=None, staticdir=None):
+    def __init__(self, client, webport, nodeurl_path=None, staticdir=None,
+                 clock=None):
         service.MultiService.__init__(self)
-        self.webport = webport
-        self.root = self.root_class()
-        self.site = site = appserver.NevowSite(self.root)
-        self.site.requestFactory = MyRequest
+        # 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.site = site = appserver.NevowSite(self.root)
+        self.site.requestFactory = MyRequest
+        self.site.remember(MyExceptionHandler(), inevow.ICanHandleException)
         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
+                fileutil.write(nodeurl_path, self.getURL() + "\n")
+            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)
+