D1944: wireproto: provide accessors for client capabilities

joerg.sonnenberger (Joerg Sonnenberger) phabricator at mercurial-scm.org
Sun Jan 28 01:17:03 UTC 2018


joerg.sonnenberger created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  For HTTP, this refactors the existing logic, including the parsing of
  the compression engine capability.
  
  For SSH, this adds a ssh-only capability "protocaps" and a command for
  informing the server on what the client supports. Since SSH is stateful,
  keep track of the capabilities in the server instance.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D1944

AFFECTED FILES
  mercurial/hgweb/protocol.py
  mercurial/sshpeer.py
  mercurial/sshserver.py
  mercurial/wireproto.py
  tests/test-ssh-bundle1.t
  tests/test-ssh.t

CHANGE DETAILS

diff --git a/tests/test-ssh.t b/tests/test-ssh.t
--- a/tests/test-ssh.t
+++ b/tests/test-ssh.t
@@ -486,9 +486,12 @@
   devel-peer-request: between
   devel-peer-request:   pairs: 81 bytes
   sending between command
-  remote: 384
-  remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
+  remote: 394
+  remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN protocaps
   remote: 1
+  devel-peer-request: protocaps
+  devel-peer-request:   caps: 25 bytes
+  sending protocaps command
   query 1; heads
   devel-peer-request: batch
   devel-peer-request:   cmds: 141 bytes
diff --git a/tests/test-ssh-bundle1.t b/tests/test-ssh-bundle1.t
--- a/tests/test-ssh-bundle1.t
+++ b/tests/test-ssh-bundle1.t
@@ -467,9 +467,10 @@
   running .* ".*/dummyssh" ['"]user at dummy['"] ('|")hg -R remote serve --stdio('|") (re)
   sending hello command
   sending between command
-  remote: 384
-  remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
+  remote: 394
+  remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN protocaps
   remote: 1
+  sending protocaps command
   preparing listkeys for "bookmarks"
   sending listkeys command
   received listkey for "bookmarks": 45 bytes
diff --git a/mercurial/wireproto.py b/mercurial/wireproto.py
--- a/mercurial/wireproto.py
+++ b/mercurial/wireproto.py
@@ -64,6 +64,17 @@
         """
         raise NotImplementedError()
 
+    def getprotocaps(self):
+        """return the wireprotocol capabilities of the current request"""
+        raise NotImplementedError()
+
+    def getcompressionsupport(self):
+        """return a list of compression methods supported by the client"""
+        for cap in self.getprotocaps():
+            if cap.startswith('comp='):
+                return cap[5:].split(',')
+        return ['zlib', 'none']
+
     def redirect(self):
         """may setup interception for stdout and stderr
 
@@ -799,6 +810,9 @@
             comptypes = ','.join(urlreq.quote(e.wireprotosupport().name)
                                  for e in compengines)
             caps.append('compression=%s' % comptypes)
+    elif proto.name == 'ssh':
+        # Advertise support for the ssh-only protocaps command
+        caps.append('protocaps')
 
     return caps
 
diff --git a/mercurial/sshserver.py b/mercurial/sshserver.py
--- a/mercurial/sshserver.py
+++ b/mercurial/sshserver.py
@@ -27,6 +27,7 @@
         self.fin = ui.fin
         self.fout = ui.fout
         self.name = 'ssh'
+        self._protocaps = []
 
         hook.redirect(True)
         ui.fout = repo.ui.fout = ui.ferr
@@ -66,6 +67,20 @@
             fpout.write(self.fin.read(count))
             count = int(self.fin.readline())
 
+    def getprotocaps(self):
+        """return the wireprotocol capabilities of the current request"""
+        return self._protocaps
+
+    def do_protocaps(self):
+        """ssh-specific command for sending the client capabilities
+
+        The ssh protocol is stateful and doesn't retransmit the wireprotocol
+        capabilities on every request.
+        """
+        self._protocaps = self.getargs(self.do_protocaps_arguments)[0]
+        self.sendresponse('OK')
+    do_protocaps_arguments = 'caps'
+
     def redirect(self):
         pass
 
diff --git a/mercurial/sshpeer.py b/mercurial/sshpeer.py
--- a/mercurial/sshpeer.py
+++ b/mercurial/sshpeer.py
@@ -13,6 +13,7 @@
 from . import (
     error,
     pycompat,
+    sshserver,
     util,
     wireproto,
 )
@@ -182,6 +183,13 @@
 
     # End of _basewirecommands interface.
 
+    def _clientcapabilities(self):
+        protoparams = []
+        comps = [e.wireprotosupport().name for e in
+                 util.compengines.supportedwireengines(util.CLIENTROLE)]
+        protoparams.append('comp=%s' % ','.join(comps))
+        return protoparams
+
     def _validaterepo(self, sshcmd, args, remotecmd, sshenv=None):
         # cleanup up previous run
         self._cleanup()
@@ -240,6 +248,12 @@
                 self._caps.update(l[:-1].split(":")[1].split())
                 break
 
+        if 'protocaps' in self._caps:
+            try:
+                self._call("protocaps",
+                           caps=' '.join(self._clientcapabilities()))
+            except IOError:
+                badresponse()
     def _readerr(self):
         _forwardoutput(self.ui, self._pipee)
 
@@ -296,7 +310,12 @@
                         dbg(line % '  %s-%s: %d' % (key, dk, len(dv)))
         self.ui.debug("sending %s command\n" % cmd)
         self._pipeo.write("%s\n" % cmd)
-        _func, names = wireproto.commands[cmd]
+        if cmd in wireproto.commands:
+            _func, names = wireproto.commands[cmd]
+        elif getattr(sshserver.sshserver, 'do_' + cmd, None):
+            names = getattr(sshserver.sshserver, 'do_%s_arguments' % cmd, None)
+        else:
+            raise KeyError(cmd)
         keys = names.split()
         wireargs = {}
         for k in keys:
diff --git a/mercurial/hgweb/protocol.py b/mercurial/hgweb/protocol.py
--- a/mercurial/hgweb/protocol.py
+++ b/mercurial/hgweb/protocol.py
@@ -52,6 +52,7 @@
         self.response = ''
         self.ui = ui
         self.name = 'http'
+        self._protocaps = None
 
     def getargs(self, args):
         knownargs = self._args()
@@ -88,6 +89,11 @@
         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 getprotocaps(self):
+        if self._protocaps is None:
+            value = decodevaluefromheaders(self.req, r'X-HgProto')
+            self._protocaps = value.split(' ')
+        return self._protocaps
     def redirect(self):
         self.oldio = self.ui.fout, self.ui.ferr
         self.ui.ferr = self.ui.fout = stringio()
@@ -109,19 +115,15 @@
         """
         # Determine the response media type and compression engine based
         # on the request parameters.
-        protocaps = decodevaluefromheaders(self.req, r'X-HgProto').split(' ')
+        protocaps = self.getprotocaps()
 
         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
+            compformats = self.getcompressionsupport()
 
             # Now find an agreed upon compression format.
             for engine in wireproto.supportedcompengines(self.ui, self,



To: joerg.sonnenberger, #hg-reviewers
Cc: mercurial-devel


More information about the Mercurial-devel mailing list