diff --git a/mercurial/debugcommands.py b/mercurial/debugcommands.py --- a/mercurial/debugcommands.py +++ b/mercurial/debugcommands.py @@ -14,6 +14,7 @@ import operator import os import random +import re import socket import ssl import stat @@ -2692,6 +2693,24 @@ This action MUST be paired with a ``batchbegin`` action. + httprequest + --------------------------- + + (HTTP peer only) + + Send an HTTP request to the peer. + + The HTTP request line follows the ``httprequest`` action. e.g. ``GET /foo``. + + Arguments of the form ``: `` are interpreted as HTTP request + headers to add to the request. e.g. ``Accept: foo``. + + The following arguments are special: + + ``BODYFILE`` + The content of the file defined as the value to this argument will be + transferred verbatim as the HTTP request body. + close ----- @@ -2754,6 +2773,7 @@ stdin = None stdout = None stderr = None + opener = None if opts['localssh']: # We start the SSH server in its own process so there is process @@ -2909,6 +2929,42 @@ ui.status(_('response #%d: %s\n') % (i, util.escapedata(chunk))) batchedcommands = None + + elif action.startswith('httprequest '): + if not opener: + raise error.Abort(_('cannot use httprequest without an HTTP ' + 'peer')) + + request = action.split(' ', 2) + if len(request) != 3: + raise error.Abort(_('invalid httprequest: expected format is ' + '"httprequest ')) + + method, httppath = request[1:] + headers = {} + body = None + for line in lines: + line = line.lstrip() + m = re.match(b'^([a-zA-Z0-9_-]+): (.*)$', line) + if m: + headers[m.group(1)] = m.group(2) + continue + + if line.startswith(b'BODYFILE '): + with open(line.split(b' ', 1), 'rb') as fh: + body = fh.read() + else: + raise error.Abort(_('unknown argument to httprequest: %s') % + line) + + url = path + httppath + req = urlmod.urlreq.request(pycompat.strurl(url), body, headers) + + try: + opener.open(req).read() + except util.urlerr.urlerror as e: + e.read() + elif action == 'close': peer.close() elif action == 'readavailable': diff --git a/tests/test-http-protocol.t b/tests/test-http-protocol.t --- a/tests/test-http-protocol.t +++ b/tests/test-http-protocol.t @@ -226,4 +226,39 @@ s> phases response: bookmarks \nnamespaces \nphases +Same thing, but with "httprequest" command + + $ hg --verbose debugwireproto --peer raw http://$LOCALIP:$HGPORT << EOF + > httprequest GET ?cmd=listkeys + > accept: application/mercurial-0.1 + > user-agent: mercurial/proto-1.0 (Mercurial 42) + > x-hgarg-1: namespace=namespaces + > EOF + using raw connection to peer + s> sendall(*, 0): (glob) + s> GET /?cmd=listkeys HTTP/1.1\r\n + s> Accept-Encoding: identity\r\n + s> accept: application/mercurial-0.1\r\n + s> user-agent: mercurial/proto-1.0 (Mercurial 42)\r\n (glob) + s> x-hgarg-1: namespace=namespaces\r\n + s> host: $LOCALIP:$HGPORT\r\n (glob) + s> \r\n + s> makefile('rb', None) + s> readline() -> 36: + s> HTTP/1.1 200 Script output follows\r\n + s> readline() -> 28: + s> Server: testing stub value\r\n + s> readline() -> *: (glob) + s> Date: $HTTP_DATE$\r\n + s> readline() -> 41: + s> Content-Type: application/mercurial-0.1\r\n + s> readline() -> 20: + s> Content-Length: 30\r\n + s> readline() -> 2: + s> \r\n + s> read(30) -> 30: + s> bookmarks \n + s> namespaces \n + s> phases + $ killdaemons.py