docker full: install missing package for SDL2 mixer
[nit.git] / lib / popcorn / pop_handlers.nit
index 2c85e15..6b1e825 100644 (file)
@@ -18,7 +18,9 @@
 module pop_handlers
 
 import pop_routes
+import json::static
 import json
+import csv
 
 # Class handler for a route.
 #
@@ -235,10 +237,16 @@ class StaticHandler
        # Static files directory to serve.
        var static_dir: String
 
+       # Default file to serve if nothing matches the request.
+       #
+       # `null` for no default file.
+       var default_file: nullable String
+
        # Internal file server used to lookup and render files.
        var file_server: FileServer is lazy do
                var srv = new FileServer(static_dir)
                srv.show_directory_listing = false
+               srv.default_file = default_file
                return srv
        end
 
@@ -307,28 +315,71 @@ class Router
        # List of handlers to match with requests.
        private var handlers = new Map[AppRoute, Handler]
 
+       # List of handlers to match before every other.
+       private var pre_handlers = new Map[AppRoute, Handler]
+
+       # List of handlers to match after every other.
+       private var post_handlers = new Map[AppRoute, Handler]
+
        # Register a `handler` for a route `path`.
        #
        # Route paths are matched in registration order.
        fun use(path: String, handler: Handler) do
-               var route
-               if handler isa Router or handler isa StaticHandler then
-                       route = new AppGlobRoute(path)
-               else if path.has_suffix("*") then
-                       route = new AppGlobRoute(path)
-               else
-                       route = new AppParamRoute(path)
-               end
+               var route = build_route(handler, path)
                handlers[route] = handler
        end
 
+       # Register a pre-handler for a route `path`.
+       #
+       # Prehandlers are matched before every other handlers in registrastion order.
+       fun use_before(path: String, handler: Handler) do
+               var route = build_route(handler, path)
+               pre_handlers[route] = handler
+       end
+
+       # Register a post-handler for a route `path`.
+       #
+       # Posthandlers are matched after every other handlers in registrastion order.
+       fun use_after(path: String, handler: Handler) do
+               var route = build_route(handler, path)
+               post_handlers[route] = handler
+       end
+
        redef fun handle(route, uri, req, res) do
                if not route.match(uri) then return
+               handle_pre(route, uri, req, res)
+               handle_in(route, uri, req, res)
+               handle_post(route, uri, req, res)
+       end
+
+       private fun handle_pre(route: AppRoute, uri: String, req: HttpRequest, res: HttpResponse) do
+               for hroute, handler in pre_handlers do
+                       handler.handle(hroute, route.uri_root(uri), req, res)
+               end
+       end
+
+       private fun handle_in(route: AppRoute, uri: String, req: HttpRequest, res: HttpResponse) do
                for hroute, handler in handlers do
                        handler.handle(hroute, route.uri_root(uri), req, res)
                        if res.sent then break
                end
        end
+
+       private fun handle_post(route: AppRoute, uri: String, req: HttpRequest, res: HttpResponse) do
+               for hroute, handler in post_handlers do
+                       handler.handle(hroute, route.uri_root(uri), req, res)
+               end
+       end
+
+       private fun build_route(handler: Handler, path: String): AppRoute do
+               if handler isa Router or handler isa StaticHandler then
+                       return new AppGlobRoute(path)
+               else if path.has_suffix("*") then
+                       return new AppGlobRoute(path)
+               else
+                       return new AppParamRoute(path)
+               end
+       end
 end
 
 # Popcorn application.
@@ -400,7 +451,7 @@ redef class HttpResponse
        end
 
        # Write data as JSON and set the right content type header.
-       fun json(json: nullable Jsonable, status: nullable Int) do
+       fun json(json: nullable Serializable, status: nullable Int) do
                header["Content-Type"] = media_types["json"].as(not null)
                if json == null then
                        send(null, status)
@@ -409,13 +460,36 @@ redef class HttpResponse
                end
        end
 
+       # Write data as CSV and set the right content type header.
+       fun csv(csv: nullable CsvDocument, status: nullable Int) do
+               header["Content-Type"] = media_types["csv"].as(not null)
+               if csv == null then
+                       send(null, status)
+               else
+                       send(csv.write_to_string, status)
+               end
+       end
+
+       # Write error as JSON.
+       #
+       # Format: `{"message": message, "status": status}`
+       fun json_error(message: String, status: Int) do
+               var obj = new JsonObject
+               obj["status"] = status
+               obj["message"] = message
+               json(obj, status)
+       end
+
        # Redirect response to `location`
+       #
+       # Use by default 303 See Other as it is the RFC
+       # way to redirect web applications to a new URI.
        fun redirect(location: String, status: nullable Int) do
                header["Location"] = location
                if status != null then
                        status_code = status
                else
-                       status_code = 302
+                       status_code = 303
                end
                check_sent
                sent = true