webmcp
annotate framework/env/request/handler.lua @ 438:ea8419658535
Another change to make <db_object>:try_save() work properly with "document_column"
(use "_col" proxy also for accessing self._col[primary_key.json_doc])
(use "_col" proxy also for accessing self._col[primary_key.json_doc])
author | jbe |
---|---|
date | Wed Jan 20 21:06:07 2016 +0100 (2016-01-20) |
parents | 545ec2e3eafa |
children | 4e03ecb28665 |
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 |