From 685487d33ca07d23c7fd9adc49b6520c9aa19273 Mon Sep 17 00:00:00 2001 From: Aldo Cortesi Date: Sun, 30 Apr 2017 22:45:31 +1200 Subject: [PATCH] commands: view.order.options, view.marked.toggle, view.create And use these commands to remove the last hard-coded keybindings from flow list. This means the flow list is now 100% command-driven, which is very exciting. --- mitmproxy/addons/view.py | 24 +++++++++- mitmproxy/master.py | 20 --------- mitmproxy/tools/console/flowlist.py | 59 +------------------------ mitmproxy/tools/console/master.py | 12 +++++ test/mitmproxy/addons/test_view.py | 16 +++++++ test/mitmproxy/console/test_flowlist.py | 8 ---- test/mitmproxy/test_flow.py | 4 -- 7 files changed, 52 insertions(+), 91 deletions(-) diff --git a/mitmproxy/addons/view.py b/mitmproxy/addons/view.py index a629ceb94..c9c9cbed7 100644 --- a/mitmproxy/addons/view.py +++ b/mitmproxy/addons/view.py @@ -19,6 +19,7 @@ import mitmproxy.flow from mitmproxy import flowfilter from mitmproxy import exceptions from mitmproxy import command +from mitmproxy import connections from mitmproxy import ctx from mitmproxy import io from mitmproxy import http # noqa @@ -201,7 +202,18 @@ class View(collections.Sequence): self.sig_view_refresh.send(self) # API - def toggle_marked(self): + @command.command("view.order.options") + def order_options(self) -> typing.Sequence[str]: + """ + A list of all the orders we support. + """ + return list(sorted(self.orders.keys())) + + @command.command("view.marked.toggle") + def toggle_marked(self) -> None: + """ + Toggle whether to show marked views only. + """ self.show_marked = not self.show_marked self._refilter() @@ -341,6 +353,16 @@ class View(collections.Sequence): raise exceptions.CommandError("Invalid flow filter: %s" % spec) return [i for i in self._store.values() if filt(i)] + @command.command("view.create") + def create(self, method: str, url: str) -> None: + req = http.HTTPRequest.make(method.upper(), url) + c = connections.ClientConnection.make_dummy(("", 0)) + s = connections.ServerConnection.make_dummy((req.host, req.port)) + f = http.HTTPFlow(c, s) + f.request = req + f.request.headers["Host"] = req.host + self.add([f]) + # Event handlers def configure(self, updated): if "view_filter" in updated: diff --git a/mitmproxy/master.py b/mitmproxy/master.py index 2a032c4a0..d21a323e1 100644 --- a/mitmproxy/master.py +++ b/mitmproxy/master.py @@ -7,7 +7,6 @@ from mitmproxy import options from mitmproxy import controller from mitmproxy import eventsequence from mitmproxy import exceptions -from mitmproxy import connections from mitmproxy import command from mitmproxy import http from mitmproxy import log @@ -78,9 +77,6 @@ class Master: self.start() try: while not self.should_exit.is_set(): - # Don't choose a very small timeout in Python 2: - # https://github.com/mitmproxy/mitmproxy/issues/443 - # TODO: Lower the timeout value if we move to Python 3. self.tick(0.1) finally: self.shutdown() @@ -109,22 +105,6 @@ class Master: self.should_exit.set() self.addons.trigger("done") - def create_request(self, method, url): - """ - Create a new artificial and minimalist request also adds it to flowlist. - - Raises: - ValueError, if the url is malformed. - """ - req = http.HTTPRequest.make(method, url) - c = connections.ClientConnection.make_dummy(("", 0)) - s = connections.ServerConnection.make_dummy((req.host, req.port)) - - f = http.HTTPFlow(c, s) - f.request = req - self.load_flow(f) - return f - def load_flow(self, f): """ Loads a flow diff --git a/mitmproxy/tools/console/flowlist.py b/mitmproxy/tools/console/flowlist.py index 4ffed15f6..7400c16c6 100644 --- a/mitmproxy/tools/console/flowlist.py +++ b/mitmproxy/tools/console/flowlist.py @@ -2,7 +2,6 @@ import urwid from mitmproxy.tools.console import common from mitmproxy.tools.console import signals -from mitmproxy.addons import view import mitmproxy.tools.console.master # noqa @@ -190,62 +189,6 @@ class FlowListBox(urwid.ListBox): self.master = master # type: "mitmproxy.tools.console.master.ConsoleMaster" super().__init__(FlowListWalker(master)) - def get_method_raw(self, k): - if k: - self.get_url(k) - - def get_method(self, k): - if k == "e": - signals.status_prompt.send( - self, - prompt = "Method", - text = "", - callback = self.get_method_raw - ) - else: - method = "" - for i in common.METHOD_OPTIONS: - if i[1] == k: - method = i[0].upper() - self.get_url(method) - - def get_url(self, method): - signals.status_prompt.send( - prompt = "URL", - text = "http://www.example.com/", - callback = self.new_request, - args = (method,) - ) - - def new_request(self, url, method): - try: - f = self.master.create_request(method, url) - except ValueError as e: - signals.status_message.send(message = "Invalid URL: " + str(e)) - return - self.master.view.focus.flow = f - def keypress(self, size, key): key = common.shortcuts(key) - if key == "M": - self.master.view.toggle_marked() - elif key == "n": - signals.status_prompt_onekey.send( - prompt = "Method", - keys = common.METHOD_OPTIONS, - callback = self.get_method - ) - elif key == "o": - orders = [(i[1], i[0]) for i in view.orders] - lookup = dict([(i[0], i[1]) for i in view.orders]) - - def change_order(k): - self.master.options.console_order = lookup[k] - - signals.status_prompt_onekey.send( - prompt = "Order", - keys = orders, - callback = change_order - ) - else: - return urwid.ListBox.keypress(self, size, key) + return urwid.ListBox.keypress(self, size, key) diff --git a/mitmproxy/tools/console/master.py b/mitmproxy/tools/console/master.py index 457f3721f..5b6d9bcbb 100644 --- a/mitmproxy/tools/console/master.py +++ b/mitmproxy/tools/console/master.py @@ -189,6 +189,18 @@ def default_keymap(km): km.add("l", "console.command cut.clip ", context="flowlist") km.add("L", "console.command view.load ", context="flowlist") km.add("m", "flow.mark.toggle @focus", context="flowlist") + km.add("M", "view.marked.toggle", context="flowlist") + km.add( + "n", + "console.command view.create get https://google.com", + context="flowlist" + ) + km.add( + "o", + "console.choose Order view.order.options " + "set console_order={choice}", + context="flowlist" + ) km.add("r", "replay.client @focus", context="flowlist") km.add("S", "console.command 'replay.server '") km.add("v", "set console_order_reversed=toggle", context="flowlist") diff --git a/test/mitmproxy/addons/test_view.py b/test/mitmproxy/addons/test_view.py index eddcb04c9..1724da493 100644 --- a/test/mitmproxy/addons/test_view.py +++ b/test/mitmproxy/addons/test_view.py @@ -137,6 +137,22 @@ def tdump(path, flows): w.add(i) +def test_create(): + v = view.View() + with taddons.context(): + v.create("get", "http://foo.com") + assert len(v) == 1 + assert v[0].request.url == "http://foo.com/" + v.create("get", "http://foo.com") + assert len(v) == 2 + + +def test_orders(): + v = view.View() + with taddons.context(): + assert v.order_options() + + def test_load(tmpdir): path = str(tmpdir.join("path")) v = view.View() diff --git a/test/mitmproxy/console/test_flowlist.py b/test/mitmproxy/console/test_flowlist.py index d63dab1c0..6d82749d4 100644 --- a/test/mitmproxy/console/test_flowlist.py +++ b/test/mitmproxy/console/test_flowlist.py @@ -1,4 +1,3 @@ -from unittest import mock import urwid import mitmproxy.tools.console.flowlist as flowlist @@ -14,13 +13,6 @@ class TestFlowlist: o = options.Options(**opts) return console.master.ConsoleMaster(o, proxy.DummyServer()) - def test_new_request(self): - m = self.mkmaster() - x = flowlist.FlowListBox(m) - with mock.patch('mitmproxy.tools.console.signals.status_message.send') as mock_thing: - x.new_request("nonexistent url", "GET") - mock_thing.assert_called_once_with(message="Invalid URL: No hostname given") - def test_logbuffer_set_focus(self): m = self.mkmaster() b = flowlist.LogBufferBox(m) diff --git a/test/mitmproxy/test_flow.py b/test/mitmproxy/test_flow.py index 78f893c08..19f0e7d9a 100644 --- a/test/mitmproxy/test_flow.py +++ b/test/mitmproxy/test_flow.py @@ -113,10 +113,6 @@ class TestFlowMaster: with pytest.raises(Exception, match="live"): fm.replay_request(f) - def test_create_flow(self): - fm = master.Master(None, DummyServer()) - assert fm.create_request("GET", "http://example.com/") - def test_all(self): s = tservers.TestState() fm = master.Master(None, DummyServer())