aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAldo Cortesi <aldo@nullcube.com>2015-05-03 13:54:52 +1200
committerAldo Cortesi <aldo@nullcube.com>2015-05-03 13:54:52 +1200
commit5b6d3a80bbb57faa1de1cc7730d28b0678b0bdee (patch)
tree0f22273c92b0bd89464d66ef49c510aa33273344
parent67d2993339cb81ff61509c57807df77dde436a3d (diff)
downloadmitmproxy-5b6d3a80bbb57faa1de1cc7730d28b0678b0bdee.tar.gz
mitmproxy-5b6d3a80bbb57faa1de1cc7730d28b0678b0bdee.tar.bz2
mitmproxy-5b6d3a80bbb57faa1de1cc7730d28b0678b0bdee.zip
websockets: code specification
-rw-r--r--libpathod/language/base.py42
-rw-r--r--libpathod/language/http.py16
-rw-r--r--libpathod/language/websockets.py23
-rw-r--r--test/test_language_base.py27
-rw-r--r--test/test_language_http.py28
-rw-r--r--test/test_language_websocket.py16
-rw-r--r--test/test_pathoc.py2
-rw-r--r--test/tutils.py7
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()