diff options
author | Aldo Cortesi <aldo@nullcube.com> | 2015-05-03 13:54:52 +1200 |
---|---|---|
committer | Aldo Cortesi <aldo@nullcube.com> | 2015-05-03 13:54:52 +1200 |
commit | 5b6d3a80bbb57faa1de1cc7730d28b0678b0bdee (patch) | |
tree | 0f22273c92b0bd89464d66ef49c510aa33273344 | |
parent | 67d2993339cb81ff61509c57807df77dde436a3d (diff) | |
download | mitmproxy-5b6d3a80bbb57faa1de1cc7730d28b0678b0bdee.tar.gz mitmproxy-5b6d3a80bbb57faa1de1cc7730d28b0678b0bdee.tar.bz2 mitmproxy-5b6d3a80bbb57faa1de1cc7730d28b0678b0bdee.zip |
websockets: code specification
-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() |