From d3f2df00be40e521aa3f3bfe76779f16e4713808 Mon Sep 17 00:00:00 2001
From: Brian Warner <warner@allmydata.com>
Date: Wed, 29 Oct 2008 15:34:31 -0700
Subject: [PATCH] webapi: serve the /static URL tree from /public_html
 (configurable)

---
 docs/configuration.txt         | 12 ++++++++++++
 docs/webapi.txt                | 18 ++++++++++++++++--
 src/allmydata/client.py        |  4 +++-
 src/allmydata/test/test_web.py | 18 ++++++++++++++++--
 src/allmydata/webish.py        |  6 ++++--
 5 files changed, 51 insertions(+), 7 deletions(-)

diff --git a/docs/configuration.txt b/docs/configuration.txt
index d25bddd9..2bfdc481 100644
--- a/docs/configuration.txt
+++ b/docs/configuration.txt
@@ -58,6 +58,18 @@ web.port = (strports string, optional)
 
  If this is not provided, the node will not run a web server.
 
+web.static = (string, optional)
+
+ This controls where the /static portion of the URL space is served. The
+ value is a directory name (~username is allowed, and non-absolute names are
+ interpreted relative to the node's basedir) which can contain HTML and other
+ files. This can be used to serve a javascript-based frontend to the Tahoe
+ node, or other services.
+
+ The default value is "public_html", which will serve $BASEDIR/public_html .
+ With the default settings, http://127.0.0.1:8123/static/foo.html will serve
+ the contents of $BASEDIR/public_html/foo.html .
+
 tub.port = (integer, optional)
 
  This controls which port the node uses to accept Foolscap connections from
diff --git a/docs/webapi.txt b/docs/webapi.txt
index b93babfa..59ac9ed1 100644
--- a/docs/webapi.txt
+++ b/docs/webapi.txt
@@ -6,8 +6,9 @@
 3. URLs, Machine-Oriented Interfaces
 4. Browser Operations: Human-Oriented Interfaces
 5. Welcome / Debug / Status pages
-6. Safety and security issues -- names vs. URIs
-7. Concurrency Issues
+6. Static Files in /public_html
+7. Safety and security issues -- names vs. URIs
+8. Concurrency Issues
 
 
 == Enabling the web-API port ==
@@ -1164,6 +1165,19 @@ GET /   (introducer status)
  clients over time.
 
 
+== Static Files in /public_html ==
+
+The webapi server will take any request for a URL that starts with /static
+and serve it from a configurable directory which defaults to
+$BASEDIR/public_html . This is configured by setting the "[node]web.static"
+value in $BASEDIR/tahoe.cfg . If this is left at the default value of
+"public_html", then http://localhost:8123/static/subdir/foo.html will be
+served with the contents of the file $BASEDIR/public_html/subdir/foo.html .
+
+This can be useful to serve a javascript application which provides a
+prettier front-end to the rest of the Tahoe webapi.
+
+
 == safety and security issues -- names vs. URIs ==
 
 Summary: use explicit file- and dir- caps whenever possible, to reduce the
diff --git a/src/allmydata/client.py b/src/allmydata/client.py
index 463a5cbd..22930ab6 100644
--- a/src/allmydata/client.py
+++ b/src/allmydata/client.py
@@ -254,7 +254,9 @@ class Client(node.Node, pollmixin.PollMixin):
 
         from allmydata.webish import WebishServer
         nodeurl_path = os.path.join(self.basedir, "node.url")
-        ws = WebishServer(webport, nodeurl_path)
+        staticdir = self.get_config("node", "web.static", "public_html")
+        staticdir = os.path.expanduser(staticdir)
+        ws = WebishServer(webport, nodeurl_path, staticdir)
         self.add_service(ws)
 
     def init_ftp_server(self):
diff --git a/src/allmydata/test/test_web.py b/src/allmydata/test/test_web.py
index 0d80b52e..684ac844 100644
--- a/src/allmydata/test/test_web.py
+++ b/src/allmydata/test/test_web.py
@@ -1,4 +1,4 @@
-import re, urllib
+import os.path, re, urllib
 import simplejson
 from twisted.application import service
 from twisted.trial import unittest
@@ -123,7 +123,8 @@ class WebMixin(object):
     def setUp(self):
         self.s = FakeClient()
         self.s.startService()
-        self.ws = s = webish.WebishServer("0")
+        self.staticdir = self.mktemp()
+        self.ws = s = webish.WebishServer("0", staticdir=self.staticdir)
         s.setServiceParent(self.s)
         self.webish_port = port = s.listener._port.getHost().port
         self.webish_url = "http://localhost:%d" % port
@@ -2396,6 +2397,19 @@ class Web(WebMixin, testutil.StallMixin, unittest.TestCase):
         d.addCallback(_done)
         return d
 
+    def test_static(self):
+        webdir = os.path.join(self.staticdir, "subdir")
+        fileutil.make_dirs(webdir)
+        f = open(os.path.join(webdir, "hello.txt"), "wb")
+        f.write("hello")
+        f.close()
+
+        d = self.GET("/static/subdir/hello.txt")
+        def _check(res):
+            self.failUnlessEqual(res, "hello")
+        d.addCallback(_check)
+        return d
+
 
 class Util(unittest.TestCase):
     def test_abbreviate_time(self):
diff --git a/src/allmydata/webish.py b/src/allmydata/webish.py
index 0b3add8c..14c62313 100644
--- a/src/allmydata/webish.py
+++ b/src/allmydata/webish.py
@@ -3,7 +3,7 @@ import time
 from twisted.application import service, strports, internet
 from twisted.web import http
 from twisted.internet import defer
-from nevow import appserver, inevow
+from nevow import appserver, inevow, static
 from allmydata.util import log
 
 from allmydata.web import introweb, root
@@ -123,7 +123,7 @@ class WebishServer(service.MultiService):
     name = "webish"
     root_class = root.Root
 
-    def __init__(self, webport, nodeurl_path=None):
+    def __init__(self, webport, nodeurl_path=None, staticdir=None):
         service.MultiService.__init__(self)
         self.webport = webport
         self.root = self.root_class()
@@ -132,6 +132,8 @@ class WebishServer(service.MultiService):
         if self.root.child_operations:
             self.site.remember(self.root.child_operations, IOpHandleTable)
             self.root.child_operations.setServiceParent(self)
+        if staticdir:
+            self.root.putChild("static", static.File(staticdir))
         s = strports.service(webport, site)
         s.setServiceParent(self)
         self.listener = s # stash it so the tests can query for the portnum
-- 
2.45.2