+ local base_path = opts.path;
+ local dir_indices = opts.index_files or dir_indices;
+ local directory_index = opts.directory_index;
+ local function serve_file(event, path)
+ local request, response = event.request, event.response;
+ local orig_path = request.path;
+ local full_path = base_path .. (path and "/"..path or "");
+ local attr = stat(full_path);
+ if not attr then
+ return 404;
+ end
+
+ local request_headers, response_headers = request.headers, response.headers;
+
+ local last_modified = os_date('!%a, %d %b %Y %H:%M:%S GMT', attr.modification);
+ response_headers.last_modified = last_modified;
+
+ local etag = ("%02x-%x-%x-%x"):format(attr.dev or 0, attr.ino or 0, attr.size or 0, attr.modification or 0);
+ response_headers.etag = etag;
+
+ local if_none_match = request_headers.if_none_match
+ local if_modified_since = request_headers.if_modified_since;
+ if etag == if_none_match
+ or (not if_none_match and last_modified == if_modified_since) then
+ return 304;
+ end
+
+ local data = cache[orig_path];
+ if data and data.etag == etag then
+ response_headers.content_type = data.content_type;
+ data = data.data;
+ elseif attr.mode == "directory" and path then
+ if full_path:sub(-1) ~= "/" then
+ local path = { is_absolute = true, is_directory = true };
+ for dir in orig_path:gmatch("[^/]+") do path[#path+1]=dir; end
+ response_headers.location = build_path(path);
+ return 301;
+ end
+ for i=1,#dir_indices do
+ if stat(full_path..dir_indices[i], "mode") == "file" then
+ return serve_file(event, path..dir_indices[i]);
+ end
+ end
+
+ if directory_index then
+ data = server._events.fire_event("directory-index", { path = request.path, full_path = full_path });
+ end
+ if not data then
+ return 403;
+ end
+ cache[orig_path] = { data = data, content_type = mime_map.html; etag = etag; };
+ response_headers.content_type = mime_map.html;
+
+ else
+ local f, err = open(full_path, "rb");
+ if f then
+ data, err = f:read("*a");
+ f:close();
+ end
+ if not data then
+ module:log("debug", "Could not open or read %s. Error was %s", full_path, err);
+ return 403;
+ end
+ local ext = full_path:match("%.([^./]+)$");
+ local content_type = ext and mime_map[ext];
+ cache[orig_path] = { data = data; content_type = content_type; etag = etag };
+ response_headers.content_type = content_type;
+ end
+
+ return response:send(data);
+ end
+
+ return serve_file;
+end
+
+function wrap_route(routes)
+ for route,handler in pairs(routes) do
+ if type(handler) ~= "function" then
+ routes[route] = serve(handler);
+ end