| 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@317 | 11 --[[-- | 
| jbe@317 | 12 _G | 
| jbe@317 | 13 | 
| jbe@317 | 14 A reference to the global namespace. To avoid accidental programming errors, global variables cannot be set directly, but they must be set through the _G reference, e.g. use _G.foo = true to set the variable "foo" to a value of true. | 
| jbe@317 | 15 | 
| jbe@317 | 16 Note that the global namespace may or may not be shared between requests (Moonbridge creates multiple forks of the Lua machine). To set variables that are to be cleared after the request has been finished, an application may use the "app" table, e.g. app.foo = true to set the variable app.foo to a value of true, which will be cleared automatically when the request has ended. | 
| jbe@317 | 17 | 
| jbe@317 | 18 --]]-- | 
| jbe@231 | 19 local _G = _G | 
| jbe@237 | 20 local allowed_globals = {} | 
| jbe@324 | 21 local protected_environment = setmetatable( | 
| jbe@324 | 22   {},  -- proxy environment used all chunks loaded through loadcached(...) | 
| jbe@324 | 23   { | 
| jbe@324 | 24     __index = _G, | 
| jbe@324 | 25     __newindex = function(self, key, value) | 
| jbe@324 | 26       if allowed_globals[key] then | 
| jbe@324 | 27         _G[key] = value | 
| jbe@317 | 28       else | 
| jbe@324 | 29         if type(key) == "string" and string.match(key, "^[A-Za-z_][A-Za-z_0-9]*$") then | 
| jbe@333 | 30           error('Attempt to set global variable "' .. key .. '" (Hint: missing local statement? Use _G.' .. key .. '=<value> to really set global variable.)', 2) | 
| jbe@324 | 31         else | 
| jbe@324 | 32           error('Attempt to set global variable', 2) | 
| jbe@324 | 33         end | 
| jbe@317 | 34       end | 
| jbe@317 | 35     end | 
| jbe@324 | 36   } | 
| jbe@324 | 37 ) | 
| jbe@318 | 38 --//-- | 
| jbe@231 | 39 | 
| jbe@294 | 40 --[[-- | 
| jbe@309 | 41 lua_func =   -- compiled Lua function | 
| jbe@309 | 42 loadcached( | 
| jbe@309 | 43   filename   -- path to a Lua source or byte-code file | 
| jbe@309 | 44 ) | 
| jbe@309 | 45 | 
| jbe@309 | 46 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 | 47 | 
| jbe@309 | 48 --]]-- | 
| jbe@309 | 49 do | 
| jbe@309 | 50   local cache = {} | 
| jbe@309 | 51   function loadcached(filename) | 
| jbe@311 | 52     local cached_func = cache[filename] | 
| jbe@311 | 53     if cached_func then | 
| jbe@311 | 54       return cached_func | 
| jbe@311 | 55     end | 
| jbe@309 | 56     local file, read_error = io.open(filename, "r") | 
| jbe@309 | 57     if file then | 
| jbe@309 | 58       local filedata = assert(file:read("*a")) | 
| jbe@309 | 59       assert(file:close()) | 
| jbe@324 | 60       local func, compile_error = load(filedata, "=" .. filename, nil, protected_environment) | 
| jbe@309 | 61       if func then | 
| jbe@309 | 62         cache[filename] = func | 
| jbe@309 | 63         return func | 
| jbe@309 | 64       end | 
| jbe@311 | 65       error(compile_error, 0) | 
| jbe@309 | 66     else | 
| jbe@309 | 67       return nil, read_error | 
| jbe@309 | 68     end | 
| jbe@309 | 69   end | 
| jbe@309 | 70 end | 
| jbe@309 | 71 --//-- | 
| jbe@309 | 72 | 
| jbe@309 | 73 --[[-- | 
| jbe@294 | 74 WEBMCP_MODE | 
| jbe@294 | 75 | 
| jbe@294 | 76 A constant set to "listen" in case of a network request, or set to "interactive" in case of interactive mode. | 
| jbe@294 | 77 --]]-- | 
| jbe@323 | 78 if _MOONBRIDGE_VERSION then | 
| jbe@203 | 79   WEBMCP_MODE = "listen" | 
| jbe@203 | 80 else | 
| jbe@203 | 81   WEBMCP_MODE = "interactive" | 
| jbe@64 | 82 end | 
| jbe@294 | 83 --//-- | 
| jbe@203 | 84 | 
| jbe@294 | 85 --[[-- | 
| jbe@294 | 86 WEBMCP_CONFIG_NAMES | 
| jbe@294 | 87 | 
| jbe@294 | 88 A list of the selected configuration names. | 
| jbe@294 | 89 --]]-- | 
| jbe@294 | 90 -- configuration names are provided as 4th, 5th, etc. command line argument | 
| jbe@206 | 91 WEBMCP_CONFIG_NAMES = {select(4, ...)} | 
| jbe@294 | 92 --//-- | 
| jbe@294 | 93 | 
| jbe@294 | 94 --[[-- | 
| jbe@294 | 95 WEBMCP_FRAMEWORK_PATH | 
| jbe@294 | 96 | 
| jbe@294 | 97 Directory of the WebMCP framework (always includes a trailing slash). | 
| jbe@294 | 98 --]]-- | 
| jbe@294 | 99 -- set in mcp.lua | 
| jbe@294 | 100 --//-- | 
| jbe@294 | 101 | 
| jbe@294 | 102 --[[-- | 
| jbe@294 | 103 WEBMCP_BASE_PATH | 
| jbe@294 | 104 | 
| jbe@294 | 105 Base directory of the application (always includes a trailing slash). | 
| jbe@294 | 106 --]]-- | 
| jbe@294 | 107 -- set in mcp.lua | 
| jbe@294 | 108 --//-- | 
| jbe@294 | 109 | 
| jbe@294 | 110 --[[-- | 
| jbe@294 | 111 WEBMCP_APP_NAME | 
| jbe@294 | 112 | 
| jbe@294 | 113 Application name (usually "main"). May be nil in case of interactive mode. | 
| jbe@294 | 114 --]]-- | 
| jbe@294 | 115 -- set in mcp.lua | 
| jbe@294 | 116 --//-- | 
| jbe@203 | 117 | 
| jbe@203 | 118 -- determine framework and bath path from command line arguments | 
| jbe@203 | 119 -- or print usage synopsis (if applicable) | 
| jbe@68 | 120 do | 
| jbe@206 | 121   local arg1, arg2, arg3 = ... | 
| jbe@203 | 122   local helpout | 
| jbe@203 | 123   if | 
| jbe@203 | 124     arg1 == "-h" or arg1 == "--help" or | 
| jbe@206 | 125     arg2 == "-h" or arg2 == "--help"  -- if first arg is provided by wrapper | 
| jbe@203 | 126   then | 
| jbe@203 | 127     helpout = io.stdout | 
| jbe@316 | 128   elseif #WEBMCP_CONFIG_NAMES < 1 then | 
| jbe@203 | 129     helpout = io.stderr | 
| jbe@203 | 130   end | 
| jbe@203 | 131   if helpout then | 
| jbe@316 | 132     helpout:write("Usage: moonbridge [moonbr opts] -- <framework path>/bin/mcp.lua <framework path> <app base path> <app name> <config name> [<config name> ...]\n") | 
| jbe@316 | 133     helpout:write("   or: lua -i     [Lua opts]    -- <framework path>/bin/mcp.lua <framework path> <app base path> <app name> <config name> [<config name> ...]\n") | 
| jbe@203 | 134     if helpout == io.stderr then | 
| jbe@203 | 135       return 1 | 
| jbe@68 | 136     else | 
| jbe@203 | 137       return 0 | 
| jbe@68 | 138     end | 
| jbe@68 | 139   end | 
| jbe@203 | 140   local function append_trailing_slash(str) | 
| jbe@217 | 141     return string.gsub(str, "([^/])$", function(last) return last .. "/" end) | 
| jbe@203 | 142   end | 
| jbe@203 | 143   WEBMCP_FRAMEWORK_PATH = append_trailing_slash(arg1) | 
| jbe@203 | 144   WEBMCP_BASE_PATH      = append_trailing_slash(arg2) | 
| jbe@316 | 145   WEBMCP_APP_NAME       = arg3 | 
| jbe@68 | 146 end | 
| jbe@1 | 147 | 
| jbe@313 | 148 -- check if framework path is correct | 
| jbe@313 | 149 do | 
| jbe@313 | 150   local file, errmsg = io.open(WEBMCP_FRAMEWORK_PATH .. "webmcp_version", "r") | 
| jbe@313 | 151   if not file then | 
| jbe@313 | 152     error('Could not find "webmcp_version" file: ' .. errmsg, 0) | 
| jbe@313 | 153   end | 
| jbe@313 | 154   local version = assert(file:read()) | 
| jbe@313 | 155   assert(file:close()) | 
| jbe@313 | 156   if version ~= WEBMCP_VERSION then | 
| jbe@313 | 157     error('Version mismatch in file "' .. WEBMCP_FRAMEWORK_PATH .. 'webmcp_version"') | 
| jbe@313 | 158   end | 
| jbe@313 | 159 end | 
| jbe@313 | 160 | 
| jbe@203 | 161 -- setup search paths for libraries | 
| jbe/bsw@0 | 162 do | 
| jbe@217 | 163   if string.match(package.path, "^[^;]") then | 
| jbe@217 | 164     package.path = ";" .. package.path | 
| jbe@217 | 165   end | 
| jbe@217 | 166   package.path = WEBMCP_FRAMEWORK_PATH .. "lib/?.lua" .. package.path | 
| jbe/bsw@0 | 167   -- find out which file name extension shared libraries have | 
| jbe/bsw@0 | 168   local slib_exts = {} | 
| jbe/bsw@0 | 169   for ext in string.gmatch(package.cpath, "%?%.([A-Za-z0-9_-]+)") do | 
| jbe@217 | 170     if not slib_exts[ext] then | 
| jbe@217 | 171       slib_exts[#slib_exts+1] = ext | 
| jbe@217 | 172       slib_exts[ext] = true | 
| jbe@217 | 173     end | 
| jbe/bsw@0 | 174   end | 
| jbe/bsw@0 | 175   local paths = {} | 
| jbe@217 | 176   for i, ext in ipairs(slib_exts) do | 
| jbe@203 | 177     paths[#paths+1] = WEBMCP_FRAMEWORK_PATH .. "accelerator/?." .. ext | 
| jbe/bsw@0 | 178   end | 
| jbe@217 | 179   for i, ext in ipairs(slib_exts) do | 
| jbe@203 | 180     paths[#paths+1] = WEBMCP_FRAMEWORK_PATH .. "lib/?." .. ext | 
| jbe/bsw@0 | 181   end | 
| jbe/bsw@0 | 182   paths[#paths+1] = package.cpath | 
| jbe/bsw@0 | 183   package.cpath = table.concat(paths, ";") | 
| jbe/bsw@0 | 184 end | 
| jbe/bsw@0 | 185 | 
| jbe@203 | 186 -- autoloader system for WebMCP environment "$WEBMCP_FRAMEWORK_PATH/env/", | 
| jbe@203 | 187 -- application environment extensions "$WEBMCP_BASE_PATH/env/" | 
| jbe@203 | 188 -- and models "$WEBMCP_BASE_PATH/model/" | 
| jbe/bsw@0 | 189 do | 
| jbe/bsw@0 | 190   local weakkey_mt = { __mode = "k" } | 
| jbe/bsw@0 | 191   local autoloader_category = setmetatable({}, weakkey_mt) | 
| jbe/bsw@0 | 192   local autoloader_path     = setmetatable({}, weakkey_mt) | 
| jbe/bsw@0 | 193   local autoloader_mt       = {} | 
| jbe@219 | 194   local function install_autoloader(self, category, path_fragment) | 
| jbe/bsw@0 | 195     autoloader_category[self] = category | 
| jbe@219 | 196     autoloader_path[self]     = path_fragment | 
| jbe/bsw@0 | 197     setmetatable(self, autoloader_mt) | 
| jbe/bsw@0 | 198   end | 
| jbe/bsw@0 | 199   local function try_exec(filename) | 
| jbe@309 | 200     local func = loadcached(filename) | 
| jbe@309 | 201     if func then | 
| jbe@309 | 202       func() | 
| jbe@309 | 203       return true | 
| jbe/bsw@0 | 204     else | 
| jbe/bsw@0 | 205       return false | 
| jbe/bsw@0 | 206     end | 
| jbe/bsw@0 | 207   end | 
| jbe/bsw@0 | 208   function autoloader_mt.__index(self, key) | 
| jbe/bsw@0 | 209     local category, base_path, merge_base_path, file_key | 
| jbe/bsw@0 | 210     local merge = false | 
| jbe/bsw@0 | 211     if | 
| jbe/bsw@0 | 212       string.find(key, "^[a-z_][A-Za-z0-9_]*$") and | 
| jbe/bsw@0 | 213       not string.find(key, "^__") | 
| jbe/bsw@0 | 214     then | 
| jbe/bsw@0 | 215       category        = "env" | 
| jbe@203 | 216       base_path       = WEBMCP_FRAMEWORK_PATH .. "env/" | 
| jbe/bsw@0 | 217       merge           = true | 
| jbe@203 | 218       merge_base_path = WEBMCP_BASE_PATH .. "env/" | 
| jbe/bsw@0 | 219       file_key        = key | 
| jbe/bsw@0 | 220     elseif string.find(key, "^[A-Z][A-Za-z0-9]*$") then | 
| jbe/bsw@0 | 221       category        = "model" | 
| jbe@203 | 222       base_path       = WEBMCP_BASE_PATH .. "model/" | 
| jbe/bsw@0 | 223       local first = true | 
| jbe/bsw@0 | 224       file_key = string.gsub(key, "[A-Z]", | 
| jbe/bsw@0 | 225         function(c) | 
| jbe/bsw@0 | 226           if first then | 
| jbe/bsw@0 | 227             first = false | 
| jbe/bsw@0 | 228             return string.lower(c) | 
| jbe/bsw@0 | 229           else | 
| jbe/bsw@0 | 230             return "_" .. string.lower(c) | 
| jbe/bsw@0 | 231           end | 
| jbe/bsw@0 | 232         end | 
| jbe/bsw@0 | 233       ) | 
| jbe/bsw@0 | 234     else | 
| jbe/bsw@0 | 235       return | 
| jbe/bsw@0 | 236     end | 
| jbe/bsw@0 | 237     local required_category = autoloader_category[self] | 
| jbe/bsw@0 | 238     if required_category and required_category ~= category then return end | 
| jbe@219 | 239     local path_fragment = autoloader_path[self] | 
| jbe@219 | 240     local path = base_path .. path_fragment .. file_key | 
| jbe@219 | 241     local merge_path | 
| jbe/bsw@0 | 242     if merge then | 
| jbe@219 | 243       merge_path = merge_base_path .. path_fragment .. file_key | 
| jbe/bsw@0 | 244     end | 
| jbe/bsw@0 | 245     local function try_dir(dirname) | 
| jbe/bsw@0 | 246       local dir = io.open(dirname) | 
| jbe/bsw@0 | 247       if dir then | 
| jbe/bsw@0 | 248         io.close(dir) | 
| jbe/bsw@0 | 249         local obj = {} | 
| jbe@219 | 250         install_autoloader(obj, category, path_fragment .. file_key .. "/") | 
| jbe/bsw@0 | 251         rawset(self, key, obj) | 
| jbe@219 | 252         try_exec(path .. "/__init.lua") | 
| jbe@219 | 253         if merge then try_exec(merge_path .. "/__init.lua") end | 
| jbe/bsw@0 | 254         return true | 
| jbe/bsw@0 | 255       else | 
| jbe/bsw@0 | 256         return false | 
| jbe/bsw@0 | 257       end | 
| jbe/bsw@0 | 258     end | 
| jbe@238 | 259     if self == _G then | 
| jbe@237 | 260       allowed_globals[key] = true | 
| jbe@233 | 261     end | 
| jbe@219 | 262     if merge and try_exec(merge_path .. ".lua") then | 
| jbe@219 | 263     elseif merge and try_dir(merge_path .. "/") then | 
| jbe@219 | 264     elseif try_exec(path .. ".lua") then | 
| jbe@219 | 265     elseif try_dir(path .. "/") then | 
| jbe/bsw@0 | 266     else end | 
| jbe@238 | 267     if self == _G then | 
| jbe@237 | 268       allowed_globals[key] = nil | 
| jbe@233 | 269     end | 
| jbe@237 | 270     return rawget(self, key) | 
| jbe/bsw@0 | 271   end | 
| jbe@219 | 272   install_autoloader(_G, nil, "") | 
| jbe@324 | 273   try_exec(WEBMCP_FRAMEWORK_PATH .. "env/__init.lua") | 
| jbe@324 | 274   try_exec(WEBMCP_BASE_PATH .. "env/__init.lua") | 
| jbe@214 | 275 end | 
| jbe@214 | 276 | 
| jbe@286 | 277 -- define post-fork initialization function (including loading of "multirand" library) | 
| jbe@286 | 278 local function postfork_init() | 
| jbe@324 | 279   multirand = require "multirand" | 
| jbe@286 | 280   execute.postfork_initializers() | 
| jbe@286 | 281 end | 
| jbe@286 | 282 | 
| jbe@336 | 283 --[[-- | 
| jbe@336 | 284 listen{ | 
| jbe@336 | 285   { | 
| jbe@336 | 286     proto     = proto,            -- "local", "tcp4", "tcp6", or "interval" | 
| jbe@336 | 287     path      = path,             -- path to unix domain socket if proto == "local" | 
| jbe@336 | 288     port      = port,             -- TCP port number | 
| jbe@336 | 289     localhost = localhost_only,   -- set to true to only listen on localhost (127.0.0.1 or ::1) interface | 
| jbe@336 | 290     name      = interval_name,    -- optional interval name (may be useful for log output) | 
| jbe@336 | 291     handler   = interval_handler  -- interval handler if proto == "interval" | 
| jbe@336 | 292   }, | 
| jbe@336 | 293   { | 
| jbe@336 | 294     ...                           -- second listener | 
| jbe@336 | 295   }, | 
| jbe@336 | 296   ...                             -- more listeners | 
| jbe@336 | 297   -- the following options are all optional and have default values: | 
| jbe@336 | 298   pre_fork         = pre_fork,          -- desired number of spare (idle) processes | 
| jbe@336 | 299   min_fork         = min_fork,          -- minimum number of processes | 
| jbe@336 | 300   max_fork         = max_fork,          -- maximum number of processes (hard limit) | 
| jbe@336 | 301   fork_delay       = fork_delay,        -- delay (seconds) between creation of spare processes | 
| jbe@336 | 302   fork_error_delay = fork_error_delay,  -- delay (seconds) before retry of failed process creation | 
| jbe@336 | 303   exit_delay       = exit_delay,        -- delay (seconds) between destruction of excessive spare processes | 
| jbe@336 | 304   idle_timeout     = idle_timeout,      -- idle time (seconds) after a fork gets terminated (0 for no timeout) | 
| jbe@336 | 305   memory_limit     = memory_limit,      -- maximum memory consumption (bytes) before process gets terminated | 
| jbe@336 | 306   min_requests_per_fork = min_requests_per_fork,  -- minimum count of requests handled before fork is terminated | 
| jbe@336 | 307   max_requests_per_fork = max_requests_per_fork,  -- maximum count of requests handled before fork is terminated | 
| jbe@336 | 308   http_options = { | 
| jbe@337 | 309     static_headers            = static_headers,             -- string or table of static headers to be returned with every request | 
| jbe@337 | 310     request_header_size_limit = request_header_size_limit,  -- maximum size of request headers sent by client | 
| jbe@337 | 311     request_body_size_limit   = request_body_size_limit,    -- maximum size of request body sent by client | 
| jbe@337 | 312     request_header_timeout    = request_header_timeout,     -- time after which request headers must have been received and processed | 
| jbe@337 | 313     timeout                   = timeout,                    -- time in which request body and response must be sent | 
| jbe@338 | 314     maximum_input_chunk_size  = maximum_input_chunk_size,   -- tweaks behavior of request-body parser | 
| jbe@337 | 315     minimum_output_chunk_size = minimum_output_chunk_size   -- chunk size for chunked-transfer-encoding | 
| jbe@336 | 316   } | 
| jbe@336 | 317 } | 
| jbe@336 | 318 | 
| jbe@336 | 319 The listen{...} function determines on which TCP port an application is answering requests. A typical call looks as follows: | 
| jbe@336 | 320 | 
| jbe@336 | 321 listen{ | 
| jbe@336 | 322   { proto = "tcp4", port = 8080, localhost = true }, | 
| jbe@336 | 323   { proto = "tcp6", port = 8080, localhost = true } | 
| jbe@336 | 324 } | 
| jbe@336 | 325 | 
| jbe@336 | 326 This function must be called in a configuration file (in the config/ directory) or in pre-fork initializers (in the app/_prefork/ or app/<application name>/_prefork/ directories), unless WebMCP is invoked in interactive mode (in which case any calls of listen{...} are ignored). | 
| jbe@336 | 327 | 
| jbe@336 | 328 This function is a variant of Moonbridge's listen{...} function which has been wrapped for WebMCP. No "prepare", "conenct", or "finish" handler can be set. Instead WebMCP automatically dispatches incoming connections. For interval timers, an interval handler may be specified in each listener. | 
| jbe@336 | 329 | 
| jbe@336 | 330 --]]-- | 
| jbe@317 | 331 -- prepare for interactive or listen mode | 
| jbe@203 | 332 if WEBMCP_MODE == "interactive" then | 
| jbe@317 | 333   function listen()  -- overwrite Moonbridge's listen function | 
| jbe@317 | 334     -- ignore listen function calls for interactive mode | 
| jbe@317 | 335   end | 
| jbe@317 | 336   trace.disable()  -- avoids memory leakage when scripts are running endlessly | 
| jbe@317 | 337 else | 
| jbe@317 | 338   local moonbridge_listen = listen | 
| jbe@207 | 339   local http = require("moonbridge_http") | 
| jbe@317 | 340   function listen(args)  -- overwrite Moonbridge's listen function | 
| jbe@322 | 341     assert(args, "No argument passed to listen function") | 
| jbe@322 | 342     local min_requests_per_fork = args.min_requests_per_fork or 50 | 
| jbe@335 | 343     local max_requests_per_fork = args.max_requests_per_fork or 200 | 
| jbe@316 | 344     local interval_handlers = {} | 
| jbe@317 | 345     for j, listener in ipairs(args) do | 
| jbe@317 | 346       if listener.proto == "interval" then | 
| jbe@317 | 347         local name = listener.name or "Unnamed interval #" .. #interval_handlers+1 | 
| jbe@336 | 348         if interval_handlers[name] ~= nil then | 
| jbe@336 | 349           error('Interval handler with duplicate name "' .. name .. '"') | 
| jbe@336 | 350         end | 
| jbe@317 | 351         interval_handlers[name] = listener.handler | 
| jbe@317 | 352         listener.name = name | 
| jbe@316 | 353       end | 
| jbe@316 | 354     end | 
| jbe@264 | 355     local request_count = 0 | 
| jbe@266 | 356     local function inner_handler(http_request) | 
| jbe@328 | 357       request_count = request_count + 1 | 
| jbe@328 | 358       if request_count >= max_requests_per_fork then | 
| jbe@328 | 359         http_request:close_after_finish() | 
| jbe@328 | 360       end | 
| jbe@317 | 361       request.initialize() | 
| jbe@328 | 362       return request.handler(http_request) | 
| jbe@264 | 363     end | 
| jbe@326 | 364     local outer_handler = http.generate_handler(inner_handler, args.http_options) | 
| jbe@317 | 365     args.prepare = postfork_init | 
| jbe@317 | 366     args.connect = function(socket) | 
| jbe@316 | 367       if socket.interval then | 
| jbe@328 | 368         request_count = request_count + 1 | 
| jbe@317 | 369         request.initialize() | 
| jbe@316 | 370         interval_handlers[socket.interval]() | 
| jbe@316 | 371       else | 
| jbe@327 | 372         local success = outer_handler(socket) | 
| jbe@327 | 373         if not success then | 
| jbe@327 | 374           return false | 
| jbe@327 | 375         end | 
| jbe@316 | 376       end | 
| jbe@288 | 377       return request_count < min_requests_per_fork | 
| jbe@264 | 378     end | 
| jbe@317 | 379     args.finish = execute.finalizers | 
| jbe@317 | 380     moonbridge_listen(args) | 
| jbe@204 | 381   end | 
| jbe@204 | 382 end | 
| jbe@336 | 383 --//-- | 
| jbe@317 | 384 | 
| jbe@317 | 385 -- execute configurations and pre-fork initializers | 
| jbe@317 | 386 for i, config_name in ipairs(WEBMCP_CONFIG_NAMES) do | 
| jbe@317 | 387   execute.config(config_name) | 
| jbe@317 | 388 end | 
| jbe@317 | 389 execute.prefork_initializers() | 
| jbe@317 | 390 | 
| jbe@324 | 391 -- perform post-fork initializations (once) in case of interactive mode | 
| jbe@317 | 392 if WEBMCP_MODE == "interactive" then | 
| jbe@317 | 393   postfork_init() | 
| jbe@317 | 394 end |