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")  | 
