Repository: mitmproxy/mitmproxy

Issue:
Global display mode key not working anymore
`M` does not allow me to set global display mode, `m` still works on a per-request/response basis. Tried from within the flow view.

Mitmproxy Version: The hottest and latest.

---

I don't mind having a look at this once I have some time

Cannot add response from UI
##### Steps to reproduce the problem:
1. `mitmproxy -n`
2. Create new request (`n`, `g`, `enter`)
3. Tab to the response.
4. Press `e`.
##### What is the expected behavior?

As per the UI:

> No response. Press e and edit any aspect to add one.
##### What went wrong?

Nothing happens.

---

Mitmproxy Version: master
Operating System: Win 10 x64



Discussion:
This is now in options.

@cortesi: Re-add functionality or remove from flowview?

Let's keep this in options. We have an over-abundance of direct key bindings at the moment. 



Resolving patch:
diff --git a/mitmproxy/console/flowview.py b/mitmproxy/console/flowview.py
--- a/mitmproxy/console/flowview.py
+++ b/mitmproxy/console/flowview.py
@@ -39,7 +39,7 @@ def _mkhelp():
         ("d", "delete flow"),
         ("e", "edit request/response"),
         ("f", "load full body data"),
-        ("m", "change body display mode for this entity"),
+        ("m", "change body display mode for this entity\n(default mode can be changed in the options)"),
         (None,
          common.highlight_key("automatic", "a") +
          [("text", ": automatic detection")]
@@ -76,7 +76,6 @@ def _mkhelp():
          common.highlight_key("xml", "x") +
          [("text", ": XML")]
          ),
-        ("M", "change default body display mode"),
         ("E", "export flow to file"),
         ("r", "replay request"),
         ("V", "revert changes to request"),
@@ -137,14 +136,15 @@ class FlowView(tabs.Tabs):
 
     def __init__(self, master, state, flow, tab_offset):
         self.master, self.state, self.flow = master, state, flow
-        tabs.Tabs.__init__(self,
-                           [
-                               (self.tab_request, self.view_request),
-                               (self.tab_response, self.view_response),
-                               (self.tab_details, self.view_details),
-                           ],
-                           tab_offset
-                           )
+        super(FlowView, self).__init__(
+            [
+                (self.tab_request, self.view_request),
+                (self.tab_response, self.view_response),
+                (self.tab_details, self.view_details),
+            ],
+            tab_offset
+        )
+
         self.show()
         self.last_displayed_body = None
         signals.flow_change.connect(self.sig_flow_change)
@@ -404,7 +404,7 @@ def edit(self, part):
             if not self.flow.response:
                 self.flow.response = models.HTTPResponse(
                     self.flow.request.http_version,
-                    200, "OK", Headers(), ""
+                    200, b"OK", Headers(), b""
                 )
                 self.flow.response.reply = controller.DummyReply()
             message = self.flow.response
@@ -533,24 +533,23 @@ def change_this_display_mode(self, t):
         signals.flow_change.send(self, flow = self.flow)
 
     def keypress(self, size, key):
+        conn = None  # type: Optional[Union[models.HTTPRequest, models.HTTPResponse]]
+        if self.tab_offset == TAB_REQ:
+            conn = self.flow.request
+        elif self.tab_offset == TAB_RESP:
+            conn = self.flow.response
+
         key = super(self.__class__, self).keypress(size, key)
 
+        # Special case: Space moves over to the next flow.
+        # We need to catch that before applying common.shortcuts()
         if key == " ":
             self.view_next_flow(self.flow)
             return
 
         key = common.shortcuts(key)
-
-        conn = None  # type: Optional[Union[models.HTTPRequest, models.HTTPResponse]]
-        if self.tab_offset == TAB_REQ:
-            conn = self.flow.request
-        elif self.tab_offset == TAB_RESP:
-            conn = self.flow.response
-        else:
-            conn = None
-
         if key in ("up", "down", "page up", "page down"):
-            # Why doesn't this just work??
+            # Pass scroll events to the wrapped widget
             self._w.keypress(size, key)
         elif key == "a":
             self.flow.accept_intercept(self.master)
@@ -581,12 +580,12 @@ def keypress(self, size, key):
                 signals.status_message.send(message=r)
             signals.flow_change.send(self, flow = self.flow)
         elif key == "V":
-            if not self.flow.modified():
+            if self.flow.modified():
+                self.state.revert(self.flow)
+                signals.flow_change.send(self, flow = self.flow)
+                signals.status_message.send(message="Reverted.")
+            else:
                 signals.status_message.send(message="Flow not modified.")
-                return
-            self.state.revert(self.flow)
-            signals.flow_change.send(self, flow = self.flow)
-            signals.status_message.send(message="Reverted.")
         elif key == "W":
             signals.status_prompt_path.send(
                 prompt = "Save this flow",
@@ -599,128 +598,128 @@ def keypress(self, size, key):
                 callback = self.master.run_script_once,
                 args = (self.flow,)
             )
-
-        if not conn and key in set(list("befgmxvzEC")):
+        elif key == "e":
+            if self.tab_offset == TAB_REQ:
+                signals.status_prompt_onekey.send(
+                    prompt="Edit request",
+                    keys=(
+                        ("cookies", "c"),
+                        ("query", "q"),
+                        ("path", "p"),
+                        ("url", "u"),
+                        ("header", "h"),
+                        ("form", "f"),
+                        ("raw body", "r"),
+                        ("method", "m"),
+                    ),
+                    callback=self.edit
+                )
+            elif self.tab_offset == TAB_RESP:
+                signals.status_prompt_onekey.send(
+                    prompt="Edit response",
+                    keys=(
+                        ("cookies", "c"),
+                        ("code", "o"),
+                        ("message", "m"),
+                        ("header", "h"),
+                        ("raw body", "r"),
+                    ),
+                    callback=self.edit
+                )
+            else:
+                signals.status_message.send(
+                    message="Tab to the request or response",
+                    expire=1
+                )
+        elif key in "bfgmxvzEC" and not conn:
             signals.status_message.send(
                 message = "Tab to the request or response",
                 expire = 1
             )
-        elif conn:
-            if key == "b":
-                if self.tab_offset == TAB_REQ:
-                    common.ask_save_body(
-                        "q", self.flow
-                    )
+            return
+        elif key == "b":
+            if self.tab_offset == TAB_REQ:
+                common.ask_save_body("q", self.flow)
+            else:
+                common.ask_save_body("s", self.flow)
+        elif key == "f":
+            signals.status_message.send(message="Loading all body data...")
+            self.state.add_flow_setting(
+                self.flow,
+                (self.tab_offset, "fullcontents"),
+                True
+            )
+            signals.flow_change.send(self, flow = self.flow)
+            signals.status_message.send(message="")
+        elif key == "m":
+            p = list(contentviews.view_prompts)
+            p.insert(0, ("Clear", "C"))
+            signals.status_prompt_onekey.send(
+                self,
+                prompt = "Display mode",
+                keys = p,
+                callback = self.change_this_display_mode
+            )
+        elif key == "E":
+            if self.tab_offset == TAB_REQ:
+                scope = "q"
+            else:
+                scope = "s"
+            signals.status_prompt_onekey.send(
+                self,
+                prompt = "Export to file",
+                keys = [(e[0], e[1]) for e in export.EXPORTERS],
+                callback = common.export_to_clip_or_file,
+                args = (scope, self.flow, common.ask_save_path)
+            )
+        elif key == "C":
+            if self.tab_offset == TAB_REQ:
+                scope = "q"
+            else:
+                scope = "s"
+            signals.status_prompt_onekey.send(
+                self,
+                prompt = "Export to clipboard",
+                keys = [(e[0], e[1]) for e in export.EXPORTERS],
+                callback = common.export_to_clip_or_file,
+                args = (scope, self.flow, common.copy_to_clipboard_or_prompt)
+            )
+        elif key == "x":
+            conn.content = None
+            signals.flow_change.send(self, flow=self.flow)
+        elif key == "v":
+            if conn.raw_content:
+                t = conn.headers.get("content-type")
+                if "EDITOR" in os.environ or "PAGER" in os.environ:
+                    self.master.spawn_external_viewer(conn.get_content(strict=False), t)
                 else:
-                    common.ask_save_body(
-                        "s", self.flow
-                    )
-            elif key == "e":
-                if self.tab_offset == TAB_REQ:
-                    signals.status_prompt_onekey.send(
-                        prompt = "Edit request",
-                        keys = (
-                            ("cookies", "c"),
-                            ("query", "q"),
-                            ("path", "p"),
-                            ("url", "u"),
-                            ("header", "h"),
-                            ("form", "f"),
-                            ("raw body", "r"),
-                            ("method", "m"),
-                        ),
-                        callback = self.edit
+                    signals.status_message.send(
+                        message = "Error! Set $EDITOR or $PAGER."
                     )
-                else:
-                    signals.status_prompt_onekey.send(
-                        prompt = "Edit response",
-                        keys = (
-                            ("cookies", "c"),
-                            ("code", "o"),
-                            ("message", "m"),
-                            ("header", "h"),
-                            ("raw body", "r"),
-                        ),
-                        callback = self.edit
+        elif key == "z":
+            self.flow.backup()
+            e = conn.headers.get("content-encoding", "identity")
+            if e != "identity":
+                try:
+                    conn.decode()
+                except ValueError:
+                    signals.status_message.send(
+                        message = "Could not decode - invalid data?"
                     )
-                key = None
-            elif key == "f":
-                signals.status_message.send(message="Loading all body data...")
-                self.state.add_flow_setting(
-                    self.flow,
-                    (self.tab_offset, "fullcontents"),
-                    True
-                )
-                signals.flow_change.send(self, flow = self.flow)
-                signals.status_message.send(message="")
-            elif key == "m":
-                p = list(contentviews.view_prompts)
-                p.insert(0, ("Clear", "C"))
-                signals.status_prompt_onekey.send(
-                    self,
-                    prompt = "Display mode",
-                    keys = p,
-                    callback = self.change_this_display_mode
-                )
-                key = None
-            elif key == "E":
-                if self.tab_offset == TAB_REQ:
-                    scope = "q"
-                else:
-                    scope = "s"
-                signals.status_prompt_onekey.send(
-                    self,
-                    prompt = "Export to file",
-                    keys = [(e[0], e[1]) for e in export.EXPORTERS],
-                    callback = common.export_to_clip_or_file,
-                    args = (scope, self.flow, common.ask_save_path)
-                )
-            elif key == "C":
-                if self.tab_offset == TAB_REQ:
-                    scope = "q"
-                else:
-                    scope = "s"
+            else:
                 signals.status_prompt_onekey.send(
-                    self,
-                    prompt = "Export to clipboard",
-                    keys = [(e[0], e[1]) for e in export.EXPORTERS],
-                    callback = common.export_to_clip_or_file,
-                    args = (scope, self.flow, common.copy_to_clipboard_or_prompt)
+                    prompt = "Select encoding: ",
+                    keys = (
+                        ("gzip", "z"),
+                        ("deflate", "d"),
+                    ),
+                    callback = self.encode_callback,
+                    args = (conn,)
                 )
-            elif key == "x":
-                conn.content = None
-                signals.flow_change.send(self, flow=self.flow)
-            elif key == "v":
-                if conn.raw_content:
-                    t = conn.headers.get("content-type")
-                    if "EDITOR" in os.environ or "PAGER" in os.environ:
-                        self.master.spawn_external_viewer(conn.get_content(strict=False), t)
-                    else:
-                        signals.status_message.send(
-                            message = "Error! Set $EDITOR or $PAGER."
-                        )
-            elif key == "z":
-                self.flow.backup()
-                e = conn.headers.get("content-encoding", "identity")
-                if e != "identity":
-                    try:
-                        conn.decode()
-                    except ValueError:
-                        signals.status_message.send(
-                            message = "Could not decode - invalid data?"
-                        )
-                else:
-                    signals.status_prompt_onekey.send(
-                        prompt = "Select encoding: ",
-                        keys = (
-                            ("gzip", "z"),
-                            ("deflate", "d"),
-                        ),
-                        callback = self.encode_callback,
-                        args = (conn,)
-                    )
-                signals.flow_change.send(self, flow = self.flow)
-        return key
+            signals.flow_change.send(self, flow = self.flow)
+        else:
+            # Key is not handled here.
+            return key
 
     def encode_callback(self, key, conn):
         encoding_map = {
diff --git a/mitmproxy/console/tabs.py b/mitmproxy/console/tabs.py
--- a/mitmproxy/console/tabs.py
+++ b/mitmproxy/console/tabs.py
@@ -25,7 +25,7 @@ def mouse_event(self, size, event, button, col, row, focus):
 class Tabs(urwid.WidgetWrap):
 
     def __init__(self, tabs, tab_offset=0):
-        urwid.WidgetWrap.__init__(self, "")
+        super(Tabs, self).__init__("")
         self.tab_offset = tab_offset
         self.tabs = tabs
         self.show()


Test patch: