Software /
code /
prosody
Comparison
net/http/files.lua @ 9949:a39f3681d685
net.http.files: Make into standalone library
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Fri, 05 Apr 2019 17:09:03 +0200 |
parent | 9948:c93fdec624c7 |
child | 9996:9bb1bed3e8e3 |
comparison
equal
deleted
inserted
replaced
9948:c93fdec624c7 | 9949:a39f3681d685 |
---|---|
4 -- | 4 -- |
5 -- This project is MIT/X11 licensed. Please see the | 5 -- This project is MIT/X11 licensed. Please see the |
6 -- COPYING file in the source package for more information. | 6 -- COPYING file in the source package for more information. |
7 -- | 7 -- |
8 | 8 |
9 module:depends("http"); | |
10 local server = require"net.http.server"; | 9 local server = require"net.http.server"; |
11 local lfs = require "lfs"; | 10 local lfs = require "lfs"; |
11 local new_cache = require "util.cache".new; | |
12 local log = require "util.logger".init("net.http.files"); | |
12 | 13 |
13 local os_date = os.date; | 14 local os_date = os.date; |
14 local open = io.open; | 15 local open = io.open; |
15 local stat = lfs.attributes; | 16 local stat = lfs.attributes; |
16 local build_path = require"socket.url".build_path; | 17 local build_path = require"socket.url".build_path; |
17 local path_sep = package.config:sub(1,1); | 18 local path_sep = package.config:sub(1,1); |
18 | 19 |
19 local base_path = module:get_option_path("http_files_dir", module:get_option_path("http_path")); | |
20 local cache_size = module:get_option_number("http_files_cache_size", 128); | |
21 local cache_max_file_size = module:get_option_number("http_files_cache_max_file_size", 4096); | |
22 local dir_indices = module:get_option_array("http_index_files", { "index.html", "index.htm" }); | |
23 local directory_index = module:get_option_boolean("http_dir_listing"); | |
24 | |
25 local mime_map = module:shared("/*/http_files/mime").types; | |
26 if not mime_map then | |
27 mime_map = { | |
28 html = "text/html", htm = "text/html", | |
29 xml = "application/xml", | |
30 txt = "text/plain", | |
31 css = "text/css", | |
32 js = "application/javascript", | |
33 png = "image/png", | |
34 gif = "image/gif", | |
35 jpeg = "image/jpeg", jpg = "image/jpeg", | |
36 svg = "image/svg+xml", | |
37 }; | |
38 module:shared("/*/http_files/mime").types = mime_map; | |
39 | |
40 local mime_types, err = open(module:get_option_path("mime_types_file", "/etc/mime.types", "config"), "r"); | |
41 if mime_types then | |
42 local mime_data = mime_types:read("*a"); | |
43 mime_types:close(); | |
44 setmetatable(mime_map, { | |
45 __index = function(t, ext) | |
46 local typ = mime_data:match("\n(%S+)[^\n]*%s"..(ext:lower()).."%s") or "application/octet-stream"; | |
47 t[ext] = typ; | |
48 return typ; | |
49 end | |
50 }); | |
51 end | |
52 end | |
53 | 20 |
54 local forbidden_chars_pattern = "[/%z]"; | 21 local forbidden_chars_pattern = "[/%z]"; |
55 if prosody.platform == "windows" then | 22 if package.config:sub(1,1) == "\\" then |
56 forbidden_chars_pattern = "[/%z\001-\031\127\"*:<>?|]" | 23 forbidden_chars_pattern = "[/%z\001-\031\127\"*:<>?|]" |
57 end | 24 end |
58 | 25 |
59 local urldecode = require "util.http".urldecode; | 26 local urldecode = require "util.http".urldecode; |
60 function sanitize_path(path) | 27 local function sanitize_path(path) --> util.paths or util.http? |
61 if not path then return end | 28 if not path then return end |
62 local out = {}; | 29 local out = {}; |
63 | 30 |
64 local c = 0; | 31 local c = 0; |
65 for component in path:gmatch("([^/]+)") do | 32 for component in path:gmatch("([^/]+)") do |
81 out[c+1] = ""; | 48 out[c+1] = ""; |
82 end | 49 end |
83 return "/"..table.concat(out, "/"); | 50 return "/"..table.concat(out, "/"); |
84 end | 51 end |
85 | 52 |
86 local cache = require "util.cache".new(cache_size); | 53 local function serve(opts) |
87 | |
88 function serve(opts) | |
89 if type(opts) ~= "table" then -- assume path string | 54 if type(opts) ~= "table" then -- assume path string |
90 opts = { path = opts }; | 55 opts = { path = opts }; |
91 end | 56 end |
57 local mime_map = opts.mime_map or { html = "text/html" }; | |
58 local cache = new_cache(opts.cache_size or 256); | |
59 local cache_max_file_size = tonumber(opts.cache_max_file_size) or 1024 | |
92 -- luacheck: ignore 431 | 60 -- luacheck: ignore 431 |
93 local base_path = opts.path; | 61 local base_path = opts.path; |
94 local dir_indices = opts.index_files or dir_indices; | 62 local dir_indices = opts.index_files or { "index.html", "index.htm" }; |
95 local directory_index = opts.directory_index; | 63 local directory_index = opts.directory_index; |
96 local function serve_file(event, path) | 64 local function serve_file(event, path) |
97 local request, response = event.request, event.response; | 65 local request, response = event.request, event.response; |
98 local sanitized_path = sanitize_path(path); | 66 local sanitized_path = sanitize_path(path); |
99 if path and not sanitized_path then | 67 if path and not sanitized_path then |
149 response_headers.content_type = mime_map.html; | 117 response_headers.content_type = mime_map.html; |
150 | 118 |
151 else | 119 else |
152 local f, err = open(full_path, "rb"); | 120 local f, err = open(full_path, "rb"); |
153 if not f then | 121 if not f then |
154 module:log("debug", "Could not open %s. Error was %s", full_path, err); | 122 log("debug", "Could not open %s. Error was %s", full_path, err); |
155 return 403; | 123 return 403; |
156 end | 124 end |
157 local ext = full_path:match("%.([^./]+)$"); | 125 local ext = full_path:match("%.([^./]+)$"); |
158 local content_type = ext and mime_map[ext]; | 126 local content_type = ext and mime_map[ext]; |
159 response_headers.content_type = content_type; | 127 response_headers.content_type = content_type; |
160 if attr.size > cache_max_file_size then | 128 if attr.size > cache_max_file_size then |
161 response_headers.content_length = attr.size; | 129 response_headers.content_length = attr.size; |
162 module:log("debug", "%d > cache_max_file_size", attr.size); | 130 log("debug", "%d > cache_max_file_size", attr.size); |
163 return response:send_file(f); | 131 return response:send_file(f); |
164 else | 132 else |
165 data = f:read("*a"); | 133 data = f:read("*a"); |
166 f:close(); | 134 f:close(); |
167 end | 135 end |
172 end | 140 end |
173 | 141 |
174 return serve_file; | 142 return serve_file; |
175 end | 143 end |
176 | 144 |
177 function wrap_route(routes) | 145 return { |
178 for route,handler in pairs(routes) do | 146 serve = serve; |
179 if type(handler) ~= "function" then | 147 } |
180 routes[route] = serve(handler); | |
181 end | |
182 end | |
183 return routes; | |
184 end | |
185 | 148 |
186 if base_path then | |
187 module:provides("http", { | |
188 route = { | |
189 ["GET /*"] = serve { | |
190 path = base_path; | |
191 directory_index = directory_index; | |
192 } | |
193 }; | |
194 }); | |
195 else | |
196 module:log("debug", "http_files_dir not set, assuming use by some other module"); | |
197 end | |
198 |