diff options
-rw-r--r-- | libpathod/app.py | 4 | ||||
-rw-r--r-- | libpathod/language.py | 56 | ||||
-rw-r--r-- | libpathod/pathoc.py | 4 | ||||
-rw-r--r-- | libpathod/pathod.py | 4 | ||||
-rw-r--r-- | test/test_language.py | 83 |
5 files changed, 64 insertions, 87 deletions
diff --git a/libpathod/app.py b/libpathod/app.py index 396e45c2..fc4e23ec 100644 --- a/libpathod/app.py +++ b/libpathod/app.py @@ -131,9 +131,9 @@ def _preview(is_request): args["pauses"] = r.preview_safe() if is_request: - r.serve(s, check=app.config["pathod"].check_policy, host="example.com") + r.serve(app.config["pathod"].request_settings, s, check=app.config["pathod"].check_policy, host="example.com") else: - r.serve(s, check=app.config["pathod"].check_policy) + r.serve(app.config["pathod"].request_settings, s, check=app.config["pathod"].check_policy) args["output"] = utils.escape_unprintables(s.getvalue()) return render(template, False, **args) diff --git a/libpathod/language.py b/libpathod/language.py index a5c53677..c9aa7f66 100644 --- a/libpathod/language.py +++ b/libpathod/language.py @@ -25,19 +25,6 @@ class ParseException(Exception): return "%s at char %s"%(self.msg, self.col) -def ready_actions(length, lst): - ret = [] - for i in lst: - itms = list(i) - if i[0] == "r": - itms[0] = random.randrange(length) - elif i[0] == "a": - itms[0] = length+1 - ret.append(tuple(itms)) - ret.sort() - return ret - - def send_chunk(fp, val, blocksize, start, end): """ (start, end): Inclusive lower bound, exclusive upper bound. @@ -441,6 +428,9 @@ class _Action: def __repr__(self): return self.spec() + + def accept(self, settings, r): + r.actions.append(self) class PauseAt(_Action): @@ -464,17 +454,14 @@ class PauseAt(_Action): def spec(self): return "p%s,%s"%(self.offset, self.seconds) - def accept(self, settings, r): - r.actions.append((self.offset, "pause", self.seconds)) + def intermediate(self, settings): + return (self.offset, "pause", self.seconds) class DisconnectAt(_Action): def __init__(self, offset): _Action.__init__(self, offset) - def accept(self, settings, r): - r.actions.append((self.offset, "disconnect")) - @classmethod def expr(klass): e = pp.Literal("d").suppress() @@ -484,6 +471,9 @@ class DisconnectAt(_Action): def spec(self): return "d%s"%self.offset + def intermediate(self, settings): + return (self.offset, "disconnect") + class InjectAt(_Action): def __init__(self, offset, value): @@ -501,14 +491,12 @@ class InjectAt(_Action): def spec(self): return "i%s,%s"%(self.offset, self.value.spec()) - def accept(self, settings, r): - r.actions.append( - ( + def intermediate(self, settings): + return ( self.offset, "inject", self.value.get_generator(settings) ) - ) class Header: @@ -577,8 +565,8 @@ class Message: """ Modify this message to be safe for previews. Returns a list of elided actions. """ - pauses = [i for i in self.actions if i[1] == "pause"] - self.actions = [i for i in self.actions if i[1] != "pause"] + pauses = [i for i in self.actions if isinstance(i, PauseAt)] + self.actions = [i for i in self.actions if not isinstance(i, PauseAt)] return pauses def effective_length(self, actions): @@ -595,7 +583,7 @@ class Message: l += len(i[2]) return l - def serve(self, fp, check, request_host): + def serve(self, settings, fp, check, request_host): """ fp: The file pointer to write to. @@ -652,13 +640,15 @@ class Message: if self.body: vals.append(self.body) vals.reverse() - actions = ready_actions(self.length(), self.actions) + actions = [i.resolve_offset(self) for i in self.actions] + actions.sort() actions.reverse() + actions = [i.intermediate(settings) for i in actions] if check: ret = check(self, actions) if ret: err = PathodErrorResponse(ret) - err.serve(fp) + err.serve(settings, fp) return dict( disconnect = True, error = ret @@ -767,8 +757,8 @@ class CraftedRequest(Request): for i in tokens: i.accept(settings, self) - def serve(self, fp, check, host): - d = Request.serve(self, fp, check, host) + def serve(self, settings, fp, check, host): + d = Request.serve(self, settings, fp, check, host) d["spec"] = self.spec return d @@ -780,8 +770,8 @@ class CraftedResponse(Response): for i in tokens: i.accept(settings, self) - def serve(self, fp, check): - d = Response.serve(self, fp, check, None) + def serve(self, settings, fp, check): + d = Response.serve(self, settings, fp, check, None) d["spec"] = self.spec return d @@ -798,8 +788,8 @@ class PathodErrorResponse(Response): ), ] - def serve(self, fp, check=None): - d = Response.serve(self, fp, check, None) + def serve(self, settings, fp, check=None): + d = Response.serve(self, settings, fp, check, None) d["internal"] = True return d diff --git a/libpathod/pathoc.py b/libpathod/pathoc.py index 3ed09190..873a989c 100644 --- a/libpathod/pathoc.py +++ b/libpathod/pathoc.py @@ -22,7 +22,7 @@ class Pathoc(tcp.TCPClient): language.FileAccessDenied. """ r = language.parse_request(self.settings, spec) - ret = r.serve(self.wfile, None, self.host) + ret = r.serve(self.settings, self.wfile, None, self.host) self.wfile.flush() return http.read_response(self.rfile, r.method, None) @@ -68,7 +68,7 @@ class Pathoc(tcp.TCPClient): if showresp: self.rfile.start_log() try: - req = r.serve(self.wfile, None, self.host) + req = r.serve(self.settings, self.wfile, None, self.host) self.wfile.flush() resp = http.read_response(self.rfile, r.method, None) except http.HttpError, v: diff --git a/libpathod/pathod.py b/libpathod/pathod.py index 4ce268fa..9d343a51 100644 --- a/libpathod/pathod.py +++ b/libpathod/pathod.py @@ -18,7 +18,7 @@ class PathodHandler(tcp.BaseHandler): self.sni = connection.get_servername() def serve_crafted(self, crafted, request_log): - response_log = crafted.serve(self.wfile, self.server.check_policy) + response_log = crafted.serve(self.server.request_settings, self.wfile, self.server.check_policy) log = dict( type = "crafted", request=request_log, @@ -96,7 +96,7 @@ class PathodHandler(tcp.BaseHandler): return self.serve_crafted(crafted, request_log) elif self.server.noweb: crafted = language.PathodErrorResponse("Access Denied") - crafted.serve(self.wfile, self.server.check_policy) + crafted.serve(self.server.request_settings, self.wfile, self.server.check_policy) return False, dict(type = "error", msg="Access denied: web interface disabled") else: self.info("app: %s %s"%(method, path)) diff --git a/test/test_language.py b/test/test_language.py index e1697f95..a0781327 100644 --- a/test/test_language.py +++ b/test/test_language.py @@ -206,7 +206,7 @@ class TestMisc: def test_internal_response(self): d = cStringIO.StringIO() s = language.PathodErrorResponse("foo") - s.serve(d) + s.serve({}, d) class Test_Action: @@ -229,8 +229,10 @@ class Test_Action: class TestDisconnects: def test_parse_response(self): - assert (0, "disconnect") in language.parse_response({}, "400:d0").actions - assert ("r", "disconnect") in language.parse_response({}, "400:dr").actions + a = language.parse_response({}, "400:d0").actions[0] + assert a.spec() == "d0" + a = language.parse_response({}, "400:dr").actions[0] + assert a.spec() == "dr" def test_at(self): e = language.DisconnectAt.expr() @@ -253,12 +255,12 @@ class TestDisconnects: class TestInject: def test_parse_response(self): a = language.parse_response({}, "400:ir,@100").actions[0] - assert a[0] == "r" - assert a[1] == "inject" + assert a.offset == "r" + assert a.value.datatype == "bytes" + assert a.value.usize == 100 a = language.parse_response({}, "400:ia,@100").actions[0] - assert a[0] == "a" - assert a[1] == "inject" + assert a.offset == "a" def test_at(self): e = language.InjectAt.expr() @@ -273,7 +275,7 @@ class TestInject: def test_serve(self): s = cStringIO.StringIO() r = language.parse_response({}, "400:i0,'foo'") - assert r.serve(s, None) + assert r.serve({}, s, None) def test_spec(self): e = language.InjectAt.expr() @@ -281,7 +283,6 @@ class TestInject: assert v.spec() == 'i0,"foo"' - class TestPauses: def test_parse_response(self): e = language.PauseAt.expr() @@ -300,7 +301,7 @@ class TestPauses: def test_request(self): r = language.parse_response({}, '400:p10,10') - assert r.actions[0] == (10, "pause", 10) + assert r.actions[0].spec() == "p10,10" def test_spec(self): assert language.PauseAt("r", 5).spec() == "pr,5" @@ -336,7 +337,7 @@ class TestParseRequest: def test_render(self): s = cStringIO.StringIO() r = language.parse_request({}, "GET:'/foo'") - assert r.serve(s, None, "foo.com") + assert r.serve({}, s, None, "foo.com") def test_str(self): r = language.parse_request({}, 'GET:"/foo"') @@ -386,15 +387,15 @@ class TestParseResponse: def test_parse_pause_before(self): r = language.parse_response({}, "400:p0,10") - assert (0, "pause", 10) in r.actions + assert r.actions[0].spec() == "p0,10" def test_parse_pause_after(self): r = language.parse_response({}, "400:pa,10") - assert ("a", "pause", 10) in r.actions + assert r.actions[0].spec() == "pa,10" def test_parse_pause_random(self): r = language.parse_response({}, "400:pr,10") - assert ("r", "pause", 10) in r.actions + assert r.actions[0].spec() == "pr,10" def test_parse_stress(self): r = language.parse_response({}, "400:b@100g") @@ -468,34 +469,18 @@ class TestWriteValues: def test_write_values_after(self): s = cStringIO.StringIO() r = language.parse_response({}, "400:da") - r.serve(s, None) + r.serve({}, s, None) s = cStringIO.StringIO() r = language.parse_response({}, "400:pa,0") - r.serve(s, None) + r.serve({}, s, None) s = cStringIO.StringIO() r = language.parse_response({}, "400:ia,'xx'") - r.serve(s, None) + r.serve({}, s, None) assert s.getvalue().endswith('xx') -def test_ready_actions(): - x = [(0, 5)] - assert language.ready_actions(100, x) == x - - x = [("r", 5)] - ret = language.ready_actions(100, x) - assert 0 <= ret[0][0] < 100 - - x = [("a", "pause", 5)] - ret = language.ready_actions(100, x) - assert ret[0][0] > 100 - - x = [(1, 5), (0, 5)] - assert language.ready_actions(100, x) == sorted(x) - - class TestResponse: def dummy_response(self): return language.parse_response({}, "400'msg'") @@ -521,24 +506,24 @@ class TestResponse: r = language.parse_response({}, "400:b@100k") def check(req, acts): return "errmsg" - assert r.serve(s, check=check)["error"] == "errmsg" + assert r.serve({}, s, check=check)["error"] == "errmsg" def test_render(self): s = cStringIO.StringIO() r = language.parse_response({}, "400'msg'") - assert r.serve(s, None) + assert r.serve({}, s, None) def test_raw(self): s = cStringIO.StringIO() r = language.parse_response({}, "400:b'foo'") - r.serve(s, None) + r.serve({}, s, None) v = s.getvalue() assert "Content-Length" in v assert "Date" in v s = cStringIO.StringIO() r = language.parse_response({}, "400:b'foo':r") - r.serve(s, None) + r.serve({}, s, None) v = s.getvalue() assert not "Content-Length" in v assert not "Date" in v @@ -546,46 +531,48 @@ class TestResponse: def test_length(self): def testlen(x): s = cStringIO.StringIO() - x.serve(s, None) + x.serve({}, s, None) assert x.length() == len(s.getvalue()) testlen(language.parse_response({}, "400'msg'")) testlen(language.parse_response({}, "400'msg':h'foo'='bar'")) testlen(language.parse_response({}, "400'msg':h'foo'='bar':b@100b")) def test_effective_length(self): + l = [None] + def check(req, actions): + l[0] = req.effective_length(actions) + def testlen(x, actions): s = cStringIO.StringIO() - x.serve(s, None) - assert x.effective_length(actions) == len(s.getvalue()) - actions = [ + x.serve({}, s, check) + assert l[0] == len(s.getvalue()) - ] r = language.parse_response({}, "400'msg':b@100") actions = [ - (0, "disconnect"), + language.DisconnectAt(0) ] r.actions = actions testlen(r, actions) actions = [ - (0, "disconnect"), - (0, "inject", "foo") + language.DisconnectAt(0), + language.InjectAt(0, language.ValueLiteral("foo")) ] r.actions = actions testlen(r, actions) actions = [ - (0, "inject", "foo") + language.InjectAt(0, language.ValueLiteral("foo")) ] r.actions = actions testlen(r, actions) def test_render(self): r = language.parse_response({}, "400:p0,100:dr") - assert r.actions[0][1] == "pause" + assert r.actions[0].spec() == "p0,100" assert len(r.preview_safe()) == 1 - assert not r.actions[0][1] == "pause" + assert not r.actions[0].spec().startswith("p") |