aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAldo Cortesi <aldo@corte.si>2017-12-16 11:53:45 +1300
committerAldo Cortesi <aldo@corte.si>2017-12-17 10:11:02 +1300
commitf14ec2d8f043f051db45dd36a1d501788c7818e6 (patch)
treee26529aa117df0e0a03cddd8a6a96b3d28f6fff3
parenteab27db7d6421fe273040dd6a2596d031e4639e1 (diff)
downloadmitmproxy-f14ec2d8f043f051db45dd36a1d501788c7818e6.tar.gz
mitmproxy-f14ec2d8f043f051db45dd36a1d501788c7818e6.tar.bz2
mitmproxy-f14ec2d8f043f051db45dd36a1d501788c7818e6.zip
console: add a data view overlay for command output
Fixes #2654
-rw-r--r--mitmproxy/command.py2
-rw-r--r--mitmproxy/test/tutils.py1
-rw-r--r--mitmproxy/tools/console/commandexecutor.py14
-rw-r--r--mitmproxy/tools/console/grideditor/col.py67
-rw-r--r--mitmproxy/tools/console/grideditor/col_bytes.py2
-rw-r--r--mitmproxy/tools/console/grideditor/editors.py18
-rw-r--r--mitmproxy/tools/console/overlay.py27
7 files changed, 123 insertions, 8 deletions
diff --git a/mitmproxy/command.py b/mitmproxy/command.py
index b3c8eb22..2d51317c 100644
--- a/mitmproxy/command.py
+++ b/mitmproxy/command.py
@@ -170,7 +170,7 @@ class Command:
if chk:
pargs.extend(remainder)
else:
- raise exceptions.CommandError("Invalid value type.")
+ raise exceptions.CommandError("Invalid value type: %s - expected %s" % (remainder, self.paramtypes[-1]))
with self.manager.master.handlecontext():
ret = self.func(*pargs)
diff --git a/mitmproxy/test/tutils.py b/mitmproxy/test/tutils.py
index bcce547a..cd9f3b3f 100644
--- a/mitmproxy/test/tutils.py
+++ b/mitmproxy/test/tutils.py
@@ -1,4 +1,3 @@
-import time
from io import BytesIO
from mitmproxy.utils import data
diff --git a/mitmproxy/tools/console/commandexecutor.py b/mitmproxy/tools/console/commandexecutor.py
index e57ddbb4..829daee1 100644
--- a/mitmproxy/tools/console/commandexecutor.py
+++ b/mitmproxy/tools/console/commandexecutor.py
@@ -2,6 +2,8 @@ import typing
from mitmproxy import exceptions
from mitmproxy import flow
+
+from mitmproxy.tools.console import overlay
from mitmproxy.tools.console import signals
@@ -21,9 +23,11 @@ class CommandExecutor:
signals.status_message.send(
message="Command returned %s flows" % len(ret)
)
- elif len(str(ret)) < 50:
- signals.status_message.send(message=str(ret))
else:
- signals.status_message.send(
- message="Command returned too much data to display."
- )
+ self.master.overlay(
+ overlay.DataViewerOverlay(
+ self.master,
+ ret,
+ ),
+ valign="top"
+ ) \ No newline at end of file
diff --git a/mitmproxy/tools/console/grideditor/col.py b/mitmproxy/tools/console/grideditor/col.py
new file mode 100644
index 00000000..3331f3e7
--- /dev/null
+++ b/mitmproxy/tools/console/grideditor/col.py
@@ -0,0 +1,67 @@
+import typing
+
+import urwid
+
+from mitmproxy.tools.console import signals
+from mitmproxy.tools.console.grideditor import base
+from mitmproxy.utils import strutils
+
+strbytes = typing.Union[str, bytes]
+
+
+class Column(base.Column):
+ def Display(self, data):
+ return Display(data)
+
+ def Edit(self, data):
+ return Edit(data)
+
+ def blank(self):
+ return ""
+
+ def keypress(self, key, editor):
+ if key in ["m_select"]:
+ editor.walker.start_edit()
+ else:
+ return key
+
+
+class Display(base.Cell):
+ def __init__(self, data: strbytes) -> None:
+ self.data = data
+ if isinstance(data, bytes):
+ escaped = strutils.bytes_to_escaped_str(data)
+ else:
+ escaped = data.encode()
+ w = urwid.Text(escaped, wrap="any")
+ super().__init__(w)
+
+ def get_data(self) -> strbytes:
+ return self.data
+
+
+class Edit(base.Cell):
+ def __init__(self, data: strbytes) -> None:
+ if isinstance(data, bytes):
+ escaped = strutils.bytes_to_escaped_str(data)
+ else:
+ escaped = data.encode()
+ self.type = type(data) # type: typing.Type
+ w = urwid.Edit(edit_text=escaped, wrap="any", multiline=True)
+ w = urwid.AttrWrap(w, "editfield")
+ super().__init__(w)
+
+ def get_data(self) -> strbytes:
+ txt = self._w.get_text()[0].strip()
+ try:
+ if self.type == bytes:
+ return strutils.escaped_str_to_bytes(txt)
+ else:
+ return txt.decode()
+ except ValueError:
+ signals.status_message.send(
+ self,
+ message="Invalid Python-style string encoding.",
+ expire=1000
+ )
+ raise
diff --git a/mitmproxy/tools/console/grideditor/col_bytes.py b/mitmproxy/tools/console/grideditor/col_bytes.py
index da10cbaf..990253ea 100644
--- a/mitmproxy/tools/console/grideditor/col_bytes.py
+++ b/mitmproxy/tools/console/grideditor/col_bytes.py
@@ -46,7 +46,7 @@ class Edit(base.Cell):
except ValueError:
signals.status_message.send(
self,
- message="Invalid Python-style string encoding.",
+ message="Invalid data.",
expire=1000
)
raise
diff --git a/mitmproxy/tools/console/grideditor/editors.py b/mitmproxy/tools/console/grideditor/editors.py
index 074cdb77..b5d16737 100644
--- a/mitmproxy/tools/console/grideditor/editors.py
+++ b/mitmproxy/tools/console/grideditor/editors.py
@@ -2,6 +2,7 @@
from mitmproxy import exceptions
from mitmproxy.tools.console import layoutwidget
from mitmproxy.tools.console.grideditor import base
+from mitmproxy.tools.console.grideditor import col
from mitmproxy.tools.console.grideditor import col_text
from mitmproxy.tools.console.grideditor import col_bytes
from mitmproxy.tools.console.grideditor import col_subgrid
@@ -169,3 +170,20 @@ class OptionsEditor(base.GridEditor, layoutwidget.LayoutWidget):
def is_error(self, col, val):
pass
+
+
+class DataViewer(base.GridEditor, layoutwidget.LayoutWidget):
+ title = None # type: str
+
+ def __init__(self, master, vals):
+ if vals:
+ if not isinstance(vals[0], list):
+ vals = [[i] for i in vals]
+ self.columns = [col.Column("")] * len(vals[0])
+ super().__init__(master, vals, self.callback)
+
+ def callback(self, vals):
+ pass
+
+ def is_error(self, col, val):
+ pass
diff --git a/mitmproxy/tools/console/overlay.py b/mitmproxy/tools/console/overlay.py
index 7072d00e..f97f23f9 100644
--- a/mitmproxy/tools/console/overlay.py
+++ b/mitmproxy/tools/console/overlay.py
@@ -148,3 +148,30 @@ class OptionsOverlay(urwid.WidgetWrap, layoutwidget.LayoutWidget):
def layout_popping(self):
return self.ge.layout_popping()
+
+
+class DataViewerOverlay(urwid.WidgetWrap, layoutwidget.LayoutWidget):
+ keyctx = "grideditor"
+
+ def __init__(self, master, vals):
+ """
+ vspace: how much vertical space to keep clear
+ """
+ cols, rows = master.ui.get_cols_rows()
+ self.ge = grideditor.DataViewer(master, vals)
+ super().__init__(
+ urwid.AttrWrap(
+ urwid.LineBox(
+ urwid.BoxAdapter(self.ge, rows - 5),
+ title="Data viewer"
+ ),
+ "background"
+ )
+ )
+ self.width = math.ceil(cols * 0.8)
+
+ def key_responder(self):
+ return self.ge.key_responder()
+
+ def layout_popping(self):
+ return self.ge.layout_popping()