diff --git a/samples/hello_config.lua b/samples/hello_config.lua index 6924341..42c8b42 100644 --- a/samples/hello_config.lua +++ b/samples/hello_config.lua @@ -1,9 +1,10 @@ isolated = false local app = dofile(docroot .. "/hello.lua") +local wsx = require "wsapi.xavante" rules = { { match = { "^/app$", "^/app/" }, - with = wsapi.xavante.makeHandler(app, "/app", docroot, docroot) + with = wsx.makeHandler(app, "/app", docroot, docroot) }, } diff --git a/src/launcher/wsapi b/src/launcher/wsapi index f87f466..abe1e52 100755 --- a/src/launcher/wsapi +++ b/src/launcher/wsapi @@ -123,17 +123,17 @@ addrule{ -- wsapihandler example } if opts.cgilua then - require "xavante.cgiluahandler" + local hcgi = require "xavante.cgiluahandler" addrule{ match = {"%.lp$", "%.lp/.*$", "%.cgi$", "%.cgi/.*$" }, - with = xavante.cgiluahandler.makeHandler (config.docroot, launcher_params) + with = hcgi.makeHandler (config.docroot, launcher_params) } end if opts.op then - require "orbit.ophandler" + local hop = require "orbit.ophandler" addrule{ match = {"%.op$", "%.op/.*$" }, - with = orbit.ophandler.makeHandler (config.docroot, launcher_params) + with = hop.makeHandler (config.docroot, launcher_params) } end diff --git a/src/wsapi/common.lua b/src/wsapi/common.lua index 589626f..6245f4f 100644 --- a/src/wsapi/common.lua +++ b/src/wsapi/common.lua @@ -6,20 +6,26 @@ -- ----------------------------------------------------------------------------- +local os = require "os" +local string = require "string" +local io = require "io" +local table = require "table" +local debug = require "debug" + local wsapi = require "wsapi" local lfs = require "lfs" + +local tostring, tonumber, pairs, ipairs, error, type, pcall, xpcall, setmetatable, dofile, rawget, rawset, assert, loadfile = + tostring, tonumber, pairs, ipairs, error, type, pcall, xpcall, setmetatable, dofile, rawget, rawset, assert, loadfile + +local package = package + local _, ringer = pcall(require, "wsapi.ringer") -local _G = _G -if _VERSION == "Lua 5.2" then - _ENV = setmetatable({}, { __index = _G }) -else - module(..., package.seeall) - _ENV = _M -end +local _M = {} -- HTTP status codes -status_codes = { +_M.status_codes = { [100] = "Continue", [101] = "Switching Protocols", [200] = "OK", @@ -68,7 +74,7 @@ status_codes = { -- environment, and provides the invariant the the WSAPI -- environment returns the empty string instead of nil for -- variables that do not exist -function sv_index(func) +function _M.sv_index(func) if type(func) == "table" then return function (env, n) local v = func[n] @@ -86,9 +92,9 @@ end -- Makes an wsapi_env.input object from a low-level input -- object and the name of the method to read from this object -function input_maker(obj, read_method) +function _M.input_maker(obj, read_method) local input = {} - read = obj[read_method or "read"] + local read = obj[read_method or "read"] function input:read(n) n = n or self.length or 0 @@ -99,7 +105,7 @@ end -- Windows only: sets stdin and stdout to binary mode so -- sending and receiving binary data works with CGI -function setmode() +function _M.setmode() pcall(lfs.setmode, io.stdin, "binary") pcall(lfs.setmode, io.stdout, "binary") end @@ -107,7 +113,7 @@ end -- Returns the actual WSAPI handler (a function) for the -- WSAPI application, whether it is a table, the name of a Lua -- module, a Lua script, or the function itself -function normalize_app(app_run, is_file) +function _M.normalize_app(app_run, is_file) local t = type(app_run) if t == "function" then return app_run @@ -115,9 +121,9 @@ function normalize_app(app_run, is_file) return app_run.run elseif t == "string" then if is_file then - return normalize_app(dofile(app_run)) + return _M.normalize_app(dofile(app_run)) else - return normalize_app(require(app_run)) + return _M.normalize_app(require(app_run)) end else error("not a valid WSAPI application") @@ -127,7 +133,7 @@ end -- Sends the respose body through the "out" pipe, using -- the provided write method. Gets the body from the -- response iterator -function send_content(out, res_iter, write_method) +function _M.send_content(out, res_iter, write_method) local write = out[write_method or "write"] local flush = out.flush local ok, res = xpcall(res_iter, debug.traceback) @@ -145,10 +151,10 @@ end -- Sends the complete response through the "out" pipe, -- using the provided write method -function send_output(out, status, headers, res_iter, write_method, res_line) +function _M.send_output(out, status, headers, res_iter, write_method, res_line) local write = out[write_method or "write"] if type(status) == "number" or status:match("^%d+$") then - status = status .. " " .. status_codes[tonumber(status)] + status = status .. " " .. _M.status_codes[tonumber(status)] end if res_line then write(out, "HTTP/1.1 " .. (status or "500 Internal Server Error") .. "\r\n") @@ -165,11 +171,11 @@ function send_output(out, status, headers, res_iter, write_method, res_line) end end write(out, "\r\n") - send_content(out, res_iter, write_method) + _M.send_content(out, res_iter, write_method) end -- Formats the standard error message for WSAPI applications -function error_html(msg) +function _M.error_html(msg) return string.format([[ WSAPI Error in Application @@ -185,12 +191,12 @@ function error_html(msg) end -- Body for a 500 response -function status_500_html(msg) - return error_html(msg) +function _M.status_500_html(msg) + return _M.error_html(msg) end -- Body for a 404 response -function status_404_html(msg) +function _M.status_404_html(msg) return string.format([[ Resource not found @@ -201,7 +207,7 @@ function status_404_html(msg) ]], tostring(msg)) end -function status_200_html(msg) +function _M.status_200_html(msg) return string.format([[ Resource not found @@ -226,36 +232,36 @@ end -- Sends an error response through the "out" pipe, replicated -- to the "err" pipe (for logging, for example) -- msg is the error message -function send_error(out, err, msg, out_method, err_method, http_response) +function _M.send_error(out, err, msg, out_method, err_method, http_response) local write = out[out_method or "write"] local write_err = err[err_method or "write"] write_err(err, "WSAPI error in application: " .. tostring(msg) .. "\n") - local msg = error_html(msg) + local msg = _M.error_html(msg) local status, headers, res_iter = "500 Internal Server Error", { ["Content-Type"] = "text/html", ["Content-Length"] = #msg }, make_iterator(msg) - send_output(out, status, headers, res_iter, out_method, http_response) + _M.send_output(out, status, headers, res_iter, out_method, http_response) return status, headers end -- Sends a 404 response to the "out" pipe, "msg" is the error -- message -function send_404(out, msg, out_method, http_response) +function _M.send_404(out, msg, out_method, http_response) local write = out[out_method or "write"] - local msg = status_404_html(msg) + local msg = _M.status_404_html(msg) local status, headers, res_iter = "404 Not Found", { ["Content-Type"] = "text/html", ["Content-Length"] = #msg }, make_iterator(msg) - send_output(out, status, headers, res_iter, out_method, http_response) + _M.send_output(out, status, headers, res_iter, out_method, http_response) return status, headers end -- Runs the application in the provided WSAPI environment, catching errors and -- returning the appropriate error repsonses -function run_app(app, env) - return xpcall(function () return (normalize_app(app))(env) end, +function _M.run_app(app, env) + return xpcall(function () return (_M.normalize_app(app))(env) end, function (msg) if type(msg) == "table" then env.STATUS = msg[1] @@ -267,10 +273,10 @@ function run_app(app, env) end -- Builds an WSAPI environment from the configuration table "t" -function wsapi_env(t) +function _M.wsapi_env(t) local env = {} - setmetatable(env, { __index = sv_index(t.env) }) - env.input = input_maker(t.input, t.read_method) + setmetatable(env, { __index = _M.sv_index(t.env) }) + env.input = _M.input_maker(t.input, t.read_method) env.error = t.error env.input.length = tonumber(env.CONTENT_LENGTH) or 0 if env.PATH_INFO == "" then env.PATH_INFO = "/" end @@ -279,10 +285,10 @@ end -- Runs an application with data from the configuration table "t", -- sending the WSAPI error/not found responses in case of errors -function run(app, t) - local env = wsapi_env(t) +function _M.run(app, t) + local env = _M.wsapi_env(t) local ok, status, headers, res_iter = - run_app(app, env) + _M.run_app(app, env) if ok then if not headers["Content-Length"] then if t.http_response then @@ -296,23 +302,23 @@ function run(app, t) end end end - send_output(t.output, status, headers, res_iter, t.write_method, t.http_response) + _M.send_output(t.output, status, headers, res_iter, t.write_method, t.http_response) else if env.STATUS == 404 then - return send_404(t.output, status, t.write_method, t.http_response) + return _M.send_404(t.output, status, t.write_method, t.http_response) else - return send_error(t.output, t.error, status, t.write_method, t.err_method, t.http_response) + return _M.send_error(t.output, t.error, status, t.write_method, t.err_method, t.http_response) end end return status, headers end -function splitpath(filename) +function _M.splitpath(filename) local path, file = string.match(filename, "^(.*)[/\\]([^/\\]*)$") return path, file end -function splitext(filename) +function _M.splitext(filename) local modname, ext = string.match(filename, "^(.+)%.([^%.]+)$") if not modname then modname, ext = filename, "" end return modname, ext @@ -323,17 +329,17 @@ end -- and modification time. If "filename" is a directory it assumes -- that the actual file is a .lua file in this directory with -- the same name as the directory (for example, "/foo/bar/bar.lua") -function find_file(filename) +function _M.find_file(filename) local mode = assert(lfs.attributes(filename, "mode")) local path, file, modname, ext if mode == "directory" then - path, modname = splitpath(filename) + path, modname = _M.splitpath(filename) path = path .. "/" .. modname file = modname .. ".lua" ext = "lua" elseif mode == "file" then - path, file = splitpath(filename) - modname, ext = splitext(file) + path, file = _M.splitpath(filename) + modname, ext = _M.splitext(file) else return nil end @@ -343,7 +349,7 @@ end -- IIS appends the PATH_INFO to PATH_TRANSLATED, this function -- corrects for that -function adjust_iis_path(wsapi_env, filename) +function _M.adjust_iis_path(wsapi_env, filename) local script_name, ext = wsapi_env.SCRIPT_NAME:match("([^/%.]+)%.([^%.]+)$") if script_name then @@ -377,7 +383,7 @@ end -- Corrects PATH_INFO and SCRIPT_NAME, so SCRIPT_NAME will be -- /cgi-bin/wsapi.cgi/bar/baz.lua and PATH_INFO will be /foo -- for the previous example -function adjust_non_wrapped(wsapi_env, filename, launcher) +function _M.adjust_non_wrapped(wsapi_env, filename, launcher) if filename == "" or not_compatible(wsapi_env, filename) or (launcher and filename:match(launcher:gsub("%.", "%.") .. "$")) then local path_info = wsapi_env.PATH_INFO @@ -413,15 +419,15 @@ end -- Tries to guess the correct path for the WSAPI application script, -- correcting for misbehaving web servers (IIS), non-wrapped launchers -- and (http://server/cgi-bin/wsapi.cgi/bar/baz.lua/foo) -function normalize_paths(wsapi_env, filename, launcher, vars) +function _M.normalize_paths(wsapi_env, filename, launcher, vars) vars = vars or { "SCRIPT_FILENAME", "PATH_TRANSLATED" } if not filename or filename == "" then for _, var in ipairs(vars) do filename = wsapi_env[var] if filename ~= "" then break end end - filename = adjust_non_wrapped(wsapi_env, filename, launcher) - filename = adjust_iis_path(wsapi_env, filename) + filename = _M.adjust_non_wrapped(wsapi_env, filename, launcher) + filename = _M.adjust_iis_path(wsapi_env, filename) wsapi_env.PATH_TRANSLATED = filename wsapi_env.SCRIPT_FILENAME = filename else @@ -436,13 +442,13 @@ function normalize_paths(wsapi_env, filename, launcher, vars) end -- Tries to find the correct script to launch for the WSAPI application -function find_module(wsapi_env, filename, launcher, vars) - normalize_paths(wsapi_env, filename or "", launcher, vars) - return find_file(wsapi_env.PATH_TRANSLATED) +function _M.find_module(wsapi_env, filename, launcher, vars) + _M.normalize_paths(wsapi_env, filename or "", launcher, vars) + return _M.find_file(wsapi_env.PATH_TRANSLATED) end -- Version of require skips searching package.path -function require_file(filename, modname) +function _M.require_file(filename, modname) package.loaded[modname] = true local res = loadfile(filename)(modname) if res then @@ -455,15 +461,15 @@ end -- a .lua script and dofile'ing it in case of other extensions), -- returning the WSAPI handler function for this application -- also moves the current directory to the application's path -function load_wsapi(path, file, modname, ext) +function _M.load_wsapi(path, file, modname, ext) lfs.chdir(path) local app if ext == "lua" then - app = require_file(file, modname) + app = _M.require_file(file, modname) else app = dofile(file) end - return normalize_app(app) + return _M.normalize_app(app) end -- Local state and helper functions for the loader if isolated applications, @@ -519,19 +525,19 @@ do -- and runs the application in the provided WSAPI environment local function wsapi_loader_isolated_helper(wsapi_env, params) local path, file, modname, ext, mtime = - find_module(wsapi_env, params.filename, params.launcher, params.vars) + _M.find_module(wsapi_env, params.filename, params.launcher, params.vars) if params.reload then mtime = nil end if not path then error({ 404, "Resource " .. wsapi_env.SCRIPT_NAME .. " not found"}) end - local app = load_wsapi_isolated(path, file, modname, ext, mtime) + local app = _M.load_wsapi_isolated(path, file, modname, ext, mtime) wsapi_env.APP_PATH = path return app(wsapi_env) end -- Loads a WSAPI application isolated in its own Lua state and returns -- the application handler (reusing an existing state if one is free) - function load_wsapi_isolated(path, file, modname, ext, mtime) + function _M.load_wsapi_isolated(path, file, modname, ext, mtime) local filename = path .. "/" .. file lfs.chdir(path) local app, data @@ -564,7 +570,7 @@ do -- Makes an WSAPI application that launches isolated WSAPI applications -- scripts with the provided parameters - see wsapi.fcgi for the -- parameters and their descriptions - function make_isolated_loader(params) + function _M.make_isolated_loader(params) params = params or {} return function (wsapi_env) collect_states(params.period, params.ttl) @@ -572,11 +578,11 @@ do end end - function wsapi_loader_isolated(wsapi_env) + function _M.wsapi_loader_isolated(wsapi_env) return wsapi_loader_isolated_helper(wsapi_env, {}) end - function wsapi_loader_isolated_reload(wsapi_env) + function _M.wsapi_loader_isolated_reload(wsapi_env) return wsapi_loader_isolated_helper(wsapi_env, { reload = true }) end @@ -628,10 +634,10 @@ do -- Loads a WSAPI application isolated in its own Lua state and returns -- the application handler (reusing an existing state if one is free) - function load_isolated_launcher(filename, app_modname, bootstrap, reload) + function _M.load_isolated_launcher(filename, app_modname, bootstrap, reload) local app, data local app_state = app_states[filename] - local path, _ = splitpath(filename) + local path, _ = _M.splitpath(filename) local mtime = lfs.attributes(filename, "modification") if not reload and app_state.mtime == mtime then for _, state in ipairs(app_state.states) do @@ -659,12 +665,12 @@ do -- Makes an WSAPI application that launches an isolated WSAPI launcher -- with the provided parameters - see op.fcgi in the Orbit sources for the -- parameters and their descriptions - function make_isolated_launcher(params) + function _M.make_isolated_launcher(params) params = params or {} return function (wsapi_env) collect_states(params.period, params.ttl) - normalize_paths(wsapi_env, params.filename, params.launcher, params.vars) - local app = load_isolated_launcher(wsapi_env.PATH_TRANSLATED, params.modname, params.bootstrap, params.reload) + _M.normalize_paths(wsapi_env, params.filename, params.launcher, params.vars) + local app = _M.load_isolated_launcher(wsapi_env.PATH_TRANSLATED, params.modname, params.bootstrap, params.reload) return app(wsapi_env) end end @@ -682,7 +688,7 @@ do -- Bootstraps a Lua state (using rings) with the provided WSAPI application local function bootstrap_app(path, file, modname, ext) - return load_wsapi(path, file, modname, ext) + return _M.load_wsapi(path, file, modname, ext) end -- "Garbage-collect" stale Lua states @@ -722,7 +728,7 @@ do -- loads and runs the application in the provided WSAPI environment local function wsapi_loader_persistent_helper(wsapi_env, params) local path, file, modname, ext, mtime = - find_module(wsapi_env, params.filename, params.launcher, params.vars) + _M.find_module(wsapi_env, params.filename, params.launcher, params.vars) if params.reload then mtime = nil end if not path then error({ 404, "Resource " .. wsapi_env.SCRIPT_NAME .. " not found"}) @@ -735,7 +741,7 @@ do -- Makes an WSAPI application that launches persistent WSAPI applications -- scripts with the provided parameters - see wsapi.fcgi for the -- parameters and their descriptions - function make_persistent_loader(params) + function _M.make_persistent_loader(params) params = params or {} return function (wsapi_env) collect_states(params.period, params.ttl) @@ -744,13 +750,13 @@ do end end -function make_loader(params) +function _M.make_loader(params) params = params or { isolated = true } if params.isolated then - return make_isolated_loader(params) + return _M.make_isolated_loader(params) else - return make_persistent_loader(params) + return _M.make_persistent_loader(params) end end -return _ENV +return _M