lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]

I've been trying to use cgilua as a template processor from within a
custom, application-specific Xavante handler.  I see a couple of key
cgilua features as beneficial, reuasable components.  One is the
cgilua code that parses and provides the "unified" access to GET and
POST cgi data.  The other feature is the handling of .lua and .lp

I've attached the complete code for a simple, working Xavante handler
at the end of this message and would appreciate a critical review. 
There are a couple of "tricks" (aka kludges) that I've employed to get
this to work.  One of them is to make the cgilua.getparams global and
thus expose it as a very useful component of the Cgilua API.  The
second trick is to force cgilua to be reloaded each pass through the
Xavante handler.  I found I had to do this in order to get
cgilua.servervariable (and possibly other module-level variables) to
be redefined in terms of the current instantiation of the SAPI table. 
The final trick is that I had to define the SAPI and cgi tables as
_G.SAPI and _G.cgi.  Without doing this the SAPI and cgi tables would
not "make it" into the environment of the relevant cgilua code and the

With the above "tricks", the Xavante handler shown below works like a
charm.  But I'm concerned that these tricks may also trigger
undesirable side effects.  I am particularly concerned that by using
_G.SAPI and _G.cgi I may be breaking the thread-safety (or
coroutine-safety) of copas.  Are the _G.SAPI and _G.cgi tables going
to be undesireably shared among copas connection instances and hence
corrupt the purity of the per-connection data?

Thanks for any insight you can share.


---------------- simpleCgiHandler.lua ------------------------

-- borrowed from xavante/cgiluahandler.lua
local function set_api (req, res)
    local SAPI = {
        Response = {},
        Request = {},
    -- Headers
    SAPI.Response.contenttype = function (s)
        res.headers ["Content-Type"] = s
    SAPI.Response.redirect = function (s)
        res.headers ["Location"] = s
        res.statusline = "HTTP/1.1 301 Moved Permanently\r\n"
        res.content = " " --one blank to force output
    SAPI.Response.header = function (h, v)
        res.headers [h] = v
    -- Contents
    SAPI.Response.write = function (s)
        httpd.send_res_data (res, s)
    SAPI.Response.errorlog = function (s) io.stderr:write (s) end
    -- Input POST data
    SAPI.Request.getpostdata = function (n)
        return req.socket:receive (n)
    -- Input general information
    SAPI.Request.servervariable = function (n)
        return req.cgivars[n]
    return SAPI

-- borrowed
local function set_cgivars (req, diskpath)
    req.cgivars = {
        SERVER_SOFTWARE = req.serversoftware,
        SERVER_NAME =,
        GATEWAY_INTERFACE = "CGI/1.1",
        SERVER_PROTOCOL = "HTTP/1.1",
        SERVER_PORT = req.parsed_url.port,
        REQUEST_METHOD = req.cmd_mth,
        PATH_INFO = "",
        PATH_TRANSLATED = diskpath .. req.parsed_url.path,
        SCRIPT_NAME = req.parsed_url.path,
        QUERY_STRING = req.parsed_url.query,
        REMOTE_HOST = req.headers["host"],
        REMOTE_ADDR = string.gsub (req.rawskt:getpeername (), ":%d*$", ""),
        AUTH_TYPE = nil,
        REMOTE_USER = nil,
        CONTENT_TYPE = req.headers ["content-type"],
        CONTENT_LENGTH = req.headers ["content-length"],
    for n,v in pairs (req.headers) do
        req.cgivars ["HTTP_"..string.gsub (string.upper (n), "-", "_")] = v

local function setupCgilua(req, res)
    -- must define SAPI before require"cgilua"
    -- CAUTION: Thread-safety ??????
    _G.SAPI = set_api(req, res)  
    -- force a reload of cgilua to catch changes
    -- to such module-level variables as cgilua.servervariable
    package.loaded.cgilua = nil
    cgilua.addscripthandler ("lua", cgilua.doscript)
    cgilua.addscripthandler ("lp", cgilua.handlelp)
    set_cgivars(req, "")
    -- CAUTION: Thread-safety ??????
    _G.cgi = {}
    -- changed cgilua.getparams to global function

local function doCgiScript(scriptname, params)
    --add programmatically defined cgi params
    if params then
        table.foreach(params, function(n,v) _G.cgi[n] = v end)

-- a container to hold values between invocations
-- (should I be using the Stable package for this?)
-- (Need to look into Sessions)
globals = {mytext=""}

local function simpleCgiHandler(req, res)
    script = cgilua.servervariable("SCRIPT_NAME")
    if script == "/form.lp" then
        cgiparams = { x =, mytext = tostring(globals.mytext) }
        doCgiScript("form.lp", cgiparams)
    elseif script == "/dummy" then
        globals.mytext = _G.cgi.mytext
        res.content = "servervariable(\"SCRIPT_NAME\") = "..script.."<br />"
        return res

function makeHandler()
     return function(req, res)
        setupCgilua(req, res)
        return simpleCgiHandler(req, res)
--------------------- end -----------------------------------

---------------- xavante/config.lua -----------------
require "simpleCgiHandler"
local simplerules = {
	{match= { "/", "/*" }, with = simpleCgiHandler.makeHandler() },
--------------------- end -----------------------------------

------------------------- form.lp ---------------------
<form method="post" action="dummy">
httpd server variable: REMOTE_ADDR = <%= cgilua.servervariable
("REMOTE_ADDR") %><br />
programmatic cgi parameter: x = <%= cgi.x %><br />
Enter your name: <input type="text" name="mytext" value="<%=
cgi.mytext %>"><br />
<input type="submit" value="Go">
--------------------- end -----------------------------------

----------------------- form.lua ------------------------
cgilua.put("You entered: "..tostring(cgi.mytext).."<br />")
cgilua.put("<a href=\"form.lp?\">Try Again</a><br />")
--------------------- end -----------------------------------