##// END OF EJS Templates
monoblue: add archive links on summary page
monoblue: add archive links on summary page

File last commit:

r25993:0851678b default
r26114:9d6d3fee default
Show More
wireproto.py
812 lines | 26.8 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
import os
import sys
import tempfile
import urllib
Matt Mackall
protocol: introduce wireproto.py...
r11581
Gregory Szorc
wireproto: use absolute_import
r25993 from .i18n import _
from .node import (
bin,
hex,
)
from . import (
bundle2,
changegroup as changegroupmod,
encoding,
error,
exchange,
peer,
pushkey as pushkeymod,
util,
)
Pierre-Yves David
wireproto: introduce an abstractserverproto class...
r20903
class abstractserverproto(object):
"""abstract class that summarizes the protocol API
Used as reference and documentation.
"""
def getargs(self, args):
"""return the value for arguments in <args>
returns a list of values (same order as <args>)"""
raise NotImplementedError()
def getfile(self, fp):
"""write the whole content of a file into a file like object
The file is in the form::
(<chunk-size>\n<chunk>)+0\n
chunk size is the ascii version of the int.
"""
raise NotImplementedError()
def redirect(self):
"""may setup interception for stdout and stderr
See also the `restore` method."""
raise NotImplementedError()
# If the `redirect` function does install interception, the `restore`
# function MUST be defined. If interception is not used, this function
# MUST NOT be defined.
#
# left commented here on purpose
#
#def restore(self):
# """reinstall previous stdout and stderr and return intercepted stdout
# """
# raise NotImplementedError()
def groupchunks(self, cg):
"""return 4096 chunks from a changegroup object
Some protocols may have compressed the contents."""
raise NotImplementedError()
Augie Fackler
batching: migrate basic noop batching into peer.peer...
r25912 class remotebatch(peer.batcher):
Peter Arrenbrecht
wireproto: add basic command batching infrastructure...
r14621 '''batches the queued calls; uses as few roundtrips as possible'''
def __init__(self, remote):
Brodie Rao
cleanup: eradicate long lines
r16683 '''remote must support _submitbatch(encbatch) and
_submitone(op, encargs)'''
Augie Fackler
batching: migrate basic noop batching into peer.peer...
r25912 peer.batcher.__init__(self)
Peter Arrenbrecht
wireproto: add basic command batching infrastructure...
r14621 self.remote = remote
def submit(self):
req, rsp = [], []
for name, args, opts, resref in self.calls:
mtd = getattr(self.remote, name)
Augie Fackler
wireproto: use safehasattr or getattr instead of hasattr
r14970 batchablefn = getattr(mtd, 'batchable', None)
if batchablefn is not None:
batchable = batchablefn(mtd.im_self, *args, **opts)
Peter Arrenbrecht
wireproto: add basic command batching infrastructure...
r14621 encargsorres, encresref = batchable.next()
if encresref:
req.append((name, encargsorres,))
rsp.append((batchable, encresref, resref,))
else:
resref.set(encargsorres)
else:
if req:
self._submitreq(req, rsp)
req, rsp = [], []
resref.set(mtd(*args, **opts))
if req:
self._submitreq(req, rsp)
def _submitreq(self, req, rsp):
encresults = self.remote._submitbatch(req)
for encres, r in zip(encresults, rsp):
batchable, encresref, resref = r
encresref.set(encres)
resref.set(batchable.next())
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:
return map(bin, l.split(sep))
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
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',
'common': 'nodes',
Pierre-Yves David
getbundle: add `obsmarkers` argument to getbundle...
r22353 'obsmarkers': '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',
'cg': 'boolean'}
Pierre-Yves David
getbundle: declare type of parameters...
r21646
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586 # client side
Peter Arrenbrecht
peer: introduce real peer classes...
r17192 class wirepeer(peer.peerrepository):
Peter Arrenbrecht
wireproto: add batching support to wirerepository...
r14622
def batch(self):
Augie Fackler
wireproto: make wirepeer look-before-you-leap on batching...
r25913 if self.capable('batch'):
return remotebatch(self)
else:
return peer.localbatch(self)
Peter Arrenbrecht
wireproto: add batching support to wirerepository...
r14622 def _submitbatch(self, req):
cmds = []
for op, argsdict in req:
Augie Fackler
wireproto: correctly escape batched args and responses (issue4739)...
r25708 args = ','.join('%s=%s' % (escapearg(k), escapearg(v))
for k, v in argsdict.iteritems())
Peter Arrenbrecht
wireproto: add batching support to wirerepository...
r14622 cmds.append('%s %s' % (op, args))
rsp = self._call("batch", cmds=';'.join(cmds))
Augie Fackler
wireproto: correctly escape batched args and responses (issue4739)...
r25708 return [unescapearg(r) for r in rsp.split(';')]
Peter Arrenbrecht
wireproto: add batching support to wirerepository...
r14622 def _submitone(self, op, args):
return self._call(op, **args)
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)
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586 self._abort(error.RepoError(data))
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)
Matt Mackall
branch: operate on branch names in local string space where possible...
r13047 branchname = encoding.tolocal(urllib.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))
def branches(self, nodes):
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 n = encodelist(nodes)
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586 d = self._call("branches", nodes=n)
try:
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 br = [tuple(decodelist(b)) for b in d.splitlines()]
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586 return br
Matt Mackall
wireproto: avoid naked excepts
r13726 except ValueError:
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586 self._abort(error.ResponseError(_("unexpected response:"), d))
def between(self, pairs):
Matt Mackall
protocol: unify basic http client requests
r11587 batch = 8 # avoid giant requests
r = []
for i in xrange(0, len(pairs), batch):
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 n = " ".join([encodelist(p, '-') for p in pairs[i:i + batch]])
Matt Mackall
protocol: unify basic http client requests
r11587 d = self._call("between", pairs=n)
try:
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 r.extend(l and decodelist(l) or [] for l in d.splitlines())
Matt Mackall
wireproto: avoid naked excepts
r13726 except ValueError:
Matt Mackall
protocol: unify basic http client requests
r11587 self._abort(error.ResponseError(_("unexpected response:"), d))
return r
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
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 @batchable
Matt Mackall
protocol: move basic ssh client commands to wirerepository
r11586 def listkeys(self, namespace):
if not self.capable('pushkey'):
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 yield {}, None
f = future()
Pierre-Yves David
pushkey: add more verbose debug output regarding pushkey...
r17293 self.ui.debug('preparing listkeys for "%s"\n' % namespace)
Augie Fackler
wireproto: remove todict() and use {} literals instead
r20671 yield {'namespace': encoding.fromlocal(namespace)}, f
Peter Arrenbrecht
wireproto: make a number of commands batchable...
r14623 d = f.value
Pierre-Yves David
listkey: display the size of the listkey payload in a debug message...
r25339 self.ui.debug('received listkey for "%s": %i bytes\n'
% (namespace, len(d)))
Pierre-Yves David
wireproto: use pushkey.decodekey
r21653 yield pushkeymod.decodekeys(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')
Matt Mackall
protocol: unify client changegroup methods
r11591 def changegroup(self, nodes, kind):
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 n = encodelist(nodes)
Pierre-Yves David
wireproto: drop the _decompress method in favor a new call type...
r20905 f = self._callcompressable("changegroup", roots=n)
Sune Foldager
changegroup: rename bundle-related functions and classes...
r22390 return changegroupmod.cg1unpacker(f, 'UN')
Matt Mackall
protocol: unify client changegroup methods
r11591
def changegroupsubset(self, bases, heads, kind):
self.requirecap('changegroupsubset', _('look up remote changes'))
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 bases = encodelist(bases)
heads = encodelist(heads)
Pierre-Yves David
wireproto: drop the _decompress method in favor a new call type...
r20905 f = self._callcompressable("changegroupsubset",
bases=bases, heads=heads)
Sune Foldager
changegroup: rename bundle-related functions and classes...
r22390 return changegroupmod.cg1unpacker(f, 'UN')
Matt Mackall
protocol: unify client changegroup methods
r11591
Pierre-Yves David
getbundle: declare type of parameters...
r21646 def getbundle(self, source, **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:
assert False, 'unexpected'
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
Pierre-Yves David
wireproto: drop the _decompress method in favor a new call type...
r20905 f = self._callcompressable("getbundle", **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
Matt Mackall
protocol: unify client unbundle support...
r11592 def unbundle(self, cg, heads, source):
'''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
result of the push (see localrepository.addchangegroup()).
When pushing a bundle20 stream, return a bundle20 stream.'''
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',
util.sha1(''.join(sorted(heads))).digest()])
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
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:
opts['three'] = three
if four is not None:
opts['four'] = four
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
The command is expected to return a stream.
returns the server reply as a file like object."""
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.
Dirkjan Ochtman
protocol: wrap non-string protocol responses in classes
r11625 class streamres(object):
Pierre-Yves David
wireproto: document possible return type...
r20902 """wireproto reply: binary stream
The call was successful and the result is a stream.
Iterate on the `self.gen` attribute to retrieve chunks.
"""
Dirkjan Ochtman
protocol: wrap non-string protocol responses in classes
r11625 def __init__(self, gen):
self.gen = gen
class pushres(object):
Pierre-Yves David
wireproto: document possible return type...
r20902 """wireproto reply: success with simple integer return
The call was successful and returned an integer contained in `self.res`.
"""
Dirkjan Ochtman
protocol: wrap non-string protocol responses in classes
r11625 def __init__(self, res):
self.res = res
Benoit Boissinot
wireproto: introduce pusherr() to deal with "unsynced changes" error...
r12703 class pusherr(object):
Pierre-Yves David
wireproto: document possible return type...
r20902 """wireproto reply: failure
The call failed. The `self.res` attribute contains the error message.
"""
Benoit Boissinot
wireproto: introduce pusherr() to deal with "unsynced changes" error...
r12703 def __init__(self, res):
self.res = res
Andrew Pritchard
wireproto: add out-of-band error class to allow remote repo to report errors...
r15017 class ooberror(object):
Pierre-Yves David
wireproto: document possible return type...
r20902 """wireproto reply: failure of a batch of operation
Something failed during a batch call. The error message is stored in
`self.message`.
"""
Andrew Pritchard
wireproto: add out-of-band error class to allow remote repo to report errors...
r15017 def __init__(self, message):
self.message = message
Matt Mackall
protocol: introduce wireproto.py...
r11581 def dispatch(repo, proto, command):
Kevin Bullock
filtering: rename filters to their antonyms...
r18382 repo = repo.filtered("served")
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:
Pierre-Yves David
wireproto: rephrase the error message for unknown getbundle parameters...
r21728 sys.stderr.write("warning: %s ignored unexpected arguments %s\n"
Peter Arrenbrecht
wireproto: fix handling of '*' args for HTTP and SSH
r13721 % (cmd, ",".join(others)))
return opts
Pierre-Yves David
wireproto: add decorator for wire protocol command...
r20906 # list of commands
commands = {}
def wireprotocommand(name, args=''):
Mads Kiilerich
spelling: fixes from spell checker
r21024 """decorator for wire protocol command"""
Pierre-Yves David
wireproto: add decorator for wire protocol command...
r20906 def register(func):
commands[name] = (func, args)
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('=')
vals[n] = unescapearg(v)
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
Peter Arrenbrecht
wireproto: add batching support to wirerepository...
r14622 res.append(escapearg(result))
return ';'.join(res)
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")
Matt Mackall
protocol: introduce wireproto.py...
r11581 return "".join(r)
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():
Matt Mackall
branch: operate on branch names in local string space where possible...
r13047 branchname = urllib.quote(encoding.fromlocal(branch))
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 branchnodes = encodelist(nodes)
heads.append('%s %s' % (branchname, branchnodes))
Matt Mackall
protocol: introduce wireproto.py...
r11581 return '\n'.join(heads)
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")
Matt Mackall
protocol: introduce wireproto.py...
r11581 return "".join(r)
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)
Dirkjan Ochtman
protocol: move the streamclone implementation into wireproto
r11627 if _allowstream(repo.ui):
Benoit Allard
protocol: Add the stream-preferred capability...
r16361 if repo.ui.configbool('server', 'preferuncompressed', False):
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
if not requiredformats - set(('revlogv1',)):
caps.append('stream')
# otherwise, add 'streamreqs' detailing our local revlog format
else:
caps.append('streamreqs=%s' % ','.join(requiredformats))
Pierre-Yves David
bundle2: advertise bundle2 by default...
r24696 if repo.ui.configbool('experimental', 'bundle2-advertise', True):
Pierre-Yves David
bundle2: introduce a `getrepocaps` to retrieve the bundle2 caps of a repo...
r22342 capsblob = bundle2.encodecaps(bundle2.getrepocaps(repo))
Pierre-Yves David
bundle2: rename format, parts and config to final names...
r24686 caps.append('bundle2=' + urllib.quote(capsblob))
Matt Mackall
protocol: unify server-side capabilities functions
r11594 caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority))
Mike Edgar
wireproto: add config knob for http header length limit...
r25691 caps.append(
'httpheader=%d' % repo.ui.configint('server', 'maxhttpheaderlen', 1024))
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):
return ' '.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)
Pierre-Yves David
localrepo: move the changegroup method in changegroup module...
r20931 cg = changegroupmod.changegroup(repo, nodes, 'serve')
Dirkjan Ochtman
protocol: wrap non-string protocol responses in classes
r11625 return streamres(proto.groupchunks(cg))
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)
Pierre-Yves David
localrepo: move the changegroupsubset method in changegroup module...
r20927 cg = changegroupmod.changegroupsubset(repo, bases, heads, 'serve')
Dirkjan Ochtman
protocol: wrap non-string protocol responses in classes
r11625 return streamres(proto.groupchunks(cg))
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)
return repo.debugwireargs(one, two, **opts)
Peter Arrenbrecht
debug: add debugwireargs to test argument passing over the wire...
r13720
Pierre-Yves David
wireproto: expose the list of getbundle arguments to extensions...
r21615 # List of options accepted by getbundle.
#
# Meant to be extended by extensions. It is the extension's responsibility to
# ensure such options are properly processed in exchange.getbundle.
gboptslist = ['heads', 'common', 'bundlecaps']
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':
Pierre-Yves David
wireprotocol: fix 'boolean' handling...
r22351 opts[k] = bool(v)
Pierre-Yves David
getbundle: declare type of parameters...
r21646 elif keytype != 'plain':
raise KeyError('unknown getbundle option type %s'
% keytype)
Pierre-Yves David
bundle2: allow bundle2 for pulling over the wire...
r21069 cg = exchange.getbundle(repo, 'serve', **opts)
Peter Arrenbrecht
wireproto: add getbundle() function...
r13741 return streamres(proto.groupchunks(cg))
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()
Benoit Boissinot
wireproto: refactor list of nodeid encoding / decoding
r11597 return 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):
'''the hello command returns a set of lines describing various
interesting things about the server, in an RFC822-like format.
Currently the only one defined is "capabilities", which
consists of a line in the form:
capabilities: space separated list of tokens
'''
return "capabilities: %s\n" % (capabilities(repo, proto))
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()
Pierre-Yves David
wireproto: use pushkey.encodekey
r21651 return 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:
Matt Mackall
protocol: introduce wireproto.py...
r11581 r = str(inst)
success = 0
return "%s %s\n" % (success, r)
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):
Peter Arrenbrecht
wireproto: add known([id]) function...
r13723 return ''.join(b and "1" or "0" for b in repo.known(decodelist(nodes)))
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
if len(new) == 20 and new.encode('string-escape') != new:
# 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
Wagner Bruna
wireproto: fix pushkey hook failure and output on remote http repo...
r17793 if util.safehasattr(proto, 'restore'):
proto.redirect()
try:
r = repo.pushkey(encoding.tolocal(namespace), encoding.tolocal(key),
encoding.tolocal(old), new) or False
except util.Abort:
r = False
output = proto.restore()
return '%s\n%s' % (int(r), output)
Andreas Freimuth
wireproto: do not call pushkey module directly (issue3041)...
r15217 r = repo.pushkey(encoding.tolocal(namespace), encoding.tolocal(key),
encoding.tolocal(old), new)
Matt Mackall
protocol: introduce wireproto.py...
r11581 return '%s\n' % int(r)
Dirkjan Ochtman
protocol: move the streamclone implementation into wireproto
r11627 def _allowstream(ui):
return ui.configbool('server', 'uncompressed', True, untrusted=True)
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.
'''
if not _allowstream(repo.ui):
return '1\n'
Gregory Szorc
exchange: move code for generating a streaming clone into exchange...
r25235 def getstream(it):
yield '0\n'
for chunk in it:
yield chunk
Bryan O'Sullivan
wireproto: don't audit local paths during stream_out...
r17556
Gregory Szorc
exchange: move code for generating a streaming clone into exchange...
r25235 try:
# LockError may be raised before the first result is yielded. Don't
# emit output until we're sure we got the lock successfully.
it = exchange.generatestreamclone(repo)
return streamres(getstream(it))
except error.LockError:
return '2\n'
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
Pierre-Yves David
unbundle: extract checkheads in its own function...
r20967 try:
proto.redirect()
Matt Mackall
protocol: unify unbundle on the server side
r11593
Pierre-Yves David
unbundle: extract checkheads in its own function...
r20967 exchange.check_heads(repo, their_heads, 'preparing changes')
Benoit Boissinot
wireproto: redirect the output earlier
r12702
Pierre-Yves David
unbundle: extract checkheads in its own function...
r20967 # write bundle data to temporary file because it can be big
fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
fp = os.fdopen(fd, 'wb+')
r = 0
Matt Mackall
protocol: unify unbundle on the server side
r11593 try:
Pierre-Yves David
unbundle: extract checkheads in its own function...
r20967 proto.getfile(fp)
Pierre-Yves David
unbundle: extract the core logic in another function...
r20968 fp.seek(0)
Pierre-Yves David
bundle2: add a ui argument to readbundle...
r21064 gen = exchange.readbundle(repo.ui, fp, None)
Pierre-Yves David
unbundle: extract the core logic in another function...
r20968 r = exchange.unbundle(repo, gen, their_heads, 'serve',
proto._client())
Pierre-Yves David
bundle2: support for push over the wire...
r21075 if util.safehasattr(r, 'addpart'):
Mads Kiilerich
spelling: fixes from proofreading of spell checker issues
r23139 # The return looks streamable, we are in the bundle2 case and
Pierre-Yves David
bundle2: support for push over the wire...
r21075 # should return a stream.
return streamres(r.getchunks())
Pierre-Yves David
unbundle: extract checkheads in its own function...
r20967 return pushres(r)
Matt Mackall
protocol: unify unbundle on the server side
r11593 finally:
Pierre-Yves David
unbundle: extract checkheads in its own function...
r20967 fp.close()
os.unlink(tempname)
Pierre-Yves David
bundle2: refactor error bundle creation for the wireprotocol...
r24796
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except (error.BundleValueError, util.Abort, error.PushRaced) as exc:
Pierre-Yves David
bundle2: refactor error bundle creation for the wireprotocol...
r24796 # handle non-bundle2 case first
if not getattr(exc, 'duringunbundle2', False):
try:
raise
except util.Abort:
# The old code we moved used sys.stderr directly.
# We did not change it to minimise code change.
# This need to be moved to something proper.
# Feel free to do it.
sys.stderr.write("abort: %s\n" % exc)
return pushres(0)
except error.PushRaced:
return pusherr(str(exc))
bundler = bundle2.bundle20(repo.ui)
Pierre-Yves David
bundle2-wireproto: properly propagate the server output on error (issue4594)...
r24797 for out in getattr(exc, '_bundle2salvagedoutput', ()):
bundler.addpart(out)
Pierre-Yves David
bundle2: refactor error bundle creation for the wireprotocol...
r24796 try:
Pierre-Yves David
bundle2: convey PushkeyFailed error over the wire...
r25493 try:
raise
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except error.PushkeyFailed as exc:
Pierre-Yves David
bundle2: convey PushkeyFailed error over the wire...
r25493 # 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)
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except error.BundleValueError as exc:
Pierre-Yves David
bundle2: rename format, parts and config to final names...
r24686 errpart = bundler.newpart('error:unsupportedcontent')
Pierre-Yves David
bundle2: support None parttype in BundleValueError...
r21627 if exc.parttype is not None:
errpart.addparam('parttype', exc.parttype)
Pierre-Yves David
bundle2: support transmission of params error over the wire...
r21622 if exc.params:
errpart.addparam('params', '\0'.join(exc.params))
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except util.Abort as exc:
Pierre-Yves David
bundle2: refactor error bundle creation for the wireprotocol...
r24796 manargs = [('message', str(exc))]
Pierre-Yves David
bundle2: gracefully handle abort during unbundle...
r21177 advargs = []
Pierre-Yves David
bundle2: refactor error bundle creation for the wireprotocol...
r24796 if exc.hint is not None:
advargs.append(('hint', exc.hint))
Pierre-Yves David
bundle2: rename format, parts and config to final names...
r24686 bundler.addpart(bundle2.bundlepart('error:abort',
Pierre-Yves David
bundle2: gracefully handle abort during unbundle...
r21177 manargs, advargs))
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except error.PushRaced as exc:
Pierre-Yves David
bundle2: rename format, parts and config to final names...
r24686 bundler.newpart('error:pushraced', [('message', str(exc))])
Pierre-Yves David
bundle2: refactor error bundle creation for the wireprotocol...
r24796 return streamres(bundler.getchunks())