The Xavante Lua Web Server Experimental Modules

Xavante
A Lua Web Server with CGILua support

## Experimental Modules

These are a few experimental modules included with Xavante; they're fairly functional but might have very rough edges, or might not seem very useful at first.

In general, these modules can be used when writing new Xavante handlers. In my view (not shared by most Kepler developers), static pages and CGILua are only two particular ways to create websites and webapps. There are many other ways to achieve those goals, and when using Xavante, most of them start with writing a Handler.

A Xavante Handler is just a function that gets called to serve a client request. The Xavante core parses the request's URL, and decides which handler to call. The chosen handler receives as parameters the request and response objects, usually we call these "the `(req,res)` parameters".

The handler analyses these parameters and produces the desired response, either calling the `res:send(data)` function repeatedly, or setting the `res.content` field with a string or an array of strings.

### cookies.lua

Cookies are a common feature of many web development platforms. This module is shamelessly copied from the CGILua implementation, and exports three functions to be used from handlers:

    value = xavante.cookies.get (req, name)

    xavante.cookies.set (res, name, value [, options])

    xavante.cookies.delete (res, name [, options])

Where `req` and `res` are the request and response objects, respectively, the handler got from the Xavante core, `name` is the name under which the cookie will be stored in the browser, `value` is the value to store as a cookie, and `options` are the cookie's options, given as a `name=>value` table.

The cookie options are managed by the browser, but as a special case, if `options.expires` is a number, it's converted into the corresponding date string. This let's you use the `os.time()` function to specify a relative time.

### session.lua

This module lets you store some data and make it persistant from one request to another by the same client. In the current implementation, the session's stored data is just a Lua table, and is only stored in memory. If Xavante is restarted, all session data is lost.

To recognize the client coming back, a cookie is stored in the client's browser with a randomly generated session ID (SID) as the value.

    S = xavante.session.open (req, res [, name])

    xavante.session.close (req, res [,name])

The `name` is optional but strongly suggested to use a string unique to your handler, to minimize any chance of collision. Remember to give the same name (if any) to the `session.close()` function.

The table `S` would be a new table the first time the client visits this webapp, but will be the same table each subsequent request. You can store any kind of Lua data in this table.

When the user wants to close his session, call the `session.close()` function. The cookie in his browser will be deleted, and the session table will be detached from the internal storage, so it will be eventually garbage collected.

### codeWeb.lua

Xavante was developed to efficiently handle a big number of handlers, each one could manage as big or as little a part of the URL space as desired. There's nothing wrong with having several handlers to manage a single URL each. codeWeb is a module to help you with that kind of setting.

As usual with Lua scripts, the Xavante config file is a script that gets executed at startup time and sets up the running environment. In most cases, this means using the `xavante.HTTP {}` function to: associate the whole tree to `xavante.filehandler`, any file ending with `.lp` or `.lua` to `xavante.cgiluahandler`, and any URL ending with `/` `xavante.redirecthandler`.

The codeWeb module adds a function to register several handlers, joined as a module, to part of the URL tree:

    xavante.codeWeb.addModule (host, urlpath, m [, as_tree])

Where `host` is the virtual host name, `urlpath` is the root of the URL subtree where the module gets registered, `m` is a table in the form `m[name]=h`, with `name` as the URL end part, and `h` as the corresponding handler. The optional boolean `as_tree` (false by default) tells if each handler should registered as a single URL, or as a subtree itself.

To use this function, the developer writes a module, where the exported functions are the handlers. The head of the subtree (`urlpath` itself) is managed by the handler called "__main". The function `coroWeb.addModule()` registers all table elements where the key is a string not beginning with '_' and the value is a function.

Example:

    local session = require ("xavante.session")
    require ("xavante.coroWeb")
    module (arg and arg[1])

    --
    -- the 'main' handler redirects to .../index
    --
    function __main (req,res)
        local path = req.parsed_url.path
        if string.sub (path, -1) ~= "/" then path = path.."/" end
        xavante.httpd.redirect (res, path.."index")
    end

    --
    -- this is a hard-coded constant page
    --
    function index (req, res)

        cookies.set (res, "mycook", "456", {["Max-Age"] = "60"})

        res.content = [[
            <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
            <HTML><HEAD>
                <TITLE>CodeWeb</TITLE>
            </HEAD><BODY>
                <H1>Hi there!</H1>

                look <a href="mira1">this</a>,
                and then <a href="mira2">that</a>.</br>
                <a href="subpage">here</a>'s another page.</br>

            </BODY></HTML>]]
        return res
    end

    --
    -- this two pages use a common session to check user status
    --
    function mira1 (req, res)
        local ss = session.open (req, res, "mirando")

        res.content = [[
            <HTML><HEAD>
            <TITLE>CodeWeb</TITLE>
            </HEAD><BODY>
        ]]

        if ss.lastvio == "mira1" then
            res.content = res.content .. "<p> repeating.... </p>"
        elseif ss.lastvio == "mira2" then
            res.content = res.content .. "<p> should've come here first... </p>"
        else
            res.content = res.content .. "<p> haven't seen anythig yet! </p>"
        end

        res.content = res.content .. "</BODY></HTML>"

        ss.lastvio = "mira1"
    end

    function mira2 (req, res)
        local ss = session.open (req, res, "mirando")

        res.content = [[
            <HTML><HEAD>
                <TITLE>CodeWeb</TITLE>
            </HEAD><BODY>
        ]]

        if ss.lastvio == "mira1" then
            res.content = res.content .. "<p> see that? this one is better! </p>"
        elseif ss.lastvio == "mira2" then
            res.content = res.content .. "<p> repeating the second... </p>"
        else
            res.content = res.content .. "<p> see the other one first! </p>"
        end

        res.content = res.content .. "</BODY></HTML>"

        ss.lastvio = "mira2"
    end

    --
    -- this page is loaded and compiled at startup time,
    -- but executed at request time
    --
    subpage = xavante.codeWeb.load_cw ("test.cw");

This sample module defines five handlers: `__main()`, `index()`, `mira1()`, `mira2()` and `subpage()`. If this code is in the `cw_samp.lua` file, and you want it to be accessible under URLs of the form: http://your.server.com/sample, the config file should include a line like this:

    xavante.codeWeb.addModule ("_", "/sample", require "cw_samp", true)

This line first does a `require "cw_samp"`, which compiles the module and returns it as a table. Then the `xavante.codeWeb.addModule()` function registers it under the "/sample" subtree of the default virtual host (named "_"), making the handlers accessible under the URLs:

- http://your.server.com/sample
- http://your.server.com/sample/index
- http://your.server.com/sample/mira1
- http://your.server.com/sample/mira2
- http://your.server.com/sample/subpage

The `main()` handler just redirects any request to `index`. In some projects this could be the most convenient (and most flexible) way to redirect or remap URLs.

The `index()` handler stores a cookie of name "mycook" and value "456" at the user's browser, with a maximum age of 60 seconds. Then it just responds with a constant HTML content.

The two handlers `mira1()` and `mira2()` use a session called "mirando", to store some data used to deduce the order in which the user clicked on those links and present some annoying messages.

The `subpage` handler uses the second feature of codeWeb: compiled templates.

A codeWeb template is a file written in the same way as luaPages for CGILua; in fact, the code is mostly copied from CGILua's luaPage translator. The main difference is that a template loaded by `codeWeb.load_cw()` gets compiled into a handler. This compilation usually happens at startup time, so the response time is as fast as other Xavante handlers.

This is the code of the test_cw template:

    <html>
        <?lua
            local cookies = require ("xavante.cookies")

            mycook, mck_opt = cookies.get (req, "mycook")
        ?>
        <?lua if mycook then ?>
            <p>cookie: <?= mycook ?> </p>
            <ul><?lua for k,v in pairs (mck_opt) do ?>
                <li>opt: <?= k ?> = <?= v ?></li>
            <?lua end ?></ul>
        <?lua else ?>
            <p> there's no cookie </p>
        <?lua end ?>
    </html>

Note: This is a bad example, there's almost no HTML code and lots of Lua, obscured by the many `<?lua ... ?>` tags.

As can be seen in the example, the `req`, and `res` objects are available. The handler built by the `codeWeb.load_cw()` is of the form

    function (req, res, ...)
        -- translated template code
    end

The Xavante core dispatcher is not the only one that can call handlers; any handler could call another handler. This is especially convenient to use several small templates to generate pieces of HTML code, under the controlling logic of an 'outer' handler.

To pass data between an 'outer' handler and a compiled template, just add more parameters after the `req` and `res` required parameters. If you wish, you could start your template with some code like:

    <?lua
        local myparam1, myparam2 = unpack(arg)
    ?>

To give local names to your extra parameters.

### coroWeb

This module uses sessions to keep track of a user, creating a coroutine to handle it as the code flow. Just write a simple handler, but instead of registering it, wrap it into a coroWeb handler, like this:

    --
    -- this is a coroHandler, the user progresses
    -- through the code with each visit
    --
    coPage = xavante.coroWeb.handler ("coPage", function (req, res)
            res.content = "<html><body>one..</body></html>"
            req, res = yield ()
            res.content = "<html><body>two..</body></html>"
            req, res = yield ()
            res.content = "<html><body>and three, the end!</body></html>"
        end)

The `coroWeb.handler()` function takes as paramters a name (used for the session cookie), and a handler (a function with `(req,res)` parameters), and returns a new handler, which could (for example) be one of many in a codeWeb module.

A coroWeb handler produces some content (with the usual `res` parameter), and calls `yield()`. The function flow is suspended, the content is sent to the browser, and the function only gets resumed when the same user returns to the handler (on any URL managed by the same handler). At this point, the `yield()` function returns, with a new set of `req,res` parameters.

The handler gets started with each new user to enter the webapp; and the session is closed when the handler finishes.

In this example, the first time the user gets to this URL, he gets a page with "`one..`", the second time he gets "`two..`", and the third time he gets "`and three, the end!`". If he reloads the page a fourth time, the cycle starts again.

A developer could want to write a webapp with a central 'event loop', much like GUI apps are written. To help on this pattern, there's the `xavante.coroWeb.event()` function.

    req,res = xavante.coroWeb.event (req, sh_t [, get_all])

This function should be called in the main loop; it returns a new set of `req, res` objects each iteration. The first parameter is the usual `req` object, `sh_t` is a table of subhandlers, and the optional `get_all` is a boolean (default false).

An event driven handler should first create the 'main' page on `res` and call `coroWeb.event()` with a table filled with the handlers for all possible actions of the user. Each of these subhandlers will be called with the `req,res` objects corresponding to the user action. If a subhandler returns a string "refresh", or if the main handler set the `get_all` parameter as true, the `coroWeb.event()` returns and the main loop should refresh the main page.

A simple example:

    coPage2 = xavante.coroWeb.handler ("coPage2", function (req, res)

        local counter = 0
        local count2 = 0
        local sh_t = {
            inc = function () counter = counter+1 return "refresh" end,
            dec = function () counter = counter-1 return "refresh" end,
        }

        while true do
            res.content = string.format ([[
                <html><body>
                        counter: %s (%s)</br>
                    <a href="coPage2/inc">inc</a>
                    <a href="coPage2/dec">dec</a>
                </body></html>]], counter, count2)

            count2 = count2+1

            req,res = xavante.coroWeb.event (req, sh_t)
        end
    end)

The first time a user enters this handler, two local variables, `counter` and `count2` are initialized at 0, and the subhandler table `sh_t` is filled with two handlers: `inc()` and `dec()`.

The main loop does three things: sets the response content with a simple page (which includes the counters values and links to the inc and dec subhandlers), increments the `count2` variable, and waits for an event.

When the user clicks on a link, the corresponding subhandler is executed and the `counter` variable is modified. The subhandler then returns a "refresh" string, which causes the `event()` function to return immediately to the main loop and refresh the main page, with the updated counter variables.

Note that these handlers don't use `req` and `res`, so we can leave the parameter lists empty. This is possible because all the data used by the subhandlers is accessible as local variables (local to the enclosing scope), and all the output needed is just refreshing the main page (in the main loop). What's missing from this example is a "logout" link that would let the main loop to finish, and eventually close the thread.

Valid XHTML 1.0!

$Id: experimental.html,v 1.3 2007/11/17 16:09:41 carregal Exp $