comparison mercurial/debugcommands.py @ 37016:b6a7070e7663

debugcommands: support sending HTTP requests with debugwireproto We implement an action that can issue an HTTP request. We can define headers via arguments and specify a file to use for the HTTP request body. The request uses the HTTP peer's opener, which is already configured for auth, etc. This is both good and bad. Good in that we get some nice behavior out of the box. Bad in that some HTTP request headers are added automatically. Differential Revision: https://phab.mercurial-scm.org/D2841
author Gregory Szorc <gregory.szorc@gmail.com>
date Tue, 13 Mar 2018 11:17:10 -0700
parents fc8939825632
children d3a9036d9ae9
comparison
equal deleted inserted replaced
37015:fc8939825632 37016:b6a7070e7663
12 import difflib 12 import difflib
13 import errno 13 import errno
14 import operator 14 import operator
15 import os 15 import os
16 import random 16 import random
17 import re
17 import socket 18 import socket
18 import ssl 19 import ssl
19 import stat 20 import stat
20 import string 21 import string
21 import subprocess 22 import subprocess
2690 2691
2691 Submit previously queued ``command`` blocks as a batch request. 2692 Submit previously queued ``command`` blocks as a batch request.
2692 2693
2693 This action MUST be paired with a ``batchbegin`` action. 2694 This action MUST be paired with a ``batchbegin`` action.
2694 2695
2696 httprequest <method> <path>
2697 ---------------------------
2698
2699 (HTTP peer only)
2700
2701 Send an HTTP request to the peer.
2702
2703 The HTTP request line follows the ``httprequest`` action. e.g. ``GET /foo``.
2704
2705 Arguments of the form ``<key>: <value>`` are interpreted as HTTP request
2706 headers to add to the request. e.g. ``Accept: foo``.
2707
2708 The following arguments are special:
2709
2710 ``BODYFILE``
2711 The content of the file defined as the value to this argument will be
2712 transferred verbatim as the HTTP request body.
2713
2695 close 2714 close
2696 ----- 2715 -----
2697 2716
2698 Close the connection to the server. 2717 Close the connection to the server.
2699 2718
2752 2771
2753 proc = None 2772 proc = None
2754 stdin = None 2773 stdin = None
2755 stdout = None 2774 stdout = None
2756 stderr = None 2775 stderr = None
2776 opener = None
2757 2777
2758 if opts['localssh']: 2778 if opts['localssh']:
2759 # We start the SSH server in its own process so there is process 2779 # We start the SSH server in its own process so there is process
2760 # separation. This prevents a whole class of potential bugs around 2780 # separation. This prevents a whole class of potential bugs around
2761 # shared state from interfering with server operation. 2781 # shared state from interfering with server operation.
2907 len(batchedcommands)) 2927 len(batchedcommands))
2908 for i, chunk in enumerate(peer._submitbatch(batchedcommands)): 2928 for i, chunk in enumerate(peer._submitbatch(batchedcommands)):
2909 ui.status(_('response #%d: %s\n') % (i, util.escapedata(chunk))) 2929 ui.status(_('response #%d: %s\n') % (i, util.escapedata(chunk)))
2910 2930
2911 batchedcommands = None 2931 batchedcommands = None
2932
2933 elif action.startswith('httprequest '):
2934 if not opener:
2935 raise error.Abort(_('cannot use httprequest without an HTTP '
2936 'peer'))
2937
2938 request = action.split(' ', 2)
2939 if len(request) != 3:
2940 raise error.Abort(_('invalid httprequest: expected format is '
2941 '"httprequest <method> <path>'))
2942
2943 method, httppath = request[1:]
2944 headers = {}
2945 body = None
2946 for line in lines:
2947 line = line.lstrip()
2948 m = re.match(b'^([a-zA-Z0-9_-]+): (.*)$', line)
2949 if m:
2950 headers[m.group(1)] = m.group(2)
2951 continue
2952
2953 if line.startswith(b'BODYFILE '):
2954 with open(line.split(b' ', 1), 'rb') as fh:
2955 body = fh.read()
2956 else:
2957 raise error.Abort(_('unknown argument to httprequest: %s') %
2958 line)
2959
2960 url = path + httppath
2961 req = urlmod.urlreq.request(pycompat.strurl(url), body, headers)
2962
2963 try:
2964 opener.open(req).read()
2965 except util.urlerr.urlerror as e:
2966 e.read()
2967
2912 elif action == 'close': 2968 elif action == 'close':
2913 peer.close() 2969 peer.close()
2914 elif action == 'readavailable': 2970 elif action == 'readavailable':
2915 if not stdout or not stderr: 2971 if not stdout or not stderr:
2916 raise error.Abort(_('readavailable not available on this peer')) 2972 raise error.Abort(_('readavailable not available on this peer'))