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.
--- a/mercurial/hgweb/hgweb_mod.py Sun Jun 29 13:16:25 2008 +0200
+++ b/mercurial/hgweb/hgweb_mod.py Sun Jun 29 15:23:09 2008 +0200
@@ -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)
--- a/mercurial/hgweb/protocol.py Sun Jun 29 13:16:25 2008 +0200
+++ b/mercurial/hgweb/protocol.py Sun Jun 29 15:23:09 2008 +0200
@@ -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)
--- a/mercurial/hgweb/server.py Sun Jun 29 13:16:25 2008 +0200
+++ b/mercurial/hgweb/server.py Sun Jun 29 15:23:09 2008 +0200
@@ -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: