##// END OF EJS Templates
verify: don't reimplement any()...
verify: don't reimplement any() Differential Revision: https://phab.mercurial-scm.org/D2372

File last commit:

r36272:af0a19d8 default
r36357:a4d41ba4 default
Show More
wireproto.py
1067 lines | 38.2 KiB | text/x-python | PythonLexer
Matt Mackall
protocol: introduce wireproto.py...
r11581 # wireproto.py - generic wire protocol support functions
#
# Copyright 2005-2010 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.
Gregory Szorc
wireproto: use absolute_import
r25993 from __future__ import absolute_import
Augie Fackler
cleanup: replace uses of util.(md5|sha1|sha256|sha512) with hashlib.\1...
r29341 import hashlib
Gregory Szorc
wireproto: use absolute_import
r25993 import os
import tempfile
Matt Mackall
protocol: introduce wireproto.py...
r11581
Gregory Szorc
wireproto: use absolute_import
r25993 from .i18n import _
from .node import (
bin,
hex,
Siddharth Agarwal
clone: add a server-side option to disable full getbundles (pull-based clones)...
r32260 nullid,
Gregory Szorc
wireproto: use absolute_import
r25993 )
from . import (
bundle2,
changegroup as changegroupmod,
Durham Goode
changegroup: replace changegroupsubset with makechangegroup...
r34099 discovery,
Gregory Szorc
wireproto: use absolute_import
r25993 encoding,
error,
exchange,
peer,
pushkey as pushkeymod,
Pulkit Goyal
py3: convert the mode argument of os.fdopen to unicodes (1 of 2)...
r30924 pycompat,
Gregory Szorc
wireproto: use new peer interface...
r33805 repository,
Gregory Szorc
streamclone: move code out of exchange.py...
r26443 streamclone,
Gregory Szorc
wireproto: use absolute_import
r25993 util,
Gregory Szorc
wireprototypes: move wire protocol response types to new module...
r36090 wireprototypes,
Gregory Szorc
wireproto: use absolute_import
r25993 )
Pierre-Yves David
wireproto: introduce an abstractserverproto class...
r20903
timeless
pycompat: switch to util.urlreq/util.urlerr for py3 compat
r28883 urlerr = util.urlerr
urlreq = util.urlreq
Gregory Szorc
wireproto: introduce type for raw byte responses (API)...
r36091 bytesresponse = wireprototypes.bytesresponse
Gregory Szorc
wireprototypes: move wire protocol response types to new module...
r36090 ooberror = wireprototypes.ooberror
pushres = wireprototypes.pushres
pusherr = wireprototypes.pusherr
streamres = wireprototypes.streamres
streamres_legacy = wireprototypes.streamreslegacy
Pierre-Yves David
bundle1: fix bundle1-denied reporting for push over ssh...
r30909 bundle2requiredmain = _('incompatible Mercurial client; bundle2 required')
bundle2requiredhint = _('see https://www.mercurial-scm.org/wiki/'
'IncompatibleClient')
bundle2required = '%s\n(%s)\n' % (bundle2requiredmain, bundle2requiredhint)
Gregory Szorc
wireproto: config options to disable bundle1...
r27246
Augie Fackler
peer: add an iterbatcher interface...
r28436 class remoteiterbatcher(peer.iterbatcher):
def __init__(self, remote):
super(remoteiterbatcher, self).__init__()
self._remote = remote
Augie Fackler
wireproto: make iterbatcher behave streamily over http(s)...
r28438 def __getattr__(self, name):
Gregory Szorc
wireproto: properly implement batchable checking...
r33759 # Validate this method is batchable, since submit() only supports
# batchable methods.
fn = getattr(self._remote, name)
if not getattr(fn, 'batchable', None):
raise error.ProgrammingError('Attempted to batch a non-batchable '
'call to %r' % name)
Augie Fackler
wireproto: make iterbatcher behave streamily over http(s)...
r28438 return super(remoteiterbatcher, self).__getattr__(name)
Augie Fackler
peer: add an iterbatcher interface...
r28436 def submit(self):
"""Break the batch request into many patch calls and pipeline them.
This is mostly valuable over http where request sizes can be
limited, but can be used in other places as well.
"""
Gregory Szorc
wireproto: overhaul iterating batcher code (API)...
r33761 # 2-tuple of (command, arguments) that represents what will be
# sent over the wire.
requests = []
# 4-tuple of (command, final future, @batchable generator, remote
# future).
results = []
for command, args, opts, finalfuture in self.calls:
mtd = getattr(self._remote, command)
Augie Fackler
python3: replace im_{self,func} with __{self,func}__ globally...
r34727 batchable = mtd.batchable(mtd.__self__, *args, **opts)
Gregory Szorc
wireproto: overhaul iterating batcher code (API)...
r33761
commandargs, fremote = next(batchable)
assert fremote
requests.append((command, commandargs))
results.append((command, finalfuture, batchable, fremote))
if requests:
self._resultiter = self._remote._submitbatch(requests)
self._results = results
Augie Fackler
peer: add an iterbatcher interface...
r28436
def results(self):
Gregory Szorc
wireproto: overhaul iterating batcher code (API)...
r33761 for command, finalfuture, batchable, remotefuture in self._results:
# Get the raw result, set it in the remote future, feed it
# back into the @batchable generator so it can be decoded, and
# set the result on the final future to this value.
remoteresult = next(self._resultiter)
remotefuture.set(remoteresult)
finalfuture.set(next(batchable))
# Verify our @batchable generators only emit 2 values.
try:
next(batchable)
except StopIteration:
pass
else:
raise error.ProgrammingError('%s @batchable generator emitted '
'unexpected value count' % command)
yield finalfuture.value
Augie Fackler
peer: add an iterbatcher interface...
r28436
Augie Fackler
batching: migrate basic noop batching into peer.peer...
r25912 # Forward a couple of names from peer to make wireproto interactions
# slightly more sensible.
batchable = peer.batchable
future = peer.future
Peter Arrenbrecht
wireproto: add basic command batching infrastructure...
r14621
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 # list of nodes encoding / decoding
def decodelist(l, sep=' '):
Peter Arrenbrecht
wireproto: fix decodelist to properly return empty list...
r13722 if l:
Augie Fackler
wireproto: use listcomp instead of map()...
r34730 return [bin(v) for v in l.split(sep)]
Peter Arrenbrecht
wireproto: fix decodelist to properly return empty list...
r13722 return []
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597
def encodelist(l, sep=' '):
Pierre-Yves David
discovery: run discovery on filtered repository...
r23848 try:
return sep.join(map(hex, l))
except TypeError:
raise
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597
Peter Arrenbrecht
wireproto: add batching support to wirerepository...
r14622 # batched call argument encoding
def escapearg(plain):
return (plain
Augie Fackler
wireproto: correctly escape batched args and responses (issue4739)...
r25708 .replace(':', ':c')
.replace(',', ':o')
.replace(';', ':s')
.replace('=', ':e'))
Peter Arrenbrecht
wireproto: add batching support to wirerepository...
r14622
def unescapearg(escaped):
return (escaped
Augie Fackler
wireproto: correctly escape batched args and responses (issue4739)...
r25708 .replace(':e', '=')
.replace(':s', ';')
.replace(':o', ',')
.replace(':c', ':'))
Peter Arrenbrecht
wireproto: add batching support to wirerepository...
r14622
Gregory Szorc
wireproto: consolidate code for obtaining "cmds" argument value...
r29733 def encodebatchcmds(req):
"""Return a ``cmds`` argument value for the ``batch`` command."""
cmds = []
for op, argsdict in req:
Gregory Szorc
wireproto: unescape argument names in batch command (BC)...
r29734 # Old servers didn't properly unescape argument names. So prevent
# the sending of argument names that may not be decoded properly by
# servers.
assert all(escapearg(k) == k for k in argsdict)
Gregory Szorc
wireproto: consolidate code for obtaining "cmds" argument value...
r29733 args = ','.join('%s=%s' % (escapearg(k), escapearg(v))
for k, v in argsdict.iteritems())
cmds.append('%s %s' % (op, args))
return ';'.join(cmds)
Pierre-Yves David
getbundle: declare type of parameters...
r21646 # mapping of options accepted by getbundle and their types
#
# Meant to be extended by extensions. It is extensions responsibility to ensure
# such options are properly processed in exchange.getbundle.
#
# supported types are:
#
# :nodes: list of binary nodes
# :csv: list of comma-separated values
Pierre-Yves David
wireprotocol: distinguish list and set in getbundle argument...
r25403 # :scsv: list of comma-separated values return as set
Pierre-Yves David
getbundle: declare type of parameters...
r21646 # :plain: string with no transformation needed.
gboptsmap = {'heads': 'nodes',
Boris Feld
getbundle: add support for 'bookmarks' boolean argument...
r35268 'bookmarks': 'boolean',
Pierre-Yves David
getbundle: declare type of parameters...
r21646 'common': 'nodes',
Pierre-Yves David
getbundle: add `obsmarkers` argument to getbundle...
r22353 'obsmarkers': 'boolean',
Boris Feld
pull: use 'phase-heads' to retrieve phase information...
r34323 'phases': 'boolean',
Pierre-Yves David
wireprotocol: distinguish list and set in getbundle argument...
r25403 'bundlecaps': 'scsv',
Pierre-Yves David
getbundle: add a ``cg`` boolean argument to control changegroup inclusion...
r21989 'listkeys': 'csv',
Gregory Szorc
exchange: advertise if a clone bundle was attempted...
r26690 'cg': 'boolean',
Boris Feld
bundle2: add support for a 'stream' parameter to 'getbundle'...
r35777 'cbattempted': 'boolean',
'stream': 'boolean',
}
Pierre-Yves David
getbundle: declare type of parameters...
r21646
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586 # client side
Gregory Szorc
wireproto: use new peer interface...
r33805 class wirepeer(repository.legacypeer):
Gregory Szorc
wireproto: add docstring for wirepeer
r27243 """Client-side interface for communicating with a peer repository.
Peter Arrenbrecht
wireproto: add batching support to wirerepository...
r14622
Gregory Szorc
wireproto: add docstring for wirepeer
r27243 Methods commonly call wire protocol commands of the same name.
See also httppeer.py and sshpeer.py for protocol-specific
implementations of this interface.
"""
Gregory Szorc
wireproto: use new peer interface...
r33805 # Begin of basewirepeer interface.
Peter Arrenbrecht
wireproto: add batching support to wirerepository...
r14622
Augie Fackler
peer: add an iterbatcher interface...
r28436 def iterbatch(self):
return remoteiterbatcher(self)
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 @batchable
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586 def lookup(self, key):
self.requirecap('lookup', _('look up remote revision'))
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 f = future()
Augie Fackler
wireproto: remove todict() and use {} literals instead
r20671 yield {'key': encoding.fromlocal(key)}, f
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 d = f.value
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586 success, data = d[:-1].split(" ", 1)
if int(success):
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 yield bin(data)
Kyle Lippincott
wireproto: do not abort after successful lookup...
r34064 else:
self._abort(error.RepoError(data))
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 @batchable
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586 def heads(self):
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 f = future()
yield {}, f
d = f.value
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586 try:
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 yield decodelist(d[:-1])
Matt Mackall
wireproto: avoid naked excepts
r13726 except ValueError:
Benoit Boissinot
fix undefined variables, spotted by pylint
r11879 self._abort(error.ResponseError(_("unexpected response:"), d))
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 @batchable
Peter Arrenbrecht
wireproto: add known([id]) function...
r13723 def known(self, nodes):
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 f = future()
Augie Fackler
wireproto: remove todict() and use {} literals instead
r20671 yield {'nodes': encodelist(nodes)}, f
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 d = f.value
Peter Arrenbrecht
wireproto: add known([id]) function...
r13723 try:
Mads Kiilerich
cleanup: fix some list comprehension redefinitions of existing vars...
r22201 yield [bool(int(b)) for b in d]
Matt Mackall
wireproto: avoid naked excepts
r13726 except ValueError:
Peter Arrenbrecht
wireproto: add known([id]) function...
r13723 self._abort(error.ResponseError(_("unexpected response:"), d))
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 @batchable
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586 def branchmap(self):
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 f = future()
yield {}, f
d = f.value
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586 try:
branchmap = {}
for branchpart in d.splitlines():
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 branchname, branchheads = branchpart.split(' ', 1)
timeless
pycompat: switch to util.urlreq/util.urlerr for py3 compat
r28883 branchname = encoding.tolocal(urlreq.unquote(branchname))
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 branchheads = decodelist(branchheads)
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586 branchmap[branchname] = branchheads
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 yield branchmap
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586 except TypeError:
self._abort(error.ResponseError(_("unexpected response:"), d))
Gregory Szorc
wireproto: use new peer interface...
r33805 @batchable
def listkeys(self, namespace):
if not self.capable('pushkey'):
yield {}, None
f = future()
self.ui.debug('preparing listkeys for "%s"\n' % namespace)
yield {'namespace': encoding.fromlocal(namespace)}, f
d = f.value
self.ui.debug('received listkey for "%s": %i bytes\n'
% (namespace, len(d)))
yield pushkeymod.decodekeys(d)
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 @batchable
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586 def pushkey(self, namespace, key, old, new):
if not self.capable('pushkey'):
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 yield False, None
f = future()
Pierre-Yves David
pushkey: add more verbose debug output regarding pushkey...
r17293 self.ui.debug('preparing pushkey for "%s:%s"\n' % (namespace, key))
Augie Fackler
wireproto: remove todict() and use {} literals instead
r20671 yield {'namespace': encoding.fromlocal(namespace),
'key': encoding.fromlocal(key),
'old': encoding.fromlocal(old),
'new': encoding.fromlocal(new)}, f
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 d = f.value
Pierre-Yves David
wireproto: handle other server output in pushkey...
r15652 d, output = d.split('\n', 1)
David Soria Parra
wireproto: catch possible cast error in pushkey...
r13450 try:
d = bool(int(d))
except ValueError:
raise error.ResponseError(
_('push failed (unexpected response):'), d)
Pierre-Yves David
wireproto: handle other server output in pushkey...
r15652 for l in output.splitlines(True):
self.ui.status(_('remote: '), l)
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 yield d
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586
Matt Mackall
protocol: unify stream_out client code
r11588 def stream_out(self):
return self._callstream('stream_out')
Pierre-Yves David
getbundle: declare type of parameters...
r21646 def getbundle(self, source, **kwargs):
Augie Fackler
wireproto: bounce kwargs to/from bytes/str as needed...
r34740 kwargs = pycompat.byteskwargs(kwargs)
Peter Arrenbrecht
wireproto: add getbundle() function...
r13741 self.requirecap('getbundle', _('look up remote changes'))
opts = {}
Pierre-Yves David
getbundle: sort bundlecaps before exchanging then over the wire...
r25128 bundlecaps = kwargs.get('bundlecaps')
if bundlecaps is not None:
kwargs['bundlecaps'] = sorted(bundlecaps)
else:
bundlecaps = () # kwargs could have it to None
Pierre-Yves David
getbundle: declare type of parameters...
r21646 for key, value in kwargs.iteritems():
if value is None:
continue
keytype = gboptsmap.get(key)
if keytype is None:
Augie Fackler
wireproto: use a proper exception instead of `assert False`...
r34731 raise error.ProgrammingError(
'Unexpectedly None keytype for key %s' % key)
Pierre-Yves David
getbundle: declare type of parameters...
r21646 elif keytype == 'nodes':
value = encodelist(value)
Pierre-Yves David
wireprotocol: distinguish list and set in getbundle argument...
r25403 elif keytype in ('csv', 'scsv'):
Pierre-Yves David
getbundle: declare type of parameters...
r21646 value = ','.join(value)
Pierre-Yves David
wireproto: add a ``boolean`` type for getbundle parameters...
r21988 elif keytype == 'boolean':
Pierre-Yves David
wireprotocol: fix 'boolean' handling...
r22351 value = '%i' % bool(value)
Pierre-Yves David
getbundle: declare type of parameters...
r21646 elif keytype != 'plain':
raise KeyError('unknown getbundle option type %s'
% keytype)
opts[key] = value
Augie Fackler
wireproto: bounce kwargs to/from bytes/str as needed...
r34740 f = self._callcompressable("getbundle", **pycompat.strkwargs(opts))
Augie Fackler
cleanup: use __builtins__.any instead of util.any...
r25149 if any((cap.startswith('HG2') for cap in bundlecaps)):
Pierre-Yves David
unbundle20: retrieve unbundler instances through a factory function...
r24641 return bundle2.getunbundler(self.ui, f)
Pierre-Yves David
bundle2: allow bundle2 for pulling over the wire...
r21069 else:
Sune Foldager
changegroup: rename bundle-related functions and classes...
r22390 return changegroupmod.cg1unpacker(f, 'UN')
Peter Arrenbrecht
wireproto: add getbundle() function...
r13741
Augie Fackler
wirepeer: rename confusing `source` parameter...
r29706 def unbundle(self, cg, heads, url):
Matt Mackall
protocol: unify client unbundle support...
r11592 '''Send cg (a readable file-like object representing the
changegroup to push, typically a chunkbuffer object) to the
Pierre-Yves David
bundle2: support for push over the wire...
r21075 remote server as a bundle.
When pushing a bundle10 stream, return an integer indicating the
Martin von Zweigbergk
wireproto: update reference to deleted addchangegroup()...
r32880 result of the push (see changegroup.apply()).
Pierre-Yves David
bundle2: support for push over the wire...
r21075
Augie Fackler
wirepeer: rename confusing `source` parameter...
r29706 When pushing a bundle20 stream, return a bundle20 stream.
`url` is the url the client thinks it's pushing to, which is
visible to hooks.
'''
Matt Mackall
protocol: unify client unbundle support...
r11592
Martin Geisler
wireproto: do not hash when heads == ['force']...
r14419 if heads != ['force'] and self.capable('unbundlehash'):
Shuhei Takahashi
wireproto: allow unbundle with hashed heads parameter (issue2126)...
r13942 heads = encodelist(['hashed',
Augie Fackler
cleanup: replace uses of util.(md5|sha1|sha256|sha512) with hashlib.\1...
r29341 hashlib.sha1(''.join(sorted(heads))).digest()])
Shuhei Takahashi
wireproto: allow unbundle with hashed heads parameter (issue2126)...
r13942 else:
heads = encodelist(heads)
Pierre-Yves David
bundle2: support for push over the wire...
r21075 if util.safehasattr(cg, 'deltaheader'):
# this a bundle10, do the old style call sequence
ret, output = self._callpush("unbundle", cg, heads=heads)
if ret == "":
raise error.ResponseError(
_('push failed:'), output)
try:
ret = int(ret)
except ValueError:
raise error.ResponseError(
_('push failed (unexpected response):'), ret)
Matt Mackall
protocol: unify client unbundle support...
r11592
Pierre-Yves David
bundle2: support for push over the wire...
r21075 for l in output.splitlines(True):
self.ui.status(_('remote: '), l)
else:
# bundle2 push. Send a stream, fetch a stream.
stream = self._calltwowaystream('unbundle', cg, heads=heads)
Pierre-Yves David
unbundle20: retrieve unbundler instances through a factory function...
r24641 ret = bundle2.getunbundler(self.ui, stream)
Matt Mackall
protocol: unify client unbundle support...
r11592 return ret
Gregory Szorc
wireproto: use new peer interface...
r33805 # End of basewirepeer interface.
# Begin of baselegacywirepeer interface.
def branches(self, nodes):
n = encodelist(nodes)
d = self._call("branches", nodes=n)
try:
br = [tuple(decodelist(b)) for b in d.splitlines()]
return br
except ValueError:
self._abort(error.ResponseError(_("unexpected response:"), d))
def between(self, pairs):
batch = 8 # avoid giant requests
r = []
for i in xrange(0, len(pairs), batch):
n = " ".join([encodelist(p, '-') for p in pairs[i:i + batch]])
d = self._call("between", pairs=n)
try:
r.extend(l and decodelist(l) or [] for l in d.splitlines())
except ValueError:
self._abort(error.ResponseError(_("unexpected response:"), d))
return r
def changegroup(self, nodes, kind):
n = encodelist(nodes)
f = self._callcompressable("changegroup", roots=n)
return changegroupmod.cg1unpacker(f, 'UN')
def changegroupsubset(self, bases, heads, kind):
self.requirecap('changegroupsubset', _('look up remote changes'))
bases = encodelist(bases)
heads = encodelist(heads)
f = self._callcompressable("changegroupsubset",
bases=bases, heads=heads)
return changegroupmod.cg1unpacker(f, 'UN')
# End of baselegacywirepeer interface.
def _submitbatch(self, req):
"""run batch request <req> on the server
Returns an iterator of the raw responses from the server.
"""
rsp = self._callstream("batch", cmds=encodebatchcmds(req))
chunk = rsp.read(1024)
work = [chunk]
while chunk:
while ';' not in chunk and chunk:
chunk = rsp.read(1024)
work.append(chunk)
merged = ''.join(work)
while ';' in merged:
one, merged = merged.split(';', 1)
yield unescapearg(one)
chunk = rsp.read(1024)
work = [merged, chunk]
yield unescapearg(''.join(work))
def _submitone(self, op, args):
Augie Fackler
wireproto: bounce kwargs to/from bytes/str as needed...
r34740 return self._call(op, **pycompat.strkwargs(args))
Gregory Szorc
wireproto: use new peer interface...
r33805
Peter Arrenbrecht
wireproto: add test for new optional arg missing on server...
r14048 def debugwireargs(self, one, two, three=None, four=None, five=None):
Peter Arrenbrecht
debug: add debugwireargs to test argument passing over the wire...
r13720 # don't pass optional arguments left at their default value
opts = {}
if three is not None:
Pulkit Goyal
py3: handle keyword arguments correctly in wireproto.py...
r35375 opts[r'three'] = three
Peter Arrenbrecht
debug: add debugwireargs to test argument passing over the wire...
r13720 if four is not None:
Pulkit Goyal
py3: handle keyword arguments correctly in wireproto.py...
r35375 opts[r'four'] = four
Peter Arrenbrecht
debug: add debugwireargs to test argument passing over the wire...
r13720 return self._call('debugwireargs', one=one, two=two, **opts)
Pierre-Yves David
wireproto: document protocol specific function of wirepeer...
r20904 def _call(self, cmd, **args):
"""execute <cmd> on the server
The command is expected to return a simple string.
returns the server reply as a string."""
raise NotImplementedError()
def _callstream(self, cmd, **args):
"""execute <cmd> on the server
Augie Fackler
wireproto: document quirk of _callstream between http and ssh...
r28435 The command is expected to return a stream. Note that if the
command doesn't return a stream, _callstream behaves
differently for ssh and http peers.
Pierre-Yves David
wireproto: document protocol specific function of wirepeer...
r20904
Augie Fackler
wireproto: document quirk of _callstream between http and ssh...
r28435 returns the server reply as a file like object.
"""
Pierre-Yves David
wireproto: document protocol specific function of wirepeer...
r20904 raise NotImplementedError()
Pierre-Yves David
wireproto: drop the _decompress method in favor a new call type...
r20905 def _callcompressable(self, cmd, **args):
"""execute <cmd> on the server
The command is expected to return a stream.
Mads Kiilerich
spelling: fixes from spell checker
r21024 The stream may have been compressed in some implementations. This
Pierre-Yves David
wireproto: drop the _decompress method in favor a new call type...
r20905 function takes care of the decompression. This is the only difference
with _callstream.
returns the server reply as a file like object.
"""
raise NotImplementedError()
Pierre-Yves David
wireproto: document protocol specific function of wirepeer...
r20904 def _callpush(self, cmd, fp, **args):
"""execute a <cmd> on server
The command is expected to be related to a push. Push has a special
return method.
returns the server reply as a (ret, output) tuple. ret is either
empty (error) or a stringified int.
"""
raise NotImplementedError()
Pierre-Yves David
wireproto: add a _calltwowaystream method on wirepeer...
r21072 def _calltwowaystream(self, cmd, fp, **args):
"""execute <cmd> on server
The command will send a stream to the server and get a stream in reply.
"""
raise NotImplementedError()
Pierre-Yves David
wireproto: document protocol specific function of wirepeer...
r20904 def _abort(self, exception):
"""clearly abort the wire protocol connection and raise the exception
"""
raise NotImplementedError()
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586 # server side
Pierre-Yves David
wireproto: document possible return type...
r20902 # wire protocol command can either return a string or one of these classes.
Andrew Pritchard
wireproto: add out-of-band error class to allow remote repo to report errors...
r15017
Gregory Szorc
wireproto: extract repo filtering to standalone function...
r29590 def getdispatchrepo(repo, proto, command):
"""Obtain the repo used for processing wire protocol commands.
The intent of this function is to serve as a monkeypatch point for
extensions that need commands to operate on different repo views under
specialized circumstances.
"""
return repo.filtered('served')
Matt Mackall
protocol: introduce wireproto.py...
r11581 def dispatch(repo, proto, command):
Gregory Szorc
wireproto: extract repo filtering to standalone function...
r29590 repo = getdispatchrepo(repo, proto, command)
Matt Mackall
protocol: introduce wireproto.py...
r11581 func, spec = commands[command]
args = proto.getargs(spec)
Dirkjan Ochtman
protocol: wrap non-string protocol responses in classes
r11625 return func(repo, proto, *args)
Matt Mackall
protocol: introduce wireproto.py...
r11581
Peter Arrenbrecht
wireproto: fix handling of '*' args for HTTP and SSH
r13721 def options(cmd, keys, others):
opts = {}
for k in keys:
if k in others:
opts[k] = others[k]
del others[k]
if others:
Yuya Nishihara
py3: bulk replace sys.stdin/out/err by util's...
r30473 util.stderr.write("warning: %s ignored unexpected arguments %s\n"
% (cmd, ",".join(others)))
Peter Arrenbrecht
wireproto: fix handling of '*' args for HTTP and SSH
r13721 return opts
Gregory Szorc
wireproto: support disabling bundle1 only if repo is generaldelta...
r27633 def bundle1allowed(repo, action):
"""Whether a bundle1 operation is allowed from the server.
Priority is:
1. server.bundle1gd.<action> (if generaldelta active)
2. server.bundle1.<action>
3. server.bundle1gd (if generaldelta active)
4. server.bundle1
"""
ui = repo.ui
gd = 'generaldelta' in repo.requirements
if gd:
Boris Feld
configitems: register the 'server.bundle*' family of config...
r34614 v = ui.configbool('server', 'bundle1gd.%s' % action)
Gregory Szorc
wireproto: support disabling bundle1 only if repo is generaldelta...
r27633 if v is not None:
return v
Boris Feld
configitems: register the 'server.bundle*' family of config...
r34614 v = ui.configbool('server', 'bundle1.%s' % action)
Gregory Szorc
wireproto: config options to disable bundle1...
r27246 if v is not None:
return v
Gregory Szorc
wireproto: support disabling bundle1 only if repo is generaldelta...
r27633 if gd:
configitems: register the 'server.bundle1gd' config
r33217 v = ui.configbool('server', 'bundle1gd')
Gregory Szorc
wireproto: support disabling bundle1 only if repo is generaldelta...
r27633 if v is not None:
return v
configitems: register the 'server.bundle1' config
r33216 return ui.configbool('server', 'bundle1')
Gregory Szorc
wireproto: config options to disable bundle1...
r27246
Gregory Szorc
wireproto: remove unused proto argument from supportedcompengines (API)...
r36088 def supportedcompengines(ui, role):
Gregory Szorc
wireproto: advertise supported media types and compression formats...
r30762 """Obtain the list of supported compression engines for a request."""
assert role in (util.CLIENTROLE, util.SERVERROLE)
compengines = util.compengines.supportedwireengines(role)
# Allow config to override default list and ordering.
if role == util.SERVERROLE:
configengines = ui.configlist('server', 'compressionengines')
config = 'server.compressionengines'
else:
# This is currently implemented mainly to facilitate testing. In most
# cases, the server should be in charge of choosing a compression engine
# because a server has the most to lose from a sub-optimal choice. (e.g.
# CPU DoS due to an expensive engine or a network DoS due to poor
# compression ratio).
configengines = ui.configlist('experimental',
'clientcompressionengines')
config = 'experimental.clientcompressionengines'
# No explicit config. Filter out the ones that aren't supposed to be
# advertised and return default ordering.
if not configengines:
attr = 'serverpriority' if role == util.SERVERROLE else 'clientpriority'
return [e for e in compengines
if getattr(e.wireprotosupport(), attr) > 0]
# If compression engines are listed in the config, assume there is a good
# reason for it (like server operators wanting to achieve specific
# performance characteristics). So fail fast if the config references
# unusable compression engines.
validnames = set(e.name() for e in compengines)
invalidnames = set(e for e in configengines if e not in validnames)
if invalidnames:
raise error.Abort(_('invalid compression engine defined in %s: %s') %
(config, ', '.join(sorted(invalidnames))))
compengines = [e for e in compengines if e.name() in configengines]
compengines = sorted(compengines,
key=lambda e: configengines.index(e.name()))
if not compengines:
raise error.Abort(_('%s config option does not specify any known '
'compression engines') % config,
hint=_('usable compression engines: %s') %
', '.sorted(validnames))
return compengines
Gregory Szorc
wireproto: define and use types for wire protocol commands...
r35999 class commandentry(object):
"""Represents a declared wire protocol command."""
def __init__(self, func, args=''):
self.func = func
self.args = args
def _merge(self, func, args):
"""Merge this instance with an incoming 2-tuple.
This is called when a caller using the old 2-tuple API attempts
to replace an instance. The incoming values are merged with
data not captured by the 2-tuple and a new instance containing
the union of the two objects is returned.
"""
return commandentry(func, args)
# Old code treats instances as 2-tuples. So expose that interface.
def __iter__(self):
yield self.func
yield self.args
def __getitem__(self, i):
if i == 0:
return self.func
elif i == 1:
return self.args
else:
raise IndexError('can only access elements 0 and 1')
class commanddict(dict):
"""Container for registered wire protocol commands.
It behaves like a dict. But __setitem__ is overwritten to allow silent
coercion of values from 2-tuples for API compatibility.
"""
def __setitem__(self, k, v):
if isinstance(v, commandentry):
pass
# Cast 2-tuples to commandentry instances.
elif isinstance(v, tuple):
if len(v) != 2:
raise ValueError('command tuples must have exactly 2 elements')
# It is common for extensions to wrap wire protocol commands via
# e.g. ``wireproto.commands[x] = (newfn, args)``. Because callers
# doing this aren't aware of the new API that uses objects to store
# command entries, we automatically merge old state with new.
if k in self:
v = self[k]._merge(v[0], v[1])
else:
v = commandentry(v[0], v[1])
else:
raise ValueError('command entries must be commandentry instances '
'or 2-tuples')
return super(commanddict, self).__setitem__(k, v)
Gregory Szorc
wireproto: function for testing if wire protocol command is available...
r36000 def commandavailable(self, command, proto):
"""Determine if a command is available for the requested protocol."""
# For now, commands are available for all protocols. So do a simple
# membership test.
return command in self
Gregory Szorc
wireproto: define and use types for wire protocol commands...
r35999 commands = commanddict()
Pierre-Yves David
wireproto: add decorator for wire protocol command...
r20906
def wireprotocommand(name, args=''):
Gregory Szorc
wireproto: improve docstring for @wireprotocommand...
r35998 """Decorator to declare a wire protocol command.
``name`` is the name of the wire protocol command being provided.
``args`` is a space-delimited list of named arguments that the command
accepts. ``*`` is a special value that says to accept all arguments.
"""
Pierre-Yves David
wireproto: add decorator for wire protocol command...
r20906 def register(func):
Gregory Szorc
wireproto: define and use types for wire protocol commands...
r35999 commands[name] = commandentry(func, args)
Pierre-Yves David
wireproto: add decorator for wire protocol command...
r20906 return func
return register
Pierre-Yves David
wireproto: use decorator for the batch command
r20907 @wireprotocommand('batch', 'cmds *')
Peter Arrenbrecht
wireproto: add batching support to wirerepository...
r14622 def batch(repo, proto, cmds, others):
Kevin Bullock
filtering: rename filters to their antonyms...
r18382 repo = repo.filtered("served")
Peter Arrenbrecht
wireproto: add batching support to wirerepository...
r14622 res = []
for pair in cmds.split(';'):
op, args = pair.split(' ', 1)
vals = {}
for a in args.split(','):
if a:
n, v = a.split('=')
Gregory Szorc
wireproto: unescape argument names in batch command (BC)...
r29734 vals[unescapearg(n)] = unescapearg(v)
Peter Arrenbrecht
wireproto: add batching support to wirerepository...
r14622 func, spec = commands[op]
if spec:
keys = spec.split()
data = {}
for k in keys:
if k == '*':
star = {}
for key in vals.keys():
if key not in keys:
star[key] = vals[key]
data['*'] = star
else:
data[k] = vals[k]
result = func(repo, proto, *[data[k] for k in keys])
else:
result = func(repo, proto)
Andrew Pritchard
wireproto: add out-of-band error class to allow remote repo to report errors...
r15017 if isinstance(result, ooberror):
return result
Gregory Szorc
wireproto: introduce type for raw byte responses (API)...
r36091
# For now, all batchable commands must return bytesresponse or
# raw bytes (for backwards compatibility).
assert isinstance(result, (bytesresponse, bytes))
if isinstance(result, bytesresponse):
result = result.data
Peter Arrenbrecht
wireproto: add batching support to wirerepository...
r14622 res.append(escapearg(result))
Gregory Szorc
wireproto: introduce type for raw byte responses (API)...
r36091
return bytesresponse(';'.join(res))
Peter Arrenbrecht
wireproto: add batching support to wirerepository...
r14622
Pierre-Yves David
wireproto: use decorator for the between command
r20908 @wireprotocommand('between', 'pairs')
Matt Mackall
protocol: add proto to method prototypes
r11583 def between(repo, proto, pairs):
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 pairs = [decodelist(p, '-') for p in pairs.split(" ")]
Matt Mackall
protocol: introduce wireproto.py...
r11581 r = []
for b in repo.between(pairs):
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 r.append(encodelist(b) + "\n")
Gregory Szorc
wireproto: introduce type for raw byte responses (API)...
r36091
return bytesresponse(''.join(r))
Matt Mackall
protocol: introduce wireproto.py...
r11581
Pierre-Yves David
wireproto: use decorator for the branchmap command
r20909 @wireprotocommand('branchmap')
Matt Mackall
protocol: add proto to method prototypes
r11583 def branchmap(repo, proto):
Pierre-Yves David
clfilter: drop extra filtering in wireprotocol...
r18281 branchmap = repo.branchmap()
Matt Mackall
protocol: introduce wireproto.py...
r11581 heads = []
for branch, nodes in branchmap.iteritems():
timeless
pycompat: switch to util.urlreq/util.urlerr for py3 compat
r28883 branchname = urlreq.quote(encoding.fromlocal(branch))
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 branchnodes = encodelist(nodes)
heads.append('%s %s' % (branchname, branchnodes))
Gregory Szorc
wireproto: introduce type for raw byte responses (API)...
r36091
return bytesresponse('\n'.join(heads))
Matt Mackall
protocol: introduce wireproto.py...
r11581
Pierre-Yves David
wireproto: use decorator for the branches command
r20910 @wireprotocommand('branches', 'nodes')
Matt Mackall
protocol: add proto to method prototypes
r11583 def branches(repo, proto, nodes):
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 nodes = decodelist(nodes)
Matt Mackall
protocol: introduce wireproto.py...
r11581 r = []
for b in repo.branches(nodes):
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 r.append(encodelist(b) + "\n")
Gregory Szorc
wireproto: introduce type for raw byte responses (API)...
r36091
return bytesresponse(''.join(r))
Matt Mackall
protocol: introduce wireproto.py...
r11581
Gregory Szorc
wireproto: move clonebundles command from extension (issue4931)...
r26857 @wireprotocommand('clonebundles', '')
def clonebundles(repo, proto):
"""Server command for returning info for available bundles to seed clones.
Clients will parse this response and determine what bundle to fetch.
Extensions may wrap this command to filter or dynamically emit data
depending on the request. e.g. you could advertise URLs for the closest
data center given the client's IP address.
"""
Gregory Szorc
wireproto: introduce type for raw byte responses (API)...
r36091 return bytesresponse(repo.vfs.tryread('clonebundles.manifest'))
Pierre-Yves David
wireproto: extract capabilities list in outside the wireproto function...
r20774
wireprotocaps = ['lookup', 'changegroupsubset', 'branchmap', 'pushkey',
'known', 'getbundle', 'unbundlehash', 'batch']
Pierre-Yves David
wireproto: move wireproto capabilities computation in a subfunction...
r20775
def _capabilities(repo, proto):
"""return a list of capabilities for a repo
This function exists to allow extensions to easily wrap capabilities
computation
- returns a lists: easy to alter
- change done here will be propagated to both `capabilities` and `hello`
Mads Kiilerich
spelling: fixes from spell checker
r21024 command without any other action needed.
Pierre-Yves David
wireproto: move wireproto capabilities computation in a subfunction...
r20775 """
Pierre-Yves David
wireproto: extract capabilities list in outside the wireproto function...
r20774 # copy to prevent modification of the global list
caps = list(wireprotocaps)
Gregory Szorc
streamclone: consider secret changesets (BC) (issue5589)...
r32744 if streamclone.allowservergeneration(repo):
configitems: register the 'server.preferuncompressed' config
r33222 if repo.ui.configbool('server', 'preferuncompressed'):
Benoit Allard
protocol: Add the stream-preferred capability...
r16361 caps.append('stream-preferred')
Sune Foldager
clone: only use stream when we understand the revlog format...
r12296 requiredformats = repo.requirements & repo.supportedformats
# if our local revlogs are just revlogv1, add 'stream' cap
Martin von Zweigbergk
cleanup: use set literals...
r32291 if not requiredformats - {'revlogv1'}:
Sune Foldager
clone: only use stream when we understand the revlog format...
r12296 caps.append('stream')
# otherwise, add 'streamreqs' detailing our local revlog format
else:
Pierre-Yves David
stream: sort stream capability before serialisation...
r26911 caps.append('streamreqs=%s' % ','.join(sorted(requiredformats)))
Jun Wu
codemod: register core configitems using a script...
r33499 if repo.ui.configbool('experimental', 'bundle2-advertise'):
Gregory Szorc
bundle2: specify what capabilities will be used for...
r35801 capsblob = bundle2.encodecaps(bundle2.getrepocaps(repo, role='server'))
timeless
pycompat: switch to util.urlreq/util.urlerr for py3 compat
r28883 caps.append('bundle2=' + urlreq.quote(capsblob))
Martin von Zweigbergk
bundle: move writebundle() from changegroup.py to bundle2.py (API)...
r28666 caps.append('unbundle=%s' % ','.join(bundle2.bundlepriority))
Gregory Szorc
wireproto: only advertise HTTP-specific capabilities to HTTP peers (BC)...
r30563
Gregory Szorc
wireprotoserver: add version to HTTP protocol name (API)...
r36241 if proto.name == 'http-v1':
Gregory Szorc
wireproto: only advertise HTTP-specific capabilities to HTTP peers (BC)...
r30563 caps.append('httpheader=%d' %
configitems: register the 'server.maxhttpheaderlen' config
r33221 repo.ui.configint('server', 'maxhttpheaderlen'))
Jun Wu
codemod: register core configitems using a script...
r33499 if repo.ui.configbool('experimental', 'httppostargs'):
Gregory Szorc
wireproto: only advertise HTTP-specific capabilities to HTTP peers (BC)...
r30563 caps.append('httppostargs')
Gregory Szorc
wireproto: advertise supported media types and compression formats...
r30762 # 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')
Gregory Szorc
wireproto: remove unused proto argument from supportedcompengines (API)...
r36088 compengines = supportedcompengines(repo.ui, util.SERVERROLE)
Gregory Szorc
wireproto: advertise supported media types and compression formats...
r30762 if compengines:
comptypes = ','.join(urlreq.quote(e.wireprotosupport().name)
for e in compengines)
caps.append('compression=%s' % comptypes)
Pierre-Yves David
wireproto: move wireproto capabilities computation in a subfunction...
r20775 return caps
Mads Kiilerich
spelling: fixes from spell checker
r21024 # If you are writing an extension and consider wrapping this function. Wrap
Pierre-Yves David
wireproto: move wireproto capabilities computation in a subfunction...
r20775 # `_capabilities` instead.
Pierre-Yves David
wireproto: use decorator for the capabilities command
r20911 @wireprotocommand('capabilities')
Pierre-Yves David
wireproto: move wireproto capabilities computation in a subfunction...
r20775 def capabilities(repo, proto):
Gregory Szorc
wireproto: introduce type for raw byte responses (API)...
r36091 return bytesresponse(' '.join(_capabilities(repo, proto)))
Matt Mackall
protocol: unify server-side capabilities functions
r11594
Pierre-Yves David
wireproto: use decorator for the changegroup command
r20912 @wireprotocommand('changegroup', 'roots')
Matt Mackall
protocol: unify changegroup commands...
r11584 def changegroup(repo, proto, roots):
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 nodes = decodelist(roots)
Durham Goode
changegroup: replace changegroup with makechangegroup...
r34102 outgoing = discovery.outgoing(repo, missingroots=nodes,
missingheads=repo.heads())
cg = changegroupmod.makechangegroup(repo, outgoing, '01', 'serve')
Gregory Szorc
wireproto: drop support for reader interface from streamres (API)...
r35723 gen = iter(lambda: cg.read(32768), '')
Joerg Sonnenberger
wireproto: split streamres into legacy and modern case...
r35768 return streamres(gen=gen)
Matt Mackall
protocol: unify changegroup commands...
r11584
Pierre-Yves David
wireproto: use decorator for the changegroupsubset command
r20913 @wireprotocommand('changegroupsubset', 'bases heads')
Matt Mackall
protocol: unify changegroup commands...
r11584 def changegroupsubset(repo, proto, bases, heads):
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 bases = decodelist(bases)
heads = decodelist(heads)
Durham Goode
changegroup: replace changegroupsubset with makechangegroup...
r34099 outgoing = discovery.outgoing(repo, missingroots=bases,
missingheads=heads)
cg = changegroupmod.makechangegroup(repo, outgoing, '01', 'serve')
Gregory Szorc
wireproto: drop support for reader interface from streamres (API)...
r35723 gen = iter(lambda: cg.read(32768), '')
Joerg Sonnenberger
wireproto: split streamres into legacy and modern case...
r35768 return streamres(gen=gen)
Matt Mackall
protocol: unify changegroup commands...
r11584
Pierre-Yves David
wireproto: use decorator for the debugwireargs command
r20914 @wireprotocommand('debugwireargs', 'one two *')
Peter Arrenbrecht
wireproto: fix handling of '*' args for HTTP and SSH
r13721 def debugwireargs(repo, proto, one, two, others):
# only accept optional args from the known set
opts = options('debugwireargs', ['three', 'four'], others)
Gregory Szorc
wireproto: introduce type for raw byte responses (API)...
r36091 return bytesresponse(repo.debugwireargs(one, two,
**pycompat.strkwargs(opts)))
Peter Arrenbrecht
debug: add debugwireargs to test argument passing over the wire...
r13720
Pierre-Yves David
wireproto: use decorator for the getbundle command
r20915 @wireprotocommand('getbundle', '*')
Peter Arrenbrecht
wireproto: add getbundle() function...
r13741 def getbundle(repo, proto, others):
Pierre-Yves David
getbundle: declare type of parameters...
r21646 opts = options('getbundle', gboptsmap.keys(), others)
Peter Arrenbrecht
wireproto: add getbundle() function...
r13741 for k, v in opts.iteritems():
Pierre-Yves David
getbundle: declare type of parameters...
r21646 keytype = gboptsmap[k]
if keytype == 'nodes':
Benoit Boissinot
bundle-ng: add bundlecaps argument to getbundle() command
r19201 opts[k] = decodelist(v)
Pierre-Yves David
getbundle: declare type of parameters...
r21646 elif keytype == 'csv':
Pierre-Yves David
wireprotocol: distinguish list and set in getbundle argument...
r25403 opts[k] = list(v.split(','))
elif keytype == 'scsv':
Benoit Boissinot
bundle-ng: add bundlecaps argument to getbundle() command
r19201 opts[k] = set(v.split(','))
Pierre-Yves David
wireproto: add a ``boolean`` type for getbundle parameters...
r21988 elif keytype == 'boolean':
Gregory Szorc
wireproto: properly parse false boolean args (BC)...
r26686 # Client should serialize False as '0', which is a non-empty string
# so it evaluates as a True bool.
if v == '0':
opts[k] = False
else:
opts[k] = bool(v)
Pierre-Yves David
getbundle: declare type of parameters...
r21646 elif keytype != 'plain':
raise KeyError('unknown getbundle option type %s'
% keytype)
Gregory Szorc
wireproto: config options to disable bundle1...
r27246
Gregory Szorc
wireproto: support disabling bundle1 only if repo is generaldelta...
r27633 if not bundle1allowed(repo, 'pull'):
Gregory Szorc
wireproto: config options to disable bundle1...
r27246 if not exchange.bundle2requested(opts.get('bundlecaps')):
Gregory Szorc
wireprotoserver: add version to HTTP protocol name (API)...
r36241 if proto.name == 'http-v1':
Pierre-Yves David
bundle1: fix bundle1-denied reporting for pull over ssh...
r30912 return ooberror(bundle2required)
raise error.Abort(bundle2requiredmain,
hint=bundle2requiredhint)
Gregory Szorc
wireproto: config options to disable bundle1...
r27246
Gregory Szorc
exchange: send bundle2 stream clones uncompressed...
r35805 prefercompressed = True
Gregory Szorc
wireproto: don't compress errors from getbundle()...
r35800
Pierre-Yves David
wireproto: properly report server Abort during 'getbundle'...
r30914 try:
configitems: register the 'server.disablefullbundle' config
r33220 if repo.ui.configbool('server', 'disablefullbundle'):
Siddharth Agarwal
clone: add a server-side option to disable full getbundles (pull-based clones)...
r32260 # Check to see if this is a full clone.
clheads = set(repo.changelog.heads())
Boris Feld
clone: allow bundle2's stream clone with 'server.disablefullbundle'...
r35778 changegroup = opts.get('cg', True)
Siddharth Agarwal
clone: add a server-side option to disable full getbundles (pull-based clones)...
r32260 heads = set(opts.get('heads', set()))
common = set(opts.get('common', set()))
common.discard(nullid)
Boris Feld
clone: allow bundle2's stream clone with 'server.disablefullbundle'...
r35778 if changegroup and not common and clheads == heads:
Siddharth Agarwal
clone: add a server-side option to disable full getbundles (pull-based clones)...
r32260 raise error.Abort(
_('server has pull-based clones disabled'),
hint=_('remove --pull if specified or upgrade Mercurial'))
Gregory Szorc
exchange: return bundle info from getbundlechunks() (API)...
r35803 info, chunks = exchange.getbundlechunks(repo, 'serve',
**pycompat.strkwargs(opts))
Gregory Szorc
exchange: send bundle2 stream clones uncompressed...
r35805 prefercompressed = info.get('prefercompressed', True)
Pierre-Yves David
wireproto: properly report server Abort during 'getbundle'...
r30914 except error.Abort as exc:
# cleanly forward Abort error to the client
if not exchange.bundle2requested(opts.get('bundlecaps')):
Gregory Szorc
wireprotoserver: add version to HTTP protocol name (API)...
r36241 if proto.name == 'http-v1':
Augie Fackler
py3: get bytes-repr of network errors portably...
r36272 return ooberror(pycompat.bytestr(exc) + '\n')
Pierre-Yves David
wireproto: properly report server Abort during 'getbundle'...
r30914 raise # cannot do better for bundle1 + ssh
# bundle2 request expect a bundle2 reply
bundler = bundle2.bundle20(repo.ui)
Augie Fackler
py3: get bytes-repr of network errors portably...
r36272 manargs = [('message', pycompat.bytestr(exc))]
Pierre-Yves David
wireproto: properly report server Abort during 'getbundle'...
r30914 advargs = []
if exc.hint is not None:
advargs.append(('hint', exc.hint))
bundler.addpart(bundle2.bundlepart('error:abort',
manargs, advargs))
Gregory Szorc
wireproto: don't compress errors from getbundle()...
r35800 chunks = bundler.getchunks()
Gregory Szorc
exchange: send bundle2 stream clones uncompressed...
r35805 prefercompressed = False
Gregory Szorc
wireproto: don't compress errors from getbundle()...
r35800
Gregory Szorc
exchange: send bundle2 stream clones uncompressed...
r35805 return streamres(gen=chunks, prefer_uncompressed=not prefercompressed)
Peter Arrenbrecht
wireproto: add getbundle() function...
r13741
Pierre-Yves David
wireproto: use decorator for the heads command
r20916 @wireprotocommand('heads')
Matt Mackall
protocol: add proto to method prototypes
r11583 def heads(repo, proto):
Pierre-Yves David
clfilter: drop extra filtering in wireprotocol...
r18281 h = repo.heads()
Gregory Szorc
wireproto: introduce type for raw byte responses (API)...
r36091 return bytesresponse(encodelist(h) + '\n')
Matt Mackall
protocol: introduce wireproto.py...
r11581
Pierre-Yves David
wireproto: use decorator for the hello command
r20917 @wireprotocommand('hello')
Matt Mackall
protocol: unify server-side capabilities functions
r11594 def hello(repo, proto):
Gregory Szorc
wireproto: improve docstring for "hello"...
r36239 """Called as part of SSH handshake to obtain server info.
Returns a list of lines describing interesting things about the
server, in an RFC822-like format.
Matt Mackall
protocol: unify server-side capabilities functions
r11594
Gregory Szorc
wireproto: improve docstring for "hello"...
r36239 Currently, the only one defined is ``capabilities``, which consists of a
line of space separated tokens describing server abilities:
capabilities: <token0> <token1> <token2>
"""
Gregory Szorc
wireproto: introduce type for raw byte responses (API)...
r36091 caps = capabilities(repo, proto).data
return bytesresponse('capabilities: %s\n' % caps)
Matt Mackall
protocol: unify server-side capabilities functions
r11594
Pierre-Yves David
wireproto: use decorator for the listkeys command
r20919 @wireprotocommand('listkeys', 'namespace')
Matt Mackall
protocol: add proto to method prototypes
r11583 def listkeys(repo, proto, namespace):
Andreas Freimuth
wireproto: do not call pushkey module directly (issue3041)...
r15217 d = repo.listkeys(encoding.tolocal(namespace)).items()
Gregory Szorc
wireproto: introduce type for raw byte responses (API)...
r36091 return bytesresponse(pushkeymod.encodekeys(d))
Matt Mackall
protocol: introduce wireproto.py...
r11581
Pierre-Yves David
wireproto: use decorator for the lookup command
r20920 @wireprotocommand('lookup', 'key')
Matt Mackall
protocol: add proto to method prototypes
r11583 def lookup(repo, proto, key):
Matt Mackall
protocol: introduce wireproto.py...
r11581 try:
Matt Mackall
wireproto: refuse to lookup secret csets
r15925 k = encoding.tolocal(key)
c = repo[k]
r = c.hex()
Matt Mackall
protocol: introduce wireproto.py...
r11581 success = 1
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except Exception as inst:
Augie Fackler
wireproto: fix lingering str(exception) with util.forcebytestr(exception)...
r36332 r = util.forcebytestr(inst)
Matt Mackall
protocol: introduce wireproto.py...
r11581 success = 0
Gregory Szorc
wireproto: introduce type for raw byte responses (API)...
r36091 return bytesresponse('%d %s\n' % (success, r))
Matt Mackall
protocol: introduce wireproto.py...
r11581
Pierre-Yves David
wireproto: use decorator for the known command
r20918 @wireprotocommand('known', 'nodes *')
Peter Arrenbrecht
wireproto: enable optional args for known() for future extensibility...
r14436 def known(repo, proto, nodes, others):
Gregory Szorc
wireproto: introduce type for raw byte responses (API)...
r36091 v = ''.join(b and '1' or '0' for b in repo.known(decodelist(nodes)))
return bytesresponse(v)
Peter Arrenbrecht
wireproto: add known([id]) function...
r13723
Pierre-Yves David
wireproto: use decorator for the pushkey command
r20921 @wireprotocommand('pushkey', 'namespace key old new')
Matt Mackall
protocol: add proto to method prototypes
r11583 def pushkey(repo, proto, namespace, key, old, new):
Matt Mackall
pushkey: use UTF-8
r13050 # compatibility with pre-1.8 clients which were accidentally
# sending raw binary nodes rather than utf-8-encoded hex
Yuya Nishihara
util: wrap s.encode('string_escape') call for future py3 compatibility
r31451 if len(new) == 20 and util.escapestr(new) != new:
Matt Mackall
pushkey: use UTF-8
r13050 # looks like it could be a binary node
try:
Alexander Solovyov
remove unused imports and variables
r14064 new.decode('utf-8')
Matt Mackall
pushkey: use UTF-8
r13050 new = encoding.tolocal(new) # but cleanly decodes as UTF-8
except UnicodeDecodeError:
pass # binary, leave unmodified
else:
new = encoding.tolocal(new) # normal path
Gregory Szorc
wireprotoserver: add context manager mechanism for redirecting stdio...
r36083 with proto.mayberedirectstdio() as output:
Gregory Szorc
wireproto: remove unnecessary exception trapping...
r35997 r = repo.pushkey(encoding.tolocal(namespace), encoding.tolocal(key),
encoding.tolocal(old), new) or False
Wagner Bruna
wireproto: fix pushkey hook failure and output on remote http repo...
r17793
Gregory Szorc
wireprotoserver: add context manager mechanism for redirecting stdio...
r36083 output = output.getvalue() if output else ''
Gregory Szorc
wireproto: introduce type for raw byte responses (API)...
r36091 return bytesresponse('%s\n%s' % (int(r), output))
Matt Mackall
protocol: introduce wireproto.py...
r11581
Pierre-Yves David
wireproto: use decorator for the stream command
r20922 @wireprotocommand('stream_out')
Matt Mackall
protocol: unify stream_out command
r11585 def stream(repo, proto):
Dirkjan Ochtman
protocol: move the streamclone implementation into wireproto
r11627 '''If the server supports streaming clone, it advertises the "stream"
capability with a value representing the version and flags of the repo
it is serving. Client checks to see if it understands the format.
'''
Joerg Sonnenberger
wireproto: split streamres into legacy and modern case...
r35768 return streamres_legacy(streamclone.generatev1wireproto(repo))
Matt Mackall
protocol: unify stream_out command
r11585
Pierre-Yves David
wireproto: use decorator for the ubundle command
r20923 @wireprotocommand('unbundle', 'heads')
Matt Mackall
protocol: unify unbundle on the server side
r11593 def unbundle(repo, proto, heads):
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 their_heads = decodelist(heads)
Matt Mackall
protocol: unify unbundle on the server side
r11593
Gregory Szorc
wireproto: use maybecapturestdio() for push responses (API)...
r36084 with proto.mayberedirectstdio() as output:
Matt Mackall
protocol: unify unbundle on the server side
r11593 try:
Gregory Szorc
wireproto: use maybecapturestdio() for push responses (API)...
r36084 exchange.check_heads(repo, their_heads, 'preparing changes')
Gregory Szorc
wireproto: config options to disable bundle1...
r27246
Gregory Szorc
wireproto: use maybecapturestdio() for push responses (API)...
r36084 # write bundle data to temporary file because it can be big
fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
fp = os.fdopen(fd, pycompat.sysstr('wb+'))
r = 0
Pierre-Yves David
bundle2: refactor error bundle creation for the wireprotocol...
r24796 try:
Gregory Szorc
wireprotoserver: rename getfile() to forwardpayload() (API)...
r36087 proto.forwardpayload(fp)
Gregory Szorc
wireproto: use maybecapturestdio() for push responses (API)...
r36084 fp.seek(0)
gen = exchange.readbundle(repo.ui, fp, None)
if (isinstance(gen, changegroupmod.cg1unpacker)
and not bundle1allowed(repo, 'push')):
Gregory Szorc
wireprotoserver: add version to HTTP protocol name (API)...
r36241 if proto.name == 'http-v1':
Gregory Szorc
wireproto: use maybecapturestdio() for push responses (API)...
r36084 # need to special case http because stderr do not get to
# the http client on failed push so we need to abuse
# some other error type to make sure the message get to
# the user.
return ooberror(bundle2required)
raise error.Abort(bundle2requiredmain,
hint=bundle2requiredhint)
Pierre-Yves David
bundle2: refactor error bundle creation for the wireprotocol...
r24796
Gregory Szorc
wireproto: use maybecapturestdio() for push responses (API)...
r36084 r = exchange.unbundle(repo, gen, their_heads, 'serve',
Gregory Szorc
wireprotoserver: rename _client to client (API)...
r36086 proto.client())
Gregory Szorc
wireproto: use maybecapturestdio() for push responses (API)...
r36084 if util.safehasattr(r, 'addpart'):
# The return looks streamable, we are in the bundle2 case
# and should return a stream.
return streamres_legacy(gen=r.getchunks())
return pushres(r, output.getvalue() if output else '')
finally:
fp.close()
os.unlink(tempname)
except (error.BundleValueError, error.Abort, error.PushRaced) as exc:
# handle non-bundle2 case first
if not getattr(exc, 'duringunbundle2', False):
try:
Pierre-Yves David
bundle2: convey PushkeyFailed error over the wire...
r25493 raise
Gregory Szorc
wireproto: use maybecapturestdio() for push responses (API)...
r36084 except error.Abort:
# The old code we moved used util.stderr directly.
# We did not change it to minimise code change.
# This need to be moved to something proper.
# Feel free to do it.
util.stderr.write("abort: %s\n" % exc)
if exc.hint is not None:
util.stderr.write("(%s)\n" % exc.hint)
return pushres(0, output.getvalue() if output else '')
except error.PushRaced:
return pusherr(str(exc),
output.getvalue() if output else '')
bundler = bundle2.bundle20(repo.ui)
for out in getattr(exc, '_bundle2salvagedoutput', ()):
bundler.addpart(out)
try:
try:
raise
except error.PushkeyFailed as exc:
# check client caps
remotecaps = getattr(exc, '_replycaps', None)
if (remotecaps is not None
and 'pushkey' not in remotecaps.get('error', ())):
# no support remote side, fallback to Abort handler.
raise
part = bundler.newpart('error:pushkey')
part.addparam('in-reply-to', exc.partid)
if exc.namespace is not None:
part.addparam('namespace', exc.namespace,
mandatory=False)
if exc.key is not None:
part.addparam('key', exc.key, mandatory=False)
if exc.new is not None:
part.addparam('new', exc.new, mandatory=False)
if exc.old is not None:
part.addparam('old', exc.old, mandatory=False)
if exc.ret is not None:
part.addparam('ret', exc.ret, mandatory=False)
except error.BundleValueError as exc:
errpart = bundler.newpart('error:unsupportedcontent')
if exc.parttype is not None:
errpart.addparam('parttype', exc.parttype)
if exc.params:
errpart.addparam('params', '\0'.join(exc.params))
except error.Abort as exc:
Augie Fackler
wireproto: fix lingering str(exception) with util.forcebytestr(exception)...
r36332 manargs = [('message', util.forcebytestr(exc))]
Gregory Szorc
wireproto: use maybecapturestdio() for push responses (API)...
r36084 advargs = []
if exc.hint is not None:
advargs.append(('hint', exc.hint))
bundler.addpart(bundle2.bundlepart('error:abort',
manargs, advargs))
except error.PushRaced as exc:
Augie Fackler
wireproto: fix lingering str(exception) with util.forcebytestr(exception)...
r36332 bundler.newpart('error:pushraced',
[('message', util.forcebytestr(exc))])
Gregory Szorc
wireproto: use maybecapturestdio() for push responses (API)...
r36084 return streamres_legacy(gen=bundler.getchunks())