webmcp
view framework/bin/mcp.lua @ 335:75be5e3f3581
Set default value of max requests per fork to 200 instead of 100
| author | jbe | 
|---|---|
| date | Tue Mar 24 17:38:50 2015 +0100 (2015-03-24) | 
| parents | 51b17cee8432 | 
| children | 209d378c0a3b | 
 line source
     1 #!/usr/bin/env moonbridge
     3 --[[--
     4 WEBMCP_VERSION
     6 A string containing the WebMCP version, e.g. "2.0.0"
     7 --]]--
     8 WEBMCP_VERSION = "2.0.0"
     9 --//--
    11 --[[--
    12 _G
    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.
    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.
    18 --]]--
    19 local _G = _G
    20 local allowed_globals = {}
    21 local protected_environment = setmetatable(
    22   {},  -- proxy environment used all chunks loaded through loadcached(...)
    23   {
    24     __index = _G,
    25     __newindex = function(self, key, value)
    26       if allowed_globals[key] then
    27         _G[key] = value
    28       else
    29         if type(key) == "string" and string.match(key, "^[A-Za-z_][A-Za-z_0-9]*$") then
    30           error('Attempt to set global variable "' .. key .. '" (Hint: missing local statement? Use _G.' .. key .. '=<value> to really set global variable.)', 2)
    31         else
    32           error('Attempt to set global variable', 2)
    33         end
    34       end
    35     end
    36   }
    37 )
    38 --//--
    40 --[[--
    41 lua_func =   -- compiled Lua function
    42 loadcached(
    43   filename   -- path to a Lua source or byte-code file
    44 )
    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.
    48 --]]--
    49 do
    50   local cache = {}
    51   function loadcached(filename)
    52     local cached_func = cache[filename]
    53     if cached_func then
    54       return cached_func
    55     end
    56     local file, read_error = io.open(filename, "r")
    57     if file then
    58       local filedata = assert(file:read("*a"))
    59       assert(file:close())
    60       local func, compile_error = load(filedata, "=" .. filename, nil, protected_environment)
    61       if func then
    62         cache[filename] = func
    63         return func
    64       end
    65       error(compile_error, 0)
    66     else
    67       return nil, read_error
    68     end
    69   end
    70 end
    71 --//--
    73 --[[--
    74 WEBMCP_MODE
    76 A constant set to "listen" in case of a network request, or set to "interactive" in case of interactive mode.
    77 --]]--
    78 if _MOONBRIDGE_VERSION then
    79   WEBMCP_MODE = "listen"
    80 else
    81   WEBMCP_MODE = "interactive"
    82 end
    83 --//--
    85 --[[--
    86 WEBMCP_CONFIG_NAMES
    88 A list of the selected configuration names.
    89 --]]--
    90 -- configuration names are provided as 4th, 5th, etc. command line argument
    91 WEBMCP_CONFIG_NAMES = {select(4, ...)}
    92 --//--
    94 --[[--
    95 WEBMCP_FRAMEWORK_PATH
    97 Directory of the WebMCP framework (always includes a trailing slash).
    98 --]]--
    99 -- set in mcp.lua
   100 --//--
   102 --[[--
   103 WEBMCP_BASE_PATH
   105 Base directory of the application (always includes a trailing slash).
   106 --]]--
   107 -- set in mcp.lua
   108 --//--
   110 --[[--
   111 WEBMCP_APP_NAME
   113 Application name (usually "main"). May be nil in case of interactive mode.
   114 --]]--
   115 -- set in mcp.lua
   116 --//--
   118 -- determine framework and bath path from command line arguments
   119 -- or print usage synopsis (if applicable)
   120 do
   121   local arg1, arg2, arg3 = ...
   122   local helpout
   123   if
   124     arg1 == "-h" or arg1 == "--help" or
   125     arg2 == "-h" or arg2 == "--help"  -- if first arg is provided by wrapper
   126   then
   127     helpout = io.stdout
   128   elseif #WEBMCP_CONFIG_NAMES < 1 then
   129     helpout = io.stderr
   130   end
   131   if helpout then
   132     helpout:write("Usage: moonbridge [moonbr opts] -- <framework path>/bin/mcp.lua <framework path> <app base path> <app name> <config name> [<config name> ...]\n")
   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")
   134     if helpout == io.stderr then
   135       return 1
   136     else
   137       return 0
   138     end
   139   end
   140   local function append_trailing_slash(str)
   141     return string.gsub(str, "([^/])$", function(last) return last .. "/" end)
   142   end
   143   WEBMCP_FRAMEWORK_PATH = append_trailing_slash(arg1)
   144   WEBMCP_BASE_PATH      = append_trailing_slash(arg2)
   145   WEBMCP_APP_NAME       = arg3
   146 end
   148 -- check if framework path is correct
   149 do
   150   local file, errmsg = io.open(WEBMCP_FRAMEWORK_PATH .. "webmcp_version", "r")
   151   if not file then
   152     error('Could not find "webmcp_version" file: ' .. errmsg, 0)
   153   end
   154   local version = assert(file:read())
   155   assert(file:close())
   156   if version ~= WEBMCP_VERSION then
   157     error('Version mismatch in file "' .. WEBMCP_FRAMEWORK_PATH .. 'webmcp_version"')
   158   end
   159 end
   161 -- setup search paths for libraries
   162 do
   163   if string.match(package.path, "^[^;]") then
   164     package.path = ";" .. package.path
   165   end
   166   package.path = WEBMCP_FRAMEWORK_PATH .. "lib/?.lua" .. package.path
   167   -- find out which file name extension shared libraries have
   168   local slib_exts = {}
   169   for ext in string.gmatch(package.cpath, "%?%.([A-Za-z0-9_-]+)") do
   170     if not slib_exts[ext] then
   171       slib_exts[#slib_exts+1] = ext
   172       slib_exts[ext] = true
   173     end
   174   end
   175   local paths = {}
   176   for i, ext in ipairs(slib_exts) do
   177     paths[#paths+1] = WEBMCP_FRAMEWORK_PATH .. "accelerator/?." .. ext
   178   end
   179   for i, ext in ipairs(slib_exts) do
   180     paths[#paths+1] = WEBMCP_FRAMEWORK_PATH .. "lib/?." .. ext
   181   end
   182   paths[#paths+1] = package.cpath
   183   package.cpath = table.concat(paths, ";")
   184 end
   186 -- autoloader system for WebMCP environment "$WEBMCP_FRAMEWORK_PATH/env/",
   187 -- application environment extensions "$WEBMCP_BASE_PATH/env/"
   188 -- and models "$WEBMCP_BASE_PATH/model/"
   189 do
   190   local weakkey_mt = { __mode = "k" }
   191   local autoloader_category = setmetatable({}, weakkey_mt)
   192   local autoloader_path     = setmetatable({}, weakkey_mt)
   193   local autoloader_mt       = {}
   194   local function install_autoloader(self, category, path_fragment)
   195     autoloader_category[self] = category
   196     autoloader_path[self]     = path_fragment
   197     setmetatable(self, autoloader_mt)
   198   end
   199   local function try_exec(filename)
   200     local func = loadcached(filename)
   201     if func then
   202       func()
   203       return true
   204     else
   205       return false
   206     end
   207   end
   208   function autoloader_mt.__index(self, key)
   209     local category, base_path, merge_base_path, file_key
   210     local merge = false
   211     if
   212       string.find(key, "^[a-z_][A-Za-z0-9_]*$") and
   213       not string.find(key, "^__")
   214     then
   215       category        = "env"
   216       base_path       = WEBMCP_FRAMEWORK_PATH .. "env/"
   217       merge           = true
   218       merge_base_path = WEBMCP_BASE_PATH .. "env/"
   219       file_key        = key
   220     elseif string.find(key, "^[A-Z][A-Za-z0-9]*$") then
   221       category        = "model"
   222       base_path       = WEBMCP_BASE_PATH .. "model/"
   223       local first = true
   224       file_key = string.gsub(key, "[A-Z]",
   225         function(c)
   226           if first then
   227             first = false
   228             return string.lower(c)
   229           else
   230             return "_" .. string.lower(c)
   231           end
   232         end
   233       )
   234     else
   235       return
   236     end
   237     local required_category = autoloader_category[self]
   238     if required_category and required_category ~= category then return end
   239     local path_fragment = autoloader_path[self]
   240     local path = base_path .. path_fragment .. file_key
   241     local merge_path
   242     if merge then
   243       merge_path = merge_base_path .. path_fragment .. file_key
   244     end
   245     local function try_dir(dirname)
   246       local dir = io.open(dirname)
   247       if dir then
   248         io.close(dir)
   249         local obj = {}
   250         install_autoloader(obj, category, path_fragment .. file_key .. "/")
   251         rawset(self, key, obj)
   252         try_exec(path .. "/__init.lua")
   253         if merge then try_exec(merge_path .. "/__init.lua") end
   254         return true
   255       else
   256         return false
   257       end
   258     end
   259     if self == _G then
   260       allowed_globals[key] = true
   261     end
   262     if merge and try_exec(merge_path .. ".lua") then
   263     elseif merge and try_dir(merge_path .. "/") then
   264     elseif try_exec(path .. ".lua") then
   265     elseif try_dir(path .. "/") then
   266     else end
   267     if self == _G then
   268       allowed_globals[key] = nil
   269     end
   270     return rawget(self, key)
   271   end
   272   install_autoloader(_G, nil, "")
   273   try_exec(WEBMCP_FRAMEWORK_PATH .. "env/__init.lua")
   274   try_exec(WEBMCP_BASE_PATH .. "env/__init.lua")
   275 end
   277 -- define post-fork initialization function (including loading of "multirand" library)
   278 local function postfork_init()
   279   multirand = require "multirand"
   280   execute.postfork_initializers()
   281 end
   283 -- prepare for interactive or listen mode
   284 if WEBMCP_MODE == "interactive" then
   285   function listen()  -- overwrite Moonbridge's listen function
   286     -- ignore listen function calls for interactive mode
   287   end
   288   trace.disable()  -- avoids memory leakage when scripts are running endlessly
   289 else
   290   local moonbridge_listen = listen
   291   local http = require("moonbridge_http")
   292   function listen(args)  -- overwrite Moonbridge's listen function
   293     assert(args, "No argument passed to listen function")
   294     local min_requests_per_fork = args.min_requests_per_fork or 50
   295     local max_requests_per_fork = args.max_requests_per_fork or 200
   296     local interval_handlers = {}
   297     for j, listener in ipairs(args) do
   298       if listener.proto == "interval" then
   299         local name = listener.name or "Unnamed interval #" .. #interval_handlers+1
   300         interval_handlers[name] = listener.handler
   301         listener.name = name
   302       end
   303     end
   304     local request_count = 0
   305     local function inner_handler(http_request)
   306       request_count = request_count + 1
   307       if request_count >= max_requests_per_fork then
   308         http_request:close_after_finish()
   309       end
   310       request.initialize()
   311       return request.handler(http_request)
   312     end
   313     local outer_handler = http.generate_handler(inner_handler, args.http_options)
   314     args.prepare = postfork_init
   315     args.connect = function(socket)
   316       if socket.interval then
   317         request_count = request_count + 1
   318         request.initialize()
   319         interval_handlers[socket.interval]()
   320       else
   321         local success = outer_handler(socket)
   322         if not success then
   323           return false
   324         end
   325       end
   326       return request_count < min_requests_per_fork
   327     end
   328     args.finish = execute.finalizers
   329     moonbridge_listen(args)
   330   end
   331 end
   333 -- execute configurations and pre-fork initializers
   334 for i, config_name in ipairs(WEBMCP_CONFIG_NAMES) do
   335   execute.config(config_name)
   336 end
   337 execute.prefork_initializers()
   339 -- perform post-fork initializations (once) in case of interactive mode
   340 if WEBMCP_MODE == "interactive" then
   341   postfork_init()
   342 end
