diff options
Diffstat (limited to 'libpathod/opathod.py')
| -rw-r--r-- | libpathod/opathod.py | 263 | 
1 files changed, 263 insertions, 0 deletions
diff --git a/libpathod/opathod.py b/libpathod/opathod.py new file mode 100644 index 00000000..6ec8367d --- /dev/null +++ b/libpathod/opathod.py @@ -0,0 +1,263 @@ +import urllib, pprint +import tornado.web, tornado.template, tornado.ioloop, tornado.httpserver +import rparse, utils, version + + +class Pathod(object): +    def __init__(self, spec, application, request, **settings): +        self.application, self.request, self.settings = application, request, settings +        try: +            self.response = rparse.parse(self.settings, spec) +        except rparse.ParseException, v: +            self.response = rparse.InternalResponse( +                800, +                "Error parsing response spec: %s\n"%v.msg + v.marked() +            ) + +    def _execute(self, transforms, *args, **kwargs): +        d = self.response.serve(self.request) +        d["request"] = dict( +            path = self.request.path, +            method = self.request.method, +            headers = self.request.headers, +            host = self.request.host, +            protocol = self.request.protocol, +            remote_address = self.request.connection.address, +            full_url = self.request.full_url(), +            query = self.request.query, +            version = self.request.version, +            uri = self.request.uri, +        ) +        self.application.add_log(d) + + +class RequestPathod(Pathod): +    anchor = "/p/" +    def __init__(self, application, request, **settings): +        spec = urllib.unquote(request.uri)[len(self.anchor):] +        Pathod.__init__(self, spec, application, request, **settings) + + +class PathodApp(tornado.web.Application): +    LOGBUF = 500 +    def __init__(self, **settings): +        self.appsettings = settings +        tornado.web.Application.__init__( +            self, +            [ +                (r"/", Index), +                (r"/log", Log), +                (r"/log/clear", ClearLog), +                (r"/log/([0-9]+)", OneLog), +                (r"/help", Help), +                (r"/preview", Preview), +                (r"/api/shutdown", APIShutdown), +                (r"/api/info", APIInfo), +                (r"/api/log", APILog), +                (r"/api/log/clear", APILogClear), +                (r"/p/.*", RequestPathod, settings), +            ], +            static_path = utils.data.path("static"), +            template_path = utils.data.path("templates"), +            debug=True +        ) +        self.log = [] +        self.logid = 0 + +    def add_anchor(self, pattern, spec): +        """ +            Anchors are added to the beginning of the handlers. +        """ +        # We assume we have only one host... +        l = self.handlers[0][1] +        class FixedPathod(Pathod): +            def __init__(self, application, request, **settings): +                Pathod.__init__(self, spec, application, request, **settings) +        FixedPathod.spec = spec +        FixedPathod.pattern = pattern +        l.insert(0, tornado.web.URLSpec(pattern, FixedPathod, self.appsettings)) + +    def get_anchors(self): +        """ +            Anchors are added to the beginning of the handlers. +        """ +        l = self.handlers[0][1] +        a = [] +        for i in l: +            if i.handler_class.__name__ == "FixedPathod": +                a.append( +                    ( +                        i.handler_class.pattern, +                        i.handler_class.spec +                    ) +                ) +        return a + +    def remove_anchor(self, pattern, spec): +        """ +            Anchors are added to the beginning of the handlers. +        """ +        l = self.handlers[0][1] +        for i, h in enumerate(l): +            if h.handler_class.__name__ == "FixedPathod": +                if (h.handler_class.pattern, h.handler_class.spec) == (pattern, spec): +                    del l[i] +                    return + +    def add_log(self, d): +        d["id"] = self.logid +        self.log.insert(0, d) +        if len(self.log) > self.LOGBUF: +            self.log.pop() +        self.logid += 1 + +    def log_by_id(self, id): +        for i in self.log: +            if i["id"] == id: +                return i + +    def clear_log(self): +        self.log = [] + +    def get_log(self): +        return self.log + + +def make_app(staticdir=None, anchors=()): +    """ +        staticdir: A directory for static assets referenced in response patterns. +        anchors: A sequence of strings of the form "pattern=pagespec" +    """ +    settings = dict( +        staticdir=staticdir +    ) +    application = PathodApp(**settings) +    for i in anchors: +        rex, spec = utils.parse_anchor_spec(i, settings) +        application.add_anchor(rex, spec) +    return application + + +def make_server(application, port, address, ssl_options): +    """ +        Returns a (server, port) tuple. + +        The returned port will match the passed port, unless the passed port +        was 0. In that case, an arbitrary empty port will be bound to, and this +        new port will be returned. +    """ +    http_server = tornado.httpserver.HTTPServer( +        application, +        ssl_options=ssl_options +    ) +    http_server.listen(port, address) +    port = port +    for i in http_server._sockets.values(): +        sn = i.getsockname() +        if sn[0] == address: +            port = sn[1] +    return http_server, port + + +# begin nocover +def run(server): +    tornado.ioloop.IOLoop.instance().start() +    server.stop() + + +class APILog(tornado.web.RequestHandler): +    def get(self): +        self.write( +            dict( +                d = self.application.get_log() +            ) +        ) + + +class APILogClear(tornado.web.RequestHandler): +    def post(self): +        self.application.clear_log() +        self.write("OK") + + +class APIShutdown(tornado.web.RequestHandler): +    def post(self): +        tornado.ioloop.IOLoop.instance().stop() +        self.write("OK") + + +class APIInfo(tornado.web.RequestHandler): +    def get(self): +        self.write( +            dict( +                version = version.IVERSION +            ) +        ) + + +class _Page(tornado.web.RequestHandler): +    def render(self, name, **kwargs): +        tornado.web.RequestHandler.render(self, name + ".html", **kwargs) + + +class Index(_Page): +    name = "index" +    section = "main" +    def get(self): +        self.render(self.name, section=self.section, spec="") + + +class Preview(_Page): +    name = "preview" +    section = "main" +    SANITY = 1024*1024 +    def get(self): +        spec = self.get_argument("spec", None) +        args = dict( +            spec = spec, +            section = self.section, +            syntaxerror = None, +            error = None +        ) +        try: +            r = rparse.parse(self.application.settings, spec) +        except rparse.ParseException, v: +            args["syntaxerror"] = str(v) +            args["marked"] = v.marked() +            return self.render(self.name, **args) +        if r.length() > self.SANITY: +            error = "Refusing to preview a response of %s bytes. This is for your own good."%r.length() +            args["error"] = error +        else: +            d = utils.DummyRequest() +            r.serve(d) +            args["output"] = d.getvalue() +        self.render(self.name, **args) + + +class Help(_Page): +    name = "help" +    section = "help" +    def get(self): +        self.render(self.name, section=self.section) + + +class Log(_Page): +    name = "log" +    section = "log" +    def get(self): +        self.render(self.name, section=self.section, log=self.application.log) + + +class OneLog(_Page): +    name = "onelog" +    section = "log" +    def get(self, lid): +        l = pprint.pformat(self.application.log_by_id(int(lid))) +        self.render(self.name, section=self.section, alog=l, lid=lid) + + +class ClearLog(_Page): +    def post(self): +        self.application.clear_logs() +        self.redirect("/log")  | 
