]> arthur.barton.de Git - bup.git/commitdiff
Convert web-cmd to use Tornado.
authorPeter McCurdy <peter.mccurdy@gmail.com>
Sat, 17 Jul 2010 04:16:09 +0000 (00:16 -0400)
committerAvery Pennarun <apenwarr@gmail.com>
Sat, 17 Jul 2010 08:05:20 +0000 (04:05 -0400)
Pleasantly, this mostly just involved deleting code, with a few tweaks.

Signed-off-by: Peter McCurdy <petermccurdy@alumni.uwaterloo.ca>
cmd/web-cmd.py

index a3ee220b9d727f2707b9f071c5de1503f440419d..6f961151641fe424bc519217acb9b7466bfc16d6 100755 (executable)
@@ -1,38 +1,23 @@
 #!/usr/bin/env python
-import sys, stat, cgi, shutil, urllib, mimetypes, posixpath
-import BaseHTTPServer
+import sys, stat, cgi, shutil, urllib, mimetypes, posixpath, time
+import tornado.httpserver
+import tornado.ioloop
+import tornado.web
 from bup import options, git, vfs
 from bup.helpers import *
-try:
-    from cStringIO import StringIO
-except ImportError:
-    from StringIO import StringIO
 
 handle_ctrl_c()
 
-class BupHTTPServer(BaseHTTPServer.HTTPServer):
-    def handle_error(self, request, client_address):
-        # If we get a KeyboardInterrupt error than just reraise it
-        # so that we cause the server to exit.
-        if sys.exc_info()[0] == KeyboardInterrupt:
-            raise
+class BupRequestHandler(tornado.web.RequestHandler):
+    def get(self, path):
+        return self._process_request(path)
 
-class BupRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
-    server_version = 'BupHTTP/%s' % version_tag()
-    protocol_version = 'HTTP/1.1'
-    def do_GET(self):
-        self._process_request()
+    def head(self, path):
+        return self._process_request(path)
 
-    def do_HEAD(self):
-        self._process_request()
-
-    def _process_request(self):
-        """Common code for GET and HEAD commands.
-
-        This sends the response code and MIME headers along with the content
-        of the response.
-        """
-        path = urllib.unquote(self.path)
+    def _process_request(self, path):
+        path = urllib.unquote(path)
+        print 'Handling request for %s' % path
         try:
             n = top.resolve(path)
         except vfs.NoSuchFile:
@@ -50,21 +35,14 @@ class BupRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
         Return value is either a file object, or None (indicating an
         error).  In either case, the headers are sent.
         """
-        if not path.endswith('/'):
-            # redirect browser - doing basically what apache does
-            self.send_response(301)
-            self.send_header("Location", path + "/")
-            self.send_header("Content-Length", 0)
-            self.end_headers()
-            return
+        if not path.endswith('/') and len(path) > 0:
+            print 'Redirecting from %s to %s' % (path, path + '/')
+            return self.redirect(path + '/', permanent=True)
+
+        self.set_header("Content-Type", "text/html")
 
-        # Note that it is necessary to buffer the output into a StringIO here
-        # so that we can compute the content length before we send the
-        # content.  The only other option would be to do chunked encoding, or
-        # not support content length.
-        f = StringIO()
         displaypath = cgi.escape(path)
-        f.write("""
+        self.write("""
 <HTML>
   <HEAD>
     <TITLE>Directory listing for %(displaypath)s</TITLE>
@@ -78,16 +56,16 @@ class BupRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
   <BODY>
     <DIV id="breadcrumb">
 """ % { 'displaypath': displaypath })
-        if self.path == "/":
-            f.write("""<STRONG>[root]</STRONG>""")
+        if path == "/":
+            self.write("""<STRONG>[root]</STRONG>""")
         else:
-            f.write("""<A href="/">[root]</A> """)
-            path_parts = self.path.split("/")
-            path_parts_cleaned = path_parts[1:len(path_parts)-1]
-            for index, value in enumerate(path_parts_cleaned[0:len(path_parts_cleaned)-1]):
-                f.write("""/ <A href="/%(path)s/">%(element)s</A> """ % { 'path' : "/".join(path_parts_cleaned[0:(index + 1)]) , 'element' : value})
-            f.write("""/ <STRONG>%s</STRONG>""" % path_parts_cleaned[len(path_parts_cleaned)-1])
-        f.write("""
+            self.write("""<A href="/">[root]</A> """)
+            path_parts = path.split("/")
+            path_parts_cleaned = path_parts[1:-1]
+            for index, value in enumerate(path_parts_cleaned[0:-1]):
+                self.write("""/ <A href="/%(path)s/">%(element)s</A> """ % { 'path' : "/".join(path_parts_cleaned[0:(index + 1)]) , 'element' : value})
+            self.write("""/ <STRONG>%s</STRONG>""" % path_parts_cleaned[-1])
+        self.write("""
     </DIV>
     <TABLE>
       <TR>
@@ -107,22 +85,14 @@ class BupRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
                 displayname = sub.name + "@"
                 # Note: a link to a directory displays with @ and links with /
                 size = '&nbsp;'
-            f.write("""      <TR>
+            self.write("""      <TR>
         <TD class="dir-name"><A href="%s">%s</A></TD>
         <TD class="dir-size">%s</TD>
       </TR>""" % (urllib.quote(linkname), cgi.escape(displayname), size))
-        f.write("""
+        self.write("""
     </TABLE>
   </BODY>
 </HTML>""")
-        length = f.tell()
-        f.seek(0)
-        self.send_response(200)
-        self.send_header("Content-type", "text/html")
-        self.send_header("Content-Length", str(length))
-        self.end_headers()
-        self._send_content(f)
-        f.close()
 
     def _get_file(self, path, n):
         """Process a request on a file.
@@ -131,20 +101,17 @@ class BupRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
         In either case, the headers are sent.
         """
         ctype = self._guess_type(path)
-        f = n.open()
-        self.send_response(200)
-        self.send_header("Content-type", ctype)
-        self.send_header("Content-Length", str(n.size()))
-        self.send_header("Last-Modified", self.date_time_string(n.mtime))
-        self.end_headers()
-        self._send_content(f)
-        f.close()
-
-    def _send_content(self, f):
-        """Send the content file as the response if necessary."""
-        if self.command != 'HEAD':
+
+        self.set_header("Last-Modified", self.date_time_string(n.mtime))
+        self.set_header("Content-Type", ctype)
+        size = n.size()
+        self.set_header("Content-Length", str(size))
+
+        if self.request.method != 'HEAD':
+            f = n.open()
             for blob in chunkyreader(f):
-                self.wfile.write(blob)
+                self.write(blob)
+            f.close()
 
     def _guess_type(self, path):
         """Guess the type of a file.
@@ -178,6 +145,9 @@ class BupRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
         '.h': 'text/plain',
         })
 
+    def date_time_string(self, t):
+        return time.strftime('%a, %d %b %Y %H:%M:%S', time.gmtime(t))
+
 
 optspec = """
 bup web [[hostname]:port]
@@ -189,7 +159,7 @@ o = options.Options('bup web', optspec)
 if len(extra) > 1:
     o.fatal("at most one argument expected")
 
-address = ('127.0.0.1', 8080)
+address = ('', 8080)
 if len(extra) > 0:
     addressl = extra[0].split(':', 1)
     addressl[1] = int(addressl[1])
@@ -198,12 +168,24 @@ if len(extra) > 0:
 git.check_repo_or_die()
 top = vfs.RefList(None)
 
-try:
-    httpd = BupHTTPServer(address, BupRequestHandler)
-except socket.error, e:
-    log('socket%r: %s\n' % (address, e.args[1]))
-    sys.exit(1)
+(pwd,junk) = os.path.split(sys.argv[0])
+
+settings = dict(
+    debug = 1,
+)
+
+# Disable buffering on stdout, for debug messages
+sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
+
+application = tornado.web.Application([
+    (r"(/.*)", BupRequestHandler),
+], **settings)
+
+if __name__ == "__main__":
+    http_server = tornado.httpserver.HTTPServer(application)
+    http_server.listen(address[1], address=address[0])
+
+    print "Listening on port %s" % http_server._socket.getsockname()[1]
+    loop = tornado.ioloop.IOLoop.instance()
+    loop.start()
 
-sa = httpd.socket.getsockname()
-log("Serving HTTP on %s:%d...\n" % sa)
-httpd.serve_forever()