diff options
Diffstat (limited to 'plugins/mod_http.lua')
-rw-r--r-- | plugins/mod_http.lua | 94 |
1 files changed, 81 insertions, 13 deletions
diff --git a/plugins/mod_http.lua b/plugins/mod_http.lua index 0cee26c4..c13a2363 100644 --- a/plugins/mod_http.lua +++ b/plugins/mod_http.lua @@ -11,24 +11,26 @@ pcall(function () module:depends("http_errors"); end); -local portmanager = require "core.portmanager"; -local moduleapi = require "core.moduleapi"; +local portmanager = require "prosody.core.portmanager"; +local moduleapi = require "prosody.core.moduleapi"; local url_parse = require "socket.url".parse; local url_build = require "socket.url".build; -local normalize_path = require "util.http".normalize_path; -local set = require "util.set"; +local http_util = require "prosody.util.http"; +local normalize_path = http_util.normalize_path; +local set = require "prosody.util.set"; +local array = require "prosody.util.array"; -local ip_util = require "util.ip"; +local ip_util = require "prosody.util.ip"; local new_ip = ip_util.new_ip; local match_ip = ip_util.match; local parse_cidr = ip_util.parse_cidr; -local server = require "net.http.server"; +local server = require "prosody.net.http.server"; server.set_default_host(module:get_option_string("http_default_host")); -server.set_option("body_size_limit", module:get_option_number("http_max_content_size")); -server.set_option("buffer_size_limit", module:get_option_number("http_max_buffer_size")); +server.set_option("body_size_limit", module:get_option_number("http_max_content_size", nil, 0)); +server.set_option("buffer_size_limit", module:get_option_number("http_max_buffer_size", nil, 0)); -- CORS settings local cors_overrides = module:get_option("http_cors_override", {}); @@ -36,7 +38,7 @@ local opt_methods = module:get_option_set("access_control_allow_methods", { "GET local opt_headers = module:get_option_set("access_control_allow_headers", { "Content-Type" }); local opt_origins = module:get_option_set("access_control_allow_origins"); local opt_credentials = module:get_option_boolean("access_control_allow_credentials", false); -local opt_max_age = module:get_option_number("access_control_max_age", 2 * 60 * 60); +local opt_max_age = module:get_option_period("access_control_max_age", "2 hours"); local opt_default_cors = module:get_option_boolean("http_default_cors_enabled", true); local function get_http_event(host, app_path, key) @@ -75,11 +77,12 @@ end local ports_by_scheme = { http = 80, https = 443, }; -- Helper to deduce a module's external URL -function moduleapi.http_url(module, app_name, default_path) +function moduleapi.http_url(module, app_name, default_path, mode) app_name = app_name or (module.name:gsub("^http_", "")); local external_url = url_parse(module:get_option_string("http_external_url")); - if external_url then + if external_url and mode ~= "internal" then + -- Current URL does not depend on knowing which ports are used, only configuration. local url = { scheme = external_url.scheme; host = external_url.host; @@ -91,6 +94,36 @@ function moduleapi.http_url(module, app_name, default_path) return url_build(url); end + if prosody.process_type ~= "prosody" then + -- We generally don't open ports outside of Prosody, so we can't rely on + -- portmanager to tell us which ports and services are used and derive the + -- URL from that, so instead we derive it entirely from configuration. + local https_ports = module:get_option_array("https_ports", { 5281 }); + local scheme = "https"; + local port = tonumber(https_ports[1]); + if not port then + -- https is disabled and no http_external_url set + scheme = "http"; + local http_ports = module:get_option_array("http_ports", { 5280 }); + port = tonumber(http_ports[1]); + if not port then + return "http://disabled.invalid/"; + end + end + + local url = { + scheme = scheme; + host = module:get_option_string("http_host", module.global and module:get_option_string("http_default_host") or module.host); + port = port; + path = get_base_path(module, app_name, default_path or "/" .. app_name); + } + if ports_by_scheme[url.scheme] == url.port then + url.port = nil + end + return url_build(url); + end + + -- Use portmanager to find the actual port of https or http services local services = portmanager.get_active_services(); local http_services = services:get("https") or services:get("http") or {}; for interface, ports in pairs(http_services) do -- luacheck: ignore 213/interface @@ -112,12 +145,16 @@ function moduleapi.http_url(module, app_name, default_path) return "http://disabled.invalid/"; end +local function header_set_tostring(header_value) + return array(header_value:items()):concat(", "); +end + local function apply_cors_headers(response, methods, headers, max_age, allow_credentials, allowed_origins, origin) if allowed_origins and not allowed_origins[origin] then return; end - response.headers.access_control_allow_methods = tostring(methods); - response.headers.access_control_allow_headers = tostring(headers); + response.headers.access_control_allow_methods = header_set_tostring(methods); + response.headers.access_control_allow_headers = header_set_tostring(headers); response.headers.access_control_max_age = tostring(max_age) response.headers.access_control_allow_origin = origin or "*"; if allow_credentials then @@ -292,7 +329,13 @@ module.add_host(module); -- set up handling on global context too local trusted_proxies = module:get_option_set("trusted_proxies", { "127.0.0.1", "::1" })._items; +--- deal with [ipv6]:port / ip:port format +local function normal_ip(ip) + return ip:match("^%[([%x:]*)%]") or ip:match("^([%d.]+)") or ip; +end + local function is_trusted_proxy(ip) + ip = normal_ip(ip); if trusted_proxies[ip] then return true; end @@ -308,6 +351,30 @@ end local function get_forwarded_connection_info(request) --> ip:string, secure:boolean local ip = request.ip; local secure = request.secure; -- set by net.http.server + + local forwarded = http_util.parse_forwarded(request.headers.forwarded); + if forwarded then + request.forwarded = forwarded; + for i = #forwarded, 1, -1 do + local proxy = forwarded[i] + if is_trusted_proxy(ip) then + ip = normal_ip(proxy["for"]); + secure = secure and proxy.proto == "https"; + else + break + end + end + end + + return ip, secure; +end + +-- TODO switch to RFC 7239 by default once support is more common +if module:get_option_boolean("http_legacy_x_forwarded", true) then +function get_forwarded_connection_info(request) --> ip:string, secure:boolean + local ip = request.ip; + local secure = request.secure; -- set by net.http.server + local forwarded_for = request.headers.x_forwarded_for; if forwarded_for then -- luacheck: ignore 631 @@ -330,6 +397,7 @@ local function get_forwarded_connection_info(request) --> ip:string, secure:bool return ip, secure; end +end module:wrap_object_event(server._events, false, function (handlers, event_name, event_data) local request = event_data.request; |