comparison plugins/mod_http.lua @ 10411:db2a06b9ff98

Merge 0.11->trunk
author Kim Alvefur <zash@zash.se>
date Sat, 16 Nov 2019 16:52:31 +0100
parents abfc05495d8b
children 5ce6cbb5ce6a
comparison
equal deleted inserted replaced
10410:659b577f280c 10411:db2a06b9ff98
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:set_global(); 9 module:set_global();
10 module:depends("http_errors"); 10 pcall(function ()
11 module:depends("http_errors");
12 end);
11 13
12 local portmanager = require "core.portmanager"; 14 local portmanager = require "core.portmanager";
13 local moduleapi = require "core.moduleapi"; 15 local moduleapi = require "core.moduleapi";
14 local url_parse = require "socket.url".parse; 16 local url_parse = require "socket.url".parse;
15 local url_build = require "socket.url".build; 17 local url_build = require "socket.url".build;
16 local normalize_path = require "util.http".normalize_path; 18 local normalize_path = require "util.http".normalize_path;
19 local set = require "util.set";
17 20
18 local server = require "net.http.server"; 21 local server = require "net.http.server";
19 22
20 server.set_default_host(module:get_option_string("http_default_host")); 23 server.set_default_host(module:get_option_string("http_default_host"));
21 24
22 server.set_option("body_size_limit", module:get_option_number("http_max_content_size")); 25 server.set_option("body_size_limit", module:get_option_number("http_max_content_size"));
23 server.set_option("buffer_size_limit", module:get_option_number("http_max_buffer_size")); 26 server.set_option("buffer_size_limit", module:get_option_number("http_max_buffer_size"));
27
28 -- CORS settigs
29 local opt_methods = module:get_option_set("access_control_allow_methods", { "GET", "OPTIONS" });
30 local opt_headers = module:get_option_set("access_control_allow_headers", { "Content-Type" });
31 local opt_credentials = module:get_option_boolean("access_control_allow_credentials", false);
32 local opt_max_age = module:get_option_number("access_control_max_age", 2 * 60 * 60);
24 33
25 local function get_http_event(host, app_path, key) 34 local function get_http_event(host, app_path, key)
26 local method, path = key:match("^(%S+)%s+(.+)$"); 35 local method, path = key:match("^(%S+)%s+(.+)$");
27 if not method then -- No path specified, default to "" (base path) 36 if not method then -- No path specified, default to "" (base path)
28 method, path = key, ""; 37 method, path = key, "";
81 end 90 end
82 module:log("warn", "No http ports enabled, can't generate an external URL"); 91 module:log("warn", "No http ports enabled, can't generate an external URL");
83 return "http://disabled.invalid/"; 92 return "http://disabled.invalid/";
84 end 93 end
85 94
95 local function apply_cors_headers(response, methods, headers, max_age, allow_credentials, origin)
96 response.headers.access_control_allow_methods = tostring(methods);
97 response.headers.access_control_allow_headers = tostring(headers);
98 response.headers.access_control_max_age = tostring(max_age)
99 response.headers.access_control_allow_origin = origin or "*";
100 if allow_credentials then
101 response.headers.access_control_allow_credentials = "true";
102 end
103 end
104
86 function module.add_host(module) 105 function module.add_host(module)
87 local host = module.host; 106 local host = module.host;
88 if host ~= "*" then 107 if host ~= "*" then
89 host = module:get_option_string("http_host", host); 108 host = module:get_option_string("http_host", host);
90 end 109 end
99 module:log("error", "HTTP app has no 'name', add one or use module:provides('http', app)"); 118 module:log("error", "HTTP app has no 'name', add one or use module:provides('http', app)");
100 return; 119 return;
101 end 120 end
102 apps[app_name] = apps[app_name] or {}; 121 apps[app_name] = apps[app_name] or {};
103 local app_handlers = apps[app_name]; 122 local app_handlers = apps[app_name];
123
124 local app_methods = opt_methods;
125
126 local function cors_handler(event_data)
127 local request, response = event_data.request, event_data.response;
128 apply_cors_headers(response, app_methods, opt_headers, opt_max_age, opt_credentials, request.headers.origin);
129 end
130
131 local function options_handler(event_data)
132 cors_handler(event_data);
133 return "";
134 end
135
104 for key, handler in pairs(event.item.route or {}) do 136 for key, handler in pairs(event.item.route or {}) do
105 local event_name = get_http_event(host, app_path, key); 137 local event_name = get_http_event(host, app_path, key);
106 if event_name then 138 if event_name then
139 local method = event_name:match("^%S+");
140 if not app_methods:contains(method) then
141 app_methods = app_methods + set.new{ method };
142 end
143 local options_event_name = event_name:gsub("^%S+", "OPTIONS");
107 if type(handler) ~= "function" then 144 if type(handler) ~= "function" then
108 local data = handler; 145 local data = handler;
109 handler = function () return data; end 146 handler = function () return data; end
110 elseif event_name:sub(-2, -1) == "/*" then 147 elseif event_name:sub(-2, -1) == "/*" then
111 local base_path_len = #event_name:match("/.+$"); 148 local base_path_len = #event_name:match("/.+$");
117 module:hook_object_event(server, event_name:sub(1, -3), redir_handler, -1); 154 module:hook_object_event(server, event_name:sub(1, -3), redir_handler, -1);
118 elseif event_name:sub(-1, -1) == "/" then 155 elseif event_name:sub(-1, -1) == "/" then
119 module:hook_object_event(server, event_name:sub(1, -2), redir_handler, -1); 156 module:hook_object_event(server, event_name:sub(1, -2), redir_handler, -1);
120 end 157 end
121 if not app_handlers[event_name] then 158 if not app_handlers[event_name] then
122 app_handlers[event_name] = handler; 159 app_handlers[event_name] = {
160 main = handler;
161 cors = cors_handler;
162 options = options_handler;
163 };
123 module:hook_object_event(server, event_name, handler); 164 module:hook_object_event(server, event_name, handler);
165 module:hook_object_event(server, event_name, cors_handler, 1);
166 module:hook_object_event(server, options_event_name, options_handler, -1);
124 else 167 else
125 module:log("warn", "App %s added handler twice for '%s', ignoring", app_name, event_name); 168 module:log("warn", "App %s added handler twice for '%s', ignoring", app_name, event_name);
126 end 169 end
127 else 170 else
128 module:log("error", "Invalid route in %s, %q. See https://prosody.im/doc/developers/http#routes", app_name, key); 171 module:log("error", "Invalid route in %s, %q. See https://prosody.im/doc/developers/http#routes", app_name, key);
137 end 180 end
138 181
139 local function http_app_removed(event) 182 local function http_app_removed(event)
140 local app_handlers = apps[event.item.name]; 183 local app_handlers = apps[event.item.name];
141 apps[event.item.name] = nil; 184 apps[event.item.name] = nil;
142 for event_name, handler in pairs(app_handlers) do 185 for event_name, handlers in pairs(app_handlers) do
143 module:unhook_object_event(server, event_name, handler); 186 module:unhook_object_event(server, event_name, handlers.main);
187 module:unhook_object_event(server, event_name, handlers.cors);
188 local options_event_name = event_name:gsub("^%S+", "OPTIONS");
189 module:unhook_object_event(server, options_event_name, handlers.options);
144 end 190 end
145 end 191 end
146 192
147 module:handle_items("http-provider", http_app_added, http_app_removed); 193 module:handle_items("http-provider", http_app_added, http_app_removed);
148 194
193 module:provides("net", { 239 module:provides("net", {
194 name = "https"; 240 name = "https";
195 listener = server.listener; 241 listener = server.listener;
196 default_port = 5281; 242 default_port = 5281;
197 encryption = "ssl"; 243 encryption = "ssl";
198 ssl_config = {
199 verify = "none";
200 };
201 multiplex = { 244 multiplex = {
202 pattern = "^[A-Z]"; 245 pattern = "^[A-Z]";
203 }; 246 };
204 }); 247 });