lib/nitcorn: cache files served by `FileServer` for 1 hour
[nit.git] / lib / nitcorn / file_server.nit
index 2b94280..80f64d2 100644 (file)
@@ -42,14 +42,34 @@ end
 class FileServer
        super Action
 
-       # Root of `self` file system
+       # Root folder of `self` file system
        var root: String
 
+       init
+       do
+               var root = self.root
+
+               # Simplify the root path as each file requested will also be simplified
+               root = root.simplify_path
+
+               # Make sure the root ends with '/', this makes a difference in the security
+               # check on each file access.
+               root = root + "/"
+
+               self.root = root
+       end
+
        # Error page template for a given `code`
-       fun error_page(code: Int): Streamable do return new ErrorTemplate(code)
+       fun error_page(code: Int): Writable do return new ErrorTemplate(code)
 
        # Header of each directory page
-       var header: nullable Streamable = null is writable
+       var header: nullable Writable = null is writable
+
+       # Custom JavaScript code added within a `<script>` block to each page
+       var javascript_header: nullable Writable = null is writable
+
+       # Caching attributes of served files, used as the `cache-control` field in response headers
+       var cache_control = "public, max-age=360" is writable
 
        redef fun answer(request, turi)
        do
@@ -58,7 +78,6 @@ class FileServer
                var local_file = root.join_path(turi.strip_start_slashes)
                local_file = local_file.simplify_path
 
-
                # Is it reachable?
                #
                # This make sure that the requested file is within the root folder.
@@ -93,14 +112,15 @@ class FileServer
                                        var files = local_file.files
 
                                        var links = new Array[String]
-                                       if local_file.length > 1 then
-                                               # The extra / is a hack
-                                               var path = "/" + (turi + "/..").simplify_path
-                                               links.add "<a href=\"{path}\">..</a>"
+                                       if turi.length > 1 then
+                                               var path = (request.uri + "/..").simplify_path
+                                               links.add "<a href=\"{path}/\">..</a>"
                                        end
                                        for file in files do
-                                               var path = (turi + "/" + file).simplify_path
-                                               links.add "<a href=\"{path}\">{file}</a>"
+                                               var local_path = local_file.join_path(file).simplify_path
+                                               var web_path = file.simplify_path
+                                               if local_path.file_stat.is_dir then web_path = web_path + "/"
+                                               links.add "<a href=\"{web_path}\">{file}</a>"
                                        end
 
                                        var header = self.header
@@ -115,6 +135,9 @@ class FileServer
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
+       <script>
+               {{{javascript_header or else ""}}}
+       </script>
        <title>{{{title}}}</title>
 </head>
 <body>
@@ -131,8 +154,7 @@ class FileServer
                                        response.header["Content-Type"] = media_types["html"].as(not null)
                                else
                                        # It's a single file
-                                       var file = new IFStream.open(local_file)
-                                       response.body = file.read_all
+                                       response.files.add local_file
 
                                        var ext = local_file.file_extension
                                        if ext != null then
@@ -142,7 +164,8 @@ class FileServer
                                                else response.header["Content-Type"] = "application/octet-stream"
                                        end
 
-                                       file.close
+                                       # Cache control
+                                       response.header["cache-control"] = cache_control
                                end
 
                        else response = new HttpResponse(404)