mercurial/wireprotoserver.py
changeset 48526 04688c51f81f
parent 48490 81805bba11f9
child 48875 6000f5b25c9b
--- a/mercurial/wireprotoserver.py	Thu Dec 30 13:25:44 2021 +0100
+++ b/mercurial/wireprotoserver.py	Tue Dec 07 16:44:22 2021 +0100
@@ -18,11 +18,9 @@
     util,
     wireprototypes,
     wireprotov1server,
-    wireprotov2server,
 )
 from .interfaces import util as interfaceutil
 from .utils import (
-    cborutil,
     compression,
     stringutil,
 )
@@ -39,7 +37,6 @@
 HGERRTYPE = b'application/hg-error'
 
 SSHV1 = wireprototypes.SSHV1
-SSHV2 = wireprototypes.SSHV2
 
 
 def decodevaluefromheaders(req, headerprefix):
@@ -244,97 +241,6 @@
     return True
 
 
-def _availableapis(repo):
-    apis = set()
-
-    # Registered APIs are made available via config options of the name of
-    # the protocol.
-    for k, v in API_HANDLERS.items():
-        section, option = v[b'config']  # pytype: disable=attribute-error
-        if repo.ui.configbool(section, option):
-            apis.add(k)
-
-    return apis
-
-
-def handlewsgiapirequest(rctx, req, res, checkperm):
-    """Handle requests to /api/*."""
-    assert req.dispatchparts[0] == b'api'
-
-    repo = rctx.repo
-
-    # 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(b'experimental', b'web.apiserver'):
-        res.status = b'404 Not Found'
-        res.headers[b'Content-Type'] = b'text/plain'
-        res.setbodybytes(_(b'Experimental API server endpoint not enabled'))
-        return
-
-    # The URL space is /api/<protocol>/*. The structure of URLs under varies
-    # by <protocol>.
-
-    availableapis = _availableapis(repo)
-
-    # Requests to /api/ list available APIs.
-    if req.dispatchparts == [b'api']:
-        res.status = b'200 OK'
-        res.headers[b'Content-Type'] = b'text/plain'
-        lines = [
-            _(
-                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(_(b'(no available APIs)\n'))
-        res.setbodybytes(b'\n'.join(lines))
-        return
-
-    proto = req.dispatchparts[1]
-
-    if proto not in API_HANDLERS:
-        res.status = b'404 Not Found'
-        res.headers[b'Content-Type'] = b'text/plain'
-        res.setbodybytes(
-            _(b'Unknown API: %s\nKnown APIs: %s')
-            % (proto, b', '.join(sorted(availableapis)))
-        )
-        return
-
-    if proto not in availableapis:
-        res.status = b'404 Not Found'
-        res.headers[b'Content-Type'] = b'text/plain'
-        res.setbodybytes(_(b'API %s not enabled\n') % proto)
-        return
-
-    API_HANDLERS[proto][b'handler'](
-        rctx, req, res, checkperm, req.dispatchparts[2:]
-    )
-
-
-# Maps API name to metadata so custom API can be registered.
-# Keys are:
-#
-# config
-#    Config option that controls whether service is enabled.
-# handler
-#    Callable receiving (rctx, req, res, checkperm, urlparts) that is called
-#    when a request to this API is received.
-# apidescriptor
-#    Callable receiving (req, repo) that is called to obtain an API
-#    descriptor for this service. The response must be serializable to CBOR.
-API_HANDLERS = {
-    wireprotov2server.HTTP_WIREPROTO_V2: {
-        b'config': (b'experimental', b'web.api.http-v2'),
-        b'handler': wireprotov2server.handlehttpv2request,
-        b'apidescriptor': wireprotov2server.httpv2apidescriptor,
-    },
-}
-
-
 def _httpresponsetype(ui, proto, prefer_uncompressed):
     """Determine the appropriate response type and compression settings.
 
@@ -371,55 +277,6 @@
     return HGTYPE, util.compengines[b'zlib'], opts
 
 
-def processcapabilitieshandshake(repo, req, res, proto):
-    """Called during a ?cmd=capabilities request.
-
-    If the client is advertising support for a newer protocol, we send
-    a CBOR response with information about available services. If no
-    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(b'experimental', b'web.apiserver'):
-        return False
-
-    clientapis = decodevaluefromheaders(req, b'X-HgUpgrade')
-    protocaps = decodevaluefromheaders(req, b'X-HgProto')
-    if not clientapis or not protocaps:
-        return False
-
-    # We currently only support CBOR responses.
-    protocaps = set(protocaps.split(b' '))
-    if b'cbor' not in protocaps:
-        return False
-
-    descriptors = {}
-
-    for api in sorted(set(clientapis.split()) & _availableapis(repo)):
-        handler = API_HANDLERS[api]
-
-        descriptorfn = handler.get(b'apidescriptor')
-        if not descriptorfn:
-            continue
-
-        descriptors[api] = descriptorfn(req, repo)
-
-    v1caps = wireprotov1server.dispatch(repo, proto, b'capabilities')
-    assert isinstance(v1caps, wireprototypes.bytesresponse)
-
-    m = {
-        # TODO allow this to be configurable.
-        b'apibase': b'api/',
-        b'apis': descriptors,
-        b'v1capabilities': v1caps.data,
-    }
-
-    res.status = b'200 OK'
-    res.headers[b'Content-Type'] = b'application/mercurial-cbor'
-    res.setbodybytes(b''.join(cborutil.streamencode(m)))
-
-    return True
-
-
 def _callhttp(repo, req, res, proto, cmd):
     # Avoid cycle involving hg module.
     from .hgweb import common as hgwebcommon
@@ -461,13 +318,6 @@
 
     proto.checkperm(wireprotov1server.commands[cmd].permission)
 
-    # Possibly handle a modern client wanting to switch protocols.
-    if cmd == b'capabilities' and processcapabilitieshandshake(
-        repo, req, res, proto
-    ):
-
-        return
-
     rsp = wireprotov1server.dispatch(repo, proto, cmd)
 
     if isinstance(rsp, bytes):
@@ -596,17 +446,6 @@
         pass
 
 
-class sshv2protocolhandler(sshv1protocolhandler):
-    """Protocol handler for version 2 of the SSH protocol."""
-
-    @property
-    def name(self):
-        return wireprototypes.SSHV2
-
-    def addcapabilities(self, repo, caps):
-        return caps
-
-
 def _runsshserver(ui, repo, fin, fout, ev):
     # This function operates like a state machine of sorts. The following
     # states are defined:
@@ -616,19 +455,6 @@
     #    new lines. These commands are processed in this state, one command
     #    after the other.
     #
-    # protov2-serving
-    #    Server is in protocol version 2 serving mode.
-    #
-    # upgrade-initial
-    #    The server is going to process an upgrade request.
-    #
-    # upgrade-v2-filter-legacy-handshake
-    #    The protocol is being upgraded to version 2. The server is expecting
-    #    the legacy handshake from version 1.
-    #
-    # upgrade-v2-finish
-    #    The upgrade to version 2 of the protocol is imminent.
-    #
     # shutdown
     #    The server is shutting down, possibly in reaction to a client event.
     #
@@ -637,32 +463,9 @@
     # protov1-serving -> shutdown
     #    When server receives an empty request or encounters another
     #    error.
-    #
-    # protov1-serving -> upgrade-initial
-    #    An upgrade request line was seen.
-    #
-    # upgrade-initial -> upgrade-v2-filter-legacy-handshake
-    #    Upgrade to version 2 in progress. Server is expecting to
-    #    process a legacy handshake.
-    #
-    # upgrade-v2-filter-legacy-handshake -> shutdown
-    #    Client did not fulfill upgrade handshake requirements.
-    #
-    # upgrade-v2-filter-legacy-handshake -> upgrade-v2-finish
-    #    Client fulfilled version 2 upgrade requirements. Finishing that
-    #    upgrade.
-    #
-    # upgrade-v2-finish -> protov2-serving
-    #    Protocol upgrade to version 2 complete. Server can now speak protocol
-    #    version 2.
-    #
-    # protov2-serving -> protov1-serving
-    #    Ths happens by default since protocol version 2 is the same as
-    #    version 1 except for the handshake.
 
     state = b'protov1-serving'
     proto = sshv1protocolhandler(ui, fin, fout)
-    protoswitched = False
 
     while not ev.is_set():
         if state == b'protov1-serving':
@@ -674,21 +477,6 @@
                 state = b'shutdown'
                 continue
 
-            # It looks like a protocol upgrade request. Transition state to
-            # handle it.
-            if request.startswith(b'upgrade '):
-                if protoswitched:
-                    _sshv1respondooberror(
-                        fout,
-                        ui.ferr,
-                        b'cannot upgrade protocols multiple times',
-                    )
-                    state = b'shutdown'
-                    continue
-
-                state = b'upgrade-initial'
-                continue
-
             available = wireprotov1server.commands.commandavailable(
                 request, proto
             )
@@ -724,108 +512,6 @@
                     b'wire protocol command: %s' % rsp
                 )
 
-        # For now, protocol version 2 serving just goes back to version 1.
-        elif state == b'protov2-serving':
-            state = b'protov1-serving'
-            continue
-
-        elif state == b'upgrade-initial':
-            # We should never transition into this state if we've switched
-            # protocols.
-            assert not protoswitched
-            assert proto.name == wireprototypes.SSHV1
-
-            # Expected: upgrade <token> <capabilities>
-            # If we get something else, the request is malformed. It could be
-            # from a future client that has altered the upgrade line content.
-            # We treat this as an unknown command.
-            try:
-                token, caps = request.split(b' ')[1:]
-            except ValueError:
-                _sshv1respondbytes(fout, b'')
-                state = b'protov1-serving'
-                continue
-
-            # Send empty response if we don't support upgrading protocols.
-            if not ui.configbool(b'experimental', b'sshserver.support-v2'):
-                _sshv1respondbytes(fout, b'')
-                state = b'protov1-serving'
-                continue
-
-            try:
-                caps = urlreq.parseqs(caps)
-            except ValueError:
-                _sshv1respondbytes(fout, b'')
-                state = b'protov1-serving'
-                continue
-
-            # We don't see an upgrade request to protocol version 2. Ignore
-            # the upgrade request.
-            wantedprotos = caps.get(b'proto', [b''])[0]
-            if SSHV2 not in wantedprotos:
-                _sshv1respondbytes(fout, b'')
-                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 = b'upgrade-v2-filter-legacy-handshake'
-            continue
-
-        elif state == b'upgrade-v2-filter-legacy-handshake':
-            # Client should have sent legacy handshake after an ``upgrade``
-            # request. Expected lines:
-            #
-            #    hello
-            #    between
-            #    pairs 81
-            #    0000...-0000...
-
-            ok = True
-            for line in (b'hello', b'between', b'pairs 81'):
-                request = fin.readline()[:-1]
-
-                if request != line:
-                    _sshv1respondooberror(
-                        fout,
-                        ui.ferr,
-                        b'malformed handshake protocol: missing %s' % line,
-                    )
-                    ok = False
-                    state = b'shutdown'
-                    break
-
-            if not ok:
-                continue
-
-            request = fin.read(81)
-            if request != b'%s-%s' % (b'0' * 40, b'0' * 40):
-                _sshv1respondooberror(
-                    fout,
-                    ui.ferr,
-                    b'malformed handshake protocol: '
-                    b'missing between argument value',
-                )
-                state = b'shutdown'
-                continue
-
-            state = b'upgrade-v2-finish'
-            continue
-
-        elif state == b'upgrade-v2-finish':
-            # Send the upgrade response.
-            fout.write(b'upgraded %s %s\n' % (token, SSHV2))
-            servercaps = wireprotov1server.capabilities(repo, proto)
-            rsp = b'capabilities: %s' % servercaps.data
-            fout.write(b'%d\n%s\n' % (len(rsp), rsp))
-            fout.flush()
-
-            proto = sshv2protocolhandler(ui, fin, fout)
-            protoswitched = True
-
-            state = b'protov2-serving'
-            continue
-
         elif state == b'shutdown':
             break