diff options
-rw-r--r-- | libpathod/app.py | 4 | ||||
-rw-r--r-- | libpathod/language.py | 112 | ||||
-rw-r--r-- | libpathod/pathoc.py | 4 | ||||
-rw-r--r-- | libpathod/pathod.py | 6 | ||||
-rw-r--r-- | test/test_language.py | 22 |
5 files changed, 73 insertions, 75 deletions
diff --git a/libpathod/app.py b/libpathod/app.py index e073921c..1fcfa078 100644 --- a/libpathod/app.py +++ b/libpathod/app.py @@ -131,9 +131,9 @@ def _preview(is_request): args["error"] = c return render(template, False, **args) if is_request: - safe.serve(s, app.config["pathod"].request_settings, host="example.com") + language.serve(safe, s, app.config["pathod"].request_settings, "example.com") else: - safe.serve(s, app.config["pathod"].request_settings) + language.serve(safe, s, app.config["pathod"].request_settings, None) args["output"] = utils.escape_unprintables(s.getvalue()) return render(template, False, **args) diff --git a/libpathod/language.py b/libpathod/language.py index ba462abe..a93d4dca 100644 --- a/libpathod/language.py +++ b/libpathod/language.py @@ -75,6 +75,51 @@ def write_values(fp, vals, actions, sofar=0, skip=0, blocksize=BLOCKSIZE): return True +def serve(msg, fp, settings, request_host=None): + """ + fp: The file pointer to write to. + + request_host: If this a request, this is the connecting host. If + None, we assume it's a response. Used to decide what standard + modifications to make if raw is not set. + + Calling this function may modify the object. + """ + started = time.time() + + hdrs = msg.headervals(settings, request_host) + + vals = msg.preamble(settings) + vals.append("\r\n") + vals.extend(hdrs) + vals.append("\r\n") + if msg.body: + vals.append(msg.body.value.get_generator(settings)) + vals.reverse() + actions = msg.ready_actions(settings, request_host) + + disconnect = write_values(fp, vals, actions[:]) + duration = time.time() - started + ret = dict( + disconnect = disconnect, + started = started, + duration = duration, + ) + for i in msg.logattrs: + v = getattr(msg, i) + # Careful not to log any VALUE specs without sanitizing them first. We truncate at 1k. + if hasattr(v, "values"): + v = [x[:TRUNCATE] for x in v.values(settings)] + v = "".join(v).encode("string_escape") + elif hasattr(v, "__len__"): + v = v[:TRUNCATE] + v = v.encode("string_escape") + ret[i] = v + ret["spec"] = msg.spec() + ret.update(msg.logflags) + return ret + + DATATYPES = dict( ascii_letters = string.ascii_letters, ascii_lowercase = string.ascii_lowercase, @@ -183,6 +228,13 @@ class _Token(object): """ return None + def resolve(self, msg): # pragma: no cover + """ + Resolves this token to ready it for transmission. This means that + the calculated offsets of actions are fixed. + """ + return self + def __repr__(self): return self.spec() @@ -679,7 +731,6 @@ class _Message(object): ValueLiteral(request_host) ) ) - else: if not utils.get_header("Date", self.headers): hdrs.append( @@ -699,49 +750,6 @@ class _Message(object): actions.reverse() return [i.intermediate(settings) for i in actions] - def serve(self, fp, settings, request_host): - """ - fp: The file pointer to write to. - - request_host: If this a request, this is the connecting host. If - None, we assume it's a response. Used to decide what standard - modifications to make if raw is not set. - - Calling this function may modify the object. - """ - started = time.time() - - hdrs = self.headervals(settings, request_host) - - vals = self.preamble(settings) - vals.append("\r\n") - vals.extend(hdrs) - vals.append("\r\n") - if self.body: - vals.append(self.body.value.get_generator(settings)) - vals.reverse() - actions = self.ready_actions(settings, request_host) - - disconnect = write_values(fp, vals, actions[:]) - duration = time.time() - started - ret = dict( - disconnect = disconnect, - started = started, - duration = duration, - ) - for i in self.logattrs: - v = getattr(self, i) - # Careful not to log any VALUE specs without sanitizing them first. We truncate at 1k. - if hasattr(v, "values"): - v = [x[:TRUNCATE] for x in v.values(settings)] - v = "".join(v).encode("string_escape") - elif hasattr(v, "__len__"): - v = v[:TRUNCATE] - v = v.encode("string_escape") - ret[i] = v - ret["spec"] = self.spec() - return ret - @abc.abstractmethod def preamble(self, settings): # pragma: no cover pass @@ -766,6 +774,7 @@ class Response(_Message): Reason ) logattrs = ["code", "reason", "version", "body"] + logflags = dict() @property def code(self): return self._get_token(Code) @@ -799,10 +808,6 @@ class Response(_Message): def spec(self): return ":".join([i.spec() for i in self.tokens]) - def serve(self, fp, settings): - d = _Message.serve(self, fp, settings, None) - return d - class Request(_Message): comps = ( @@ -815,6 +820,7 @@ class Request(_Message): Raw ) logattrs = ["method", "path", "body"] + logflags = dict() @property def method(self): return self._get_token(Method) @@ -848,12 +854,9 @@ class Request(_Message): def spec(self): return ":".join([i.spec() for i in self.tokens]) - def serve(self, fp, settings, host): - d = _Message.serve(self, fp, settings, host) - return d - class PathodErrorResponse(Response): + logflags = dict(internal=True) def __init__(self, reason, body=None): tokens = [ Code("800"), @@ -863,11 +866,6 @@ class PathodErrorResponse(Response): ] Response.__init__(self, tokens) - def serve(self, fp, settings): - d = Response.serve(self, fp, settings) - d["internal"] = True - return d - FILESTART = "+" def read_file(settings, s): diff --git a/libpathod/pathoc.py b/libpathod/pathoc.py index 4e592a06..b4020a3f 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, self.settings, self.host) + ret = language.serve(r, self.wfile, self.settings, 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, self.settings, self.host) + req = language.serve(r, self.wfile, self.settings, 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 131dbc3c..b3a32ef9 100644 --- a/libpathod/pathod.py +++ b/libpathod/pathod.py @@ -21,14 +21,14 @@ class PathodHandler(tcp.BaseHandler): c = self.server.check_policy(crafted, self.server.request_settings) if c: err = language.PathodErrorResponse(c) - err.serve(self.wfile, self.server.request_settings) + language.serve(err, self.wfile, self.server.request_settings) log = dict( type = "error", msg = c ) return False, log - response_log = crafted.serve(self.wfile, self.server.request_settings) + response_log = language.serve(crafted, self.wfile, self.server.request_settings, None) log = dict( type = "crafted", request=request_log, @@ -103,7 +103,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.request_settings) + language.serve(crafted, self.wfile, self.server.request_settings) 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 d0f43198..56c2249d 100644 --- a/test/test_language.py +++ b/test/test_language.py @@ -205,7 +205,7 @@ class TestMisc: def test_internal_response(self): d = cStringIO.StringIO() s = language.PathodErrorResponse("foo") - s.serve(d, {}) + language.serve(s, d, {}) class TestHeaders: @@ -316,7 +316,7 @@ class TestInject: def test_serve(self): s = cStringIO.StringIO() r = language.parse_response({}, "400:i0,'foo'") - assert r.serve(s, {}) + assert language.serve(r, s, {}) def test_spec(self): e = language.InjectAt.expr() @@ -376,7 +376,7 @@ class TestParseRequest: def test_render(self): s = cStringIO.StringIO() r = language.parse_request({}, "GET:'/foo'") - assert r.serve(s, {}, "foo.com") + assert language.serve(r, s, {}, "foo.com") def test_multiline(self): l = """ @@ -522,15 +522,15 @@ class TestWriteValues: def test_write_values_after(self): s = cStringIO.StringIO() r = language.parse_response({}, "400:da") - r.serve(s, {}) + language.serve(r, s, {}) s = cStringIO.StringIO() r = language.parse_response({}, "400:pa,0") - r.serve(s, {}) + language.serve(r, s, {}) s = cStringIO.StringIO() r = language.parse_response({}, "400:ia,'xx'") - r.serve(s, {}) + language.serve(r, s, {}) assert s.getvalue().endswith('xx') @@ -562,19 +562,19 @@ class TestResponse: def test_render(self): s = cStringIO.StringIO() r = language.parse_response({}, "400:m'msg'") - assert r.serve(s, {}) + assert language.serve(r, s, {}) def test_raw(self): s = cStringIO.StringIO() r = language.parse_response({}, "400:b'foo'") - r.serve(s, {}) + language.serve(r, s, {}) 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, {}) + language.serve(r, s, {}) v = s.getvalue() assert not "Content-Length" in v assert not "Date" in v @@ -582,7 +582,7 @@ class TestResponse: def test_length(self): def testlen(x): s = cStringIO.StringIO() - x.serve(s, {}) + language.serve(x, s, {}) assert x.length({}, None) == len(s.getvalue()) testlen(language.parse_response({}, "400:m'msg'")) testlen(language.parse_response({}, "400:m'msg':h'foo'='bar'")) @@ -592,7 +592,7 @@ class TestResponse: def testlen(x): s = cStringIO.StringIO() m = x.maximum_length({}, None) - x.serve(s, {}) + language.serve(x, s, {}) assert m >= len(s.getvalue()) r = language.parse_response({}, "400:m'msg':b@100:d0") |