diff options
| -rw-r--r-- | libpathod/language/__init__.py | 15 | ||||
| -rw-r--r-- | libpathod/language/base.py | 55 | ||||
| -rw-r--r-- | libpathod/language/websockets.py | 11 | ||||
| -rw-r--r-- | libpathod/templates/docs_lang_websockets.html | 39 | ||||
| -rw-r--r-- | test/test_language_base.py | 26 | ||||
| -rw-r--r-- | test/test_language_websocket.py | 6 | 
6 files changed, 120 insertions, 32 deletions
| diff --git a/libpathod/language/__init__.py b/libpathod/language/__init__.py index 8837a174..36ec19e2 100644 --- a/libpathod/language/__init__.py +++ b/libpathod/language/__init__.py @@ -5,20 +5,7 @@ import pyparsing as pp  from . import base, http, websockets, writer, exceptions  from exceptions import * - - -class Settings: -    def __init__( -        self, -        staticdir = None, -        unconstrained_file_access = False, -        request_host = None, -        websocket_key = None -    ): -        self.staticdir = staticdir -        self.unconstrained_file_access = unconstrained_file_access -        self.request_host = request_host -        self.websocket_key = websocket_key +from base import Settings  def parse_response(s): diff --git a/libpathod/language/base.py b/libpathod/language/base.py index 4179fa7d..41ad639a 100644 --- a/libpathod/language/base.py +++ b/libpathod/language/base.py @@ -7,6 +7,21 @@ from .. import utils  from . import generators, exceptions +class Settings: +    def __init__( +        self, +        staticdir = None, +        unconstrained_file_access = False, +        request_host = None, +        websocket_key = None +    ): +        self.staticdir = staticdir +        self.unconstrained_file_access = unconstrained_file_access +        self.request_host = request_host +        self.websocket_key = websocket_key + + +  Sep = pp.Optional(pp.Literal(":")).suppress() @@ -375,6 +390,46 @@ class Value(_Component):          return self.__class__(self.value.freeze(settings)) +class FixedLengthValue(Value): +    """ +        A value component lead by an optional preamble. +    """ +    preamble = "" +    length = None + +    def __init__(self, value): +        Value.__init__(self, value) +        lenguess = None +        try: +            lenguess = len(value.get_generator(Settings())) +        except exceptions.RenderError: +            pass +        # This check will fail if we know the length upfront +        if lenguess is not None and lenguess != self.length: +            raise exceptions.RenderError( +                "Invalid value length: '%s' is %s bytes, should be %s."%( +                    self.spec(), +                    lenguess, +                    self.length +                ) +            ) + +    def values(self, settings): +        ret = Value.values(self, settings) +        l = sum(len(i) for i in ret) +        # This check will fail if we don't know the length upfront - i.e. for +        # file inputs +        if l != self.length: +            raise exceptions.RenderError( +                "Invalid value length: '%s' is %s bytes, should be %s."%( +                    self.spec(), +                    l, +                    self.length +                ) +            ) +        return ret + +  class Boolean(_Component):      """          A boolean flag. diff --git a/libpathod/language/websockets.py b/libpathod/language/websockets.py index a6674988..3abdf9d8 100644 --- a/libpathod/language/websockets.py +++ b/libpathod/language/websockets.py @@ -8,8 +8,9 @@ from . import base, generators, actions, message      wf:c15:r'foo'      wf:fin:rsv1:rsv2:rsv3:mask      wf:-fin:-rsv1:-rsv2:-rsv3:-mask -    wf:p234 -    wf:m"mask" + +    wf:k"mask" +    wf:l234  """ @@ -58,6 +59,11 @@ class Mask(base.Boolean):      name = "mask" +class Key(base.FixedLengthValue): +    preamble = "k" +    length = 4 + +  class WebsocketFrame(message.Message):      comps = (          Body, @@ -72,6 +78,7 @@ class WebsocketFrame(message.Message):          actions.PauseAt,          actions.DisconnectAt,          actions.InjectAt, +        Key,          Raw,      ) diff --git a/libpathod/templates/docs_lang_websockets.html b/libpathod/templates/docs_lang_websockets.html index 9b595e74..c50d081f 100644 --- a/libpathod/templates/docs_lang_websockets.html +++ b/libpathod/templates/docs_lang_websockets.html @@ -24,6 +24,13 @@          </tr>          <tr> +            <td> d<a href="#offsetspec">OFFSET</a> </td> +            <td> +                Disconnect after OFFSET bytes. +            </td> +        </tr> + +        <tr>              <td> [-]fin </td>              <td>                  Set or un-set the <b>fin</b> bit. @@ -31,54 +38,56 @@          </tr>          <tr> -            <td> [-]rsv1 </td> +            <td> i<a href="#offsetspec">OFFSET</a>,<a href="#valuespec">VALUE</a> </td>              <td> -                Set or un-set the <b>rsv1</b> bit. +                Inject the specified value at the offset.              </td>          </tr>          <tr> -            <td> [-]rsv2 </td> +            <td> [-]mask </td>              <td> -                Set or un-set the <b>rsv2</b> bit. +                Set or un-set the <b>mask</b> bit.              </td>          </tr>          <tr> -            <td> [-]rsv3 </td> +            <td> p<a href="#offsetspec">OFFSET</a>,SECONDS </td>              <td> -                Set or un-set the <b>rsv3</b> bit. +                Pause for SECONDS seconds after OFFSET bytes. SECONDS can +                be an integer or "f" to pause forever.              </td>          </tr>          <tr> -            <td> [-]mask </td> +            <td> r </td>              <td> -                Set or un-set the <b>mask</b> bit. +                Create a "raw" frame - disables auto-generation of the masking +                key if the mask bit is on.              </td>          </tr>          <tr> -            <td> d<a href="#offsetspec">OFFSET</a> </td> +            <td> [-]rsv1 </td>              <td> -                Disconnect after OFFSET bytes. +                Set or un-set the <b>rsv1</b> bit.              </td>          </tr>          <tr> -            <td> i<a href="#offsetspec">OFFSET</a>,<a href="#valuespec">VALUE</a> </td> +            <td> [-]rsv2 </td>              <td> -                Inject the specified value at the offset. +                Set or un-set the <b>rsv2</b> bit.              </td>          </tr>          <tr> -            <td> p<a href="#offsetspec">OFFSET</a>,SECONDS </td> +            <td> [-]rsv3 </td>              <td> -                Pause for SECONDS seconds after OFFSET bytes. SECONDS can -                be an integer or "f" to pause forever. +                Set or un-set the <b>rsv3</b> bit.              </td>          </tr> +      </tbody>  </table> diff --git a/test/test_language_base.py b/test/test_language_base.py index c6cee7b3..bd67c010 100644 --- a/test/test_language_base.py +++ b/test/test_language_base.py @@ -168,7 +168,7 @@ class TestMisc:          assert base.TokValue.parseString('"val"')[0].val == "val"          assert base.TokValue.parseString('"\'val\'"')[0].val == "'val'" -    def test_prevalue(self): +    def test_value(self):          class TT(base.Value):              preamble = "m"          e = TT.expr() @@ -183,6 +183,30 @@ class TestMisc:          v3 = v2.freeze({})          assert v2.value.val == v3.value.val +    def test_fixedlengthvalue(self): +        class TT(base.FixedLengthValue): +            preamble = "m" +            length = 4 + +        e = TT.expr() +        assert e.parseString("m@4") +        tutils.raises("invalid value length", e.parseString, "m@100") +        tutils.raises("invalid value length", e.parseString, "m@1") + +        with tutils.tmpdir() as t: +            p = os.path.join(t, "path") +            s = base.Settings(staticdir=t) +            with open(p, "wb") as f: +                f.write("a" * 20) +            v = e.parseString("m<path")[0] +            tutils.raises("invalid value length", v.values, s) + +            p = os.path.join(t, "path") +            with open(p, "wb") as f: +                f.write("a" * 4) +            v = e.parseString("m<path")[0] +            assert v.values(s) +  class TKeyValue(base.KeyValue):      preamble = "h" diff --git a/test/test_language_websocket.py b/test/test_language_websocket.py index 8abb55aa..4b384f61 100644 --- a/test/test_language_websocket.py +++ b/test/test_language_websocket.py @@ -21,6 +21,7 @@ class TestWebsocketFrame:              "wf:fin",              "wf:fin:rsv1:rsv2:rsv3:mask",              "wf:-fin:-rsv1:-rsv2:-rsv3:-mask", +            "wf:k@4",          ]          for i in specs:              wf = parse_request(i) @@ -62,3 +63,8 @@ class TestWebsocketFrame:          frm = netlib.websockets.Frame.from_bytes(tutils.render(wf))          assert wf.opcode.value == frm.header.opcode          assert wf.opcode.value == netlib.websockets.OPCODE.BINARY + +    def test_auto_raw(self): +        wf = parse_request("wf:b'foo':mask") +        frm = netlib.websockets.Frame.from_bytes(tutils.render(wf)) +        print frm.human_readable() | 
