jbe@215: --[[-- jbe@327: success = -- false if an error occurred, true otherwise jbe@215: request.handler( jbe@349: http_request -- HTTP request object jbe@215: ) jbe@215: jbe@494: Called by bin/mcp.lua to process an HTTP request. Calls request.router() and handles the request. Note: request initializers (see request.initialize()) are to be executed by mcp.lua before this function is invoked by mcp.lua. jbe@215: jbe@215: --]]-- jbe@210: jbe@347: function request.handler(http_request) jbe@459: jbe@212: request._http_request = http_request jbe@215: local path = http_request.path jbe@215: if path then jbe@221: local relative_baseurl_elements = {} jbe@215: for match in string.gmatch(path, "/") do jbe@221: relative_baseurl_elements[#relative_baseurl_elements+1] = "../" jbe@215: end jbe@280: if #relative_baseurl_elements > 0 then jbe@280: request._relative_baseurl = table.concat(relative_baseurl_elements) jbe@280: else jbe@280: request._relative_baseurl = "./" jbe@280: end jbe@215: else jbe@215: request._relative_baseurl = nil jbe@215: end jbe@255: jbe@255: local success, error_info = xpcall( jbe@255: function() jbe@255: jbe@459: local function require_method(errmsg, ...) jbe@461: for i = 1, select("#", ...) do jbe@459: if http_request.method == select(i, ...) then return end jbe@459: end jbe@459: request.set_status("405 Method Not Allowed") jbe@459: request.add_header("Allow", table.concat({...}, ", ")) jbe@459: error(errmsg) jbe@459: end jbe@459: jbe@459: request._route = request.router() jbe@459: do jbe@459: local post_id = http_request.post_params["_webmcp_id"] jbe@459: if post_id then jbe@459: request._route.id = post_id jbe@459: end jbe@459: end jbe@459: local options_handler = loadcached(encode.file_path( jbe@459: WEBMCP_BASE_PATH, 'app', WEBMCP_APP_NAME, 'http_options.lua' jbe@459: )) jbe@459: if options_handler then jbe@459: options_handler() jbe@459: end jbe@459: if http_request.method == "OPTIONS" then jbe@459: return jbe@459: end jbe@459: jbe@329: if not request._route then jbe@329: request._route = {} jbe@329: if request.get_404_route() then jbe@329: request.set_status("404 Not Found") jbe@329: request.forward(request.get_404_route()) jbe@329: else jbe@329: error("Could not route request URL") jbe@329: end jbe@329: end jbe@329: jbe@255: if request._route.static then jbe@358: local subpath = request._route.static jbe@491: local errmsg jbe@358: for element in string.gmatch(subpath, "[^/]+") do jbe@358: if element == "." or element == ".." then jbe@491: errmsg = "Illegal path" jbe@358: break jbe@358: end jbe@358: end jbe@491: local fstat, f jbe@491: if not errmsg then jbe@358: local filename = WEBMCP_BASE_PATH .. "static/" .. subpath jbe@358: fstat, errmsg = extos.stat(filename) jbe@358: if fstat then jbe@358: if fstat.isdir then jbe@358: errmsg = "Is a directory" jbe@358: elseif not fstat.isreg then jbe@358: errmsg = "Not a regular file" jbe@358: else jbe@358: f, errmsg = io.open(filename, "r") jbe@358: end jbe@347: end jbe@347: end jbe@270: if not f then jbe@270: if request.get_404_route() then jbe@329: request.set_status("404 Not Found") jbe@270: request.forward(request.get_404_route()) jbe@270: else jbe@358: error('Could not open static file "' .. subpath .. '": ' .. errmsg) jbe@270: end jbe@327: else jbe@459: require_method( jbe@459: "Invalid HTTP method for static file", jbe@459: "HEAD", "GET", "POST" jbe@459: ) jbe@327: local d = assert(f:read("*a")) jbe@327: f:close() jbe@327: slot.put_into("data", d) jbe@358: local filename_extension = string.match(subpath, "%.([^.]+)$") jbe@327: slot.set_layout(nil, request._mime_types[filename_extension] or "application/octet-stream") jbe@327: request.allow_caching() jbe@327: return jbe@270: end jbe@255: end jbe@255: jbe@255: -- restore slots if coming from http redirect jbe@327: do jbe@327: local tempstore_value = http_request.get_params["_tempstore"] jbe@327: if tempstore_value then jbe@327: trace.restore_slots{} jbe@327: local blob = tempstore.pop(tempstore_value) jbe@327: if blob then slot.restore_all(blob) end jbe@327: end jbe@255: end jbe@255: jbe@255: if request.get_action() then jbe@255: trace.request{ jbe@255: module = request.get_module(), jbe@255: action = request.get_action() jbe@255: } jbe@255: if jbe@352: not execute.action{ jbe@352: module = request.get_module(), jbe@352: action = request.get_action(), jbe@352: test_existence = true jbe@358: } and jbe@358: request.get_404_route() jbe@255: then jbe@255: request.set_status("404 Not Found") jbe@255: request.forward(request.get_404_route()) jbe@255: else jbe@459: require_method( jbe@459: "Invalid HTTP method for action (POST request required)", jbe@459: "POST" jbe@459: ) jbe@255: local action_status = execute.filtered_action{ jbe@255: module = request.get_module(), jbe@255: action = request.get_action(), jbe@255: } jbe@492: if not (request.is_rerouted() or slot.layout_is_set()) then jbe@255: local routing_mode, routing_module, routing_view, routing_anchor jbe@255: routing_mode = http_request.post_params["_webmcp_routing." .. action_status .. ".mode"] jbe@255: routing_module = http_request.post_params["_webmcp_routing." .. action_status .. ".module"] jbe@255: routing_view = http_request.post_params["_webmcp_routing." .. action_status .. ".view"] jbe@255: routing_anchor = http_request.post_params["_webmcp_routing." .. action_status .. ".anchor"] jbe@255: if not (routing_mode or routing_module or routing_view) then jbe@255: action_status = "default" jbe@255: routing_mode = http_request.post_params["_webmcp_routing.default.mode"] jbe@255: routing_module = http_request.post_params["_webmcp_routing.default.module"] jbe@255: routing_view = http_request.post_params["_webmcp_routing.default.view"] jbe@255: routing_anchor = http_request.post_params["_webmcp_routing.default.anchor"] jbe@255: end jbe@255: assert(routing_module, "Routing information has no module.") jbe@255: assert(routing_view, "Routing information has no view.") jbe@255: if routing_mode == "redirect" then jbe@255: local routing_params = {} jbe@255: for key, value in pairs(request.get_param_strings{ method="POST", include_internal=true }) do jbe@255: local status, stripped_key = string.match( jbe@255: key, "^_webmcp_routing%.([^%.]*)%.params%.(.*)$" jbe@255: ) jbe@255: if status == action_status then jbe@255: routing_params[stripped_key] = value jbe@255: end jbe@255: end jbe@255: request.redirect{ jbe@255: module = routing_module, jbe@255: view = routing_view, jbe@255: id = http_request.post_params["_webmcp_routing." .. action_status .. ".id"], jbe@255: params = routing_params, jbe@255: anchor = routing_anchor jbe@255: } jbe@255: elseif routing_mode == "forward" then jbe@255: request.forward{ module = routing_module, view = routing_view } jbe@255: else jbe@255: error("Missing or unknown routing mode in request parameters.") jbe@255: end jbe@255: end jbe@255: end jbe@255: else jbe@255: -- no action jbe@255: trace.request{ jbe@255: module = request.get_module(), jbe@255: view = request.get_view() jbe@255: } jbe@255: if jbe@352: not execute.view{ jbe@352: module = request.get_module(), jbe@352: view = request.get_view(), jbe@352: test_existence = true jbe@358: } and request.get_404_route() jbe@255: then jbe@255: request.set_status("404 Not Found") jbe@255: request.forward(request.get_404_route()) jbe@255: end jbe@255: end jbe@255: jbe@255: if not request.get_redirect_data() then jbe@255: request.process_forward() jbe@255: local view = request.get_view() jbe@255: if string.find(view, "^_") then jbe@255: error("Tried to call a private view (prefixed with underscore).") jbe@255: end jbe@459: require_method( jbe@459: "Invalid HTTP method", jbe@459: "HEAD", "GET", "POST" jbe@459: ) jbe@255: execute.filtered_view{ jbe@255: module = request.get_module(), jbe@255: view = view, jbe@255: } jbe@255: end jbe@255: jbe@255: end, jbe@255: jbe@255: function(errobj) jbe@255: return { jbe@255: errobj = errobj, jbe@255: stacktrace = string.gsub( jbe@255: debug.traceback('', 2), jbe@255: "^\r?\n?stack traceback:\r?\n?", "" jbe@255: ) jbe@255: } jbe@255: end jbe@255: ) jbe@255: jbe@255: if not success then trace.error{} end jbe@255: jbe@255: slot.select('trace', trace.render) -- render trace information jbe@255: jbe@255: local redirect_data = request.get_redirect_data() jbe@255: jbe@255: -- log error and switch to error layout, unless success jbe@255: if not success then jbe@255: local errobj = error_info.errobj jbe@255: local stacktrace = error_info.stacktrace jbe@255: if not request._status then jbe@255: request._status = "500 Internal Server Error" jbe@255: end jbe@328: http_request:close_after_finish() jbe@255: slot.set_layout('system_error') jbe@255: slot.select('system_error', function() jbe@255: if getmetatable(errobj) == mondelefant.errorobject_metatable then jbe@255: slot.put( jbe@255: "

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

" jbe@255: ) jbe@255: else jbe@255: slot.put("

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

") jbe@255: end jbe@255: slot.put("

Stack trace follows:
") jbe@255: slot.put(encode.html_newlines(encode.html(stacktrace))) jbe@255: slot.put("

") jbe@255: end) jbe@255: elseif redirect_data then jbe@455: if jbe@455: redirect_data.include_tempstore == true or ( jbe@455: redirect_data.include_tempstore ~= false and jbe@455: not redirect_data.external jbe@455: ) jbe@455: then jbe@455: redirect_data = table.new(redirect_data) jbe@454: redirect_data.params = table.new(redirect_data.params) jbe@454: local slot_dump = slot.dump_all() jbe@454: if slot_dump ~= "" then jbe@454: redirect_data.params._tempstore = tempstore.save(slot_dump) jbe@454: end jbe@255: end jbe@255: http_request:send_status("303 See Other") jbe@264: for i, header in ipairs(request._response_headers) do jbe@264: http_request:send_header(header[1], header[2]) jbe@264: end jbe@267: http_request:send_header("Location", encode.url(redirect_data)) jbe@255: http_request:finish() jbe@255: end jbe@255: jbe@255: if not success or not redirect_data then jbe@255: jbe@255: http_request:send_status(request._status or "200 OK") jbe@255: for i, header in ipairs(request._response_headers) do jbe@255: http_request:send_header(header[1], header[2]) jbe@255: end jbe@291: if not request._cache_manual then jbe@291: local cache_time = request._cache_time jbe@291: if request._cache and cache_time and cache_time > 0 then jbe@291: http_request:send_header("Cache-Control", "max-age=" .. cache_time) jbe@291: else jbe@291: http_request:send_header("Cache-Control", "no-cache") jbe@291: end jbe@291: end jbe@255: http_request:send_header("Content-Type", slot.get_content_type()) jbe@255: http_request:send_data(slot.render_layout()) jbe@255: http_request:finish() jbe@255: end jbe@255: jbe@327: return success jbe@327: jbe@215: end