Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/debugcommands.py @ 37483:61e405fb6372
wireproto: crude support for version 2 HTTP peer
As part of implementing the server-side bits of the wire protocol
command handlers for version 2, we want a way to easily test those
commands. Currently, we use the "httprequest" action of `hg
debugwireproto`. But this requires explicitly specifying the HTTP
request headers, low-level frame details, and the data structure
to encode with CBOR. That's a lot of boilerplate and a lot of it can
change as the wire protocol evolves.
`hg debugwireproto` has a mechanism to issue commands via the peer
interface. That is *much* easier to use and we prefer to test with
that going forward.
This commit implements enough parts of the peer API to send basic
requests via the HTTP version 2 transport.
The peer code is super hacky. Again, the goal is to facilitate
server testing, not robustly implement a client. The client code
will receive love at a later time.
Differential Revision: https://phab.mercurial-scm.org/D3177
author | Gregory Szorc <gregory.szorc@gmail.com> |
---|---|
date | Wed, 28 Mar 2018 15:09:34 -0700 |
parents | 9966f44ecab4 |
children | 835ccc2a5ef1 |
comparison
equal
deleted
inserted
replaced
37482:fa9faf58959d | 37483:61e405fb6372 |
---|---|
2629 and connect to that. By default, the connection will perform a handshake | 2629 and connect to that. By default, the connection will perform a handshake |
2630 and establish an appropriate peer instance. | 2630 and establish an appropriate peer instance. |
2631 | 2631 |
2632 ``--peer`` can be used to bypass the handshake protocol and construct a | 2632 ``--peer`` can be used to bypass the handshake protocol and construct a |
2633 peer instance using the specified class type. Valid values are ``raw``, | 2633 peer instance using the specified class type. Valid values are ``raw``, |
2634 ``ssh1``, and ``ssh2``. ``raw`` instances only allow sending raw data | 2634 ``http2``, ``ssh1``, and ``ssh2``. ``raw`` instances only allow sending |
2635 payloads and don't support higher-level command actions. | 2635 raw data payloads and don't support higher-level command actions. |
2636 | 2636 |
2637 ``--noreadstderr`` can be used to disable automatic reading from stderr | 2637 ``--noreadstderr`` can be used to disable automatic reading from stderr |
2638 of the peer (for SSH connections only). Disabling automatic reading of | 2638 of the peer (for SSH connections only). Disabling automatic reading of |
2639 stderr is useful for making output more deterministic. | 2639 stderr is useful for making output more deterministic. |
2640 | 2640 |
2676 each line is ``<key> <value>``. e.g.:: | 2676 each line is ``<key> <value>``. e.g.:: |
2677 | 2677 |
2678 command listkeys | 2678 command listkeys |
2679 namespace bookmarks | 2679 namespace bookmarks |
2680 | 2680 |
2681 Values are interpreted as Python b'' literals. This allows encoding | 2681 If the value begins with ``eval:``, it will be interpreted as a Python |
2682 special byte sequences via backslash escaping. | 2682 literal expression. Otherwise values are interpreted as Python b'' literals. |
2683 This allows sending complex types and encoding special byte sequences via | |
2684 backslash escaping. | |
2683 | 2685 |
2684 The following arguments have special meaning: | 2686 The following arguments have special meaning: |
2685 | 2687 |
2686 ``PUSHFILE`` | 2688 ``PUSHFILE`` |
2687 When defined, the *push* mechanism of the peer will be used instead | 2689 When defined, the *push* mechanism of the peer will be used instead |
2801 opts = pycompat.byteskwargs(opts) | 2803 opts = pycompat.byteskwargs(opts) |
2802 | 2804 |
2803 if opts['localssh'] and not repo: | 2805 if opts['localssh'] and not repo: |
2804 raise error.Abort(_('--localssh requires a repository')) | 2806 raise error.Abort(_('--localssh requires a repository')) |
2805 | 2807 |
2806 if opts['peer'] and opts['peer'] not in ('raw', 'ssh1', 'ssh2'): | 2808 if opts['peer'] and opts['peer'] not in ('raw', 'http2', 'ssh1', 'ssh2'): |
2807 raise error.Abort(_('invalid value for --peer'), | 2809 raise error.Abort(_('invalid value for --peer'), |
2808 hint=_('valid values are "raw", "ssh1", and "ssh2"')) | 2810 hint=_('valid values are "raw", "ssh1", and "ssh2"')) |
2809 | 2811 |
2810 if path and opts['localssh']: | 2812 if path and opts['localssh']: |
2811 raise error.Abort(_('cannot specify --localssh with an explicit ' | 2813 raise error.Abort(_('cannot specify --localssh with an explicit ' |
2875 u = util.url(path) | 2877 u = util.url(path) |
2876 if u.scheme != 'http': | 2878 if u.scheme != 'http': |
2877 raise error.Abort(_('only http:// paths are currently supported')) | 2879 raise error.Abort(_('only http:// paths are currently supported')) |
2878 | 2880 |
2879 url, authinfo = u.authinfo() | 2881 url, authinfo = u.authinfo() |
2880 openerargs = {} | 2882 openerargs = { |
2883 r'useragent': b'Mercurial debugwireproto', | |
2884 } | |
2881 | 2885 |
2882 # Turn pipes/sockets into observers so we can log I/O. | 2886 # Turn pipes/sockets into observers so we can log I/O. |
2883 if ui.verbose: | 2887 if ui.verbose: |
2884 openerargs = { | 2888 openerargs.update({ |
2885 r'loggingfh': ui, | 2889 r'loggingfh': ui, |
2886 r'loggingname': b's', | 2890 r'loggingname': b's', |
2887 r'loggingopts': { | 2891 r'loggingopts': { |
2888 r'logdata': True, | 2892 r'logdata': True, |
2889 r'logdataapis': False, | 2893 r'logdataapis': False, |
2890 }, | 2894 }, |
2891 } | 2895 }) |
2892 | 2896 |
2893 if ui.debugflag: | 2897 if ui.debugflag: |
2894 openerargs[r'loggingopts'][r'logdataapis'] = True | 2898 openerargs[r'loggingopts'][r'logdataapis'] = True |
2895 | 2899 |
2896 # Don't send default headers when in raw mode. This allows us to | 2900 # Don't send default headers when in raw mode. This allows us to |
2899 if opts['peer'] == 'raw': | 2903 if opts['peer'] == 'raw': |
2900 openerargs[r'sendaccept'] = False | 2904 openerargs[r'sendaccept'] = False |
2901 | 2905 |
2902 opener = urlmod.opener(ui, authinfo, **openerargs) | 2906 opener = urlmod.opener(ui, authinfo, **openerargs) |
2903 | 2907 |
2904 if opts['peer'] == 'raw': | 2908 if opts['peer'] == 'http2': |
2909 ui.write(_('creating http peer for wire protocol version 2\n')) | |
2910 peer = httppeer.httpv2peer(ui, path, opener) | |
2911 elif opts['peer'] == 'raw': | |
2905 ui.write(_('using raw connection to peer\n')) | 2912 ui.write(_('using raw connection to peer\n')) |
2906 peer = None | 2913 peer = None |
2907 elif opts['peer']: | 2914 elif opts['peer']: |
2908 raise error.Abort(_('--peer %s not supported with HTTP peers') % | 2915 raise error.Abort(_('--peer %s not supported with HTTP peers') % |
2909 opts['peer']) | 2916 opts['peer']) |
2949 key = fields[0] | 2956 key = fields[0] |
2950 value = '' | 2957 value = '' |
2951 else: | 2958 else: |
2952 key, value = fields | 2959 key, value = fields |
2953 | 2960 |
2954 args[key] = stringutil.unescapestr(value) | 2961 if value.startswith('eval:'): |
2962 value = stringutil.evalpythonliteral(value[5:]) | |
2963 else: | |
2964 value = stringutil.unescapestr(value) | |
2965 | |
2966 args[key] = value | |
2955 | 2967 |
2956 if batchedcommands is not None: | 2968 if batchedcommands is not None: |
2957 batchedcommands.append((command, args)) | 2969 batchedcommands.append((command, args)) |
2958 continue | 2970 continue |
2959 | 2971 |