[PATCH 2 of 4] hgweb: all protocol functions have become generators

Dirkjan Ochtman dirkjan at ochtman.nl
Sun Jun 29 08:24:41 CDT 2008


# HG changeset patch
# User Dirkjan Ochtman <dirkjan at ochtman.nl>
# Date 1214745789 -7200
# Node ID 4732a9526cb7a3ead91176a8dd92a9cf44c23d92
# Parent  b9d6ab1875231120c01fe804178aa2798e114d44
hgweb: all protocol functions have become generators

Using the write() callable supplied by the start_response() call is
frowned upon by the WSGI spec, returning an iterable over the content chunks
is the recommended way. Be aware, though: returning many small chunks will
slow down responses, because the server has to flush each chunk separately.

diff --git a/mercurial/hgweb/hgweb_mod.py b/mercurial/hgweb/hgweb_mod.py
--- a/mercurial/hgweb/hgweb_mod.py
+++ b/mercurial/hgweb/hgweb_mod.py
@@ -76,8 +76,7 @@
 
     def __call__(self, env, respond):
         req = wsgirequest(env, respond)
-        self.run_wsgi(req)
-        return req
+        return self.run_wsgi(req)
 
     def run_wsgi(self, req):
 
@@ -90,10 +89,9 @@
         cmd = req.form.get('cmd', [''])[0]
         if cmd and cmd in protocol.__all__:
             if cmd in perms and not self.check_perm(req, perms[cmd]):
-                return
+                return []
             method = getattr(protocol, cmd)
-            method(self.repo, req)
-            return
+            return method(self.repo, req)
 
         # work with CGI variables to create coherent structure
         # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME
@@ -171,6 +169,7 @@
 
             req.write(content)
             del tmpl
+            return req
 
         except revlog.LookupError, err:
             req.respond(HTTP_NOT_FOUND, ctype)
diff --git a/mercurial/hgweb/protocol.py b/mercurial/hgweb/protocol.py
--- a/mercurial/hgweb/protocol.py
+++ b/mercurial/hgweb/protocol.py
@@ -30,12 +30,12 @@
         success = 0
     resp = "%s %s\n" % (success, r)
     req.respond(HTTP_OK, HGTYPE, length=len(resp))
-    req.write(resp)
+    yield resp
 
 def heads(repo, req):
     resp = " ".join(map(hex, repo.heads())) + "\n"
     req.respond(HTTP_OK, HGTYPE, length=len(resp))
-    req.write(resp)
+    yield resp
 
 def branches(repo, req):
     nodes = []
@@ -46,7 +46,7 @@
         resp.write(" ".join(map(hex, b)) + "\n")
     resp = resp.getvalue()
     req.respond(HTTP_OK, HGTYPE, length=len(resp))
-    req.write(resp)
+    yield resp
 
 def between(repo, req):
     if 'pairs' in req.form:
@@ -57,7 +57,7 @@
         resp.write(" ".join(map(hex, b)) + "\n")
     resp = resp.getvalue()
     req.respond(HTTP_OK, HGTYPE, length=len(resp))
-    req.write(resp)
+    yield resp
 
 def changegroup(repo, req):
     req.respond(HTTP_OK, HGTYPE)
@@ -72,9 +72,9 @@
         chunk = f.read(4096)
         if not chunk:
             break
-        req.write(z.compress(chunk))
+        yield z.compress(chunk)
 
-    req.write(z.flush())
+    yield z.flush()
 
 def changegroupsubset(repo, req):
     req.respond(HTTP_OK, HGTYPE)
@@ -92,9 +92,9 @@
         chunk = f.read(4096)
         if not chunk:
             break
-        req.write(z.compress(chunk))
+        yield z.compress(chunk)
 
-    req.write(z.flush())
+    yield z.flush()
 
 def capabilities(repo, req):
     caps = ['lookup', 'changegroupsubset']
@@ -104,23 +104,11 @@
         caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority))
     rsp = ' '.join(caps)
     req.respond(HTTP_OK, HGTYPE, length=len(rsp))
-    req.write(rsp)
+    yield rsp
 
 def unbundle(repo, req):
 
-    def bail(response, headers={}):
-        length = int(req.env.get('CONTENT_LENGTH', 0))
-        for s in util.filechunkiter(req, limit=length):
-            # drain incoming bundle, else client will not see
-            # response when run outside cgi script
-            pass
-
-        status = headers.pop('status', HTTP_OK)
-        req.header(headers.items())
-        req.respond(status, HGTYPE)
-        req.write('0\n')
-        req.write(response)
-
+    errorfmt = '0\n%s\n'
     proto = req.env.get('wsgi.url_scheme') or 'http'
     their_heads = req.form['heads'][0].split(' ')
 
@@ -130,7 +118,13 @@
 
     # fail early if possible
     if not check_heads():
-        bail('unsynced changes\n')
+        length = int(req.env.get('CONTENT_LENGTH', 0))
+        for s in util.filechunkiter(req, limit=length):
+            # drain incoming bundle, else client will not see
+            # response when run outside cgi script
+            pass
+        req.respond(HTTP_OK, HGTYPE)
+        yield errorfmt % 'unsynced changes'
         return
 
     req.respond(HTTP_OK, HGTYPE)
@@ -149,8 +143,7 @@
             lock = repo.lock()
             try:
                 if not check_heads():
-                    req.write('0\n')
-                    req.write('unsynced changes\n')
+                    yield errorfmt % 'unsynced changes'
                     return
 
                 fp.seek(0)
@@ -177,15 +170,12 @@
                 finally:
                     val = sys.stdout.getvalue()
                     sys.stdout, sys.stderr = oldio
-                req.write('%d\n' % ret)
-                req.write(val)
+                yield '%d\n%s' % (ret, val)
             finally:
                 del lock
         except ValueError, inst:
-            req.write('0\n')
-            req.write(str(inst) + '\n')
+            yield errorfmt % inst
         except (OSError, IOError), inst:
-            req.write('0\n')
             filename = getattr(inst, 'filename', '')
             # Don't send our filesystem layout to the client
             if filename.startswith(repo.root):
@@ -198,12 +188,11 @@
             else:
                 code = HTTP_SERVER_ERROR
             req.respond(code)
-            req.write('%s: %s\n' % (error, filename))
+            yield '0\n%s: %s\n' % (error, filename)
     finally:
         fp.close()
         os.unlink(tempname)
 
 def stream_out(repo, req):
     req.respond(HTTP_OK, HGTYPE)
-    for chunk in streamclone.stream_out(repo, untrusted=True):
-        req.write(chunk)
+    return streamclone.stream_out(repo, untrusted=True)
diff --git a/mercurial/hgweb/server.py b/mercurial/hgweb/server.py
--- a/mercurial/hgweb/server.py
+++ b/mercurial/hgweb/server.py
@@ -122,7 +122,8 @@
         self.saved_headers = []
         self.sent_headers = False
         self.length = None
-        self.server.application(env, self._start_response)
+        for chunk in self.server.application(env, self._start_response):
+            self._write(chunk)
 
     def send_headers(self):
         if not self.saved_status:


More information about the Mercurial-devel mailing list