Merge pull request #2716 from cortesi/cmdfixes

various command-related fixes
This commit is contained in:
Aldo Cortesi 2017-12-20 19:49:02 +13:00 committed by GitHub
commit a6a4b1c33b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 49 additions and 3 deletions

View File

@ -36,6 +36,8 @@ def extract(cut: str, f: flow.Flow) -> typing.Union[str, bytes]:
if spec == "host" and is_addr(current): if spec == "host" and is_addr(current):
return str(current[0]) return str(current[0])
elif spec.startswith("header["): elif spec.startswith("header["):
if not current:
return ""
return current.headers.get(headername(spec), "") return current.headers.get(headername(spec), "")
elif isinstance(part, bytes): elif isinstance(part, bytes):
return part return part

View File

@ -23,6 +23,10 @@ class CommandExecutor:
signals.status_message.send( signals.status_message.send(
message="Command returned %s flows" % len(ret) message="Command returned %s flows" % len(ret)
) )
elif type(ret) == flow.Flow:
signals.status_message.send(
message="Command returned 1 flow"
)
else: else:
self.master.overlay( self.master.overlay(
overlay.DataViewerOverlay( overlay.DataViewerOverlay(

View File

@ -124,7 +124,7 @@ class CommandHelp(urwid.Frame):
class Commands(urwid.Pile, layoutwidget.LayoutWidget): class Commands(urwid.Pile, layoutwidget.LayoutWidget):
title = "Commands" title = "Command Reference"
keyctx = "commands" keyctx = "commands"
def __init__(self, master): def __init__(self, master):

View File

@ -322,6 +322,7 @@ class ConsoleAddon:
signals.pop_view_state.send(self) signals.pop_view_state.send(self)
@command.command("console.bodyview") @command.command("console.bodyview")
@command.argument("part", type=mitmproxy.types.Choice("console.bodyview.options"))
def bodyview(self, f: flow.Flow, part: str) -> None: def bodyview(self, f: flow.Flow, part: str) -> None:
""" """
Spawn an external viewer for a flow request or response body based Spawn an external viewer for a flow request or response body based
@ -338,6 +339,13 @@ class ConsoleAddon:
raise exceptions.CommandError("No content to view.") raise exceptions.CommandError("No content to view.")
self.master.spawn_external_viewer(content, t) self.master.spawn_external_viewer(content, t)
@command.command("console.bodyview.options")
def bodyview_options(self) -> typing.Sequence[str]:
"""
Possible parts for console.bodyview.
"""
return ["request", "response"]
@command.command("console.edit.focus.options") @command.command("console.edit.focus.options")
def edit_focus_options(self) -> typing.Sequence[str]: def edit_focus_options(self) -> typing.Sequence[str]:
""" """

View File

@ -17,6 +17,13 @@ Contexts = {
} }
navkeys = [
"m_start", "m_end", "m_next", "m_select",
"up", "down", "page_up", "page_down",
"left", "right"
]
class Binding: class Binding:
def __init__(self, key, command, contexts, help): def __init__(self, key, command, contexts, help):
self.key, self.command, self.contexts = key, command, sorted(contexts) self.key, self.command, self.contexts = key, command, sorted(contexts)
@ -122,3 +129,13 @@ class Keymap:
if b: if b:
return self.executor(b.command) return self.executor(b.command)
return key return key
def handle_only(self, context: str, key: str) -> typing.Optional[str]:
"""
Like handle, but ignores global bindings. Returns the key if it has
not been handled, or None.
"""
b = self.get(context, key)
if b:
return self.executor(b.command)
return key

View File

@ -117,6 +117,7 @@ class OptionListWalker(urwid.ListWalker):
def stop_editing(self): def stop_editing(self):
self.editing = False self.editing = False
self.focus_obj = self._get(self.index, False) self.focus_obj = self._get(self.index, False)
self.set_focus(self.index)
self._modified() self._modified()
def get_edit_text(self): def get_edit_text(self):

View File

@ -5,6 +5,7 @@ import urwid
from mitmproxy.tools.console import signals from mitmproxy.tools.console import signals
from mitmproxy.tools.console import grideditor from mitmproxy.tools.console import grideditor
from mitmproxy.tools.console import layoutwidget from mitmproxy.tools.console import layoutwidget
from mitmproxy.tools.console import keymap
class SimpleOverlay(urwid.Overlay, layoutwidget.LayoutWidget): class SimpleOverlay(urwid.Overlay, layoutwidget.LayoutWidget):
@ -114,13 +115,21 @@ class Chooser(urwid.WidgetWrap, layoutwidget.LayoutWidget):
return True return True
def keypress(self, size, key): def keypress(self, size, key):
key = self.master.keymap.handle("chooser", key) key = self.master.keymap.handle_only("chooser", key)
if key == "m_select": if key == "m_select":
self.callback(self.choices[self.walker.index]) self.callback(self.choices[self.walker.index])
signals.pop_view_state.send(self) signals.pop_view_state.send(self)
return
elif key == "esc": elif key == "esc":
signals.pop_view_state.send(self) signals.pop_view_state.send(self)
return super().keypress(size, key) return
binding = self.master.keymap.get("global", key)
# This is extremely awkward. We need a better way to match nav keys only.
if binding and binding.command.startswith("console.nav"):
self.master.keymap.handle("global", key)
elif key in keymap.navkeys:
return super().keypress(size, key)
class OptionsOverlay(urwid.WidgetWrap, layoutwidget.LayoutWidget): class OptionsOverlay(urwid.WidgetWrap, layoutwidget.LayoutWidget):

View File

@ -135,6 +135,11 @@ def test_cut():
with pytest.raises(exceptions.CommandError): with pytest.raises(exceptions.CommandError):
assert c.cut(tflows, ["__dict__"]) == [[""]] assert c.cut(tflows, ["__dict__"]) == [[""]]
with taddons.context():
tflows = [tflow.tflow(resp=False)]
assert c.cut(tflows, ["response.reason"]) == [[""]]
assert c.cut(tflows, ["response.header[key]"]) == [[""]]
c = cut.Cut() c = cut.Cut()
with taddons.context(): with taddons.context():
tflows = [tflow.ttcpflow()] tflows = [tflow.ttcpflow()]