diff options
| -rw-r--r-- | libpathod/language/base.py | 42 | ||||
| -rw-r--r-- | libpathod/language/http.py | 16 | ||||
| -rw-r--r-- | libpathod/language/websockets.py | 23 | ||||
| -rw-r--r-- | test/test_language_base.py | 27 | ||||
| -rw-r--r-- | test/test_language_http.py | 28 | ||||
| -rw-r--r-- | test/test_language_websocket.py | 16 | ||||
| -rw-r--r-- | test/test_pathoc.py | 2 | ||||
| -rw-r--r-- | test/tutils.py | 7 | 
8 files changed, 131 insertions, 30 deletions
| diff --git a/libpathod/language/base.py b/libpathod/language/base.py index 41855da3..725a0d42 100644 --- a/libpathod/language/base.py +++ b/libpathod/language/base.py @@ -291,14 +291,19 @@ class OptionsOrValue(_Component):          Can be any of a specified set of options, or a value specifier.      """      preamble = "" +    options = []      def __init__(self, value):          # If it's a string, we were passed one of the options, so we lower-case          # it to be canonical. The user can specify a different case by using a          # string value literal.          self.option_used = False          if isinstance(value, basestring): -            value = TokValueLiteral(value.lower()) -            self.option_used = True +            for i in self.options: +                # Find the exact option value in a case-insensitive way +                if i.lower() == value.lower(): +                    self.option_used = True +                    value = TokValueLiteral(i) +                    break          self.value = value      @classmethod @@ -369,3 +374,36 @@ class Value(_Component):      def freeze(self, settings):          return self.__class__(self.value.freeze(settings)) + + +class IntField(_Component): +    """ +        An integer field, where values can optionally specified by name. +    """ +    names = {} +    max = 16 +    preamble = "" + +    def __init__(self, value): +        self.origvalue = value +        self.value = self.names.get(value, value) +        if self.value > self.max: +            raise exceptions.ParseException( +                "Value can't exceed %s"%self.max, 0, 0 +            ) + +    @classmethod +    def expr(klass): +        parts = [pp.CaselessLiteral(i) for i in klass.names.keys()] +        m = pp.MatchFirst(parts) +        spec = m | v_integer.copy() +        spec = spec.setParseAction(lambda x: klass(*x)) +        if klass.preamble: +            spec = pp.Literal(klass.preamble).suppress() + spec +        return spec + +    def values(self, settings): +        return [str(self.value)] + +    def spec(self): +        return "%s%s"%(self.preamble, self.origvalue) diff --git a/libpathod/language/http.py b/libpathod/language/http.py index ba43a367..94de7237 100644 --- a/libpathod/language/http.py +++ b/libpathod/language/http.py @@ -34,14 +34,14 @@ class Body(base.Value):  class Method(base.OptionsOrValue):      options = [ -        "get", -        "head", -        "post", -        "put", -        "delete", -        "options", -        "trace", -        "connect", +        "GET", +        "HEAD", +        "POST", +        "PUT", +        "DELETE", +        "OPTIONS", +        "TRACE", +        "CONNECT",      ] diff --git a/libpathod/language/websockets.py b/libpathod/language/websockets.py index ae755f30..ddffdab9 100644 --- a/libpathod/language/websockets.py +++ b/libpathod/language/websockets.py @@ -17,6 +17,19 @@ class WF(base.CaselessLiteral):      TOK = "wf" +class Code(base.IntField): +    names = { +        "continue": netlib.websockets.OPCODE.CONTINUE, +        "text": netlib.websockets.OPCODE.TEXT, +        "binary": netlib.websockets.OPCODE.BINARY, +        "close": netlib.websockets.OPCODE.CLOSE, +        "ping": netlib.websockets.OPCODE.PING, +        "pong": netlib.websockets.OPCODE.PONG, +    } +    max = 15 +    preamble = "c" + +  class Body(base.Value):      preamble = "b" @@ -24,6 +37,7 @@ class Body(base.Value):  class WebsocketFrame(message.Message):      comps = (          Body, +        Code,          actions.PauseAt,          actions.DisconnectAt,          actions.InjectAt @@ -37,6 +51,10 @@ class WebsocketFrame(message.Message):      def body(self):          return self.tok(Body) +    @property +    def code(self): +        return self.tok(Code) +      @classmethod      def expr(klass):          parts = [i.expr() for i in klass.comps] @@ -59,10 +77,13 @@ class WebsocketFrame(message.Message):          else:              bodygen = None              length = 0 -        frame = netlib.websockets.FrameHeader( +        frameparts = dict(              mask = True,              payload_length = length          ) +        if self.code: +            frameparts["opcode"] = self.code.value +        frame = netlib.websockets.FrameHeader(**frameparts)          vals = [frame.to_bytes()]          if self.body:              masker = netlib.websockets.Masker(frame.masking_key) diff --git a/test/test_language_base.py b/test/test_language_base.py index 2e8446ea..4149de3e 100644 --- a/test/test_language_base.py +++ b/test/test_language_base.py @@ -219,6 +219,33 @@ class TestKeyValue:          assert v2.value.val == v3.value.val +def test_intfield(): +    class TT(base.IntField): +        preamble = "t" +        names = { +            "one": 1, +            "two": 2, +            "three": 3 +        } +        max = 4 +    e = TT.expr() + +    v = e.parseString("tone")[0] +    assert v.value == 1 +    assert v.spec() == "tone" +    assert v.values(language.Settings()) + +    v = e.parseString("t1")[0] +    assert v.value == 1 +    assert v.spec() == "t1" + +    v = e.parseString("t4")[0] +    assert v.value == 4 +    assert v.spec() == "t4" + +    tutils.raises("can't exceed", e.parseString, "t5") + +  def test_options_or_value():      class TT(base.OptionsOrValue):          options = [ diff --git a/test/test_language_http.py b/test/test_language_http.py index f36d7617..a7313bfb 100644 --- a/test/test_language_http.py +++ b/test/test_language_http.py @@ -9,12 +9,6 @@ def parse_request(s):      return language.parse_requests(s)[0] -def render(r, settings=language.Settings()): -    s = cStringIO.StringIO() -    assert language.serve(r, s, settings) -    return s.getvalue() - -  def test_make_error_response():      d = cStringIO.StringIO()      s = http.make_error_response("foo") @@ -30,7 +24,7 @@ class TestRequest:      def test_simple(self):          r = parse_request('GET:"/foo"') -        assert r.method.string() == "get" +        assert r.method.string() == "GET"          assert r.path.string() == "/foo"          r = parse_request('GET:/foo')          assert r.path.string() == "/foo" @@ -39,8 +33,8 @@ class TestRequest:      def test_multiple(self):          r = language.parse_requests("GET:/ PUT:/") -        assert r[0].method.string() == "get" -        assert r[1].method.string() == "put" +        assert r[0].method.string() == "GET" +        assert r[1].method.string() == "PUT"          assert len(r) == 2          l = """ @@ -60,8 +54,8 @@ class TestRequest:          """          r = language.parse_requests(l)          assert len(r) == 2 -        assert r[0].method.string() == "get" -        assert r[1].method.string() == "put" +        assert r[0].method.string() == "GET" +        assert r[1].method.string() == "PUT"          l = """              get:"http://localhost:9999/p/200":ir,@1 @@ -69,8 +63,8 @@ class TestRequest:          """          r = language.parse_requests(l)          assert len(r) == 2 -        assert r[0].method.string() == "get" -        assert r[1].method.string() == "get" +        assert r[0].method.string() == "GET" +        assert r[1].method.string() == "GET"      def test_pathodspec(self):          l = "get:/p:s'200'" @@ -96,7 +90,7 @@ class TestRequest:              ir,@1          """          r = parse_request(l) -        assert r.method.string() == "get" +        assert r.method.string() == "GET"          assert r.path.string() == "/foo"          assert r.actions @@ -112,7 +106,7 @@ class TestRequest:              ir,@1          """          r = parse_request(l) -        assert r.method.string() == "get" +        assert r.method.string() == "GET"          assert r.path.string().endswith("bar")          assert r.actions @@ -302,8 +296,8 @@ def test_shortcuts():      assert language.parse_response("400:c'foo'").headers[0].key.val == "Content-Type"      assert language.parse_response("400:l'foo'").headers[0].key.val == "Location" -    assert "Android" in render(parse_request("get:/:ua")) -    assert "User-Agent" in render(parse_request("get:/:ua")) +    assert "Android" in tutils.render(parse_request("get:/:ua")) +    assert "User-Agent" in tutils.render(parse_request("get:/:ua"))  def test_user_agent(): diff --git a/test/test_language_websocket.py b/test/test_language_websocket.py index 3b1d4852..f2f0b2a8 100644 --- a/test/test_language_websocket.py +++ b/test/test_language_websocket.py @@ -1,6 +1,8 @@  from libpathod import language  from libpathod.language import websockets +import netlib.websockets +import tutils  def parse_request(s): @@ -11,7 +13,9 @@ class TestWebsocketFrame:      def test_values(self):          specs = [              "wf", -            "wf:b'foo'" +            "wf:b'foo'", +            "wf:cbinary", +            "wf:c1"          ]          for i in specs:              wf = parse_request(i) @@ -23,3 +27,13 @@ class TestWebsocketFrame:              spec = wf.spec()              wf2 = parse_request(spec)              assert wf2.spec() == spec + +    def test_construction(self): +        wf = parse_request("wf:c1") +        frm = netlib.websockets.Frame.from_bytes(tutils.render(wf)) +        assert wf.code.value == 1 == frm.header.opcode + +        wf = parse_request("wf:cbinary") +        frm = netlib.websockets.Frame.from_bytes(tutils.render(wf)) +        assert wf.code.value == frm.header.opcode +        assert wf.code.value == netlib.websockets.OPCODE.BINARY diff --git a/test/test_pathoc.py b/test/test_pathoc.py index 466081cd..8d0d5972 100644 --- a/test/test_pathoc.py +++ b/test/test_pathoc.py @@ -162,7 +162,7 @@ class TestDaemon(_TestDaemon):      def test_showreq(self):          reqs = ["get:/api/info:p0,0", "get:/api/info:p0,0"] -        assert self.tval(reqs, showreq=True).count("get /api") == 2 +        assert self.tval(reqs, showreq=True).count("GET /api") == 2          assert self.tval(              reqs, showreq=True, hexdump=True          ).count("0000000000") == 2 diff --git a/test/tutils.py b/test/tutils.py index f188029d..2387e752 100644 --- a/test/tutils.py +++ b/test/tutils.py @@ -2,6 +2,7 @@ import tempfile  import os  import re  import shutil +import cStringIO  from contextlib import contextmanager  from libpathod import utils, test, pathoc, pathod, language  import requests @@ -137,3 +138,9 @@ def raises(exc, obj, *args, **kwargs):      raise AssertionError("No exception raised.")  test_data = utils.Data(__name__) + + +def render(r, settings=language.Settings()): +    s = cStringIO.StringIO() +    assert language.serve(r, s, settings) +    return s.getvalue() | 
