Add hooks for policy checks of served data.

This commit is contained in:
Aldo Cortesi 2012-07-23 14:37:00 +12:00
parent 204a556aa7
commit c7b5faf7db
3 changed files with 55 additions and 10 deletions

View File

@ -86,7 +86,7 @@ class PathodHandler(tcp.BaseHandler):
httpversion = httpversion,
)
if crafted:
response_log = crafted.serve(self.wfile)
response_log = crafted.serve(self.wfile, self.check_size)
if response_log["disconnect"]:
return
self.server.add_log(
@ -110,6 +110,9 @@ class PathodHandler(tcp.BaseHandler):
self.debug("%s %s"%(method, path))
return True
def check_size(self, req, actions):
return False
def handle(self):
if self.server.ssloptions:
try:
@ -145,7 +148,7 @@ class PathodHandler(tcp.BaseHandler):
class Pathod(tcp.TCPServer):
LOGBUF = 500
def __init__(self, addr, ssloptions=None, prefix="/p/", staticdir=None, anchors=None):
def __init__(self, addr, ssloptions=None, prefix="/p/", staticdir=None, anchors=None, sizelimit=None):
"""
addr: (address, port) tuple. If port is 0, a free port will be
automatically chosen.
@ -153,11 +156,13 @@ class Pathod(tcp.TCPServer):
prefix: string specifying the prefix at which to anchor response generation.
staticdir: path to a directory of static resources, or None.
anchors: A list of (regex, spec) tuples, or None.
sizelimit: Limit size of served data.
"""
tcp.TCPServer.__init__(self, addr)
self.ssloptions = ssloptions
self.staticdir = staticdir
self.prefix = prefix
self.sizelimit = sizelimit
self.app = app.app
self.app.config["pathod"] = self
self.log = []

View File

@ -25,6 +25,18 @@ class ParseException(Exception):
class ServerError(Exception): pass
def actions_log(lst):
ret = []
for i in lst:
if i[1] == "inject":
ret.append(
[i[0], i[1], repr(i[2])]
)
else:
ret.append(i)
return ret
def ready_actions(length, lst):
ret = []
for i in lst:
@ -527,7 +539,15 @@ class Message:
l += len(i[2])
return l
def serve(self, fp):
def serve(self, fp, check):
"""
fp: The file pointer to write to.
check: A function called with the effective actions (after random
values have been calculated). If it returns False service
proceeds, otherwise the return is treated as an error message to be
sent to the client, and service stops.
"""
started = time.time()
if self.body and not utils.get_header("Content-Length", self.headers):
self.headers.append(
@ -554,13 +574,26 @@ class Message:
vals.reverse()
actions = ready_actions(self.length(), self.actions)
actions.reverse()
if check:
ret = check(self, actions)
if ret:
err = InternalResponse(
800,
ret
)
err.serve(fp)
return dict(
disconnect = True,
actions = actions_log(actions),
error = ret
)
disconnect = write_values(fp, vals, actions[:])
duration = time.time() - started
ret = dict(
disconnect = disconnect,
started = started,
duration = duration,
actions = actions,
actions = actions_log(actions),
)
for i in self.logattrs:
v = getattr(self, i)
@ -661,8 +694,8 @@ class CraftedRequest(Request):
for i in tokens:
i.accept(settings, self)
def serve(self, fp):
d = Request.serve(self, fp)
def serve(self, fp, check=None):
d = Request.serve(self, fp, check)
d["spec"] = self.spec
return d
@ -674,8 +707,8 @@ class CraftedResponse(Response):
for i in tokens:
i.accept(settings, self)
def serve(self, fp):
d = Response.serve(self, fp)
def serve(self, fp, check=None):
d = Response.serve(self, fp, check)
d["spec"] = self.spec
return d
@ -697,8 +730,8 @@ class InternalResponse(Response):
)
]
def serve(self, fp):
d = Response.serve(self, fp)
def serve(self, fp, check=None):
d = Response.serve(self, fp, check)
d["internal"] = True
return d

View File

@ -419,6 +419,13 @@ class TestResponse:
assert r.body[:]
assert str(r)
def test_checkfunc(self):
s = cStringIO.StringIO()
r = rparse.parse_response({}, "400:b@100k")
def check(req, acts):
return "errmsg"
assert r.serve(s, check=check)["error"] == "errmsg"
def test_render(self):
s = cStringIO.StringIO()
r = rparse.parse_response({}, "400'msg'")