rev |
line source |
jbe@203
|
1 #!/usr/bin/env moonbridge
|
jbe/bsw@0
|
2
|
jbe@294
|
3 --[[--
|
jbe@294
|
4 WEBMCP_VERSION
|
jbe@294
|
5
|
jbe@294
|
6 A string containing the WebMCP version, e.g. "2.0.0"
|
jbe@294
|
7 --]]--
|
jbe@278
|
8 WEBMCP_VERSION = "2.0.0"
|
jbe@294
|
9 --//--
|
jbe@64
|
10
|
jbe@231
|
11 -- allow control of global environment
|
jbe@231
|
12 local _G = _G
|
jbe@237
|
13 local allowed_globals = {}
|
jbe@231
|
14 local global_metatable = {
|
jbe@231
|
15 __index = _G,
|
jbe@231
|
16 __newindex = function(self, key, value)
|
jbe@231
|
17 _G[key] = value
|
jbe@231
|
18 end
|
jbe@231
|
19 }
|
jbe@231
|
20 _ENV = setmetatable({}, global_metatable)
|
jbe@231
|
21
|
jbe@294
|
22 --[[--
|
jbe@309
|
23 lua_func = -- compiled Lua function
|
jbe@309
|
24 loadcached(
|
jbe@309
|
25 filename -- path to a Lua source or byte-code file
|
jbe@309
|
26 )
|
jbe@309
|
27
|
jbe@309
|
28 Loads, compiles and caches a Lua chunk. If the file does not exist, nil and an error string are returned. Otherwise the file is loaded, compiled, and cached. The cached value (i.e. the compiled function) is returned. An error is raised if the compilation was not successful.
|
jbe@309
|
29
|
jbe@309
|
30 --]]--
|
jbe@309
|
31 do
|
jbe@309
|
32 local cache = {}
|
jbe@309
|
33 function loadcached(filename)
|
jbe@311
|
34 local cached_func = cache[filename]
|
jbe@311
|
35 if cached_func then
|
jbe@311
|
36 return cached_func
|
jbe@311
|
37 end
|
jbe@309
|
38 local file, read_error = io.open(filename, "r")
|
jbe@309
|
39 if file then
|
jbe@309
|
40 local filedata = assert(file:read("*a"))
|
jbe@309
|
41 assert(file:close())
|
jbe@309
|
42 local func, compile_error = load(filedata, "=" .. filename, nil, _ENV)
|
jbe@309
|
43 if func then
|
jbe@309
|
44 cache[filename] = func
|
jbe@309
|
45 return func
|
jbe@309
|
46 end
|
jbe@311
|
47 error(compile_error, 0)
|
jbe@309
|
48 else
|
jbe@309
|
49 return nil, read_error
|
jbe@309
|
50 end
|
jbe@309
|
51 end
|
jbe@309
|
52 end
|
jbe@309
|
53 --//--
|
jbe@309
|
54
|
jbe@309
|
55 --[[--
|
jbe@294
|
56 WEBMCP_MODE
|
jbe@294
|
57
|
jbe@294
|
58 A constant set to "listen" in case of a network request, or set to "interactive" in case of interactive mode.
|
jbe@294
|
59 --]]--
|
jbe@203
|
60 -- check if interactive mode
|
jbe@203
|
61 if listen then -- defined by moonbridge
|
jbe@203
|
62 WEBMCP_MODE = "listen"
|
jbe@203
|
63 else
|
jbe@203
|
64 WEBMCP_MODE = "interactive"
|
jbe@64
|
65 end
|
jbe@294
|
66 --//--
|
jbe@203
|
67
|
jbe@294
|
68 --[[--
|
jbe@294
|
69 WEBMCP_CONFIG_NAMES
|
jbe@294
|
70
|
jbe@294
|
71 A list of the selected configuration names.
|
jbe@294
|
72 --]]--
|
jbe@294
|
73 -- configuration names are provided as 4th, 5th, etc. command line argument
|
jbe@206
|
74 WEBMCP_CONFIG_NAMES = {select(4, ...)}
|
jbe@294
|
75 --//--
|
jbe@294
|
76
|
jbe@294
|
77 --[[--
|
jbe@294
|
78 WEBMCP_FRAMEWORK_PATH
|
jbe@294
|
79
|
jbe@294
|
80 Directory of the WebMCP framework (always includes a trailing slash).
|
jbe@294
|
81 --]]--
|
jbe@294
|
82 -- set in mcp.lua
|
jbe@294
|
83 --//--
|
jbe@294
|
84
|
jbe@294
|
85 --[[--
|
jbe@294
|
86 WEBMCP_BASE_PATH
|
jbe@294
|
87
|
jbe@294
|
88 Base directory of the application (always includes a trailing slash).
|
jbe@294
|
89 --]]--
|
jbe@294
|
90 -- set in mcp.lua
|
jbe@294
|
91 --//--
|
jbe@294
|
92
|
jbe@294
|
93 --[[--
|
jbe@294
|
94 WEBMCP_APP_NAME
|
jbe@294
|
95
|
jbe@294
|
96 Application name (usually "main"). May be nil in case of interactive mode.
|
jbe@294
|
97 --]]--
|
jbe@294
|
98 -- set in mcp.lua
|
jbe@294
|
99 --//--
|
jbe@203
|
100
|
jbe@203
|
101 -- determine framework and bath path from command line arguments
|
jbe@203
|
102 -- or print usage synopsis (if applicable)
|
jbe@68
|
103 do
|
jbe@206
|
104 local arg1, arg2, arg3 = ...
|
jbe@203
|
105 local helpout
|
jbe@203
|
106 if
|
jbe@203
|
107 arg1 == "-h" or arg1 == "--help" or
|
jbe@206
|
108 arg2 == "-h" or arg2 == "--help" -- if first arg is provided by wrapper
|
jbe@203
|
109 then
|
jbe@203
|
110 helpout = io.stdout
|
jbe@203
|
111 elseif
|
jbe@217
|
112 #WEBMCP_CONFIG_NAMES < 1 or
|
jbe@206
|
113 (WEBMCP_MODE == "interactive") ~= (arg3 == "INTERACTIVE")
|
jbe@203
|
114 then
|
jbe@203
|
115 helpout = io.stderr
|
jbe@203
|
116 end
|
jbe@203
|
117 if helpout then
|
jbe@217
|
118 helpout:write("Usage: moonbridge -- <framework path>/bin/mcp.lua <framework path> <app base path> <app name> <config name> [<config name> ...]\n")
|
jbe@217
|
119 helpout:write(" or: lua -i <framework path>/bin/mcp.lua <framework path> <app base path> INTERACTIVE <config name> [<config name> ...]\n")
|
jbe@203
|
120 if helpout == io.stderr then
|
jbe@203
|
121 return 1
|
jbe@68
|
122 else
|
jbe@203
|
123 return 0
|
jbe@68
|
124 end
|
jbe@68
|
125 end
|
jbe@203
|
126 local function append_trailing_slash(str)
|
jbe@217
|
127 return string.gsub(str, "([^/])$", function(last) return last .. "/" end)
|
jbe@203
|
128 end
|
jbe@203
|
129 WEBMCP_FRAMEWORK_PATH = append_trailing_slash(arg1)
|
jbe@203
|
130 WEBMCP_BASE_PATH = append_trailing_slash(arg2)
|
jbe@206
|
131 if WEBMCP_MODE == "listen" then
|
jbe@206
|
132 WEBMCP_APP_NAME = arg3
|
jbe@206
|
133 end
|
jbe@68
|
134 end
|
jbe@1
|
135
|
jbe@203
|
136 -- setup search paths for libraries
|
jbe/bsw@0
|
137 do
|
jbe@217
|
138 if string.match(package.path, "^[^;]") then
|
jbe@217
|
139 package.path = ";" .. package.path
|
jbe@217
|
140 end
|
jbe@217
|
141 package.path = WEBMCP_FRAMEWORK_PATH .. "lib/?.lua" .. package.path
|
jbe/bsw@0
|
142 -- find out which file name extension shared libraries have
|
jbe/bsw@0
|
143 local slib_exts = {}
|
jbe/bsw@0
|
144 for ext in string.gmatch(package.cpath, "%?%.([A-Za-z0-9_-]+)") do
|
jbe@217
|
145 if not slib_exts[ext] then
|
jbe@217
|
146 slib_exts[#slib_exts+1] = ext
|
jbe@217
|
147 slib_exts[ext] = true
|
jbe@217
|
148 end
|
jbe/bsw@0
|
149 end
|
jbe/bsw@0
|
150 local paths = {}
|
jbe@217
|
151 for i, ext in ipairs(slib_exts) do
|
jbe@203
|
152 paths[#paths+1] = WEBMCP_FRAMEWORK_PATH .. "accelerator/?." .. ext
|
jbe/bsw@0
|
153 end
|
jbe@217
|
154 for i, ext in ipairs(slib_exts) do
|
jbe@203
|
155 paths[#paths+1] = WEBMCP_FRAMEWORK_PATH .. "lib/?." .. ext
|
jbe/bsw@0
|
156 end
|
jbe/bsw@0
|
157 paths[#paths+1] = package.cpath
|
jbe/bsw@0
|
158 package.cpath = table.concat(paths, ";")
|
jbe/bsw@0
|
159 end
|
jbe/bsw@0
|
160
|
jbe@203
|
161 -- autoloader system for WebMCP environment "$WEBMCP_FRAMEWORK_PATH/env/",
|
jbe@203
|
162 -- application environment extensions "$WEBMCP_BASE_PATH/env/"
|
jbe@203
|
163 -- and models "$WEBMCP_BASE_PATH/model/"
|
jbe/bsw@0
|
164 do
|
jbe/bsw@0
|
165 local weakkey_mt = { __mode = "k" }
|
jbe/bsw@0
|
166 local autoloader_category = setmetatable({}, weakkey_mt)
|
jbe/bsw@0
|
167 local autoloader_path = setmetatable({}, weakkey_mt)
|
jbe/bsw@0
|
168 local autoloader_mt = {}
|
jbe@219
|
169 local function install_autoloader(self, category, path_fragment)
|
jbe/bsw@0
|
170 autoloader_category[self] = category
|
jbe@219
|
171 autoloader_path[self] = path_fragment
|
jbe/bsw@0
|
172 setmetatable(self, autoloader_mt)
|
jbe/bsw@0
|
173 end
|
jbe/bsw@0
|
174 local function try_exec(filename)
|
jbe@309
|
175 local func = loadcached(filename)
|
jbe@309
|
176 if func then
|
jbe@309
|
177 func()
|
jbe@309
|
178 return true
|
jbe/bsw@0
|
179 else
|
jbe/bsw@0
|
180 return false
|
jbe/bsw@0
|
181 end
|
jbe/bsw@0
|
182 end
|
jbe/bsw@0
|
183 function autoloader_mt.__index(self, key)
|
jbe/bsw@0
|
184 local category, base_path, merge_base_path, file_key
|
jbe/bsw@0
|
185 local merge = false
|
jbe/bsw@0
|
186 if
|
jbe/bsw@0
|
187 string.find(key, "^[a-z_][A-Za-z0-9_]*$") and
|
jbe/bsw@0
|
188 not string.find(key, "^__")
|
jbe/bsw@0
|
189 then
|
jbe/bsw@0
|
190 category = "env"
|
jbe@203
|
191 base_path = WEBMCP_FRAMEWORK_PATH .. "env/"
|
jbe/bsw@0
|
192 merge = true
|
jbe@203
|
193 merge_base_path = WEBMCP_BASE_PATH .. "env/"
|
jbe/bsw@0
|
194 file_key = key
|
jbe/bsw@0
|
195 elseif string.find(key, "^[A-Z][A-Za-z0-9]*$") then
|
jbe/bsw@0
|
196 category = "model"
|
jbe@203
|
197 base_path = WEBMCP_BASE_PATH .. "model/"
|
jbe/bsw@0
|
198 local first = true
|
jbe/bsw@0
|
199 file_key = string.gsub(key, "[A-Z]",
|
jbe/bsw@0
|
200 function(c)
|
jbe/bsw@0
|
201 if first then
|
jbe/bsw@0
|
202 first = false
|
jbe/bsw@0
|
203 return string.lower(c)
|
jbe/bsw@0
|
204 else
|
jbe/bsw@0
|
205 return "_" .. string.lower(c)
|
jbe/bsw@0
|
206 end
|
jbe/bsw@0
|
207 end
|
jbe/bsw@0
|
208 )
|
jbe/bsw@0
|
209 else
|
jbe/bsw@0
|
210 return
|
jbe/bsw@0
|
211 end
|
jbe/bsw@0
|
212 local required_category = autoloader_category[self]
|
jbe/bsw@0
|
213 if required_category and required_category ~= category then return end
|
jbe@219
|
214 local path_fragment = autoloader_path[self]
|
jbe@219
|
215 local path = base_path .. path_fragment .. file_key
|
jbe@219
|
216 local merge_path
|
jbe/bsw@0
|
217 if merge then
|
jbe@219
|
218 merge_path = merge_base_path .. path_fragment .. file_key
|
jbe/bsw@0
|
219 end
|
jbe/bsw@0
|
220 local function try_dir(dirname)
|
jbe/bsw@0
|
221 local dir = io.open(dirname)
|
jbe/bsw@0
|
222 if dir then
|
jbe/bsw@0
|
223 io.close(dir)
|
jbe/bsw@0
|
224 local obj = {}
|
jbe@219
|
225 install_autoloader(obj, category, path_fragment .. file_key .. "/")
|
jbe/bsw@0
|
226 rawset(self, key, obj)
|
jbe@219
|
227 try_exec(path .. "/__init.lua")
|
jbe@219
|
228 if merge then try_exec(merge_path .. "/__init.lua") end
|
jbe/bsw@0
|
229 return true
|
jbe/bsw@0
|
230 else
|
jbe/bsw@0
|
231 return false
|
jbe/bsw@0
|
232 end
|
jbe/bsw@0
|
233 end
|
jbe@238
|
234 if self == _G then
|
jbe@237
|
235 allowed_globals[key] = true
|
jbe@233
|
236 end
|
jbe@219
|
237 if merge and try_exec(merge_path .. ".lua") then
|
jbe@219
|
238 elseif merge and try_dir(merge_path .. "/") then
|
jbe@219
|
239 elseif try_exec(path .. ".lua") then
|
jbe@219
|
240 elseif try_dir(path .. "/") then
|
jbe/bsw@0
|
241 else end
|
jbe@238
|
242 if self == _G then
|
jbe@237
|
243 allowed_globals[key] = nil
|
jbe@233
|
244 end
|
jbe@237
|
245 return rawget(self, key)
|
jbe/bsw@0
|
246 end
|
jbe@219
|
247 install_autoloader(_G, nil, "")
|
jbe@203
|
248 try_exec(WEBMCP_FRAMEWORK_PATH .. "env/__init.lua")
|
jbe@203
|
249 try_exec(WEBMCP_BASE_PATH .. "env/__init.lua")
|
jbe/bsw@0
|
250 end
|
jbe/bsw@0
|
251
|
jbe@214
|
252 -- replace Moonbridge listen function
|
jbe@214
|
253 local moonbridge_listen = listen
|
jbe@225
|
254 local listeners = {}
|
jbe@214
|
255 function listen(args)
|
jbe@214
|
256 listeners[#listeners+1] = args
|
jbe@214
|
257 end
|
jbe@214
|
258
|
jbe@203
|
259 -- prohibit (unintended) definition of new global variables
|
jbe@237
|
260 function global_metatable.__newindex(self, key, value)
|
jbe@237
|
261 if not allowed_globals[key] then
|
jbe@237
|
262 error("Setting of global variable prohibited", 2)
|
jbe@237
|
263 end
|
jbe@239
|
264 _G[key] = value
|
jbe@231
|
265 end
|
jbe@203
|
266
|
jbe@220
|
267 -- execute configurations and pre-fork initializers
|
jbe@206
|
268 for i, config_name in ipairs(WEBMCP_CONFIG_NAMES) do
|
jbe@206
|
269 execute.config(config_name)
|
jbe@206
|
270 end
|
jbe@220
|
271 execute.prefork_initializers()
|
jbe@206
|
272
|
jbe@286
|
273 -- define post-fork initialization function (including loading of "multirand" library)
|
jbe@286
|
274 local function postfork_init()
|
jbe@286
|
275 _G.multirand = require "multirand"
|
jbe@286
|
276 execute.postfork_initializers()
|
jbe@286
|
277 end
|
jbe@286
|
278
|
jbe/bsw@0
|
279 -- interactive console mode
|
jbe@203
|
280 if WEBMCP_MODE == "interactive" then
|
jbe@286
|
281 postfork_init()
|
jbe@289
|
282 trace.disable() -- avoids memory leakage
|
jbe/bsw@0
|
283 end
|
jbe/bsw@0
|
284
|
jbe@204
|
285 -- invoke moonbridge
|
jbe@206
|
286 if WEBMCP_MODE == "listen" then
|
jbe@264
|
287 local http_options = request.get_http_options()
|
jbe@288
|
288 local min_requests_per_fork = http_options.min_requests_per_fork or 50
|
jbe@288
|
289 local max_requests_per_fork = http_options.max_requests_per_fork or 100
|
jbe@207
|
290 local http = require("moonbridge_http")
|
jbe@211
|
291 for i, listener in ipairs(listeners) do
|
jbe@264
|
292 local request_count = 0
|
jbe@266
|
293 local function inner_handler(http_request)
|
jbe@264
|
294 request_count = request_count + 1
|
jbe@288
|
295 request.handler(http_request, request_count >= max_requests_per_fork)
|
jbe@264
|
296 end
|
jbe@264
|
297 local outer_handler = http.generate_handler(inner_handler, http_options)
|
jbe@286
|
298 listener.prepare = postfork_init
|
jbe@264
|
299 listener.connect = function(socket)
|
jbe@264
|
300 outer_handler(socket)
|
jbe@288
|
301 return request_count < min_requests_per_fork
|
jbe@264
|
302 end
|
jbe@211
|
303 listener.finish = execute.finalizers
|
jbe@211
|
304 moonbridge_listen(listener)
|
jbe@204
|
305 end
|
jbe@204
|
306 end
|
jbe@204
|
307
|