diff options
| author | Aldo Cortesi <aldo@nullcube.com> | 2014-10-26 10:50:32 +1300 | 
|---|---|---|
| committer | Aldo Cortesi <aldo@nullcube.com> | 2014-10-26 10:50:32 +1300 | 
| commit | fc1fc80469dca11ff0241c4b263e4b39e5506ddd (patch) | |
| tree | 80432368738828a096561a0a676e82fe46f0148d | |
| parent | c00ae41486de06192865f8539da0f00985a16a90 (diff) | |
| download | mitmproxy-fc1fc80469dca11ff0241c4b263e4b39e5506ddd.tar.gz mitmproxy-fc1fc80469dca11ff0241c4b263e4b39e5506ddd.tar.bz2 mitmproxy-fc1fc80469dca11ff0241c4b263e4b39e5506ddd.zip | |
Allow nesting of pathod response specs in pathoc specs
This opens the door to really neat, repeatable, client-side driven
fuzzing, especially of proxies.
| -rw-r--r-- | libpathod/language.py | 48 | ||||
| -rw-r--r-- | test/test_language.py | 25 | ||||
| -rw-r--r-- | test/test_pathoc.py | 5 | 
3 files changed, 73 insertions, 5 deletions
| diff --git a/libpathod/language.py b/libpathod/language.py index 56cbc18b..d8e87145 100644 --- a/libpathod/language.py +++ b/libpathod/language.py @@ -1,3 +1,4 @@ +from __future__ import print_function  import operator  import string  import random @@ -6,7 +7,6 @@ import os  import time  import copy  import abc -from email.utils import formatdate  import contrib.pyparsing as pp  from netlib import http_status, tcp, http_uastrings @@ -527,6 +527,43 @@ class Body(_Component):          return Body(self.value.freeze(settings)) +class PathodSpec(_Token): +    def __init__(self, value): +        self.value = value +        try: +            self.parsed = Response( +                Response.expr().parseString( +                    value.val, +                    parseAll=True +                ) +            ) +        except pp.ParseException, v: +            raise ParseException(v.msg, v.line, v.col) + +    @classmethod +    def expr(klass): +        e = pp.Literal("s").suppress() +        e = e + ValueLiteral.expr() +        return e.setParseAction(lambda x: klass(*x)) + +    def values(self, settings): +        return [ +            self.value.get_generator(settings), +        ] + +    def quote(self, s): +        quotechar = s[0] +        s = s[1:-1] +        s = s.replace(quotechar, "\\" + quotechar) +        return quotechar + s + quotechar + +    def spec(self): +        return "s%s"%(self.quote(self.value.spec())) + +    def freeze(self, settings): +        return PathodSpec(ValueLiteral(self.parsed.freeze(settings).spec())) + +  class Path(_Component):      def __init__(self, value):          if isinstance(value, basestring): @@ -934,7 +971,8 @@ class Request(_Message):          InjectAt,          ShortcutContentType,          ShortcutUserAgent, -        Raw +        Raw, +        PathodSpec,      )      logattrs = ["method", "path", "body"] @@ -946,10 +984,16 @@ class Request(_Message):      def path(self):          return self._get_token(Path) +    @property +    def pathodspec(self): +        return self._get_token(PathodSpec) +      def preamble(self, settings):          v = self.method.values(settings)          v.append(" ")          v.extend(self.path.values(settings)) +        if self.pathodspec: +            v.append(self.pathodspec.parsed.spec())          v.append(" ")          v.append(self.version)          return v diff --git a/test/test_language.py b/test/test_language.py index 0818c587..f3cfa5a9 100644 --- a/test/test_language.py +++ b/test/test_language.py @@ -223,6 +223,29 @@ class TestMisc:          s = v.spec()          assert s == e.parseString(s)[0].spec() +    def test_pathodspec(self): +        e = language.PathodSpec.expr() +        v = e.parseString("s'200'")[0] +        assert v.value.val == "200" +        tutils.raises( +            language.ParseException, +            e.parseString, +            "s'foo'" +        ) + +        v = e.parseString('s"200:b@1"')[0] +        assert "@1" in v.spec() +        f = v.freeze({}) +        assert "@1" not in f.spec() + +        r = parse_request('GET:"/foo":s"200"') +        assert "200" in r.preamble({}) + +        f = r.freeze({}) +        assert parse_request(f.spec()) + + +      def test_code(self):          e = language.Code.expr()          v = e.parseString("200")[0] @@ -661,14 +684,12 @@ class TestResponse:          language.serve(r, s, {})          v = s.getvalue()          assert "Content-Length" in v -        assert "Date" in v          s = cStringIO.StringIO()          r = language.parse_response("400:b'foo':r")          language.serve(r, s, {})          v = s.getvalue()          assert not "Content-Length" in v -        assert not "Date" in v      def test_length(self):          def testlen(x): diff --git a/test/test_pathoc.py b/test/test_pathoc.py index 2542b622..23b42994 100644 --- a/test/test_pathoc.py +++ b/test/test_pathoc.py @@ -59,8 +59,11 @@ class _TestDaemon:              c.settimeout(timeout)          s = cStringIO.StringIO()          for i in requests: +            r = language.parse_requests(i)[0] +            if explain: +                r = r.freeze({})              c.print_request( -                language.parse_requests(i)[0], +                r,                  showreq = showreq,                  showresp = showresp,                  explain = explain, | 
