webmcp

changeset 216:fd0360594636

Work on request.process(...), request.default_router(...), request.router(), request.handler(...)
author jbe
date Mon Jan 12 03:02:26 2015 +0100 (2015-01-12)
parents ba3dd4a17e3d
children 7f9c9c4434a1
files framework/env/request/default_router.lua framework/env/request/handler.lua framework/env/request/process.lua framework/env/request/router.lua
line diff
     1.1 --- a/framework/env/request/default_router.lua	Mon Jan 12 01:48:11 2015 +0100
     1.2 +++ b/framework/env/request/default_router.lua	Mon Jan 12 03:02:26 2015 +0100
     1.3 @@ -1,267 +1,40 @@
     1.4 --- TODO: function incomplete yet
     1.5 +--[[--
     1.6 +route =
     1.7 +request.default_router(
     1.8 +  path                   -- URL path, including a leading slash
     1.9 +)
    1.10  
    1.11 -local function file_exists(filename)
    1.12 -  local file = io.open(filename, "r")
    1.13 -  if file then
    1.14 -    io.close(file)
    1.15 -    return true
    1.16 -  else
    1.17 -    return false
    1.18 -  end
    1.19 -end
    1.20 +Default conversion from a path to a route. Called by request.router().
    1.21 +
    1.22 +--]]--
    1.23  
    1.24  function request.default_router(path)
    1.25 -  request._http_request = http_request
    1.26 -  local path = http_request.path
    1.27 -  local function parse_path()
    1.28 -    if not path then
    1.29 -      return
    1.30 -    end
    1.31 -    if path == "/" then
    1.32 -      return {module = "index", view = "index"}
    1.33 -    end
    1.34 -    module = string.match(path, "^/([^/]+)/$")
    1.35 -    if module then
    1.36 -      return {module = module, view = "index"}
    1.37 -    end
    1.38 -    module, action = string.match(path, "^/([^/]+)/([^/.]+)$")
    1.39 -    if module then
    1.40 -      return {module = module, action = action}
    1.41 -    end
    1.42 -    module, view, suffix = string.match(path, "^/([^/]+)/([^/.]+)%.([^/]+)$")
    1.43 -    if module then
    1.44 -      return {module = module, view = view, suffix = suffix}
    1.45 -    end
    1.46 -    module, view, id, suffix = string.match(path, "^/([^/]+)/([^/]+)/([^/.]+)%.([^/]+)$")
    1.47 -    if module then
    1.48 -      return {module = module, view = view, id = id, suffix = suffix}
    1.49 -    end
    1.50 +  if not path then
    1.51 +    return nil
    1.52 +  end
    1.53 +  if not string.match(path, "^/") then
    1.54 +    path = "/" .. path
    1.55 +  end
    1.56 +  if path == "/" then
    1.57 +    return {module = "index", view = "index"}
    1.58 +  end
    1.59 +  module = string.match(path, "^/([^/]+)/$")
    1.60 +  if module then
    1.61 +    return {module = module, view = "index"}
    1.62    end
    1.63 -  request._data = parse_path()
    1.64 -  if path then
    1.65 -    local elements = {}
    1.66 -    for match in string.gmatch(path, "/") do
    1.67 -      elements[#elements+1] = "../"
    1.68 -    end
    1.69 -    elements[#elements] = nil
    1.70 -    if #elements > 0 then
    1.71 -      request._relative_baseurl = table.concat(elements)
    1.72 -    else
    1.73 -      request._relative_baseurl = "./"
    1.74 -    end
    1.75 +  module, action = string.match(path, "^/([^/]+)/([^/.]+)$")
    1.76 +  if module then
    1.77 +    return {module = module, action = action}
    1.78    end
    1.79 -  local success, error_info = xpcall(
    1.80 -    function()
    1.81 -
    1.82 -      -- restore slots if coming from http redirect
    1.83 -      local tempstore_value = request.get_param{method = "GET", name = "_tempstore"}
    1.84 -      if tempstore_value then
    1.85 -        trace.restore_slots{}
    1.86 -        local blob = tempstore.pop(tempstore_value)
    1.87 -        if blob then slot.restore_all(blob) end
    1.88 -      end
    1.89 -
    1.90 +  module, view, suffix = string.match(path, "^/([^/]+)/([^/.]+)%.([^/]+)$")
    1.91 +  if module then
    1.92 +    return {module = module, view = view, suffix = suffix}
    1.93 +  end
    1.94 +  module, view, id, suffix = string.match(path, "^/([^/]+)/([^/]+)/([^/.]+)%.([^/]+)$")
    1.95 +  if module then
    1.96 +    return {module = module, view = view, id = id, suffix = suffix}
    1.97 +  end
    1.98 +  return nil
    1.99 +end
   1.100  
   1.101 -      if request.is_404() then
   1.102 -        request.set_status("404 Not Found")
   1.103 -        if request.get_404_route() then
   1.104 -          request.forward(request.get_404_route())
   1.105 -        else
   1.106 -          error("No 404 page set.")
   1.107 -        end
   1.108 -      elseif request.get_action() then
   1.109 -        trace.request{
   1.110 -          module = request.get_module(),
   1.111 -          action = request.get_action()
   1.112 -        }
   1.113 -        if
   1.114 -          request.get_404_route() and
   1.115 -          not file_exists(
   1.116 -            encode.action_file_path{
   1.117 -              module = request.get_module(),
   1.118 -              action = request.get_action()
   1.119 -            }
   1.120 -          )
   1.121 -        then
   1.122 -          request.set_status("404 Not Found")
   1.123 -          request.forward(request.get_404_route())
   1.124 -        else
   1.125 -          if cgi.method ~= "POST" then
   1.126 -            request.set_status("405 Method Not Allowed")
   1.127 -            cgi.add_header("Allow: POST")
   1.128 -            error("Tried to invoke an action with a GET request.")
   1.129 -          end
   1.130 -          local action_status = execute.filtered_action{
   1.131 -            module = request.get_module(),
   1.132 -            action = request.get_action(),
   1.133 -          }
   1.134 -          if not request.is_rerouted() then
   1.135 -            local routing_mode, routing_module, routing_view
   1.136 -            routing_mode   = cgi.params["_webmcp_routing." .. action_status .. ".mode"]
   1.137 -            routing_module = cgi.params["_webmcp_routing." .. action_status .. ".module"]
   1.138 -            routing_view   = cgi.params["_webmcp_routing." .. action_status .. ".view"]
   1.139 -            routing_anchor = cgi.params["_webmcp_routing." .. action_status .. ".anchor"]
   1.140 -            if not (routing_mode or routing_module or routing_view) then
   1.141 -              action_status = "default"
   1.142 -              routing_mode   = cgi.params["_webmcp_routing.default.mode"]
   1.143 -              routing_module = cgi.params["_webmcp_routing.default.module"]
   1.144 -              routing_view   = cgi.params["_webmcp_routing.default.view"]
   1.145 -              routing_anchor = cgi.params["_webmcp_routing.default.anchor"]
   1.146 -            end
   1.147 -            assert(routing_module, "Routing information has no module.")
   1.148 -            assert(routing_view,   "Routing information has no view.")
   1.149 -            if routing_mode == "redirect" then
   1.150 -              local routing_params = {}
   1.151 -              for key, value in pairs(cgi.params) do
   1.152 -                local status, stripped_key = string.match(
   1.153 -                  key, "^_webmcp_routing%.([^%.]*)%.params%.(.*)$"
   1.154 -                )
   1.155 -                if status == action_status then
   1.156 -                  routing_params[stripped_key] = value
   1.157 -                end
   1.158 -              end
   1.159 -              request.redirect{
   1.160 -                module = routing_module,
   1.161 -                view   = routing_view,
   1.162 -                id     = cgi.params["_webmcp_routing." .. action_status .. ".id"],
   1.163 -                params = routing_params,
   1.164 -                anchor = routing_anchor
   1.165 -              }
   1.166 -            elseif routing_mode == "forward" then
   1.167 -              request.forward{ module = routing_module, view = routing_view }
   1.168 -            else
   1.169 -              error("Missing or unknown routing mode in request parameters.")
   1.170 -            end
   1.171 -          end
   1.172 -        end
   1.173 -      else
   1.174 -        -- no action
   1.175 -        trace.request{
   1.176 -          module = request.get_module(),
   1.177 -          view   = request.get_view()
   1.178 -        }
   1.179 -        if
   1.180 -          request.get_404_route() and
   1.181 -          not file_exists(
   1.182 -            encode.view_file_path{
   1.183 -              module = request.get_module(),
   1.184 -              view   = request.get_view()
   1.185 -            }
   1.186 -          )
   1.187 -        then
   1.188 -          request.set_status("404 Not Found")
   1.189 -          request.forward(request.get_404_route())
   1.190 -        end
   1.191 -      end
   1.192 -
   1.193 -      if not request.get_redirect_data() then
   1.194 -        request.process_forward()
   1.195 -        local view = request.get_view()
   1.196 -        if string.find(view, "^_") then
   1.197 -          error("Tried to call a private view (prefixed with underscore).")
   1.198 -        end
   1.199 -        execute.filtered_view{
   1.200 -          module = request.get_module(),
   1.201 -          view   = view,
   1.202 -        }
   1.203 -      end
   1.204 -
   1.205 -      -- force error due to missing absolute base URL until its too late to display error message
   1.206 -      --if request.get_redirect_data() then
   1.207 -      --  request.get_absolute_baseurl()
   1.208 -      --end
   1.209 -
   1.210 -    end,
   1.211 -
   1.212 -    function(errobj)
   1.213 -      return {
   1.214 -        errobj = errobj,
   1.215 -        stacktrace = string.gsub(
   1.216 -          debug.traceback('', 2),
   1.217 -          "^\r?\n?stack traceback:\r?\n?", ""
   1.218 -        )
   1.219 -      }
   1.220 -    end
   1.221 -  )
   1.222 -
   1.223 -  if not success then trace.error{} end
   1.224 -
   1.225 -  -- laufzeitermittlung
   1.226 -  trace.exectime{ real = extos.monotonic_hires_time(), cpu = os.clock() }
   1.227 -
   1.228 -  slot.select('trace', trace.render)  -- render trace information
   1.229 -
   1.230 -  local redirect_data = request.get_redirect_data()
   1.231 -
   1.232 -  -- log error and switch to error layout, unless success
   1.233 -  if not success then
   1.234 -    local errobj     = error_info.errobj
   1.235 -    local stacktrace = error_info.stacktrace
   1.236 -    if not request.get_status() and not request.get_json_request_slots() then
   1.237 -      request.set_status("500 Internal Server Error")
   1.238 -    end
   1.239 -    slot.set_layout('system_error')
   1.240 -    slot.select('system_error', function()
   1.241 -      if getmetatable(errobj) == mondelefant.errorobject_metatable then
   1.242 -        slot.put(
   1.243 -          "<p>Database error of class <b>",
   1.244 -          encode.html(errobj.code),
   1.245 -          "</b> occured:<br/><b>",
   1.246 -          encode.html(errobj.message),
   1.247 -          "</b></p>"
   1.248 -        )
   1.249 -      else
   1.250 -        slot.put("<p><b>", encode.html(tostring(errobj)), "</b></p>")
   1.251 -      end
   1.252 -      slot.put("<p>Stack trace follows:<br/>")
   1.253 -      slot.put(encode.html_newlines(encode.html(stacktrace)))
   1.254 -      slot.put("</p>")
   1.255 -    end)
   1.256 -  elseif redirect_data then
   1.257 -    local redirect_params = {}
   1.258 -    for key, value in pairs(redirect_data.params) do
   1.259 -      redirect_params[key] = value
   1.260 -    end
   1.261 -    local slot_dump = slot.dump_all()
   1.262 -    if slot_dump ~= "" then
   1.263 -      redirect_params.tempstore = tempstore.save(slot_dump)
   1.264 -    end
   1.265 -    local json_request_slots = request.get_json_request_slots()
   1.266 -    if json_request_slots then
   1.267 -      redirect_params["_webmcp_json_slots[]"] = json_request_slots  
   1.268 -    end
   1.269 -    cgi.redirect(
   1.270 -      encode.url{
   1.271 -        base   = request.get_absolute_baseurl(),
   1.272 -        module = redirect_data.module,
   1.273 -        view   = redirect_data.view,
   1.274 -        id     = redirect_data.id,
   1.275 -        params = redirect_params,
   1.276 -        anchor = redirect_data.anchor
   1.277 -      }
   1.278 -    )
   1.279 -    cgi.send_data()
   1.280 -  end
   1.281 -
   1.282 -  if not success or not redirect_data then
   1.283 -
   1.284 -    local http_status = request.get_status()
   1.285 -    if http_status then
   1.286 -      cgi.set_status(http_status)
   1.287 -    end
   1.288 -
   1.289 -    local json_request_slots = request.get_json_request_slots()
   1.290 -    if json_request_slots then
   1.291 -      cgi.set_content_type('application/json')
   1.292 -      local data = {}
   1.293 -      for idx, slot_ident in ipairs(json_request_slots) do
   1.294 -        data[slot_ident] = slot.get_content(slot_ident)
   1.295 -      end
   1.296 -      cgi.send_data(encode.json(data))
   1.297 -    else
   1.298 -      cgi.set_content_type(slot.get_content_type())
   1.299 -      cgi.send_data(slot.render_layout())
   1.300 -    end
   1.301 -  end
   1.302 -
   1.303 -end
   1.304 +--//--
     2.1 --- a/framework/env/request/handler.lua	Mon Jan 12 01:48:11 2015 +0100
     2.2 +++ b/framework/env/request/handler.lua	Mon Jan 12 03:02:26 2015 +0100
     2.3 @@ -24,7 +24,7 @@
     2.4    else
     2.5      request._relative_baseurl = nil
     2.6    end
     2.7 -  request.router()
     2.8 +  request.process(request.router())
     2.9  end
    2.10  
    2.11  --//--
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/framework/env/request/process.lua	Mon Jan 12 03:02:26 2015 +0100
     3.3 @@ -0,0 +1,229 @@
     3.4 +-- TODO: function incomplete yet
     3.5 +
     3.6 +local function file_exists(filename)
     3.7 +  local file = io.open(filename, "r")
     3.8 +  if file then
     3.9 +    io.close(file)
    3.10 +    return true
    3.11 +  else
    3.12 +    return false
    3.13 +  end
    3.14 +end
    3.15 +
    3.16 +function request.process(route)
    3.17 +
    3.18 +  local success, error_info = xpcall(
    3.19 +    function()
    3.20 +
    3.21 +      -- restore slots if coming from http redirect
    3.22 +      local tempstore_value = request.get_param{method = "GET", name = "_tempstore"}
    3.23 +      if tempstore_value then
    3.24 +        trace.restore_slots{}
    3.25 +        local blob = tempstore.pop(tempstore_value)
    3.26 +        if blob then slot.restore_all(blob) end
    3.27 +      end
    3.28 +
    3.29 +
    3.30 +      if request.is_404() then
    3.31 +        request.set_status("404 Not Found")
    3.32 +        if request.get_404_route() then
    3.33 +          request.forward(request.get_404_route())
    3.34 +        else
    3.35 +          error("No 404 page set.")
    3.36 +        end
    3.37 +      elseif request.get_action() then
    3.38 +        trace.request{
    3.39 +          module = request.get_module(),
    3.40 +          action = request.get_action()
    3.41 +        }
    3.42 +        if
    3.43 +          request.get_404_route() and
    3.44 +          not file_exists(
    3.45 +            encode.action_file_path{
    3.46 +              module = request.get_module(),
    3.47 +              action = request.get_action()
    3.48 +            }
    3.49 +          )
    3.50 +        then
    3.51 +          request.set_status("404 Not Found")
    3.52 +          request.forward(request.get_404_route())
    3.53 +        else
    3.54 +          if cgi.method ~= "POST" then
    3.55 +            request.set_status("405 Method Not Allowed")
    3.56 +            cgi.add_header("Allow: POST")
    3.57 +            error("Tried to invoke an action with a GET request.")
    3.58 +          end
    3.59 +          local action_status = execute.filtered_action{
    3.60 +            module = request.get_module(),
    3.61 +            action = request.get_action(),
    3.62 +          }
    3.63 +          if not request.is_rerouted() then
    3.64 +            local routing_mode, routing_module, routing_view
    3.65 +            routing_mode   = cgi.params["_webmcp_routing." .. action_status .. ".mode"]
    3.66 +            routing_module = cgi.params["_webmcp_routing." .. action_status .. ".module"]
    3.67 +            routing_view   = cgi.params["_webmcp_routing." .. action_status .. ".view"]
    3.68 +            routing_anchor = cgi.params["_webmcp_routing." .. action_status .. ".anchor"]
    3.69 +            if not (routing_mode or routing_module or routing_view) then
    3.70 +              action_status = "default"
    3.71 +              routing_mode   = cgi.params["_webmcp_routing.default.mode"]
    3.72 +              routing_module = cgi.params["_webmcp_routing.default.module"]
    3.73 +              routing_view   = cgi.params["_webmcp_routing.default.view"]
    3.74 +              routing_anchor = cgi.params["_webmcp_routing.default.anchor"]
    3.75 +            end
    3.76 +            assert(routing_module, "Routing information has no module.")
    3.77 +            assert(routing_view,   "Routing information has no view.")
    3.78 +            if routing_mode == "redirect" then
    3.79 +              local routing_params = {}
    3.80 +              for key, value in pairs(cgi.params) do
    3.81 +                local status, stripped_key = string.match(
    3.82 +                  key, "^_webmcp_routing%.([^%.]*)%.params%.(.*)$"
    3.83 +                )
    3.84 +                if status == action_status then
    3.85 +                  routing_params[stripped_key] = value
    3.86 +                end
    3.87 +              end
    3.88 +              request.redirect{
    3.89 +                module = routing_module,
    3.90 +                view   = routing_view,
    3.91 +                id     = cgi.params["_webmcp_routing." .. action_status .. ".id"],
    3.92 +                params = routing_params,
    3.93 +                anchor = routing_anchor
    3.94 +              }
    3.95 +            elseif routing_mode == "forward" then
    3.96 +              request.forward{ module = routing_module, view = routing_view }
    3.97 +            else
    3.98 +              error("Missing or unknown routing mode in request parameters.")
    3.99 +            end
   3.100 +          end
   3.101 +        end
   3.102 +      else
   3.103 +        -- no action
   3.104 +        trace.request{
   3.105 +          module = request.get_module(),
   3.106 +          view   = request.get_view()
   3.107 +        }
   3.108 +        if
   3.109 +          request.get_404_route() and
   3.110 +          not file_exists(
   3.111 +            encode.view_file_path{
   3.112 +              module = request.get_module(),
   3.113 +              view   = request.get_view()
   3.114 +            }
   3.115 +          )
   3.116 +        then
   3.117 +          request.set_status("404 Not Found")
   3.118 +          request.forward(request.get_404_route())
   3.119 +        end
   3.120 +      end
   3.121 +
   3.122 +      if not request.get_redirect_data() then
   3.123 +        request.process_forward()
   3.124 +        local view = request.get_view()
   3.125 +        if string.find(view, "^_") then
   3.126 +          error("Tried to call a private view (prefixed with underscore).")
   3.127 +        end
   3.128 +        execute.filtered_view{
   3.129 +          module = request.get_module(),
   3.130 +          view   = view,
   3.131 +        }
   3.132 +      end
   3.133 +
   3.134 +      -- force error due to missing absolute base URL until its too late to display error message
   3.135 +      --if request.get_redirect_data() then
   3.136 +      --  request.get_absolute_baseurl()
   3.137 +      --end
   3.138 +
   3.139 +    end,
   3.140 +
   3.141 +    function(errobj)
   3.142 +      return {
   3.143 +        errobj = errobj,
   3.144 +        stacktrace = string.gsub(
   3.145 +          debug.traceback('', 2),
   3.146 +          "^\r?\n?stack traceback:\r?\n?", ""
   3.147 +        )
   3.148 +      }
   3.149 +    end
   3.150 +  )
   3.151 +
   3.152 +  if not success then trace.error{} end
   3.153 +
   3.154 +  -- laufzeitermittlung
   3.155 +  trace.exectime{ real = extos.monotonic_hires_time(), cpu = os.clock() }
   3.156 +
   3.157 +  slot.select('trace', trace.render)  -- render trace information
   3.158 +
   3.159 +  local redirect_data = request.get_redirect_data()
   3.160 +
   3.161 +  -- log error and switch to error layout, unless success
   3.162 +  if not success then
   3.163 +    local errobj     = error_info.errobj
   3.164 +    local stacktrace = error_info.stacktrace
   3.165 +    if not request.get_status() and not request.get_json_request_slots() then
   3.166 +      request.set_status("500 Internal Server Error")
   3.167 +    end
   3.168 +    slot.set_layout('system_error')
   3.169 +    slot.select('system_error', function()
   3.170 +      if getmetatable(errobj) == mondelefant.errorobject_metatable then
   3.171 +        slot.put(
   3.172 +          "<p>Database error of class <b>",
   3.173 +          encode.html(errobj.code),
   3.174 +          "</b> occured:<br/><b>",
   3.175 +          encode.html(errobj.message),
   3.176 +          "</b></p>"
   3.177 +        )
   3.178 +      else
   3.179 +        slot.put("<p><b>", encode.html(tostring(errobj)), "</b></p>")
   3.180 +      end
   3.181 +      slot.put("<p>Stack trace follows:<br/>")
   3.182 +      slot.put(encode.html_newlines(encode.html(stacktrace)))
   3.183 +      slot.put("</p>")
   3.184 +    end)
   3.185 +  elseif redirect_data then
   3.186 +    local redirect_params = {}
   3.187 +    for key, value in pairs(redirect_data.params) do
   3.188 +      redirect_params[key] = value
   3.189 +    end
   3.190 +    local slot_dump = slot.dump_all()
   3.191 +    if slot_dump ~= "" then
   3.192 +      redirect_params.tempstore = tempstore.save(slot_dump)
   3.193 +    end
   3.194 +    local json_request_slots = request.get_json_request_slots()
   3.195 +    if json_request_slots then
   3.196 +      redirect_params["_webmcp_json_slots[]"] = json_request_slots  
   3.197 +    end
   3.198 +    cgi.redirect(
   3.199 +      encode.url{
   3.200 +        base   = request.get_absolute_baseurl(),
   3.201 +        module = redirect_data.module,
   3.202 +        view   = redirect_data.view,
   3.203 +        id     = redirect_data.id,
   3.204 +        params = redirect_params,
   3.205 +        anchor = redirect_data.anchor
   3.206 +      }
   3.207 +    )
   3.208 +    cgi.send_data()
   3.209 +  end
   3.210 +
   3.211 +  if not success or not redirect_data then
   3.212 +
   3.213 +    local http_status = request.get_status()
   3.214 +    if http_status then
   3.215 +      cgi.set_status(http_status)
   3.216 +    end
   3.217 +
   3.218 +    local json_request_slots = request.get_json_request_slots()
   3.219 +    if json_request_slots then
   3.220 +      cgi.set_content_type('application/json')
   3.221 +      local data = {}
   3.222 +      for idx, slot_ident in ipairs(json_request_slots) do
   3.223 +        data[slot_ident] = slot.get_content(slot_ident)
   3.224 +      end
   3.225 +      cgi.send_data(encode.json(data))
   3.226 +    else
   3.227 +      cgi.set_content_type(slot.get_content_type())
   3.228 +      cgi.send_data(slot.render_layout())
   3.229 +    end
   3.230 +  end
   3.231 +
   3.232 +end
     4.1 --- a/framework/env/request/router.lua	Mon Jan 12 01:48:11 2015 +0100
     4.2 +++ b/framework/env/request/router.lua	Mon Jan 12 03:02:26 2015 +0100
     4.3 @@ -1,12 +1,13 @@
     4.4  --[[--
     4.5 +route =
     4.6  request.router()
     4.7  
     4.8 -Processes a request. May be overwritten by an application, calls request.default_router() in its default implementation.
     4.9 +Returns a table with routing information for a request. The table contains the mandatory field "module", one of "view" or "action", and may contain the optional fields "id" and "suffix". This function may be overwritten by an application and calls request.default_router(request.get_path()) in its default implementation.
    4.10  
    4.11  --]]--
    4.12  
    4.13  function request.router()
    4.14 -  request.default_router()
    4.15 +  return (request.default_router(request.get_path()))
    4.16  end
    4.17  
    4.18  --//--

Impressum / About Us