| rev | line source | 
| jbe@215 | 1 --[[-- | 
| jbe@327 | 2 success =         -- false if an error occurred, true otherwise | 
| jbe@215 | 3 request.handler( | 
| jbe@349 | 4   http_request    -- HTTP request object | 
| jbe@215 | 5 ) | 
| jbe@215 | 6 | 
| jbe@358 | 7 Called by 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 | 8 | 
| jbe@215 | 9 --]]-- | 
| jbe@210 | 10 | 
| jbe@347 | 11 function request.handler(http_request) | 
| jbe@212 | 12   request._http_request = http_request | 
| jbe@215 | 13   local path = http_request.path | 
| jbe@215 | 14   if path then | 
| jbe@221 | 15     local relative_baseurl_elements = {} | 
| jbe@215 | 16     for match in string.gmatch(path, "/") do | 
| jbe@221 | 17       relative_baseurl_elements[#relative_baseurl_elements+1] = "../" | 
| jbe@215 | 18     end | 
| jbe@280 | 19     if #relative_baseurl_elements > 0 then | 
| jbe@280 | 20       request._relative_baseurl = table.concat(relative_baseurl_elements) | 
| jbe@280 | 21     else | 
| jbe@280 | 22       request._relative_baseurl = "./" | 
| jbe@280 | 23     end | 
| jbe@215 | 24   else | 
| jbe@215 | 25     request._relative_baseurl = nil | 
| jbe@215 | 26   end | 
| jbe@329 | 27   request._route = request.router() | 
| jbe@275 | 28   do | 
| jbe@275 | 29     local post_id = http_request.post_params["_webmcp_id"] | 
| jbe@275 | 30     if post_id then | 
| jbe@275 | 31       request._route.id = post_id | 
| jbe@275 | 32     end | 
| jbe@275 | 33   end | 
| jbe@255 | 34 | 
| jbe@255 | 35   local success, error_info = xpcall( | 
| jbe@255 | 36     function() | 
| jbe@255 | 37 | 
| jbe@329 | 38       if not request._route then | 
| jbe@329 | 39         request._route = {} | 
| jbe@329 | 40         if request.get_404_route() then | 
| jbe@329 | 41           request.set_status("404 Not Found") | 
| jbe@329 | 42           request.forward(request.get_404_route()) | 
| jbe@329 | 43         else | 
| jbe@329 | 44           error("Could not route request URL") | 
| jbe@329 | 45         end | 
| jbe@329 | 46       end | 
| jbe@329 | 47 | 
| jbe@255 | 48       if request._route.static then | 
| jbe@358 | 49         local subpath = request._route.static | 
| jbe@358 | 50         for element in string.gmatch(subpath, "[^/]+") do | 
| jbe@358 | 51           if element == "." or element == ".." then | 
| jbe@358 | 52             subpath = nil | 
| jbe@358 | 53             break | 
| jbe@358 | 54           end | 
| jbe@358 | 55         end | 
| jbe@347 | 56         local fstat, f, errmsg | 
| jbe@358 | 57         if subpath then | 
| jbe@358 | 58           local filename = WEBMCP_BASE_PATH .. "static/" .. subpath | 
| jbe@358 | 59           fstat, errmsg = extos.stat(filename) | 
| jbe@358 | 60           if fstat then | 
| jbe@358 | 61             if fstat.isdir then | 
| jbe@358 | 62               errmsg = "Is a directory" | 
| jbe@358 | 63             elseif not fstat.isreg then | 
| jbe@358 | 64               errmsg = "Not a regular file" | 
| jbe@358 | 65             else | 
| jbe@358 | 66               f, errmsg = io.open(filename, "r") | 
| jbe@358 | 67             end | 
| jbe@347 | 68           end | 
| jbe@347 | 69         end | 
| jbe@270 | 70         if not f then | 
| jbe@270 | 71           if request.get_404_route() then | 
| jbe@329 | 72             request.set_status("404 Not Found") | 
| jbe@270 | 73             request.forward(request.get_404_route()) | 
| jbe@270 | 74           else | 
| jbe@358 | 75             error('Could not open static file "' .. subpath .. '": ' .. errmsg) | 
| jbe@270 | 76           end | 
| jbe@327 | 77         else | 
| jbe@327 | 78           local d = assert(f:read("*a")) | 
| jbe@327 | 79           f:close() | 
| jbe@327 | 80           slot.put_into("data", d) | 
| jbe@358 | 81           local filename_extension = string.match(subpath, "%.([^.]+)$") | 
| jbe@327 | 82           slot.set_layout(nil, request._mime_types[filename_extension] or "application/octet-stream") | 
| jbe@327 | 83           request.allow_caching() | 
| jbe@327 | 84           return | 
| jbe@270 | 85         end | 
| jbe@255 | 86       end | 
| jbe@255 | 87 | 
| jbe@255 | 88       -- restore slots if coming from http redirect | 
| jbe@327 | 89       do | 
| jbe@327 | 90         local tempstore_value = http_request.get_params["_tempstore"] | 
| jbe@327 | 91         if tempstore_value then | 
| jbe@327 | 92           trace.restore_slots{} | 
| jbe@327 | 93           local blob = tempstore.pop(tempstore_value) | 
| jbe@327 | 94           if blob then slot.restore_all(blob) end | 
| jbe@327 | 95         end | 
| jbe@255 | 96       end | 
| jbe@255 | 97 | 
| jbe@255 | 98       if request.get_action() then | 
| jbe@255 | 99         trace.request{ | 
| jbe@255 | 100           module = request.get_module(), | 
| jbe@255 | 101           action = request.get_action() | 
| jbe@255 | 102         } | 
| jbe@255 | 103         if | 
| jbe@352 | 104           not execute.action{ | 
| jbe@352 | 105             module = request.get_module(), | 
| jbe@352 | 106             action = request.get_action(), | 
| jbe@352 | 107             test_existence = true | 
| jbe@358 | 108           } and | 
| jbe@358 | 109           request.get_404_route() | 
| jbe@255 | 110         then | 
| jbe@255 | 111           request.set_status("404 Not Found") | 
| jbe@255 | 112           request.forward(request.get_404_route()) | 
| jbe@255 | 113         else | 
| jbe@255 | 114           if http_request.method ~= "POST" then | 
| jbe@255 | 115             request.set_status("405 Method Not Allowed") | 
| jbe@255 | 116             request.add_header("Allow", "POST") | 
| jbe@255 | 117             error("Tried to invoke an action with a GET request.") | 
| jbe@255 | 118           end | 
| jbe@255 | 119           local action_status = execute.filtered_action{ | 
| jbe@255 | 120             module = request.get_module(), | 
| jbe@255 | 121             action = request.get_action(), | 
| jbe@255 | 122           } | 
| jbe@255 | 123           if not request.is_rerouted() then | 
| jbe@255 | 124             local routing_mode, routing_module, routing_view, routing_anchor | 
| jbe@255 | 125             routing_mode   = http_request.post_params["_webmcp_routing." .. action_status .. ".mode"] | 
| jbe@255 | 126             routing_module = http_request.post_params["_webmcp_routing." .. action_status .. ".module"] | 
| jbe@255 | 127             routing_view   = http_request.post_params["_webmcp_routing." .. action_status .. ".view"] | 
| jbe@255 | 128             routing_anchor = http_request.post_params["_webmcp_routing." .. action_status .. ".anchor"] | 
| jbe@255 | 129             if not (routing_mode or routing_module or routing_view) then | 
| jbe@255 | 130               action_status = "default" | 
| jbe@255 | 131               routing_mode   = http_request.post_params["_webmcp_routing.default.mode"] | 
| jbe@255 | 132               routing_module = http_request.post_params["_webmcp_routing.default.module"] | 
| jbe@255 | 133               routing_view   = http_request.post_params["_webmcp_routing.default.view"] | 
| jbe@255 | 134               routing_anchor = http_request.post_params["_webmcp_routing.default.anchor"] | 
| jbe@255 | 135             end | 
| jbe@255 | 136             assert(routing_module, "Routing information has no module.") | 
| jbe@255 | 137             assert(routing_view,   "Routing information has no view.") | 
| jbe@255 | 138             if routing_mode == "redirect" then | 
| jbe@255 | 139               local routing_params = {} | 
| jbe@255 | 140               for key, value in pairs(request.get_param_strings{ method="POST", include_internal=true }) do | 
| jbe@255 | 141                 local status, stripped_key = string.match( | 
| jbe@255 | 142                   key, "^_webmcp_routing%.([^%.]*)%.params%.(.*)$" | 
| jbe@255 | 143                 ) | 
| jbe@255 | 144                 if status == action_status then | 
| jbe@255 | 145                   routing_params[stripped_key] = value | 
| jbe@255 | 146                 end | 
| jbe@255 | 147               end | 
| jbe@255 | 148               request.redirect{ | 
| jbe@255 | 149                 module = routing_module, | 
| jbe@255 | 150                 view   = routing_view, | 
| jbe@255 | 151                 id     = http_request.post_params["_webmcp_routing." .. action_status .. ".id"], | 
| jbe@255 | 152                 params = routing_params, | 
| jbe@255 | 153                 anchor = routing_anchor | 
| jbe@255 | 154               } | 
| jbe@255 | 155             elseif routing_mode == "forward" then | 
| jbe@255 | 156               request.forward{ module = routing_module, view = routing_view } | 
| jbe@255 | 157             else | 
| jbe@255 | 158               error("Missing or unknown routing mode in request parameters.") | 
| jbe@255 | 159             end | 
| jbe@255 | 160           end | 
| jbe@255 | 161         end | 
| jbe@255 | 162       else | 
| jbe@255 | 163         -- no action | 
| jbe@255 | 164         trace.request{ | 
| jbe@255 | 165           module = request.get_module(), | 
| jbe@255 | 166           view   = request.get_view() | 
| jbe@255 | 167         } | 
| jbe@255 | 168         if | 
| jbe@352 | 169           not execute.view{ | 
| jbe@352 | 170             module = request.get_module(), | 
| jbe@352 | 171             view   = request.get_view(), | 
| jbe@352 | 172             test_existence = true | 
| jbe@358 | 173           } and request.get_404_route() | 
| jbe@255 | 174         then | 
| jbe@255 | 175           request.set_status("404 Not Found") | 
| jbe@255 | 176           request.forward(request.get_404_route()) | 
| jbe@255 | 177         end | 
| jbe@255 | 178       end | 
| jbe@255 | 179 | 
| jbe@255 | 180       if not request.get_redirect_data() then | 
| jbe@255 | 181         request.process_forward() | 
| jbe@255 | 182         local view = request.get_view() | 
| jbe@255 | 183         if string.find(view, "^_") then | 
| jbe@255 | 184           error("Tried to call a private view (prefixed with underscore).") | 
| jbe@255 | 185         end | 
| jbe@255 | 186         execute.filtered_view{ | 
| jbe@255 | 187           module = request.get_module(), | 
| jbe@255 | 188           view   = view, | 
| jbe@255 | 189         } | 
| jbe@255 | 190       end | 
| jbe@255 | 191 | 
| jbe@255 | 192     end, | 
| jbe@255 | 193 | 
| jbe@255 | 194     function(errobj) | 
| jbe@255 | 195       return { | 
| jbe@255 | 196         errobj = errobj, | 
| jbe@255 | 197         stacktrace = string.gsub( | 
| jbe@255 | 198           debug.traceback('', 2), | 
| jbe@255 | 199           "^\r?\n?stack traceback:\r?\n?", "" | 
| jbe@255 | 200         ) | 
| jbe@255 | 201       } | 
| jbe@255 | 202     end | 
| jbe@255 | 203   ) | 
| jbe@255 | 204 | 
| jbe@255 | 205   if not success then trace.error{} end | 
| jbe@255 | 206 | 
| jbe@255 | 207   slot.select('trace', trace.render)  -- render trace information | 
| jbe@255 | 208 | 
| jbe@255 | 209   local redirect_data = request.get_redirect_data() | 
| jbe@255 | 210 | 
| jbe@255 | 211   -- log error and switch to error layout, unless success | 
| jbe@255 | 212   if not success then | 
| jbe@255 | 213     local errobj     = error_info.errobj | 
| jbe@255 | 214     local stacktrace = error_info.stacktrace | 
| jbe@255 | 215     if not request._status then | 
| jbe@255 | 216       request._status = "500 Internal Server Error" | 
| jbe@255 | 217     end | 
| jbe@328 | 218     http_request:close_after_finish() | 
| jbe@255 | 219     slot.set_layout('system_error') | 
| jbe@255 | 220     slot.select('system_error', function() | 
| jbe@255 | 221       if getmetatable(errobj) == mondelefant.errorobject_metatable then | 
| jbe@255 | 222         slot.put( | 
| jbe@255 | 223           "<p>Database error of class <b>", | 
| jbe@255 | 224           encode.html(errobj.code), | 
| jbe@255 | 225           "</b> occured:<br/><b>", | 
| jbe@255 | 226           encode.html(errobj.message), | 
| jbe@255 | 227           "</b></p>" | 
| jbe@255 | 228         ) | 
| jbe@255 | 229       else | 
| jbe@255 | 230         slot.put("<p><b>", encode.html(tostring(errobj)), "</b></p>") | 
| jbe@255 | 231       end | 
| jbe@255 | 232       slot.put("<p>Stack trace follows:<br/>") | 
| jbe@255 | 233       slot.put(encode.html_newlines(encode.html(stacktrace))) | 
| jbe@255 | 234       slot.put("</p>") | 
| jbe@255 | 235     end) | 
| jbe@255 | 236   elseif redirect_data then | 
| jbe@267 | 237     redirect_data = table.new(redirect_data) | 
| jbe@267 | 238     redirect_data.params = table.new(redirect_data.params) | 
| jbe@255 | 239     local slot_dump = slot.dump_all() | 
| jbe@255 | 240     if slot_dump ~= "" then | 
| jbe@312 | 241       redirect_data.params._tempstore = tempstore.save(slot_dump) | 
| jbe@255 | 242     end | 
| jbe@255 | 243     http_request:send_status("303 See Other") | 
| jbe@264 | 244     for i, header in ipairs(request._response_headers) do | 
| jbe@264 | 245       http_request:send_header(header[1], header[2]) | 
| jbe@264 | 246     end | 
| jbe@267 | 247     http_request:send_header("Location", encode.url(redirect_data)) | 
| jbe@255 | 248     http_request:finish() | 
| jbe@255 | 249   end | 
| jbe@255 | 250 | 
| jbe@255 | 251   if not success or not redirect_data then | 
| jbe@255 | 252 | 
| jbe@255 | 253     http_request:send_status(request._status or "200 OK") | 
| jbe@255 | 254     for i, header in ipairs(request._response_headers) do | 
| jbe@255 | 255       http_request:send_header(header[1], header[2]) | 
| jbe@255 | 256     end | 
| jbe@291 | 257     if not request._cache_manual then | 
| jbe@291 | 258       local cache_time = request._cache_time | 
| jbe@291 | 259       if request._cache and cache_time and cache_time > 0 then | 
| jbe@291 | 260         http_request:send_header("Cache-Control", "max-age=" .. cache_time) | 
| jbe@291 | 261       else | 
| jbe@291 | 262         http_request:send_header("Cache-Control", "no-cache") | 
| jbe@291 | 263       end | 
| jbe@291 | 264     end | 
| jbe@255 | 265     http_request:send_header("Content-Type", slot.get_content_type()) | 
| jbe@255 | 266     http_request:send_data(slot.render_layout()) | 
| jbe@255 | 267     http_request:finish() | 
| jbe@255 | 268   end | 
| jbe@255 | 269 | 
| jbe@327 | 270   return success | 
| jbe@327 | 271 | 
| jbe@215 | 272 end |