# HG changeset patch # User Dirkjan Ochtman # Date 1214745789 -7200 # Node ID 18c429ea3a0e41f2f050ba7eed7e52d567bfd647 # Parent 6d824dc86907e095934823ff7441ef76d76d85b3 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 -r 6d824dc86907 -r 18c429ea3a0e mercurial/hgweb/hgweb_mod.py --- 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) diff -r 6d824dc86907 -r 18c429ea3a0e mercurial/hgweb/protocol.py --- 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) diff -r 6d824dc86907 -r 18c429ea3a0e mercurial/hgweb/server.py --- 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: