--- a/mercurial/hgweb/protocol.py Wed Apr 04 10:35:09 2018 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,202 +0,0 @@
-#
-# 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
-import struct
-
-from .common import (
- HTTP_OK,
-)
-
-from .. import (
- error,
- pycompat,
- util,
- wireproto,
-)
-stringio = util.stringio
-
-urlerr = util.urlerr
-urlreq = util.urlreq
-
-HGTYPE = 'application/mercurial-0.1'
-HGTYPE2 = 'application/mercurial-0.2'
-HGERRTYPE = 'application/hg-error'
-
-def decodevaluefromheaders(req, headerprefix):
- """Decode a long value from multiple HTTP request headers.
-
- Returns the value as a bytes, not a str.
- """
- chunks = []
- i = 1
- prefix = headerprefix.upper().replace(r'-', r'_')
- while True:
- v = req.env.get(r'HTTP_%s_%d' % (prefix, i))
- if v is None:
- break
- chunks.append(pycompat.bytesurl(v))
- i += 1
-
- return ''.join(chunks)
-
-class webproto(wireproto.abstractserverproto):
- def __init__(self, req, ui):
- self.req = req
- self.response = ''
- self.ui = ui
- self.name = 'http'
- self.checkperm = req.checkperm
-
- 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()
- if pycompat.ispy3:
- args = {k.encode('ascii'): [v.encode('ascii') for v in vs]
- for k, vs in args.items()}
- postlen = int(self.req.env.get(r'HTTP_X_HGARGS_POST', 0))
- if postlen:
- args.update(cgi.parse_qs(
- self.req.read(postlen), keep_blank_values=True))
- return args
-
- argvalue = decodevaluefromheaders(self.req, r'X-HgArg')
- args.update(cgi.parse_qs(argvalue, keep_blank_values=True))
- return args
- def getfile(self, fp):
- length = int(self.req.env[r'CONTENT_LENGTH'])
- # If httppostargs is used, we need to read Content-Length
- # minus the amount that was consumed by args.
- length -= int(self.req.env.get(r'HTTP_X_HGARGS_POST', 0))
- 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 _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 responsetype(self, prefer_uncompressed):
- """Determine the appropriate response type and compression settings.
-
- Returns a tuple of (mediatype, compengine, engineopts).
- """
- # Determine the response media type and compression engine based
- # on the request parameters.
- protocaps = decodevaluefromheaders(self.req, r'X-HgProto').split(' ')
-
- if '0.2' in protocaps:
- # All clients are expected to support uncompressed data.
- if prefer_uncompressed:
- return HGTYPE2, util._noopengine(), {}
-
- # Default as defined by wire protocol spec.
- compformats = ['zlib', 'none']
- for cap in protocaps:
- if cap.startswith('comp='):
- compformats = cap[5:].split(',')
- break
-
- # Now find an agreed upon compression format.
- for engine in wireproto.supportedcompengines(self.ui, self,
- util.SERVERROLE):
- if engine.wireprotosupport().name in compformats:
- opts = {}
- level = self.ui.configint('server',
- '%slevel' % engine.name())
- if level is not None:
- opts['level'] = level
-
- return HGTYPE2, engine, opts
-
- # No mutually supported compression format. Fall back to the
- # legacy protocol.
-
- # 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')}
- return HGTYPE, util.compengines['zlib'], opts
-
-def iscmd(cmd):
- return cmd in wireproto.commands
-
-def call(repo, req, cmd):
- p = webproto(req, repo.ui)
-
- def genversion2(gen, engine, engineopts):
- # application/mercurial-0.2 always sends a payload header
- # identifying the compression engine.
- name = engine.wireprotosupport().name
- assert 0 < len(name) < 256
- yield struct.pack('B', len(name))
- yield name
-
- for chunk in gen:
- yield chunk
-
- rsp = wireproto.dispatch(repo, p, cmd)
- if isinstance(rsp, bytes):
- req.respond(HTTP_OK, HGTYPE, body=rsp)
- return []
- elif isinstance(rsp, wireproto.streamres_legacy):
- gen = rsp.gen
- req.respond(HTTP_OK, HGTYPE)
- return gen
- elif isinstance(rsp, wireproto.streamres):
- gen = rsp.gen
-
- # This code for compression should not be streamres specific. It
- # is here because we only compress streamres at the moment.
- mediatype, engine, engineopts = p.responsetype(rsp.prefer_uncompressed)
- gen = engine.compressstream(gen, engineopts)
-
- if mediatype == HGTYPE2:
- gen = genversion2(gen, engine, engineopts)
-
- req.respond(HTTP_OK, mediatype)
- 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 []
- raise error.ProgrammingError('hgweb.protocol internal failure', rsp)