diff options
| -rw-r--r-- | CHANGELOG | 4 | ||||
| -rw-r--r-- | README.rst | 6 | ||||
| -rw-r--r-- | docs/src/content/concepts-modes.md | 8 | ||||
| -rw-r--r-- | docs/src/content/overview-tools.md | 2 | ||||
| -rw-r--r-- | mitmproxy/net/http/cookies.py | 14 | ||||
| -rw-r--r-- | mitmproxy/tools/console/commander/commander.py | 22 | ||||
| -rw-r--r-- | mitmproxy/tools/console/master.py | 47 | ||||
| -rw-r--r-- | setup.py | 2 | ||||
| -rw-r--r-- | test/mitmproxy/net/http/test_cookies.py | 21 | ||||
| -rw-r--r-- | test/mitmproxy/test_command.py | 16 | 
10 files changed, 109 insertions, 33 deletions
| @@ -1,3 +1,7 @@ +23 February 2018: mitmproxy 3.0 + +  * Fix a quote-related issue affecting the mitmproxy console command prompt +  22 February 2018: mitmproxy 3.0    ** Major Changes ** @@ -95,6 +95,12 @@ requirements installed, and you can run the full test suite (including tests for      tox +To run complete tests with a full coverage report, you can use the following command: + +.. code-block:: bash + +    tox -- --verbose --cov-report=term +  For speedier testing, we recommend you run `pytest`_ directly on individual test files or folders:  .. code-block:: bash diff --git a/docs/src/content/concepts-modes.md b/docs/src/content/concepts-modes.md index 86bb7b0f..7a0b835a 100644 --- a/docs/src/content/concepts-modes.md +++ b/docs/src/content/concepts-modes.md @@ -157,20 +157,20 @@ There are various use-cases:      example.com domain and get all requests recorded in mitmproxy.  - Say you have some toy project that should get SSL support. Simply set up      mitmproxy as a reverse proxy on port 443 and you're done (`mitmdump -p 443 -    -R http://localhost:80/`). Mitmproxy auto-detects TLS traffic and intercepts +    --mode reverse:http://localhost:80/`). Mitmproxy auto-detects TLS traffic and intercepts      it dynamically. There are better tools for this specific task, but mitmproxy      is very quick and simple way to set up an SSL-speaking server.  - Want to add a non-SSL-capable compression proxy in front of your server? You -    could even spawn a mitmproxy instance that terminates SSL (`-R http://...`), +    could even spawn a mitmproxy instance that terminates SSL (`--mode reverse:http://...`),      point it to the compression proxy and let the compression proxy point to a -    SSL-initiating mitmproxy (`-R https://...`), which then points to the real +    SSL-initiating mitmproxy (`--mode reverse:https://...`), which then points to the real      server. As you see, it's a fairly flexible thing.  ### Host Header  In reverse proxy mode, mitmproxy automatically rewrites the Host header to match  the upstream server. This allows mitmproxy to easily connect to existing -endpoints on the open web (e.g. `mitmproxy -R https://example.com`). You can +endpoints on the open web (e.g. `mitmproxy --mode reverse:https://example.com`). You can  disable this behaviour with the `keep_host_header` option.  However, keep in mind that absolute URLs within the returned document or HTTP diff --git a/docs/src/content/overview-tools.md b/docs/src/content/overview-tools.md index 7612383a..0200e899 100644 --- a/docs/src/content/overview-tools.md +++ b/docs/src/content/overview-tools.md @@ -8,7 +8,7 @@ menu:  # Overview -You should thin of the mitmproxy project's tools as a set of front-ends that +You should think of the mitmproxy project's tools as a set of front-ends that  expose the same underlying functionality. We aim to have feature parity across  all of our tooling, and all tools share a common configuration mechanism and  most command-line options. diff --git a/mitmproxy/net/http/cookies.py b/mitmproxy/net/http/cookies.py index 7bef8757..39e8d60f 100644 --- a/mitmproxy/net/http/cookies.py +++ b/mitmproxy/net/http/cookies.py @@ -159,13 +159,17 @@ def _read_set_cookie_pairs(s: str, off=0) -> Tuple[List[TPairs], int]:                  if len(rhs) <= 3:                      trail, off = _read_value(s, off + 1, ";,")                      rhs = rhs + "," + trail -        if rhs or lhs: + +            # as long as there's a "=", we consider it a pair +            pairs.append([lhs, rhs]) + +        elif lhs:              pairs.append([lhs, rhs]) -            # comma marks the beginning of a new cookie -            if off < len(s) and s[off] == ",": -                cookies.append(pairs) -                pairs = [] +        # comma marks the beginning of a new cookie +        if off < len(s) and s[off] == ",": +            cookies.append(pairs) +            pairs = []          off += 1 diff --git a/mitmproxy/tools/console/commander/commander.py b/mitmproxy/tools/console/commander/commander.py index 566c42e6..f3e499f8 100644 --- a/mitmproxy/tools/console/commander/commander.py +++ b/mitmproxy/tools/console/commander/commander.py @@ -68,6 +68,21 @@ class CommandBuffer:          else:              self._cursor = x +    def maybequote(self, value): +        if " " in value and not value.startswith("\""): +            return "\"%s\"" % value +        return value + +    def parse_quoted(self, txt): +        parts, remhelp = self.master.commands.parse_partial(txt) +        for i, p in enumerate(parts): +            parts[i] = mitmproxy.command.ParseResult( +                value = self.maybequote(p.value), +                type = p.type, +                valid = p.valid +            ) +        return parts, remhelp +      def render(self):          """              This function is somewhat tricky - in order to make the cursor @@ -75,7 +90,7 @@ class CommandBuffer:              character-for-character offset match in the rendered output, up              to the cursor. Beyond that, we can add stuff.          """ -        parts, remhelp = self.master.commands.parse_partial(self.text) +        parts, remhelp = self.parse_quoted(self.text)          ret = []          for p in parts:              if p.valid: @@ -95,8 +110,9 @@ class CommandBuffer:          return ret      def flatten(self, txt): -        parts, _ = self.master.commands.parse_partial(txt) -        return " ".join([x.value for x in parts]) +        parts, _ = self.parse_quoted(txt) +        ret = [x.value for x in parts] +        return " ".join(ret)      def left(self) -> None:          self.cursor = self.cursor - 1 diff --git a/mitmproxy/tools/console/master.py b/mitmproxy/tools/console/master.py index da35047e..76d8724a 100644 --- a/mitmproxy/tools/console/master.py +++ b/mitmproxy/tools/console/master.py @@ -10,6 +10,7 @@ import sys  import tempfile  import traceback  import typing  # noqa +import contextlib  import urwid @@ -102,6 +103,16 @@ class ConsoleMaster(master.Master):              return callback(*args)          self.loop.set_alarm_in(seconds, cb) +    @contextlib.contextmanager +    def uistopped(self): +        self.loop.stop() +        try: +            yield +        finally: +            self.loop.start() +            self.loop.screen_size = None +            self.loop.draw_screen() +      def spawn_editor(self, data):          text = not isinstance(data, bytes)          fd, name = tempfile.mkstemp('', "mproxy", text=text) @@ -111,17 +122,16 @@ class ConsoleMaster(master.Master):          c = os.environ.get("EDITOR") or "vi"          cmd = shlex.split(c)          cmd.append(name) -        self.ui.stop() -        try: -            subprocess.call(cmd) -        except: -            signals.status_message.send( -                message="Can't start editor: %s" % " ".join(c) -            ) -        else: -            with open(name, "r" if text else "rb") as f: -                data = f.read() -        self.ui.start() +        with self.uistopped(): +            try: +                subprocess.call(cmd) +            except: +                signals.status_message.send( +                    message="Can't start editor: %s" % " ".join(c) +                ) +            else: +                with open(name, "r" if text else "rb") as f: +                    data = f.read()          os.unlink(name)          return data @@ -153,14 +163,13 @@ class ConsoleMaster(master.Master):                  c = "less"              cmd = shlex.split(c)              cmd.append(name) -        self.ui.stop() -        try: -            subprocess.call(cmd, shell=shell) -        except: -            signals.status_message.send( -                message="Can't start external viewer: %s" % " ".join(c) -            ) -        self.ui.start() +        with self.uistopped(): +            try: +                subprocess.call(cmd, shell=shell) +            except: +                signals.status_message.send( +                    message="Can't start external viewer: %s" % " ".join(c) +                )          os.unlink(name)      def set_palette(self, opts, updated): @@ -75,7 +75,7 @@ setup(          "pyasn1>=0.3.1,<0.5",          "pyOpenSSL>=17.5,<17.6",          "pyparsing>=2.1.3, <2.3", -        "pyperclip>=1.5.22, <1.7", +        "pyperclip>=1.6.0, <1.7",          "requests>=2.9.1, <3",          "ruamel.yaml>=0.13.2, <0.16",          "sortedcontainers>=1.5.4, <1.6", diff --git a/test/mitmproxy/net/http/test_cookies.py b/test/mitmproxy/net/http/test_cookies.py index e12b0f00..74233cca 100644 --- a/test/mitmproxy/net/http/test_cookies.py +++ b/test/mitmproxy/net/http/test_cookies.py @@ -143,6 +143,27 @@ def test_cookie_roundtrips():  def test_parse_set_cookie_pairs():      pairs = [          [ +            "=", +            [[ +                ["", ""] +            ]] +        ], +        [ +            "=;foo=bar", +            [[ +                ["", ""], +                ["foo", "bar"] +            ]] +        ], +        [ +            "=;=;foo=bar", +            [[ +                ["", ""], +                ["", ""], +                ["foo", "bar"] +            ]] +        ], +        [              "=uno",              [[                  ["", "uno"] diff --git a/test/mitmproxy/test_command.py b/test/mitmproxy/test_command.py index c777192d..e2b80753 100644 --- a/test/mitmproxy/test_command.py +++ b/test/mitmproxy/test_command.py @@ -211,6 +211,22 @@ class TestCommand:                  ],                  []              ], +            [ +                "flow \"one two", +                [ +                    command.ParseResult(value = "flow", type = mitmproxy.types.Cmd, valid = True), +                    command.ParseResult(value = "\"one two", type = flow.Flow, valid = False), +                ], +                ["str"] +            ], +            [ +                "flow \"one two\"", +                [ +                    command.ParseResult(value = "flow", type = mitmproxy.types.Cmd, valid = True), +                    command.ParseResult(value = "one two", type = flow.Flow, valid = False), +                ], +                ["str"] +            ],          ]          with taddons.context() as tctx:              tctx.master.addons.add(TAddon()) | 
