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