# HG changeset patch # User jbe # Date 1421023691 -3600 # Node ID ba3dd4a17e3d7c2bbaddb40d596a716cfa413c60 # Parent 6ac7133bb58e94480a9b9d6f32e8421e1cc77fa7 Some code cleanup/rearrangement for request handling diff -r 6ac7133bb58e -r ba3dd4a17e3d framework/bin/mcp.lua --- a/framework/bin/mcp.lua Sat Jan 10 10:44:17 2015 +0100 +++ b/framework/bin/mcp.lua Mon Jan 12 01:48:11 2015 +0100 @@ -188,10 +188,12 @@ -- execute configurations for i, config_name in ipairs(WEBMCP_CONFIG_NAMES) do execute.config(config_name) + execute.prefork_initializers() end -- interactive console mode if WEBMCP_MODE == "interactive" then + execute.postfork_initializers() trace.disable() -- avoids memory leakage (TODO: needs general solution for moonbridge?) end @@ -199,13 +201,10 @@ if WEBMCP_MODE == "listen" then local http = require("moonbridge_http") for i, listener in ipairs(listeners) do - listener.prepare = execute.prefork_initializers + listener.prepare = execute.postfork_initializers listener.connect = http.generate_handler( - request.get_http_options(), - function(http_request) - execute.postfork_initializers() - request.handler(http_request) - end + request.handler, + request.get_http_options() ) listener.finish = execute.finalizers moonbridge_listen(listener) diff -r 6ac7133bb58e -r ba3dd4a17e3d framework/env/request/__init.lua --- a/framework/env/request/__init.lua Sat Jan 10 10:44:17 2015 +0100 +++ b/framework/env/request/__init.lua Mon Jan 12 01:48:11 2015 +0100 @@ -14,82 +14,6 @@ request._json_requests_allowed = false request._params = {} -local depth -if cgi then -- if-clause to support interactive mode - if cgi.params._webmcp_404 then - request.force_absolute_baseurl() - request._is_404 = true - end - for key, value in pairs(cgi.params) do - if not string.match(key, "^_webmcp_") then - request._params[key] = value - end - end - local path = cgi.params._webmcp_path - if path then - local function parse() - local module, action, view, suffix, id - if path == "" then - request._module = "index" - request._view = "index" - return - end - module = string.match(path, "^([^/]+)/$") - if module then - request._module = module - request._view = "index" - return - end - module, action = string.match(path, "^([^/]+)/([^/.]+)$") - if module then - request._module = module - request._action = action - return - end - module, view, suffix = string.match(path, "^([^/]+)/([^/.]+)%.([^/]+)$") - if module then - request._module = module - request._view = view - request._suffix = suffix - return - end - module, view, id, suffix = string.match(path, "^([^/]+)/([^/]+)/([^/.]+)%.([^/]+)$") - if module then - request._module = module - request._view = view - request._id = id - request._suffix = suffix - return - end - request._is_404 = true - end - parse() - -- allow id to also be set by "_webmcp_id" parameter - if cgi.params._webmcp_id ~= nil then - request._id = cgi.params._webmcp_id - end - depth = 0 - for match in string.gmatch(path, "/") do - depth = depth + 1 - end - else - request._module = cgi.params._webmcp_module - request._action = cgi.params._webmcp_action - request._view = cgi.params._webmcp_view - request._suffix = cgi.params._webmcp_suffix - request._id = cgi.params._webmcp_id - depth = tonumber(cgi.params._webmcp_urldepth) - end -end -if depth and depth > 0 then - local elements = {} - for i = 1, depth do - elements[#elements+1] = "../" - end - request._relative_baseurl = table.concat(elements) -else - request._relative_baseurl = "./" -end request._app_basepath = assert( os.getenv("WEBMCP_APP_BASEPATH"), diff -r 6ac7133bb58e -r ba3dd4a17e3d framework/env/request/default_router.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/framework/env/request/default_router.lua Mon Jan 12 01:48:11 2015 +0100 @@ -0,0 +1,267 @@ +-- TODO: function incomplete yet + +local function file_exists(filename) + local file = io.open(filename, "r") + if file then + io.close(file) + return true + else + return false + end +end + +function request.default_router(path) + request._http_request = http_request + local path = http_request.path + local function parse_path() + if not path then + return + end + if path == "/" then + return {module = "index", view = "index"} + end + module = string.match(path, "^/([^/]+)/$") + if module then + return {module = module, view = "index"} + end + module, action = string.match(path, "^/([^/]+)/([^/.]+)$") + if module then + return {module = module, action = action} + end + module, view, suffix = string.match(path, "^/([^/]+)/([^/.]+)%.([^/]+)$") + if module then + return {module = module, view = view, suffix = suffix} + end + module, view, id, suffix = string.match(path, "^/([^/]+)/([^/]+)/([^/.]+)%.([^/]+)$") + if module then + return {module = module, view = view, id = id, suffix = suffix} + end + end + request._data = parse_path() + if path then + local elements = {} + for match in string.gmatch(path, "/") do + elements[#elements+1] = "../" + end + elements[#elements] = nil + if #elements > 0 then + request._relative_baseurl = table.concat(elements) + else + request._relative_baseurl = "./" + end + end + local success, error_info = xpcall( + function() + + -- restore slots if coming from http redirect + local tempstore_value = request.get_param{method = "GET", name = "_tempstore"} + if tempstore_value then + trace.restore_slots{} + local blob = tempstore.pop(tempstore_value) + if blob then slot.restore_all(blob) end + end + + + if request.is_404() then + request.set_status("404 Not Found") + if request.get_404_route() then + request.forward(request.get_404_route()) + else + error("No 404 page set.") + end + elseif request.get_action() then + trace.request{ + module = request.get_module(), + action = request.get_action() + } + if + request.get_404_route() and + not file_exists( + encode.action_file_path{ + module = request.get_module(), + action = request.get_action() + } + ) + then + request.set_status("404 Not Found") + request.forward(request.get_404_route()) + else + if cgi.method ~= "POST" then + request.set_status("405 Method Not Allowed") + cgi.add_header("Allow: POST") + error("Tried to invoke an action with a GET request.") + end + local action_status = execute.filtered_action{ + module = request.get_module(), + action = request.get_action(), + } + if not request.is_rerouted() then + local routing_mode, routing_module, routing_view + routing_mode = cgi.params["_webmcp_routing." .. action_status .. ".mode"] + routing_module = cgi.params["_webmcp_routing." .. action_status .. ".module"] + routing_view = cgi.params["_webmcp_routing." .. action_status .. ".view"] + routing_anchor = cgi.params["_webmcp_routing." .. action_status .. ".anchor"] + if not (routing_mode or routing_module or routing_view) then + action_status = "default" + routing_mode = cgi.params["_webmcp_routing.default.mode"] + routing_module = cgi.params["_webmcp_routing.default.module"] + routing_view = cgi.params["_webmcp_routing.default.view"] + routing_anchor = cgi.params["_webmcp_routing.default.anchor"] + end + assert(routing_module, "Routing information has no module.") + assert(routing_view, "Routing information has no view.") + if routing_mode == "redirect" then + local routing_params = {} + for key, value in pairs(cgi.params) do + local status, stripped_key = string.match( + key, "^_webmcp_routing%.([^%.]*)%.params%.(.*)$" + ) + if status == action_status then + routing_params[stripped_key] = value + end + end + request.redirect{ + module = routing_module, + view = routing_view, + id = cgi.params["_webmcp_routing." .. action_status .. ".id"], + params = routing_params, + anchor = routing_anchor + } + elseif routing_mode == "forward" then + request.forward{ module = routing_module, view = routing_view } + else + error("Missing or unknown routing mode in request parameters.") + end + end + end + else + -- no action + trace.request{ + module = request.get_module(), + view = request.get_view() + } + if + request.get_404_route() and + not file_exists( + encode.view_file_path{ + module = request.get_module(), + view = request.get_view() + } + ) + then + request.set_status("404 Not Found") + request.forward(request.get_404_route()) + end + end + + if not request.get_redirect_data() then + request.process_forward() + local view = request.get_view() + if string.find(view, "^_") then + error("Tried to call a private view (prefixed with underscore).") + end + execute.filtered_view{ + module = request.get_module(), + view = view, + } + end + + -- force error due to missing absolute base URL until its too late to display error message + --if request.get_redirect_data() then + -- request.get_absolute_baseurl() + --end + + end, + + function(errobj) + return { + errobj = errobj, + stacktrace = string.gsub( + debug.traceback('', 2), + "^\r?\n?stack traceback:\r?\n?", "" + ) + } + end + ) + + if not success then trace.error{} end + + -- laufzeitermittlung + trace.exectime{ real = extos.monotonic_hires_time(), cpu = os.clock() } + + slot.select('trace', trace.render) -- render trace information + + local redirect_data = request.get_redirect_data() + + -- log error and switch to error layout, unless success + if not success then + local errobj = error_info.errobj + local stacktrace = error_info.stacktrace + if not request.get_status() and not request.get_json_request_slots() then + request.set_status("500 Internal Server Error") + end + slot.set_layout('system_error') + slot.select('system_error', function() + if getmetatable(errobj) == mondelefant.errorobject_metatable then + slot.put( + "

Database error of class ", + encode.html(errobj.code), + " occured:
", + encode.html(errobj.message), + "

" + ) + else + slot.put("

", encode.html(tostring(errobj)), "

") + end + slot.put("

Stack trace follows:
") + slot.put(encode.html_newlines(encode.html(stacktrace))) + slot.put("

") + end) + elseif redirect_data then + local redirect_params = {} + for key, value in pairs(redirect_data.params) do + redirect_params[key] = value + end + local slot_dump = slot.dump_all() + if slot_dump ~= "" then + redirect_params.tempstore = tempstore.save(slot_dump) + end + local json_request_slots = request.get_json_request_slots() + if json_request_slots then + redirect_params["_webmcp_json_slots[]"] = json_request_slots + end + cgi.redirect( + encode.url{ + base = request.get_absolute_baseurl(), + module = redirect_data.module, + view = redirect_data.view, + id = redirect_data.id, + params = redirect_params, + anchor = redirect_data.anchor + } + ) + cgi.send_data() + end + + if not success or not redirect_data then + + local http_status = request.get_status() + if http_status then + cgi.set_status(http_status) + end + + local json_request_slots = request.get_json_request_slots() + if json_request_slots then + cgi.set_content_type('application/json') + local data = {} + for idx, slot_ident in ipairs(json_request_slots) do + data[slot_ident] = slot.get_content(slot_ident) + end + cgi.send_data(encode.json(data)) + else + cgi.set_content_type(slot.get_content_type()) + cgi.send_data(slot.render_layout()) + end + end + +end diff -r 6ac7133bb58e -r ba3dd4a17e3d framework/env/request/handler.lua --- a/framework/env/request/handler.lua Sat Jan 10 10:44:17 2015 +0100 +++ b/framework/env/request/handler.lua Mon Jan 12 01:48:11 2015 +0100 @@ -1,228 +1,30 @@ --- TODO: function incomplete yet +--[[-- +request.handler( + request -- HTTP request object +) + +Called by mcp.lua to process an HTTP request. Performs some initializations, then calls request.router(). + +--]]-- function request.handler(http_request) request._http_request = http_request - - local success, error_info = xpcall( - function() - - -- restore slots if coming from http redirect - if cgi.params.tempstore then - trace.restore_slots{} - local blob = tempstore.pop(cgi.params.tempstore) - if blob then slot.restore_all(blob) end - end - - local function file_exists(filename) - local file = io.open(filename, "r") - if file then - io.close(file) - return true - else - return false - end - end + local path = http_request.path + if path then + local elements = {} + for match in string.gmatch(path, "/") do + elements[#elements+1] = "../" + end + elements[#elements] = nil + if #elements > 0 then + request._relative_baseurl = table.concat(elements) + else + request._relative_baseurl = "./" + end + else + request._relative_baseurl = nil + end + request.router() +end - if request.is_404() then - request.set_status("404 Not Found") - if request.get_404_route() then - request.forward(request.get_404_route()) - else - error("No 404 page set.") - end - elseif request.get_action() then - trace.request{ - module = request.get_module(), - action = request.get_action() - } - if - request.get_404_route() and - not file_exists( - encode.action_file_path{ - module = request.get_module(), - action = request.get_action() - } - ) - then - request.set_status("404 Not Found") - request.forward(request.get_404_route()) - else - if cgi.method ~= "POST" then - request.set_status("405 Method Not Allowed") - cgi.add_header("Allow: POST") - error("Tried to invoke an action with a GET request.") - end - local action_status = execute.filtered_action{ - module = request.get_module(), - action = request.get_action(), - } - if not request.is_rerouted() then - local routing_mode, routing_module, routing_view - routing_mode = cgi.params["_webmcp_routing." .. action_status .. ".mode"] - routing_module = cgi.params["_webmcp_routing." .. action_status .. ".module"] - routing_view = cgi.params["_webmcp_routing." .. action_status .. ".view"] - routing_anchor = cgi.params["_webmcp_routing." .. action_status .. ".anchor"] - if not (routing_mode or routing_module or routing_view) then - action_status = "default" - routing_mode = cgi.params["_webmcp_routing.default.mode"] - routing_module = cgi.params["_webmcp_routing.default.module"] - routing_view = cgi.params["_webmcp_routing.default.view"] - routing_anchor = cgi.params["_webmcp_routing.default.anchor"] - end - assert(routing_module, "Routing information has no module.") - assert(routing_view, "Routing information has no view.") - if routing_mode == "redirect" then - local routing_params = {} - for key, value in pairs(cgi.params) do - local status, stripped_key = string.match( - key, "^_webmcp_routing%.([^%.]*)%.params%.(.*)$" - ) - if status == action_status then - routing_params[stripped_key] = value - end - end - request.redirect{ - module = routing_module, - view = routing_view, - id = cgi.params["_webmcp_routing." .. action_status .. ".id"], - params = routing_params, - anchor = routing_anchor - } - elseif routing_mode == "forward" then - request.forward{ module = routing_module, view = routing_view } - else - error("Missing or unknown routing mode in request parameters.") - end - end - end - else - -- no action - trace.request{ - module = request.get_module(), - view = request.get_view() - } - if - request.get_404_route() and - not file_exists( - encode.view_file_path{ - module = request.get_module(), - view = request.get_view() - } - ) - then - request.set_status("404 Not Found") - request.forward(request.get_404_route()) - end - end - - if not request.get_redirect_data() then - request.process_forward() - local view = request.get_view() - if string.find(view, "^_") then - error("Tried to call a private view (prefixed with underscore).") - end - execute.filtered_view{ - module = request.get_module(), - view = view, - } - end - - -- force error due to missing absolute base URL until its too late to display error message - --if request.get_redirect_data() then - -- request.get_absolute_baseurl() - --end - - end, - - function(errobj) - return { - errobj = errobj, - stacktrace = string.gsub( - debug.traceback('', 2), - "^\r?\n?stack traceback:\r?\n?", "" - ) - } - end - ) - - if not success then trace.error{} end - - -- laufzeitermittlung - trace.exectime{ real = extos.monotonic_hires_time(), cpu = os.clock() } - - slot.select('trace', trace.render) -- render trace information - - local redirect_data = request.get_redirect_data() - - -- log error and switch to error layout, unless success - if not success then - local errobj = error_info.errobj - local stacktrace = error_info.stacktrace - if not request.get_status() and not request.get_json_request_slots() then - request.set_status("500 Internal Server Error") - end - slot.set_layout('system_error') - slot.select('system_error', function() - if getmetatable(errobj) == mondelefant.errorobject_metatable then - slot.put( - "

Database error of class ", - encode.html(errobj.code), - " occured:
", - encode.html(errobj.message), - "

" - ) - else - slot.put("

", encode.html(tostring(errobj)), "

") - end - slot.put("

Stack trace follows:
") - slot.put(encode.html_newlines(encode.html(stacktrace))) - slot.put("

") - end) - elseif redirect_data then - local redirect_params = {} - for key, value in pairs(redirect_data.params) do - redirect_params[key] = value - end - local slot_dump = slot.dump_all() - if slot_dump ~= "" then - redirect_params.tempstore = tempstore.save(slot_dump) - end - local json_request_slots = request.get_json_request_slots() - if json_request_slots then - redirect_params["_webmcp_json_slots[]"] = json_request_slots - end - cgi.redirect( - encode.url{ - base = request.get_absolute_baseurl(), - module = redirect_data.module, - view = redirect_data.view, - id = redirect_data.id, - params = redirect_params, - anchor = redirect_data.anchor - } - ) - cgi.send_data() - end - - if not success or not redirect_data then - - local http_status = request.get_status() - if http_status then - cgi.set_status(http_status) - end - - local json_request_slots = request.get_json_request_slots() - if json_request_slots then - cgi.set_content_type('application/json') - local data = {} - for idx, slot_ident in ipairs(json_request_slots) do - data[slot_ident] = slot.get_content(slot_ident) - end - cgi.send_data(encode.json(data)) - else - cgi.set_content_type(slot.get_content_type()) - cgi.send_data(slot.render_layout()) - end - end - -end +--//-- diff -r 6ac7133bb58e -r ba3dd4a17e3d framework/env/request/router.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/framework/env/request/router.lua Mon Jan 12 01:48:11 2015 +0100 @@ -0,0 +1,12 @@ +--[[-- +request.router() + +Processes a request. May be overwritten by an application, calls request.default_router() in its default implementation. + +--]]-- + +function request.router() + request.default_router() +end + +--//--