From 595d2a2fa09ea8c16507266a0c4d435bb1c68983 Mon Sep 17 00:00:00 2001 From: Will Coster Date: Tue, 16 Feb 2016 18:35:42 -0800 Subject: [PATCH] Add a content view for query parameters The query content view uses format_dict to display a table of query parameters and is made the default content view for requests with query parameters and no request body. To facilitate this the query parameter dictionary of HTTPRequests is added to the metadata content view parameter under the "query" key. Additionally, the logic for handling "no content" messages is moved from contentviews.get_content_view to ViewAuto. This is necessary as it allows the query content view to be displayed when there is no request body. --- mitmproxy/mitmproxy/console/flowview.py | 5 ++++- mitmproxy/mitmproxy/contentviews.py | 27 ++++++++++++++++++++----- test/mitmproxy/test_contentview.py | 21 +++++++++++++++++++ 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/mitmproxy/mitmproxy/console/flowview.py b/mitmproxy/mitmproxy/console/flowview.py index d2b98b684..f490732e4 100644 --- a/mitmproxy/mitmproxy/console/flowview.py +++ b/mitmproxy/mitmproxy/console/flowview.py @@ -193,8 +193,11 @@ class FlowView(tabs.Tabs): def _get_content_view(self, viewmode, message, max_lines, _): try: + query = None + if isinstance(message, HTTPRequest): + query = message.query description, lines = contentviews.get_content_view( - viewmode, message.content, headers=message.headers + viewmode, message.content, headers=message.headers, query=query ) except ContentViewException: s = "Content viewer failed: \n" + traceback.format_exc() diff --git a/mitmproxy/mitmproxy/contentviews.py b/mitmproxy/mitmproxy/contentviews.py index c0652c189..5335b59c5 100644 --- a/mitmproxy/mitmproxy/contentviews.py +++ b/mitmproxy/mitmproxy/contentviews.py @@ -8,7 +8,8 @@ in the future, e.g. to decode protobuf messages sent as WebSocket frames. Thus, the View API is very minimalistic. The only arguments are `data` and `**metadata`, where `data` is the actual content (as bytes). The contents on metadata depend on the protocol in -use. For HTTP, the message headers are passed as the ``headers`` keyword argument. +use. For HTTP, the message headers are passed as the ``headers`` keyword argument. For HTTP +requests, the query parameters are passed as the ``query`` keyword argument. """ from __future__ import (absolute_import, print_function, division) @@ -118,15 +119,19 @@ class ViewAuto(View): def __call__(self, data, **metadata): headers = metadata.get("headers", {}) ctype = headers.get("content-type") - if ctype: + if data and ctype: ct = parse_content_type(ctype) if ctype else None ct = "%s/%s" % (ct[0], ct[1]) if ct in content_types_map: return content_types_map[ct][0](data, **metadata) elif utils.isXML(data): return get("XML")(data, **metadata) - if utils.isMostlyBin(data): + if metadata.get("query"): + return get("Query")(data, **metadata) + if data and utils.isMostlyBin(data): return get("Hex")(data) + if not data: + return "No content", [] return get("Raw")(data) @@ -460,6 +465,19 @@ class ViewProtobuf(View): return "Protobuf", format_text(decoded) +class ViewQuery(View): + name = "Query" + prompt = ("query", "q") + content_types = [] + + def __call__(self, data, **metadata): + query = metadata.get("query") + if query: + return "Query", format_dict(query) + else: + return "Query", format_text("") + + class ViewWBXML(View): name = "WBXML" prompt = ("wbxml", "w") @@ -541,6 +559,7 @@ add(ViewCSS()) add(ViewURLEncoded()) add(ViewMultipart()) add(ViewImage()) +add(ViewQuery()) if pyamf: add(ViewAMF()) @@ -577,8 +596,6 @@ def get_content_view(viewmode, data, **metadata): Raises: ContentViewException, if the content view threw an error. """ - if not data: - return "No content", [] msg = [] headers = metadata.get("headers", {}) diff --git a/test/mitmproxy/test_contentview.py b/test/mitmproxy/test_contentview.py index 7f1d735e9..c00afa5f7 100644 --- a/test/mitmproxy/test_contentview.py +++ b/test/mitmproxy/test_contentview.py @@ -1,5 +1,6 @@ from mitmproxy.exceptions import ContentViewException from netlib.http import Headers +from netlib.odict import ODict import netlib.utils from netlib import encoding @@ -45,6 +46,19 @@ class TestContentView: ) assert f[0].startswith("XML") + f = v( + "", + headers=Headers() + ) + assert f[0] == "No content" + + f = v( + "", + headers=Headers(), + query=ODict([("foo", "bar")]), + ) + assert f[0] == "Query" + def test_view_urlencoded(self): d = netlib.utils.urlencode([("one", "two"), ("three", "four")]) v = cv.ViewURLEncoded() @@ -158,6 +172,13 @@ Larry h = Headers(content_type="unparseable") assert not view(v, headers=h) + def test_view_query(self): + d = "" + v = cv.ViewQuery() + f = v(d, query=ODict([("foo", "bar")])) + assert f[0] == "Query" + assert [x for x in f[1]] == [[("header", "foo: "), ("text", "bar")]] + def test_get_content_view(self): r = cv.get_content_view( cv.get("Raw"),