Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/debugcommands.py @ 37015:fc8939825632
debugcommands: support connecting to HTTP peers
Now that we have the plumbing for logging socket activity, let's hook
it up to `hg debugwireproto` so we can collect low-level activity on
sockets.
The new code is a bit incomplete. But it is better than nothing:
`hg debugwireproto` is still heavily evolving.
The added test demonstrates some interesting behavior. For example,
we're calling socket.makefile() and doing I/O on that. TIL. We're also
sending an "Accept-Encoding: identity" request header.
Differential Revision: https://phab.mercurial-scm.org/D2726
author | Gregory Szorc <gregory.szorc@gmail.com> |
---|---|
date | Tue, 13 Mar 2018 10:02:03 -0700 |
parents | 143219fc2620 |
children | b6a7070e7663 |
comparison
equal
deleted
inserted
replaced
37014:02221d6fb041 | 37015:fc8939825632 |
---|---|
46 extensions, | 46 extensions, |
47 filemerge, | 47 filemerge, |
48 fileset, | 48 fileset, |
49 formatter, | 49 formatter, |
50 hg, | 50 hg, |
51 httppeer, | |
51 localrepo, | 52 localrepo, |
52 lock as lockmod, | 53 lock as lockmod, |
53 logcmdutil, | 54 logcmdutil, |
54 merge as mergemod, | 55 merge as mergemod, |
55 obsolete, | 56 obsolete, |
2600 [ | 2601 [ |
2601 ('', 'localssh', False, _('start an SSH server for this repo')), | 2602 ('', 'localssh', False, _('start an SSH server for this repo')), |
2602 ('', 'peer', '', _('construct a specific version of the peer')), | 2603 ('', 'peer', '', _('construct a specific version of the peer')), |
2603 ('', 'noreadstderr', False, _('do not read from stderr of the remote')), | 2604 ('', 'noreadstderr', False, _('do not read from stderr of the remote')), |
2604 ] + cmdutil.remoteopts, | 2605 ] + cmdutil.remoteopts, |
2605 _('[REPO]'), | 2606 _('[PATH]'), |
2606 optionalrepo=True) | 2607 optionalrepo=True) |
2607 def debugwireproto(ui, repo, **opts): | 2608 def debugwireproto(ui, repo, path=None, **opts): |
2608 """send wire protocol commands to a server | 2609 """send wire protocol commands to a server |
2609 | 2610 |
2610 This command can be used to issue wire protocol commands to remote | 2611 This command can be used to issue wire protocol commands to remote |
2611 peers and to debug the raw data being exchanged. | 2612 peers and to debug the raw data being exchanged. |
2612 | 2613 |
2738 | 2739 |
2739 if opts['peer'] and opts['peer'] not in ('raw', 'ssh1', 'ssh2'): | 2740 if opts['peer'] and opts['peer'] not in ('raw', 'ssh1', 'ssh2'): |
2740 raise error.Abort(_('invalid value for --peer'), | 2741 raise error.Abort(_('invalid value for --peer'), |
2741 hint=_('valid values are "raw", "ssh1", and "ssh2"')) | 2742 hint=_('valid values are "raw", "ssh1", and "ssh2"')) |
2742 | 2743 |
2744 if path and opts['localssh']: | |
2745 raise error.Abort(_('cannot specify --localssh with an explicit ' | |
2746 'path')) | |
2747 | |
2743 if ui.interactive(): | 2748 if ui.interactive(): |
2744 ui.write(_('(waiting for commands on stdin)\n')) | 2749 ui.write(_('(waiting for commands on stdin)\n')) |
2745 | 2750 |
2746 blocks = list(_parsewirelangblocks(ui.fin)) | 2751 blocks = list(_parsewirelangblocks(ui.fin)) |
2747 | 2752 |
2748 proc = None | 2753 proc = None |
2754 stdin = None | |
2755 stdout = None | |
2756 stderr = None | |
2749 | 2757 |
2750 if opts['localssh']: | 2758 if opts['localssh']: |
2751 # We start the SSH server in its own process so there is process | 2759 # We start the SSH server in its own process so there is process |
2752 # separation. This prevents a whole class of potential bugs around | 2760 # separation. This prevents a whole class of potential bugs around |
2753 # shared state from interfering with server operation. | 2761 # shared state from interfering with server operation. |
2791 else: | 2799 else: |
2792 ui.write(_('creating ssh peer from handshake results\n')) | 2800 ui.write(_('creating ssh peer from handshake results\n')) |
2793 peer = sshpeer.makepeer(ui, url, proc, stdin, stdout, stderr, | 2801 peer = sshpeer.makepeer(ui, url, proc, stdin, stdout, stderr, |
2794 autoreadstderr=autoreadstderr) | 2802 autoreadstderr=autoreadstderr) |
2795 | 2803 |
2804 elif path: | |
2805 # We bypass hg.peer() so we can proxy the sockets. | |
2806 # TODO consider not doing this because we skip | |
2807 # ``hg.wirepeersetupfuncs`` and potentially other useful functionality. | |
2808 u = util.url(path) | |
2809 if u.scheme != 'http': | |
2810 raise error.Abort(_('only http:// paths are currently supported')) | |
2811 | |
2812 url, authinfo = u.authinfo() | |
2813 openerargs = {} | |
2814 | |
2815 # Turn pipes/sockets into observers so we can log I/O. | |
2816 if ui.verbose: | |
2817 openerargs = { | |
2818 r'loggingfh': ui, | |
2819 r'loggingname': b's', | |
2820 r'loggingopts': { | |
2821 r'logdata': True, | |
2822 }, | |
2823 } | |
2824 | |
2825 opener = urlmod.opener(ui, authinfo, **openerargs) | |
2826 | |
2827 if opts['peer'] == 'raw': | |
2828 ui.write(_('using raw connection to peer\n')) | |
2829 peer = None | |
2830 elif opts['peer']: | |
2831 raise error.Abort(_('--peer %s not supported with HTTP peers') % | |
2832 opts['peer']) | |
2833 else: | |
2834 peer = httppeer.httppeer(ui, path, url, opener) | |
2835 peer._fetchcaps() | |
2836 | |
2837 # We /could/ populate stdin/stdout with sock.makefile()... | |
2796 else: | 2838 else: |
2797 raise error.Abort(_('only --localssh is currently supported')) | 2839 raise error.Abort(_('unsupported connection configuration')) |
2798 | 2840 |
2799 batchedcommands = None | 2841 batchedcommands = None |
2800 | 2842 |
2801 # Now perform actions based on the parsed wire language instructions. | 2843 # Now perform actions based on the parsed wire language instructions. |
2802 for action, lines in blocks: | 2844 for action, lines in blocks: |
2803 if action in ('raw', 'raw+'): | 2845 if action in ('raw', 'raw+'): |
2846 if not stdin: | |
2847 raise error.Abort(_('cannot call raw/raw+ on this peer')) | |
2848 | |
2804 # Concatenate the data together. | 2849 # Concatenate the data together. |
2805 data = ''.join(l.lstrip() for l in lines) | 2850 data = ''.join(l.lstrip() for l in lines) |
2806 data = util.unescapestr(data) | 2851 data = util.unescapestr(data) |
2807 stdin.write(data) | 2852 stdin.write(data) |
2808 | 2853 |
2809 if action == 'raw+': | 2854 if action == 'raw+': |
2810 stdin.flush() | 2855 stdin.flush() |
2811 elif action == 'flush': | 2856 elif action == 'flush': |
2857 if not stdin: | |
2858 raise error.Abort(_('cannot call flush on this peer')) | |
2812 stdin.flush() | 2859 stdin.flush() |
2813 elif action.startswith('command'): | 2860 elif action.startswith('command'): |
2814 if not peer: | 2861 if not peer: |
2815 raise error.Abort(_('cannot send commands unless peer instance ' | 2862 raise error.Abort(_('cannot send commands unless peer instance ' |
2816 'is available')) | 2863 'is available')) |
2863 | 2910 |
2864 batchedcommands = None | 2911 batchedcommands = None |
2865 elif action == 'close': | 2912 elif action == 'close': |
2866 peer.close() | 2913 peer.close() |
2867 elif action == 'readavailable': | 2914 elif action == 'readavailable': |
2915 if not stdout or not stderr: | |
2916 raise error.Abort(_('readavailable not available on this peer')) | |
2917 | |
2868 stdin.close() | 2918 stdin.close() |
2869 stdout.read() | 2919 stdout.read() |
2870 stderr.read() | 2920 stderr.read() |
2921 | |
2871 elif action == 'readline': | 2922 elif action == 'readline': |
2923 if not stdout: | |
2924 raise error.Abort(_('readline not available on this peer')) | |
2872 stdout.readline() | 2925 stdout.readline() |
2873 elif action == 'ereadline': | 2926 elif action == 'ereadline': |
2927 if not stderr: | |
2928 raise error.Abort(_('ereadline not available on this peer')) | |
2874 stderr.readline() | 2929 stderr.readline() |
2875 elif action.startswith('read '): | 2930 elif action.startswith('read '): |
2876 count = int(action.split(' ', 1)[1]) | 2931 count = int(action.split(' ', 1)[1]) |
2932 if not stdout: | |
2933 raise error.Abort(_('read not available on this peer')) | |
2877 stdout.read(count) | 2934 stdout.read(count) |
2878 elif action.startswith('eread '): | 2935 elif action.startswith('eread '): |
2879 count = int(action.split(' ', 1)[1]) | 2936 count = int(action.split(' ', 1)[1]) |
2937 if not stderr: | |
2938 raise error.Abort(_('eread not available on this peer')) | |
2880 stderr.read(count) | 2939 stderr.read(count) |
2881 else: | 2940 else: |
2882 raise error.Abort(_('unknown action: %s') % action) | 2941 raise error.Abort(_('unknown action: %s') % action) |
2883 | 2942 |
2884 if batchedcommands is not None: | 2943 if batchedcommands is not None: |