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