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