webmcp

diff framework/bin/mcp.lua @ 203:c6ef9991b911

Removed CGI support; Created draft for new MCP program to be used with "Moonbridge Network Server for Lua Applications"
author jbe
date Fri Jan 09 01:57:20 2015 +0100 (2015-01-09)
parents framework/cgi-bin/webmcp.lua@8d7665e0d490
children b059efd81649
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/framework/bin/mcp.lua	Fri Jan 09 01:57:20 2015 +0100
     1.3 @@ -0,0 +1,420 @@
     1.4 +#!/usr/bin/env moonbridge
     1.5 +
     1.6 +WEBMCP_VERSION = "2.0.0_devel"
     1.7 +
     1.8 +-- check if interactive mode
     1.9 +if listen then  -- defined by moonbridge
    1.10 +  WEBMCP_MODE = "listen"
    1.11 +else
    1.12 +  WEBMCP_MODE = "interactive"
    1.13 +end
    1.14 +
    1.15 +-- store extra command line arguments
    1.16 +local extraargs = {select(3, ...)}
    1.17 +
    1.18 +-- determine framework and bath path from command line arguments
    1.19 +-- or print usage synopsis (if applicable)
    1.20 +do
    1.21 +  local arg1, arg2 = ...
    1.22 +  local helpout
    1.23 +  if
    1.24 +    arg1 == "-h" or arg1 == "--help" or
    1.25 +    arg2 == "-h" or arg2 == "--help"
    1.26 +  then
    1.27 +    helpout = io.stdout
    1.28 +  elseif
    1.29 +    (WEBMCP_MODE == "listen" and (#extraargs < 2 or #extraargs % 2 ~= 0)) or
    1.30 +    (WEBMCP_MODE == "interactive" and arg2 == nil)
    1.31 +  then
    1.32 +    helpout = io.stderr
    1.33 +  end
    1.34 +  helpout:write("Usage: moonbridge -- mcp.lua <framework path> <app base path> <app name 1> <config name 1> [<app name 2> <config name 2> ...]\n")
    1.35 +  helpout:write("   or: lua -i mcp.lua <framework path> <app base path> [<config name>]\n")
    1.36 +  if helpout then
    1.37 +    if helpout == io.stderr then
    1.38 +      return 1
    1.39 +    else
    1.40 +      return 0
    1.41 +    end
    1.42 +  end
    1.43 +  local function append_trailing_slash(str)
    1.44 +    return string.sub(str, "([^/])$", function(last) return last .. "/" end)
    1.45 +  end
    1.46 +  WEBMCP_FRAMEWORK_PATH = append_trailing_slash(arg1)
    1.47 +  WEBMCP_BASE_PATH      = append_trailing_slash(arg2)
    1.48 +end
    1.49 +
    1.50 +-- setup search paths for libraries
    1.51 +do
    1.52 +  package.path = WEBMCP_FRAMEWORK_PATH .. "lib/?.lua;" .. package.path
    1.53 +  -- find out which file name extension shared libraries have
    1.54 +  local slib_exts = {}
    1.55 +  for ext in string.gmatch(package.cpath, "%?%.([A-Za-z0-9_-]+)") do
    1.56 +    slib_exts[ext] = true
    1.57 +  end
    1.58 +  local paths = {}
    1.59 +  for ext in pairs(slib_exts) do
    1.60 +    paths[#paths+1] = WEBMCP_FRAMEWORK_PATH .. "accelerator/?." .. ext
    1.61 +  end
    1.62 +  for ext in pairs(slib_exts) do
    1.63 +    paths[#paths+1] = WEBMCP_FRAMEWORK_PATH .. "lib/?." .. ext
    1.64 +  end
    1.65 +  paths[#paths+1] = package.cpath
    1.66 +  package.cpath = table.concat(paths, ";")
    1.67 +end
    1.68 +
    1.69 +-- autoloader system for WebMCP environment "$WEBMCP_FRAMEWORK_PATH/env/",
    1.70 +-- application environment extensions "$WEBMCP_BASE_PATH/env/"
    1.71 +-- and models "$WEBMCP_BASE_PATH/model/"
    1.72 +do
    1.73 +  local weakkey_mt = { __mode = "k" }
    1.74 +  local autoloader_category = setmetatable({}, weakkey_mt)
    1.75 +  local autoloader_path     = setmetatable({}, weakkey_mt)
    1.76 +  local autoloader_mt       = {}
    1.77 +  local function install_autoloader(self, category, path)
    1.78 +    autoloader_category[self] = category
    1.79 +    autoloader_path[self]     = path
    1.80 +    setmetatable(self, autoloader_mt)
    1.81 +  end
    1.82 +  local function try_exec(filename)
    1.83 +    local file = io.open(filename, "r")
    1.84 +    if file then
    1.85 +      local filedata = file:read("*a")
    1.86 +      io.close(file)
    1.87 +      local func, errmsg = load(filedata, "=" .. filename)
    1.88 +      if func then
    1.89 +        func()
    1.90 +        return true
    1.91 +      else
    1.92 +        error(errmsg, 0)
    1.93 +      end
    1.94 +    else
    1.95 +      return false
    1.96 +    end
    1.97 +  end
    1.98 +  local function compose_path_string(base, path, key)
    1.99 +    if #path == 0 then
   1.100 +      return base .. "/" .. key
   1.101 +    else
   1.102 +      return base .. table.concat(path, "/") .. "/" .. key
   1.103 +    end
   1.104 +  end
   1.105 +  function autoloader_mt.__index(self, key)
   1.106 +    local category, base_path, merge_base_path, file_key
   1.107 +    local merge = false
   1.108 +    if
   1.109 +      string.find(key, "^[a-z_][A-Za-z0-9_]*$") and
   1.110 +      not string.find(key, "^__")
   1.111 +    then
   1.112 +      category        = "env"
   1.113 +      base_path       = WEBMCP_FRAMEWORK_PATH .. "env/"
   1.114 +      merge           = true
   1.115 +      merge_base_path = WEBMCP_BASE_PATH .. "env/"
   1.116 +      file_key        = key
   1.117 +    elseif string.find(key, "^[A-Z][A-Za-z0-9]*$") then
   1.118 +      category        = "model"
   1.119 +      base_path       = WEBMCP_BASE_PATH .. "model/"
   1.120 +      local first = true
   1.121 +      file_key = string.gsub(key, "[A-Z]",
   1.122 +        function(c)
   1.123 +          if first then
   1.124 +            first = false
   1.125 +            return string.lower(c)
   1.126 +          else
   1.127 +            return "_" .. string.lower(c)
   1.128 +          end
   1.129 +        end
   1.130 +      )
   1.131 +    else
   1.132 +      return
   1.133 +    end
   1.134 +    local required_category = autoloader_category[self]
   1.135 +    if required_category and required_category ~= category then return end
   1.136 +    local path = autoloader_path[self]
   1.137 +    local path_string = compose_path_string(base_path, path, file_key)
   1.138 +    local merge_path_string
   1.139 +    if merge then
   1.140 +      merge_path_string = compose_path_string(
   1.141 +        merge_base_path, path, file_key
   1.142 +      )
   1.143 +    end
   1.144 +    local function try_dir(dirname)
   1.145 +      local dir = io.open(dirname)
   1.146 +      if dir then
   1.147 +        io.close(dir)
   1.148 +        local obj = {}
   1.149 +        local sub_path = {}
   1.150 +        for i = 1, #path do sub_path[i] = path[i] end
   1.151 +        sub_path[#path+1] = file_key
   1.152 +        install_autoloader(obj, category, sub_path)
   1.153 +        rawset(self, key, obj)
   1.154 +        try_exec(path_string .. "/__init.lua")
   1.155 +        if merge then try_exec(merge_path_string .. "/__init.lua") end
   1.156 +        return true
   1.157 +      else
   1.158 +        return false
   1.159 +      end
   1.160 +    end
   1.161 +    if merge and try_exec(merge_path_string .. ".lua") then
   1.162 +    elseif merge and try_dir(merge_path_string .. "/") then
   1.163 +    elseif try_exec(path_string .. ".lua") then
   1.164 +    elseif try_dir(path_string .. "/") then
   1.165 +    else end
   1.166 +    return rawget(self, key)
   1.167 +  end
   1.168 +  install_autoloader(_G, nil, {})
   1.169 +  try_exec(WEBMCP_FRAMEWORK_PATH .. "env/__init.lua")
   1.170 +  try_exec(WEBMCP_BASE_PATH .. "env/__init.lua")
   1.171 +end
   1.172 +
   1.173 +-- prohibit (unintended) definition of new global variables
   1.174 +_ENV = setmetatable({}, {
   1.175 +  __index = _G,
   1.176 +  __newindex = function()
   1.177 +    error("Setting of global variable prohibited")
   1.178 +  end
   1.179 +})
   1.180 +
   1.181 +-- interactive console mode
   1.182 +if WEBMCP_MODE == "interactive" then
   1.183 +  trace.disable()  -- avoids memory leakage (TODO: needs general solution for moonbridge?)
   1.184 +  local config_name = select(3, ...)
   1.185 +  if config_name then
   1.186 +    execute.config(config_name)
   1.187 +  end
   1.188 +  return
   1.189 +end
   1.190 +
   1.191 +-- TODO: moonbridge support below this line and in env/request and env/slot
   1.192 +
   1.193 +local success, error_info = xpcall(
   1.194 +  function()
   1.195 +
   1.196 +    -- execute configuration file
   1.197 +    do
   1.198 +      local config_name = request.get_config_name()
   1.199 +      if config_name then
   1.200 +        execute.config(config_name)
   1.201 +      end
   1.202 +    end
   1.203 +
   1.204 +    -- restore slots if coming from http redirect
   1.205 +    if cgi.params.tempstore then
   1.206 +      trace.restore_slots{}
   1.207 +      local blob = tempstore.pop(cgi.params.tempstore)
   1.208 +      if blob then slot.restore_all(blob) end
   1.209 +    end
   1.210 +
   1.211 +    local function file_exists(filename)
   1.212 +      local file = io.open(filename, "r")
   1.213 +      if file then
   1.214 +        io.close(file)
   1.215 +        return true
   1.216 +      else
   1.217 +        return false
   1.218 +      end
   1.219 +    end
   1.220 +
   1.221 +    if request.is_404() then
   1.222 +      request.set_status("404 Not Found")
   1.223 +      if request.get_404_route() then
   1.224 +        request.forward(request.get_404_route())
   1.225 +      else
   1.226 +        error("No 404 page set.")
   1.227 +      end
   1.228 +    elseif request.get_action() then
   1.229 +      trace.request{
   1.230 +        module = request.get_module(),
   1.231 +        action = request.get_action()
   1.232 +      }
   1.233 +      if
   1.234 +        request.get_404_route() and
   1.235 +        not file_exists(
   1.236 +          encode.action_file_path{
   1.237 +            module = request.get_module(),
   1.238 +            action = request.get_action()
   1.239 +          }
   1.240 +        )
   1.241 +      then
   1.242 +        request.set_status("404 Not Found")
   1.243 +        request.forward(request.get_404_route())
   1.244 +      else
   1.245 +        if cgi.method ~= "POST" then
   1.246 +          request.set_status("405 Method Not Allowed")
   1.247 +          cgi.add_header("Allow: POST")
   1.248 +          error("Tried to invoke an action with a GET request.")
   1.249 +        end
   1.250 +        local action_status = execute.filtered_action{
   1.251 +          module = request.get_module(),
   1.252 +          action = request.get_action(),
   1.253 +        }
   1.254 +        if not request.is_rerouted() then
   1.255 +          local routing_mode, routing_module, routing_view
   1.256 +          routing_mode   = cgi.params["_webmcp_routing." .. action_status .. ".mode"]
   1.257 +          routing_module = cgi.params["_webmcp_routing." .. action_status .. ".module"]
   1.258 +          routing_view   = cgi.params["_webmcp_routing." .. action_status .. ".view"]
   1.259 +          routing_anchor = cgi.params["_webmcp_routing." .. action_status .. ".anchor"]
   1.260 +          if not (routing_mode or routing_module or routing_view) then
   1.261 +            action_status = "default"
   1.262 +            routing_mode   = cgi.params["_webmcp_routing.default.mode"]
   1.263 +            routing_module = cgi.params["_webmcp_routing.default.module"]
   1.264 +            routing_view   = cgi.params["_webmcp_routing.default.view"]
   1.265 +            routing_anchor = cgi.params["_webmcp_routing.default.anchor"]
   1.266 +          end
   1.267 +          assert(routing_module, "Routing information has no module.")
   1.268 +          assert(routing_view,   "Routing information has no view.")
   1.269 +          if routing_mode == "redirect" then
   1.270 +            local routing_params = {}
   1.271 +            for key, value in pairs(cgi.params) do
   1.272 +              local status, stripped_key = string.match(
   1.273 +                key, "^_webmcp_routing%.([^%.]*)%.params%.(.*)$"
   1.274 +              )
   1.275 +              if status == action_status then
   1.276 +                routing_params[stripped_key] = value
   1.277 +              end
   1.278 +            end
   1.279 +            request.redirect{
   1.280 +              module = routing_module,
   1.281 +              view   = routing_view,
   1.282 +              id     = cgi.params["_webmcp_routing." .. action_status .. ".id"],
   1.283 +              params = routing_params,
   1.284 +              anchor = routing_anchor
   1.285 +            }
   1.286 +          elseif routing_mode == "forward" then
   1.287 +            request.forward{ module = routing_module, view = routing_view }
   1.288 +          else
   1.289 +            error("Missing or unknown routing mode in request parameters.")
   1.290 +          end
   1.291 +        end
   1.292 +      end
   1.293 +    else
   1.294 +      -- no action
   1.295 +      trace.request{
   1.296 +        module = request.get_module(),
   1.297 +        view   = request.get_view()
   1.298 +      }
   1.299 +      if
   1.300 +        request.get_404_route() and
   1.301 +        not file_exists(
   1.302 +          encode.view_file_path{
   1.303 +            module = request.get_module(),
   1.304 +            view   = request.get_view()
   1.305 +          }
   1.306 +        )
   1.307 +      then
   1.308 +        request.set_status("404 Not Found")
   1.309 +        request.forward(request.get_404_route())
   1.310 +      end
   1.311 +    end
   1.312 +
   1.313 +    if not request.get_redirect_data() then
   1.314 +      request.process_forward()
   1.315 +      local view = request.get_view()
   1.316 +      if string.find(view, "^_") then
   1.317 +        error("Tried to call a private view (prefixed with underscore).")
   1.318 +      end
   1.319 +      execute.filtered_view{
   1.320 +        module = request.get_module(),
   1.321 +        view   = view,
   1.322 +      }
   1.323 +    end
   1.324 +
   1.325 +    -- force error due to missing absolute base URL until its too late to display error message
   1.326 +    --if request.get_redirect_data() then
   1.327 +    --  request.get_absolute_baseurl()
   1.328 +    --end
   1.329 +
   1.330 +  end,
   1.331 +
   1.332 +  function(errobj)
   1.333 +    return {
   1.334 +      errobj = errobj,
   1.335 +      stacktrace = string.gsub(
   1.336 +        debug.traceback('', 2),
   1.337 +        "^\r?\n?stack traceback:\r?\n?", ""
   1.338 +      )
   1.339 +    }
   1.340 +  end
   1.341 +)
   1.342 +
   1.343 +if not success then trace.error{} end
   1.344 +
   1.345 +-- laufzeitermittlung
   1.346 +trace.exectime{ real = extos.monotonic_hires_time(), cpu = os.clock() }
   1.347 +
   1.348 +slot.select('trace', trace.render)  -- render trace information
   1.349 +
   1.350 +local redirect_data = request.get_redirect_data()
   1.351 +
   1.352 +-- log error and switch to error layout, unless success
   1.353 +if not success then
   1.354 +  local errobj     = error_info.errobj
   1.355 +  local stacktrace = error_info.stacktrace
   1.356 +  if not request.get_status() and not request.get_json_request_slots() then
   1.357 +    request.set_status("500 Internal Server Error")
   1.358 +  end
   1.359 +  slot.set_layout('system_error')
   1.360 +  slot.select('system_error', function()
   1.361 +    if getmetatable(errobj) == mondelefant.errorobject_metatable then
   1.362 +      slot.put(
   1.363 +        "<p>Database error of class <b>",
   1.364 +        encode.html(errobj.code),
   1.365 +        "</b> occured:<br/><b>",
   1.366 +        encode.html(errobj.message),
   1.367 +        "</b></p>"
   1.368 +      )
   1.369 +    else
   1.370 +      slot.put("<p><b>", encode.html(tostring(errobj)), "</b></p>")
   1.371 +    end
   1.372 +    slot.put("<p>Stack trace follows:<br/>")
   1.373 +    slot.put(encode.html_newlines(encode.html(stacktrace)))
   1.374 +    slot.put("</p>")
   1.375 +  end)
   1.376 +elseif redirect_data then
   1.377 +  local redirect_params = {}
   1.378 +  for key, value in pairs(redirect_data.params) do
   1.379 +    redirect_params[key] = value
   1.380 +  end
   1.381 +  local slot_dump = slot.dump_all()
   1.382 +  if slot_dump ~= "" then
   1.383 +    redirect_params.tempstore = tempstore.save(slot_dump)
   1.384 +  end
   1.385 +  local json_request_slots = request.get_json_request_slots()
   1.386 +  if json_request_slots then
   1.387 +    redirect_params["_webmcp_json_slots[]"] = json_request_slots  
   1.388 +  end
   1.389 +  cgi.redirect(
   1.390 +    encode.url{
   1.391 +      base   = request.get_absolute_baseurl(),
   1.392 +      module = redirect_data.module,
   1.393 +      view   = redirect_data.view,
   1.394 +      id     = redirect_data.id,
   1.395 +      params = redirect_params,
   1.396 +      anchor = redirect_data.anchor
   1.397 +    }
   1.398 +  )
   1.399 +  cgi.send_data()
   1.400 +end
   1.401 +
   1.402 +if not success or not redirect_data then
   1.403 +
   1.404 +  local http_status = request.get_status()
   1.405 +  if http_status then
   1.406 +    cgi.set_status(http_status)
   1.407 +  end
   1.408 +
   1.409 +  local json_request_slots = request.get_json_request_slots()
   1.410 +  if json_request_slots then
   1.411 +    cgi.set_content_type('application/json')
   1.412 +    local data = {}
   1.413 +    for idx, slot_ident in ipairs(json_request_slots) do
   1.414 +      data[slot_ident] = slot.get_content(slot_ident)
   1.415 +    end
   1.416 +    cgi.send_data(encode.json(data))
   1.417 +  else
   1.418 +    cgi.set_content_type(slot.get_content_type())
   1.419 +    cgi.send_data(slot.render_layout())
   1.420 +  end
   1.421 +end
   1.422 +
   1.423 +exit()

Impressum / About Us