# HG changeset patch # User Vadim Gelfer # Date 1150401358 25200 # Node ID a2df85adface311f6aeb266c1f1eaaa59a5bab59 # Parent d09da6fc106193bd24d6c60cbbb59ee07353cacf http server: support persistent connections. only "hg serve" affected yet. http server running cgi script will not use persistent connections. support for fastcgi will help that. clients that support keepalive can use one tcp connection for all commands during clone and pull. this makes latency of binary search during pull much lower over wan. if server does not know content-length, it will force connection to close at end. right fix is to use chunked transfer-encoding but this is easier and does not hurt performance. only command that is affected is "changegroup" which is always last command during a pull. diff -r d09da6fc1061 -r a2df85adface mercurial/hgweb/hgweb_mod.py --- a/mercurial/hgweb/hgweb_mod.py Wed Jun 14 15:41:37 2006 -0700 +++ b/mercurial/hgweb/hgweb_mod.py Thu Jun 15 12:55:58 2006 -0700 @@ -10,7 +10,7 @@ import os.path import mimetypes from mercurial.demandload import demandload -demandload(globals(), "re zlib ConfigParser") +demandload(globals(), "re zlib ConfigParser cStringIO") demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,templater") demandload(globals(), "mercurial.hgweb.request:hgrequest") demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile") @@ -761,26 +761,32 @@ req.form['filenode'][0])) elif cmd == 'heads': - req.httphdr("application/mercurial-0.1") - h = self.repo.heads() - req.write(" ".join(map(hex, h)) + "\n") + resp = " ".join(map(hex, self.repo.heads())) + "\n" + req.httphdr("application/mercurial-0.1", length=len(resp)) + req.write(resp) elif cmd == 'branches': - req.httphdr("application/mercurial-0.1") nodes = [] if req.form.has_key('nodes'): nodes = map(bin, req.form['nodes'][0].split(" ")) + resp = cStringIO.StringIO() for b in self.repo.branches(nodes): - req.write(" ".join(map(hex, b)) + "\n") + resp.write(" ".join(map(hex, b)) + "\n") + resp = resp.getvalue() + req.httphdr("application/mercurial-0.1", length=len(resp)) + req.write(resp) elif cmd == 'between': - req.httphdr("application/mercurial-0.1") nodes = [] if req.form.has_key('pairs'): pairs = [map(bin, p.split("-")) for p in req.form['pairs'][0].split(" ")] + resp = cStringIO.StringIO() for b in self.repo.between(pairs): - req.write(" ".join(map(hex, b)) + "\n") + resp.write(" ".join(map(hex, b)) + "\n") + resp = resp.getvalue() + req.httphdr("application/mercurial-0.1", length=len(resp)) + req.write(resp) elif cmd == 'changegroup': req.httphdr("application/mercurial-0.1") @@ -819,3 +825,4 @@ else: req.write(self.t("error")) + req.done() diff -r d09da6fc1061 -r a2df85adface mercurial/hgweb/request.py --- a/mercurial/hgweb/request.py Wed Jun 14 15:41:37 2006 -0700 +++ b/mercurial/hgweb/request.py Thu Jun 15 12:55:58 2006 -0700 @@ -16,6 +16,7 @@ self.out = out or sys.stdout self.env = env or os.environ self.form = cgi.parse(self.inp, self.env, keep_blank_values=1) + self.will_close = True def write(self, *things): for thing in things: @@ -29,16 +30,30 @@ if inst[0] != errno.ECONNRESET: raise + def done(self): + if self.will_close: + self.inp.close() + self.out.close() + else: + self.out.flush() + def header(self, headers=[('Content-type','text/html')]): for header in headers: self.out.write("%s: %s\r\n" % header) self.out.write("\r\n") - def httphdr(self, type, file="", size=0): + def httphdr(self, type, filename=None, length=0): headers = [('Content-type', type)] - if file: - headers.append(('Content-disposition', 'attachment; filename=%s' % file)) - if size > 0: - headers.append(('Content-length', str(size))) + if filename: + headers.append(('Content-disposition', 'attachment; filename=%s' % + filename)) + # we do not yet support http 1.1 chunked transfer, so we have + # to force connection to close if content-length not known + if length: + headers.append(('Content-length', str(length))) + self.will_close = False + else: + headers.append(('Connection', 'close')) + self.will_close = True self.header(headers) diff -r d09da6fc1061 -r a2df85adface mercurial/hgweb/server.py --- a/mercurial/hgweb/server.py Wed Jun 14 15:41:37 2006 -0700 +++ b/mercurial/hgweb/server.py Thu Jun 15 12:55:58 2006 -0700 @@ -27,6 +27,7 @@ class _hgwebhandler(object, BaseHTTPServer.BaseHTTPRequestHandler): def __init__(self, *args, **kargs): + self.protocol_version = 'HTTP/1.1' BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, *args, **kargs) def log_error(self, format, *args): @@ -85,7 +86,7 @@ req = hgrequest(self.rfile, self.wfile, env) self.send_response(200, "Script output follows") - self.server.make_and_run_handler(req) + self.close_connection = self.server.make_and_run_handler(req) def create_server(ui, repo): use_threads = True @@ -135,6 +136,7 @@ else: raise hg.RepoError(_('no repo found')) hgwebobj.run(req) + return req.will_close class IPv6HTTPServer(MercurialHTTPServer): address_family = getattr(socket, 'AF_INET6', None)