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()