Really fix http headers for web UI and issue 254.
authorEric Hopper <hopper@omnifarious.org>
Tue, 27 Jun 2006 09:33:12 -0700
changeset 2514 419c42223bee
parent 2513 f22e3e8fd457
child 2515 a6700c222314
child 2532 84655f721f39
Really fix http headers for web UI and issue 254. This also arranges for static content to allow a keepalive connection.
mercurial/hgweb/common.py
mercurial/hgweb/hgweb_mod.py
mercurial/hgweb/hgwebdir_mod.py
mercurial/hgweb/request.py
--- a/mercurial/hgweb/common.py
+++ b/mercurial/hgweb/common.py
@@ -17,7 +17,7 @@ def get_mtime(repo_path):
     else:
         return os.stat(hg_path).st_mtime
 
-def staticfile(directory, fname):
+def staticfile(directory, fname, req):
     """return a file inside directory with guessed content-type header
 
     fname always uses '/' as directory separator and isn't allowed to
@@ -36,7 +36,9 @@ def staticfile(directory, fname):
     try:
         os.stat(path)
         ct = mimetypes.guess_type(path)[0] or "text/plain"
-        return "Content-type: %s\n\n%s" % (ct, file(path).read())
+        req.header([('Content-type', ct),
+                    ('Content-length', os.path.getsize(path))])
+        return file(path).read()
     except (TypeError, OSError):
         # illegal fname or unreadable file
         return ""
--- a/mercurial/hgweb/hgweb_mod.py
+++ b/mercurial/hgweb/hgweb_mod.py
@@ -10,7 +10,7 @@ import os
 import os.path
 import mimetypes
 from mercurial.demandload import demandload
-demandload(globals(), "re zlib ConfigParser cStringIO sys tempfile")
+demandload(globals(), "re zlib ConfigParser mimetools cStringIO sys tempfile")
 demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,templater")
 demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile")
 from mercurial.node import *
@@ -652,7 +652,10 @@ class hgweb(object):
 
     def run(self, req):
         def header(**map):
-            yield self.t("header", **map)
+            header_file = cStringIO.StringIO(''.join(self.t("header", **map)))
+            msg = mimetools.Message(header_file, 0)
+            req.header(msg.items())
+            yield header_file.read()
 
         def footer(**map):
             yield self.t("footer",
@@ -828,7 +831,7 @@ class hgweb(object):
         static = self.repo.ui.config("web", "static",
                                      os.path.join(self.templatepath,
                                                   "static"))
-        req.write(staticfile(static, fname)
+        req.write(staticfile(static, fname, req)
                   or self.t("error", error="%r not found" % fname))
 
     def do_capabilities(self, req):
--- a/mercurial/hgweb/hgwebdir_mod.py
+++ b/mercurial/hgweb/hgwebdir_mod.py
@@ -8,7 +8,7 @@
 
 import os
 from mercurial.demandload import demandload
-demandload(globals(), "ConfigParser")
+demandload(globals(), "ConfigParser mimetools cStringIO")
 demandload(globals(), "mercurial:ui,hg,util,templater")
 demandload(globals(), "mercurial.hgweb.hgweb_mod:hgweb")
 demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile")
@@ -48,7 +48,10 @@ class hgwebdir(object):
 
     def run(self, req):
         def header(**map):
-            yield tmpl("header", **map)
+            header_file = cStringIO.StringIO(''.join(tmpl("header", **map)))
+            msg = mimetools.Message(header_file, 0)
+            req.header(msg.items())
+            yield header_file.read()
 
         def footer(**map):
             yield tmpl("footer", motd=self.motd, **map)
@@ -132,7 +135,7 @@ class hgwebdir(object):
             if req.form.has_key('static'):
                 static = os.path.join(templater.templatepath(), "static")
                 fname = req.form['static'][0]
-                req.write(staticfile(static, fname)
+                req.write(staticfile(static, fname, req)
                           or tmpl("error", error="%r not found" % fname))
             else:
                 sortable = ["name", "description", "contact", "lastchange"]
--- a/mercurial/hgweb/request.py
+++ b/mercurial/hgweb/request.py
@@ -57,20 +57,21 @@ class _wsgirequest(object):
         return self.inp.read(count)
 
     def write(self, *things):
-        if self.server_write is None:
-            if not self.headers:
-                self.header()
-            self.server_write = self.start_response('200 Script output follows',
-                                                    self.headers)
-            self.start_response = None
-            self.headers = None
         for thing in things:
             if hasattr(thing, "__iter__"):
                 for part in thing:
                     self.write(part)
             else:
+                thing = str(thing)
+                if self.server_write is None:
+                    if not self.headers:
+                        raise RuntimeError("request.write called before headers sent (%s)." % thing)
+                    self.server_write = self.start_response('200 Script output follows',
+                                                            self.headers)
+                    self.start_response = None
+                    self.headers = None
                 try:
-                    self.server_write(str(thing))
+                    self.server_write(thing)
                 except socket.error, inst:
                     if inst[0] != errno.ECONNRESET:
                         raise