From 29f41643442cd11f638fa14c1d9c297c083c7e8a Mon Sep 17 00:00:00 2001 From: itzikBraun Date: Mon, 2 Apr 2018 12:57:33 +0200 Subject: added option to export request as httpie command --- mitmproxy/addons/export.py | 26 +++++++++++++++++++++++--- test/mitmproxy/addons/test_export.py | 30 +++++++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/mitmproxy/addons/export.py b/mitmproxy/addons/export.py index 4bb44548..f3300079 100644 --- a/mitmproxy/addons/export.py +++ b/mitmproxy/addons/export.py @@ -11,9 +11,13 @@ import mitmproxy.types import pyperclip -def curl_command(f: flow.Flow) -> str: +def raise_if_missing_request(f: flow.Flow) -> None: if not hasattr(f, "request"): raise exceptions.CommandError("Can't export flow with no request.") + + +def curl_command(f: flow.Flow) -> str: + raise_if_missing_request(f) data = "curl " request = f.request.copy() # type: ignore request.decode(strict=False) @@ -30,14 +34,30 @@ def curl_command(f: flow.Flow) -> str: return data +def httpie_command(f: flow.Flow) -> str: + raise_if_missing_request(f) + request = f.request.copy() # type: ignore + data = "http %s " % request.method + request.decode(strict=False) + data += "%s" % request.url + for k, v in request.headers.items(multi=True): + data += " '%s:%s'" % (k, v) + if request.content: + data += " <<< '%s'" % strutils.bytes_to_escaped_str( + request.content, + escape_single_quotes=True + ) + return data + + def raw(f: flow.Flow) -> bytes: - if not hasattr(f, "request"): - raise exceptions.CommandError("Can't export flow with no request.") + raise_if_missing_request(f) return assemble.assemble_request(f.request) # type: ignore formats = dict( curl = curl_command, + httpie = httpie_command, raw = raw, ) diff --git a/test/mitmproxy/addons/test_export.py b/test/mitmproxy/addons/test_export.py index 07227a7a..b625df56 100644 --- a/test/mitmproxy/addons/test_export.py +++ b/test/mitmproxy/addons/test_export.py @@ -65,6 +65,26 @@ class TestExportCurlCommand: export.curl_command(tcp_flow) +class TestExportHttpieCommand: + def test_get(self, get_request): + result = """http GET http://address:22/path?a=foo&a=bar&b=baz 'header:qvalue' 'content-length:0'""" + assert export.httpie_command(get_request) == result + + def test_post(self, post_request): + result = "http POST http://address:22/path 'content-length:256' <<< '{}'".format( + str(bytes(range(256)))[2:-1] + ) + assert export.httpie_command(post_request) == result + + def test_patch(self, patch_request): + result = """http PATCH http://address:22/path?query=param 'header:qvalue' 'content-length:7' <<< 'content'""" + assert export.httpie_command(patch_request) == result + + def test_tcp(self, tcp_flow): + with pytest.raises(exceptions.CommandError): + export.httpie_command(tcp_flow) + + class TestRaw: def test_get(self, get_request): assert b"header: qvalue" in export.raw(get_request) @@ -83,7 +103,7 @@ def test_export(tmpdir): f = str(tmpdir.join("path")) e = export.Export() with taddons.context(): - assert e.formats() == ["curl", "raw"] + assert e.formats() == ["curl", "httpie", "raw"] with pytest.raises(exceptions.CommandError): e.file("nonexistent", tflow.tflow(resp=True), f) @@ -95,6 +115,10 @@ def test_export(tmpdir): assert qr(f) os.unlink(f) + e.file("httpie", tflow.tflow(resp=True), f) + assert qr(f) + os.unlink(f) + @pytest.mark.parametrize("exception, log_message", [ (PermissionError, "Permission denied"), @@ -125,6 +149,10 @@ def test_clip(tmpdir): e.clip("curl", tflow.tflow(resp=True)) assert pc.called + with mock.patch('pyperclip.copy') as pc: + e.clip("httpie", tflow.tflow(resp=True)) + assert pc.called + with mock.patch('pyperclip.copy') as pc: log_message = "Pyperclip could not find a " \ "copy/paste mechanism for your system." -- cgit v1.2.3