aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libpathod/language/__init__.py2
-rw-r--r--libpathod/language/base.py39
-rw-r--r--libpathod/language/http.py47
-rw-r--r--libpathod/language/websockets.py88
-rw-r--r--test/test_language_http.py12
-rw-r--r--test/test_language_websocket.py29
6 files changed, 112 insertions, 105 deletions
diff --git a/libpathod/language/__init__.py b/libpathod/language/__init__.py
index e2e3e57d..12378607 100644
--- a/libpathod/language/__init__.py
+++ b/libpathod/language/__init__.py
@@ -40,7 +40,7 @@ def parse_pathoc(s):
reqs = pp.OneOrMore(
pp.Or(
[
- websockets.WebsocketFrame.expr(),
+ websockets.WebsocketClientFrame.expr(),
http.Request.expr(),
]
)
diff --git a/libpathod/language/base.py b/libpathod/language/base.py
index 4475d15b..ee5d05b5 100644
--- a/libpathod/language/base.py
+++ b/libpathod/language/base.py
@@ -519,3 +519,42 @@ class IntField(_Component):
def spec(self):
return "%s%s" % (self.preamble, self.origvalue)
+
+
+class NestedMessage(Token):
+ """
+ A nested message, as an escaped string with a preamble.
+ """
+ preamble = ""
+ nest_type = None
+
+ def __init__(self, value):
+ Token.__init__(self)
+ self.value = value
+ try:
+ self.parsed = self.nest_type(
+ self.nest_type.expr().parseString(
+ value.val,
+ parseAll=True
+ )
+ )
+ except pp.ParseException as v:
+ raise exceptions.ParseException(v.msg, v.line, v.col)
+
+ @classmethod
+ def expr(klass):
+ e = pp.Literal(klass.preamble).suppress()
+ e = e + TokValueLiteral.expr()
+ return e.setParseAction(lambda x: klass(*x))
+
+ def values(self, settings):
+ return [
+ self.value.get_generator(settings),
+ ]
+
+ def spec(self):
+ return "%s%s" % (self.preamble, self.value.spec())
+
+ def freeze(self, settings):
+ f = self.parsed.freeze(settings).spec()
+ return self.__class__(TokValueLiteral(f.encode("string_escape")))
diff --git a/libpathod/language/http.py b/libpathod/language/http.py
index 76362253..9a8404f0 100644
--- a/libpathod/language/http.py
+++ b/libpathod/language/http.py
@@ -94,38 +94,6 @@ class ShortcutUserAgent(_HeaderMixin, base.OptionsOrValue):
)
-class PathodResponse(base.Token):
- def __init__(self, value):
- self.value = value
- try:
- self.parsed = Response(
- Response.expr().parseString(
- value.val,
- parseAll=True
- )
- )
- except pp.ParseException as v:
- raise exceptions.ParseException(v.msg, v.line, v.col)
-
- @classmethod
- def expr(klass):
- e = pp.Literal("s").suppress()
- e = e + base.TokValueLiteral.expr()
- return e.setParseAction(lambda x: klass(*x))
-
- def values(self, settings):
- return [
- self.value.get_generator(settings),
- ]
-
- def spec(self):
- return "s%s" % (self.value.spec())
-
- def freeze(self, settings):
- f = self.parsed.freeze(settings).spec()
- return PathodResponse(base.TokValueLiteral(f.encode("string_escape")))
-
-
def get_header(val, headers):
"""
Header keys may be Values, so we have to "generate" them as we try the
@@ -281,6 +249,11 @@ class Response(_HTTPMessage):
return ":".join([i.spec() for i in self.tokens])
+class NestedResponse(base.NestedMessage):
+ preamble = "s"
+ nest_type = Response
+
+
class Request(_HTTPMessage):
comps = (
Body,
@@ -288,7 +261,7 @@ class Request(_HTTPMessage):
ShortcutContentType,
ShortcutUserAgent,
Raw,
- PathodResponse,
+ NestedResponse,
Times,
actions.PauseAt,
@@ -314,15 +287,15 @@ class Request(_HTTPMessage):
return self.tok(Times)
@property
- def pathodspec(self):
- return self.tok(PathodResponse)
+ def nested_response(self):
+ return self.tok(NestedResponse)
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())
+ if self.nested_response:
+ v.append(self.nested_response.parsed.spec())
v.append(" ")
v.append(self.version)
return v
diff --git a/libpathod/language/websockets.py b/libpathod/language/websockets.py
index 3869bd85..44c12f64 100644
--- a/libpathod/language/websockets.py
+++ b/libpathod/language/websockets.py
@@ -68,60 +68,31 @@ class Length(base.Integer):
class Times(base.Integer):
preamble = "x"
-
-class NestedFrame(base.Token):
- def __init__(self, value):
- self.value = value
- try:
- self.parsed = WebsocketFrame(
- Response.expr().parseString(
- value.val,
- parseAll=True
- )
- )
- except pp.ParseException as v:
- raise exceptions.ParseException(v.msg, v.line, v.col)
-
- @classmethod
- def expr(klass):
- e = pp.Literal("wf").suppress()
- e = e + base.TokValueLiteral.expr()
- return e.setParseAction(lambda x: klass(*x))
-
- def values(self, settings):
- return [
- self.value.get_generator(settings),
- ]
-
- def spec(self):
- return "s%s" % (self.value.spec())
-
- def freeze(self, settings):
- f = self.parsed.freeze(settings).spec()
- return NestedFrame(base.TokValueLiteral(f.encode("string_escape")))
+COMPONENTS = (
+ OpCode,
+ Length,
+ # Bit flags
+ Fin,
+ RSV1,
+ RSV2,
+ RSV3,
+ Mask,
+ actions.PauseAt,
+ actions.DisconnectAt,
+ actions.InjectAt,
+ KeyNone,
+ Key,
+ Times,
+
+ Body,
+ RawBody,
+)
class WebsocketFrame(message.Message):
- comps = (
- OpCode,
- Length,
- # Bit flags
- Fin,
- RSV1,
- RSV2,
- RSV3,
- Mask,
- actions.PauseAt,
- actions.DisconnectAt,
- actions.InjectAt,
- KeyNone,
- Key,
- Times,
-
- Body,
- RawBody,
- )
+ components = COMPONENTS
logattrs = ["body"]
+ unique_name = "body"
@property
def actions(self):
@@ -177,7 +148,7 @@ class WebsocketFrame(message.Message):
@classmethod
def expr(klass):
- parts = [i.expr() for i in klass.comps]
+ parts = [i.expr() for i in klass.components]
atom = pp.MatchFirst(parts)
resp = pp.And(
[
@@ -246,3 +217,18 @@ class WebsocketFrame(message.Message):
def spec(self):
return ":".join([i.spec() for i in self.tokens])
+
+
+class NestedFrame(base.NestedMessage):
+ preamble = "f"
+ nest_type = WebsocketFrame
+
+
+class WebsocketClientFrame(WebsocketFrame):
+ components = COMPONENTS + (
+ NestedFrame,
+ )
+
+ @property
+ def nested_frame(self):
+ return self.tok(NestedFrame)
diff --git a/test/test_language_http.py b/test/test_language_http.py
index 4851beaa..f1484da5 100644
--- a/test/test_language_http.py
+++ b/test/test_language_http.py
@@ -66,12 +66,12 @@ class TestRequest:
assert r[0].method.string() == "GET"
assert r[1].method.string() == "GET"
- def test_pathodspec(self):
+ def test_nested_response(self):
l = "get:/p:s'200'"
r = language.parse_pathoc(l)
assert len(r) == 1
assert len(r[0].tokens) == 3
- assert isinstance(r[0].tokens[2], http.PathodResponse)
+ assert isinstance(r[0].tokens[2], http.NestedResponse)
assert r[0].values({})
def test_render(self):
@@ -318,8 +318,8 @@ def test_user_agent():
assert v2.value.val == v3.value.val
-def test_pathodspec():
- e = http.PathodResponse.expr()
+def test_nested_response():
+ e = http.NestedResponse.expr()
v = e.parseString("s'200'")[0]
assert v.value.val == "200"
tutils.raises(
@@ -334,8 +334,8 @@ def test_pathodspec():
assert "@1" not in f.spec()
-def test_pathodspec_freeze():
- e = http.PathodResponse(
+def test_nested_response_freeze():
+ e = http.NestedResponse(
base.TokValueLiteral(
"200:b'foo':i10,'\\x27'".encode(
"string_escape"
diff --git a/test/test_language_websocket.py b/test/test_language_websocket.py
index f55e6e37..70a626e6 100644
--- a/test/test_language_websocket.py
+++ b/test/test_language_websocket.py
@@ -10,7 +10,19 @@ def parse_request(s):
class TestWebsocketFrame:
- def test_values(self):
+ def _test_messages(self, specs, message_klass):
+ for i in specs:
+ wf = parse_request(i)
+ assert isinstance(wf, message_klass)
+ assert wf
+ assert wf.values(language.Settings())
+ assert wf.resolve(language.Settings())
+
+ spec = wf.spec()
+ wf2 = parse_request(spec)
+ assert wf2.spec() == spec
+
+ def test_server_values(self):
specs = [
"wf",
"wf:dr",
@@ -25,16 +37,13 @@ class TestWebsocketFrame:
"wf:-fin:-rsv1:-rsv2:-rsv3:-mask",
"wf:k@4",
]
- for i in specs:
- wf = parse_request(i)
- assert isinstance(wf, websockets.WebsocketFrame)
- assert wf
- assert wf.values(language.Settings())
- assert wf.resolve(language.Settings())
+ self._test_messages(specs, websockets.WebsocketFrame)
- spec = wf.spec()
- wf2 = parse_request(spec)
- assert wf2.spec() == spec
+ def test_client_values(self):
+ specs = [
+ "wf:f'wf'",
+ ]
+ self._test_messages(specs, websockets.WebsocketClientFrame)
def test_flags(self):
wf = parse_request("wf:fin:mask:rsv1:rsv2:rsv3")