Skip to content

Instantly share code, notes, and snippets.

@rafis
Last active December 25, 2015 06:59
Show Gist options
  • Save rafis/6936230 to your computer and use it in GitHub Desktop.
Save rafis/6936230 to your computer and use it in GitHub Desktop.
This is unrolled version of wsapi.fcgi, so the only dependency is `lfcgi`. Beware! Unlike WSAPI in this implementation call to app.run assumes that it returns a string as third parameter, not an iterator. I have renamed it to `body` from `res_iter`. Also there is MobDebug (something like analog of Zend XDebug) being included and logging to `wsap…
#!/usr/bin/env luajit
local jit = rawget(_G or _ENV, "jit")
--#if ENV ~= "PRODUCTION" then
jit.off(true, true)
--#end
if jit.arch == "x64" then
package.cpath = package.cpath .. ";/usr/lib/x86_64-linux-gnu/lua/5.1/?.so"
elseif jit.arch == "x86" then
package.cpath = package.cpath .. ";/usr/lib/i386-linux-gnu/lua/5.1/?.so"
end
-- Here is unrolled version of wsapi.fcgi launcher
local lfcgi = require("lfcgi")
local os = require("os")
local io = require("io")
local ipairs, pairs, type, assert = ipairs, pairs, type, assert
--#if ENV ~= "PRODUCTION" then
local collect_all_garbage = require("lua-nucleo/misc").collect_all_garbage
--#end
io.stdout = lfcgi.stdout
io.stderr = lfcgi.stderr
io.stdin = lfcgi.stdin
local app = require("index")
local mobdebug
if app.MOB_DEBUG then
mobdebug = require("mobdebug")
end
local fcgi_loop = function(app, wsapi_log)
-- HTTP status codes
local status_codes = {
[100] = "Continue",
[101] = "Switching Protocols",
[200] = "OK",
[201] = "Created",
[202] = "Accepted",
[203] = "Non-Authoritative Information",
[204] = "No Content",
[205] = "Reset Content",
[206] = "Partial Content",
[300] = "Multiple Choices",
[301] = "Moved Permanently",
[302] = "Found",
[303] = "See Other",
[304] = "Not Modified",
[305] = "Use Proxy",
[307] = "Temporary Redirect",
[400] = "Bad Request",
[401] = "Unauthorized",
[402] = "Payment Required",
[403] = "Forbidden",
[404] = "Not Found",
[405] = "Method Not Allowed",
[406] = "Not Acceptable",
[407] = "Proxy Authentication Required",
[408] = "Request Time-out",
[409] = "Conflict",
[410] = "Gone",
[411] = "Length Required",
[412] = "Precondition Failed",
[413] = "Request Entity Too Large",
[414] = "Request-URI Too Large",
[415] = "Unsupported Media Type",
[416] = "Requested range not satisfiable",
[417] = "Expectation Failed",
[500] = "Internal Server Error",
[501] = "Not Implemented",
[502] = "Bad Gateway",
[503] = "Service Unavailable",
[504] = "Gateway Time-out",
[505] = "HTTP Version not supported",
}
-- Makes an index metamethod for the environment, from
-- a function that returns the value of a server variable
-- a metamethod lets us do "on-demand" loading of the WSAPI
-- environment, and provides the invariant the the WSAPI
-- environment returns the empty string instead of nil for
-- variables that do not exist
local mt_wsapi_env = {
__index = function(self, n)
local v = lfcgi.getenv(n) or os.getenv(n)
self[n] = v or ""
return v or ""
end;
}
-- Runs an WSAPI application for each FastCGI request that comes
-- from the FastCGI pipeline, until USR1 signal or other error happened
while lfcgi.accept() >= 0 do
--#if ENV ~= "PRODUCTION" then
collect_all_garbage()
--#end
local time_request_start = os.clock()
local memory_request_start = collectgarbage("count")
-- Builds an WSAPI environment from the configuration table "t"
local wsapi_env = { }
setmetatable(wsapi_env, mt_wsapi_env)
wsapi_env.input = { }
wsapi_env.input.read = function(self, n)
n = n or self.length or 0
if n > 0 then
return lfcgi.stdin:read(n)
end
end
wsapi_env.input.length = tonumber(wsapi_env.CONTENT_LENGTH) or 0
wsapi_env.error = lfcgi.stderr
if wsapi_env.PATH_INFO == "" then wsapi_env.PATH_INFO = "/" end
local os_exit
if app.MOB_DEBUG and (app.MOB_DEBUG_ONLY_REMOTE_ADDR == nil or wsapi_env.REMOTE_ADDR == app.MOB_DEBUG_ONLY_REMOTE_ADDR) then
os_exit = os.exit
os.exit = mobdebug.done
mobdebug.coro()
mobdebug.start(wsapi_env.REMOTE_ADDR)
end
-- Runs an application with data from the configuration table "t",
-- sending the WSAPI error/not found responses in case of errors
local ok, status, headers, body = pcall(app.run, wsapi_env, wsapi_log)
if not ok then
-- In case of error the variable "status" contains error message
body = status
status = 500
headers = {
["Content-Type"] = "text/html; charset=utf-8",
["Content-Length"] = #body,
}
end
assert(type(body) == "string")
-- Sends the complete response through the "out" pipe,
-- using the provided write method
assert(type(status) == "number" and status_codes[status] ~= nil)
lfcgi.stdout:write("Status: " .. status .. " " .. status_codes[status] .. "\r\n")
for h, v in pairs(headers or { }) do
if type(v) ~= "table" then
lfcgi.stdout:write(h .. ": " .. tostring(v) .. "\r\n")
else
for _, v in ipairs(v) do
lfcgi.stdout:write(h .. ": " .. tostring(v) .. "\r\n")
end
end
end
lfcgi.stdout:write("\r\n")
lfcgi.stdout:write(body)
if lfcgi.stdout.flush then
lfcgi.stdout:flush()
end
if app.MOB_DEBUG and (app.MOB_DEBUG_ONLY_REMOTE_ADDR == nil or wsapi_env.REMOTE_ADDR == app.MOB_DEBUG_ONLY_REMOTE_ADDR) then
mobdebug.done()
os.exit = os_exit
end
--#if ENV ~= "PRODUCTION" then
collect_all_garbage()
--#end
wsapi_log:write(
wsapi_env["REMOTE_ADDR"], " - ",
wsapi_env["REMOTE_USER"] == "" and "-" or wsapi_env["REMOTE_USER"], " ",
os.date("[%d/%b/%Y:%H:%M:%S +0000]"), " ",
string.format('"%s %s %s"', wsapi_env["REQUEST_METHOD"], wsapi_env["REQUEST_URI"], wsapi_env["SERVER_PROTOCOL"]), " ",
tostring(status), " ",
string.format('"%s"', headers["Content-Type"]), " ",
tostring(headers["Content-Length"]), " ",
string.format('"%s"', wsapi_env["HTTP_REFERER"] == "" and "-" or wsapi_env["HTTP_REFERER"]), " ",
string.format('"%s"', wsapi_env["HTTP_USER_AGENT"]), " ",
--#if ENV ~= "PRODUCTION" then
string.format("%.2fs", os.clock() - time_request_start), " ",
string.format("%.2fKb", collectgarbage("count") - memory_request_start),
--#end
"\n"
)
wsapi_log:flush()
end
end
local wsapi_log = io.open("../log/wsapi.log", "a")
local ok, err = xpcall(
function()
fcgi_loop(app, wsapi_log)
end,
function(err)
return debug.traceback(err, 2)
end
)
if not ok then
wsapi_log:write("ERROR:\n", err, "\n")
else
wsapi_log:write("NOTE: Exited FastCGI loop nicely\n")
end
wsapi_log:close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment