mercurial/wireprotoserver.py
changeset 43077 687b865b95ad
parent 43076 2372284d9457
child 43117 8ff1ecfadcd1
--- a/mercurial/wireprotoserver.py	Sun Oct 06 09:45:02 2019 -0400
+++ b/mercurial/wireprotoserver.py	Sun Oct 06 09:48:39 2019 -0400
@@ -34,9 +34,9 @@
 
 HTTP_OK = 200
 
-HGTYPE = 'application/mercurial-0.1'
-HGTYPE2 = 'application/mercurial-0.2'
-HGERRTYPE = 'application/hg-error'
+HGTYPE = b'application/mercurial-0.1'
+HGTYPE2 = b'application/mercurial-0.2'
+HGERRTYPE = b'application/hg-error'
 
 SSHV1 = wireprototypes.SSHV1
 SSHV2 = wireprototypes.SSHV2
@@ -56,7 +56,7 @@
         chunks.append(pycompat.bytesurl(v))
         i += 1
 
-    return ''.join(chunks)
+    return b''.join(chunks)
 
 
 @interfaceutil.implementer(wireprototypes.baseprotocolhandler)
@@ -69,19 +69,19 @@
 
     @property
     def name(self):
-        return 'http-v1'
+        return b'http-v1'
 
     def getargs(self, args):
         knownargs = self._args()
         data = {}
         keys = args.split()
         for k in keys:
-            if k == '*':
+            if k == b'*':
                 star = {}
                 for key in knownargs.keys():
-                    if key != 'cmd' and key not in keys:
+                    if key != b'cmd' and key not in keys:
                         star[key] = knownargs[key][0]
-                data['*'] = star
+                data[b'*'] = star
             else:
                 data[k] = knownargs[k][0]
         return [data[k] for k in keys]
@@ -104,7 +104,7 @@
     def getprotocaps(self):
         if self._protocaps is None:
             value = decodevaluefromheaders(self._req, b'X-HgProto')
-            self._protocaps = set(value.split(' '))
+            self._protocaps = set(value.split(b' '))
         return self._protocaps
 
     def getpayload(self):
@@ -132,33 +132,33 @@
             self._ui.ferr = olderr
 
     def client(self):
-        return 'remote:%s:%s:%s' % (
+        return b'remote:%s:%s:%s' % (
             self._req.urlscheme,
-            urlreq.quote(self._req.remotehost or ''),
-            urlreq.quote(self._req.remoteuser or ''),
+            urlreq.quote(self._req.remotehost or b''),
+            urlreq.quote(self._req.remoteuser or b''),
         )
 
     def addcapabilities(self, repo, caps):
         caps.append(b'batch')
 
         caps.append(
-            'httpheader=%d' % repo.ui.configint('server', 'maxhttpheaderlen')
+            b'httpheader=%d' % repo.ui.configint(b'server', b'maxhttpheaderlen')
         )
-        if repo.ui.configbool('experimental', 'httppostargs'):
-            caps.append('httppostargs')
+        if repo.ui.configbool(b'experimental', b'httppostargs'):
+            caps.append(b'httppostargs')
 
         # FUTURE advertise 0.2rx once support is implemented
         # FUTURE advertise minrx and mintx after consulting config option
-        caps.append('httpmediatype=0.1rx,0.1tx,0.2tx')
+        caps.append(b'httpmediatype=0.1rx,0.1tx,0.2tx')
 
         compengines = wireprototypes.supportedcompengines(
             repo.ui, compression.SERVERROLE
         )
         if compengines:
-            comptypes = ','.join(
+            comptypes = b','.join(
                 urlreq.quote(e.wireprotosupport().name) for e in compengines
             )
-            caps.append('compression=%s' % comptypes)
+            caps.append(b'compression=%s' % comptypes)
 
         return caps
 
@@ -194,10 +194,10 @@
     # HTTP version 1 wire protocol requests are denoted by a "cmd" query
     # string parameter. If it isn't present, this isn't a wire protocol
     # request.
-    if 'cmd' not in req.qsparams:
+    if b'cmd' not in req.qsparams:
         return False
 
-    cmd = req.qsparams['cmd']
+    cmd = req.qsparams[b'cmd']
 
     # The "cmd" request parameter is used by both the wire protocol and hgweb.
     # While not all wire protocol commands are available for all transports,
@@ -215,10 +215,10 @@
     # in this case. We send an HTTP 404 for backwards compatibility reasons.
     if req.dispatchpath:
         res.status = hgwebcommon.statusmessage(404)
-        res.headers['Content-Type'] = HGTYPE
+        res.headers[b'Content-Type'] = HGTYPE
         # TODO This is not a good response to issue for this request. This
         # is mostly for BC for now.
-        res.setbodybytes('0\n%s\n' % b'Not Found')
+        res.setbodybytes(b'0\n%s\n' % b'Not Found')
         return True
 
     proto = httpv1protocolhandler(
@@ -237,7 +237,7 @@
         res.status = hgwebcommon.statusmessage(e.code, pycompat.bytestr(e))
         # TODO This response body assumes the failed command was
         # "unbundle." That assumption is not always valid.
-        res.setbodybytes('0\n%s\n' % pycompat.bytestr(e))
+        res.setbodybytes(b'0\n%s\n' % pycompat.bytestr(e))
 
     return True
 
@@ -248,7 +248,7 @@
     # Registered APIs are made available via config options of the name of
     # the protocol.
     for k, v in API_HANDLERS.items():
-        section, option = v['config']
+        section, option = v[b'config']
         if repo.ui.configbool(section, option):
             apis.add(k)
 
@@ -263,10 +263,10 @@
 
     # This whole URL space is experimental for now. But we want to
     # reserve the URL space. So, 404 all URLs if the feature isn't enabled.
-    if not repo.ui.configbool('experimental', 'web.apiserver'):
+    if not repo.ui.configbool(b'experimental', b'web.apiserver'):
         res.status = b'404 Not Found'
         res.headers[b'Content-Type'] = b'text/plain'
-        res.setbodybytes(_('Experimental API server endpoint not enabled'))
+        res.setbodybytes(_(b'Experimental API server endpoint not enabled'))
         return
 
     # The URL space is /api/<protocol>/*. The structure of URLs under varies
@@ -280,14 +280,14 @@
         res.headers[b'Content-Type'] = b'text/plain'
         lines = [
             _(
-                'APIs can be accessed at /api/<name>, where <name> can be '
-                'one of the following:\n'
+                b'APIs can be accessed at /api/<name>, where <name> can be '
+                b'one of the following:\n'
             )
         ]
         if availableapis:
             lines.extend(sorted(availableapis))
         else:
-            lines.append(_('(no available APIs)\n'))
+            lines.append(_(b'(no available APIs)\n'))
         res.setbodybytes(b'\n'.join(lines))
         return
 
@@ -297,7 +297,7 @@
         res.status = b'404 Not Found'
         res.headers[b'Content-Type'] = b'text/plain'
         res.setbodybytes(
-            _('Unknown API: %s\nKnown APIs: %s')
+            _(b'Unknown API: %s\nKnown APIs: %s')
             % (proto, b', '.join(sorted(availableapis)))
         )
         return
@@ -305,10 +305,10 @@
     if proto not in availableapis:
         res.status = b'404 Not Found'
         res.headers[b'Content-Type'] = b'text/plain'
-        res.setbodybytes(_('API %s not enabled\n') % proto)
+        res.setbodybytes(_(b'API %s not enabled\n') % proto)
         return
 
-    API_HANDLERS[proto]['handler'](
+    API_HANDLERS[proto][b'handler'](
         rctx, req, res, checkperm, req.dispatchparts[2:]
     )
 
@@ -326,9 +326,9 @@
 #    descriptor for this service. The response must be serializable to CBOR.
 API_HANDLERS = {
     wireprotov2server.HTTP_WIREPROTO_V2: {
-        'config': ('experimental', 'web.api.http-v2'),
-        'handler': wireprotov2server.handlehttpv2request,
-        'apidescriptor': wireprotov2server.httpv2apidescriptor,
+        b'config': (b'experimental', b'web.api.http-v2'),
+        b'handler': wireprotov2server.handlehttpv2request,
+        b'apidescriptor': wireprotov2server.httpv2apidescriptor,
     },
 }
 
@@ -341,7 +341,7 @@
     # Determine the response media type and compression engine based
     # on the request parameters.
 
-    if '0.2' in proto.getprotocaps():
+    if b'0.2' in proto.getprotocaps():
         # All clients are expected to support uncompressed data.
         if prefer_uncompressed:
             return HGTYPE2, compression._noopengine(), {}
@@ -353,9 +353,9 @@
         ):
             if engine.wireprotosupport().name in compformats:
                 opts = {}
-                level = ui.configint('server', '%slevel' % engine.name())
+                level = ui.configint(b'server', b'%slevel' % engine.name())
                 if level is not None:
-                    opts['level'] = level
+                    opts[b'level'] = level
 
                 return HGTYPE2, engine, opts
 
@@ -365,8 +365,8 @@
     # 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': ui.configint('server', 'zliblevel')}
-    return HGTYPE, util.compengines['zlib'], opts
+    opts = {b'level': ui.configint(b'server', b'zliblevel')}
+    return HGTYPE, util.compengines[b'zlib'], opts
 
 
 def processcapabilitieshandshake(repo, req, res, proto):
@@ -377,7 +377,7 @@
     advertised services are available, we don't handle the request.
     """
     # Fall back to old behavior unless the API server is enabled.
-    if not repo.ui.configbool('experimental', 'web.apiserver'):
+    if not repo.ui.configbool(b'experimental', b'web.apiserver'):
         return False
 
     clientapis = decodevaluefromheaders(req, b'X-HgUpgrade')
@@ -386,7 +386,7 @@
         return False
 
     # We currently only support CBOR responses.
-    protocaps = set(protocaps.split(' '))
+    protocaps = set(protocaps.split(b' '))
     if b'cbor' not in protocaps:
         return False
 
@@ -395,20 +395,20 @@
     for api in sorted(set(clientapis.split()) & _availableapis(repo)):
         handler = API_HANDLERS[api]
 
-        descriptorfn = handler.get('apidescriptor')
+        descriptorfn = handler.get(b'apidescriptor')
         if not descriptorfn:
             continue
 
         descriptors[api] = descriptorfn(req, repo)
 
-    v1caps = wireprotov1server.dispatch(repo, proto, 'capabilities')
+    v1caps = wireprotov1server.dispatch(repo, proto, b'capabilities')
     assert isinstance(v1caps, wireprototypes.bytesresponse)
 
     m = {
         # TODO allow this to be configurable.
-        'apibase': 'api/',
-        'apis': descriptors,
-        'v1capabilities': v1caps.data,
+        b'apibase': b'api/',
+        b'apis': descriptors,
+        b'v1capabilities': v1caps.data,
     }
 
     res.status = b'200 OK'
@@ -427,7 +427,7 @@
         # identifying the compression engine.
         name = engine.wireprotosupport().name
         assert 0 < len(name) < 256
-        yield struct.pack('B', len(name))
+        yield struct.pack(b'B', len(name))
         yield name
 
         for chunk in gen:
@@ -435,11 +435,11 @@
 
     def setresponse(code, contenttype, bodybytes=None, bodygen=None):
         if code == HTTP_OK:
-            res.status = '200 Script output follows'
+            res.status = b'200 Script output follows'
         else:
             res.status = hgwebcommon.statusmessage(code)
 
-        res.headers['Content-Type'] = contenttype
+        res.headers[b'Content-Type'] = contenttype
 
         if bodybytes is not None:
             res.setbodybytes(bodybytes)
@@ -450,14 +450,17 @@
         setresponse(
             HTTP_OK,
             HGERRTYPE,
-            _('requested wire protocol command is not available over ' 'HTTP'),
+            _(
+                b'requested wire protocol command is not available over '
+                b'HTTP'
+            ),
         )
         return
 
     proto.checkperm(wireprotov1server.commands[cmd].permission)
 
     # Possibly handle a modern client wanting to switch protocols.
-    if cmd == 'capabilities' and processcapabilitieshandshake(
+    if cmd == b'capabilities' and processcapabilitieshandshake(
         repo, req, res, proto
     ):
 
@@ -486,21 +489,21 @@
 
         setresponse(HTTP_OK, mediatype, bodygen=gen)
     elif isinstance(rsp, wireprototypes.pushres):
-        rsp = '%d\n%s' % (rsp.res, rsp.output)
+        rsp = b'%d\n%s' % (rsp.res, rsp.output)
         setresponse(HTTP_OK, HGTYPE, bodybytes=rsp)
     elif isinstance(rsp, wireprototypes.pusherr):
-        rsp = '0\n%s\n' % rsp.res
+        rsp = b'0\n%s\n' % rsp.res
         res.drain = True
         setresponse(HTTP_OK, HGTYPE, bodybytes=rsp)
     elif isinstance(rsp, wireprototypes.ooberror):
         setresponse(HTTP_OK, HGERRTYPE, bodybytes=rsp.message)
     else:
-        raise error.ProgrammingError('hgweb.protocol internal failure', rsp)
+        raise error.ProgrammingError(b'hgweb.protocol internal failure', rsp)
 
 
 def _sshv1respondbytes(fout, value):
     """Send a bytes response for protocol version 1."""
-    fout.write('%d\n' % len(value))
+    fout.write(b'%d\n' % len(value))
     fout.write(value)
     fout.flush()
 
@@ -540,15 +543,15 @@
             argline = self._fin.readline()[:-1]
             arg, l = argline.split()
             if arg not in keys:
-                raise error.Abort(_("unexpected parameter %r") % arg)
-            if arg == '*':
+                raise error.Abort(_(b"unexpected parameter %r") % arg)
+            if arg == b'*':
                 star = {}
                 for k in pycompat.xrange(int(l)):
                     argline = self._fin.readline()[:-1]
                     arg, l = argline.split()
                     val = self._fin.read(int(l))
                     star[arg] = val
-                data['*'] = star
+                data[b'*'] = star
             else:
                 val = self._fin.read(int(l))
                 data[arg] = val
@@ -578,8 +581,8 @@
         yield None
 
     def client(self):
-        client = encoding.environ.get('SSH_CLIENT', '').split(' ', 1)[0]
-        return 'remote:ssh:' + client
+        client = encoding.environ.get(b'SSH_CLIENT', b'').split(b' ', 1)[0]
+        return b'remote:ssh:' + client
 
     def addcapabilities(self, repo, caps):
         if self.name == wireprototypes.SSHV1:
@@ -655,18 +658,18 @@
     #    Ths happens by default since protocol version 2 is the same as
     #    version 1 except for the handshake.
 
-    state = 'protov1-serving'
+    state = b'protov1-serving'
     proto = sshv1protocolhandler(ui, fin, fout)
     protoswitched = False
 
     while not ev.is_set():
-        if state == 'protov1-serving':
+        if state == b'protov1-serving':
             # Commands are issued on new lines.
             request = fin.readline()[:-1]
 
             # Empty lines signal to terminate the connection.
             if not request:
-                state = 'shutdown'
+                state = b'shutdown'
                 continue
 
             # It looks like a protocol upgrade request. Transition state to
@@ -678,10 +681,10 @@
                         ui.ferr,
                         b'cannot upgrade protocols multiple ' b'times',
                     )
-                    state = 'shutdown'
+                    state = b'shutdown'
                     continue
 
-                state = 'upgrade-initial'
+                state = b'upgrade-initial'
                 continue
 
             available = wireprotov1server.commands.commandavailable(
@@ -715,16 +718,16 @@
                 _sshv1respondooberror(fout, ui.ferr, rsp.message)
             else:
                 raise error.ProgrammingError(
-                    'unhandled response type from '
-                    'wire protocol command: %s' % rsp
+                    b'unhandled response type from '
+                    b'wire protocol command: %s' % rsp
                 )
 
         # For now, protocol version 2 serving just goes back to version 1.
-        elif state == 'protov2-serving':
-            state = 'protov1-serving'
+        elif state == b'protov2-serving':
+            state = b'protov1-serving'
             continue
 
-        elif state == 'upgrade-initial':
+        elif state == b'upgrade-initial':
             # We should never transition into this state if we've switched
             # protocols.
             assert not protoswitched
@@ -738,20 +741,20 @@
                 token, caps = request.split(b' ')[1:]
             except ValueError:
                 _sshv1respondbytes(fout, b'')
-                state = 'protov1-serving'
+                state = b'protov1-serving'
                 continue
 
             # Send empty response if we don't support upgrading protocols.
-            if not ui.configbool('experimental', 'sshserver.support-v2'):
+            if not ui.configbool(b'experimental', b'sshserver.support-v2'):
                 _sshv1respondbytes(fout, b'')
-                state = 'protov1-serving'
+                state = b'protov1-serving'
                 continue
 
             try:
                 caps = urlreq.parseqs(caps)
             except ValueError:
                 _sshv1respondbytes(fout, b'')
-                state = 'protov1-serving'
+                state = b'protov1-serving'
                 continue
 
             # We don't see an upgrade request to protocol version 2. Ignore
@@ -759,15 +762,15 @@
             wantedprotos = caps.get(b'proto', [b''])[0]
             if SSHV2 not in wantedprotos:
                 _sshv1respondbytes(fout, b'')
-                state = 'protov1-serving'
+                state = b'protov1-serving'
                 continue
 
             # It looks like we can honor this upgrade request to protocol 2.
             # Filter the rest of the handshake protocol request lines.
-            state = 'upgrade-v2-filter-legacy-handshake'
+            state = b'upgrade-v2-filter-legacy-handshake'
             continue
 
-        elif state == 'upgrade-v2-filter-legacy-handshake':
+        elif state == b'upgrade-v2-filter-legacy-handshake':
             # Client should have sent legacy handshake after an ``upgrade``
             # request. Expected lines:
             #
@@ -787,7 +790,7 @@
                         b'malformed handshake protocol: ' b'missing %s' % line,
                     )
                     ok = False
-                    state = 'shutdown'
+                    state = b'shutdown'
                     break
 
             if not ok:
@@ -801,13 +804,13 @@
                     b'malformed handshake protocol: '
                     b'missing between argument value',
                 )
-                state = 'shutdown'
+                state = b'shutdown'
                 continue
 
-            state = 'upgrade-v2-finish'
+            state = b'upgrade-v2-finish'
             continue
 
-        elif state == 'upgrade-v2-finish':
+        elif state == b'upgrade-v2-finish':
             # Send the upgrade response.
             fout.write(b'upgraded %s %s\n' % (token, SSHV2))
             servercaps = wireprotov1server.capabilities(repo, proto)
@@ -818,15 +821,15 @@
             proto = sshv2protocolhandler(ui, fin, fout)
             protoswitched = True
 
-            state = 'protov2-serving'
+            state = b'protov2-serving'
             continue
 
-        elif state == 'shutdown':
+        elif state == b'shutdown':
             break
 
         else:
             raise error.ProgrammingError(
-                'unhandled ssh server state: %s' % state
+                b'unhandled ssh server state: %s' % state
             )
 
 
@@ -839,10 +842,10 @@
         # Log write I/O to stdout and stderr if configured.
         if logfh:
             self._fout = util.makeloggingfileobject(
-                logfh, self._fout, 'o', logdata=True
+                logfh, self._fout, b'o', logdata=True
             )
             ui.ferr = util.makeloggingfileobject(
-                logfh, ui.ferr, 'e', logdata=True
+                logfh, ui.ferr, b'e', logdata=True
             )
 
     def serve_forever(self):