Mercurial > hg
view mercurial/hgweb/protocol.py @ 30466:2add671bf55b
wireproto: perform chunking and compression at protocol layer (API)
Currently, the "streamres" response type is populated with a generator
of chunks with compression possibly already applied. This puts the onus
on commands to perform chunking and compression. Architecturally, I
think this is the wrong place to perform this work. I think commands
should say "here is the data" and the protocol layer should take care
of encoding the final bytes to put on the wire.
Additionally, upcoming commits will improve wire protocol support for
compression. Having a central place for performing compression in the
protocol transport layer will be easier than having to deal with
compression at the commands layer.
This commit refactors the "streamres" response type to accept either
a generator or an object with "read." Additionally, the type now
accepts a flag indicating whether the response is a "version 1
compressible" response. This basically identifies all commands
currently performing compression. I could have used a special type
for this, but a flag works just as well. The argument name
foreshadows the introduction of wire protocol changes, hence the "v1."
The code for chunking and compressing has been moved to the output
generation function for each protocol transport. Some code has been
inlined, resulting in the deletion of now unused methods.
author | Gregory Szorc <gregory.szorc@gmail.com> |
---|---|
date | Sun, 20 Nov 2016 13:50:45 -0800 |
parents | 038547a14d85 |
children | b3a9ef3d30e8 |
line wrap: on
line source
# # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net> # Copyright 2005-2007 Matt Mackall <mpm@selenic.com> # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. from __future__ import absolute_import import cgi from .common import ( HTTP_OK, ) from .. import ( util, wireproto, ) stringio = util.stringio urlerr = util.urlerr urlreq = util.urlreq HGTYPE = 'application/mercurial-0.1' HGERRTYPE = 'application/hg-error' class webproto(wireproto.abstractserverproto): def __init__(self, req, ui): self.req = req self.response = '' self.ui = ui def getargs(self, args): knownargs = self._args() data = {} keys = args.split() for k in keys: if k == '*': star = {} for key in knownargs.keys(): if key != 'cmd' and key not in keys: star[key] = knownargs[key][0] data['*'] = star else: data[k] = knownargs[k][0] return [data[k] for k in keys] def _args(self): args = self.req.form.copy() postlen = int(self.req.env.get('HTTP_X_HGARGS_POST', 0)) if postlen: args.update(cgi.parse_qs( self.req.read(postlen), keep_blank_values=True)) return args chunks = [] i = 1 while True: h = self.req.env.get('HTTP_X_HGARG_' + str(i)) if h is None: break chunks += [h] i += 1 args.update(cgi.parse_qs(''.join(chunks), keep_blank_values=True)) return args def getfile(self, fp): length = int(self.req.env['CONTENT_LENGTH']) for s in util.filechunkiter(self.req, limit=length): fp.write(s) def redirect(self): self.oldio = self.ui.fout, self.ui.ferr self.ui.ferr = self.ui.fout = stringio() def restore(self): val = self.ui.fout.getvalue() self.ui.ferr, self.ui.fout = self.oldio return val def compresschunks(self, chunks): # Don't allow untrusted settings because disabling compression or # setting a very high compression level could lead to flooding # the server's network or CPU. opts = {'level': self.ui.configint('server', 'zliblevel', -1)} return util.compengines['zlib'].compressstream(chunks, opts) def _client(self): return 'remote:%s:%s:%s' % ( self.req.env.get('wsgi.url_scheme') or 'http', urlreq.quote(self.req.env.get('REMOTE_HOST', '')), urlreq.quote(self.req.env.get('REMOTE_USER', ''))) def iscmd(cmd): return cmd in wireproto.commands def call(repo, req, cmd): p = webproto(req, repo.ui) rsp = wireproto.dispatch(repo, p, cmd) if isinstance(rsp, str): req.respond(HTTP_OK, HGTYPE, body=rsp) return [] elif isinstance(rsp, wireproto.streamres): if rsp.reader: gen = iter(lambda: rsp.reader.read(32768), '') else: gen = rsp.gen if rsp.v1compressible: gen = p.compresschunks(gen) req.respond(HTTP_OK, HGTYPE) return gen elif isinstance(rsp, wireproto.pushres): val = p.restore() rsp = '%d\n%s' % (rsp.res, val) req.respond(HTTP_OK, HGTYPE, body=rsp) return [] elif isinstance(rsp, wireproto.pusherr): # drain the incoming bundle req.drain() p.restore() rsp = '0\n%s\n' % rsp.res req.respond(HTTP_OK, HGTYPE, body=rsp) return [] elif isinstance(rsp, wireproto.ooberror): rsp = rsp.message req.respond(HTTP_OK, HGERRTYPE, body=rsp) return []