Lua can be used to generate configs (like a shortcut to [`include_shell`](core_config.html#core_config__includes)) or to write actual response handlers. Using Lua to generate configs doesn't have any performance impact; in this case Lua is only run at startup to generate the config, and there is no Lua involved for processing requests. As a `lua_State` itself is not thread-safe, you have two ways to use Lua configs: * [`include_lua`](core_config.html#core_config__includes) and [`lua.plugin`](mod_lua.html#mod_lua__setup_lua-plugin): using a global server lock, but with sharing the same `lua_State` in all workers * [`lua_handler`](mod_lua.html#mod_lua__action_lua-handler): without locking, and every worker has its own `lua_State` (and they cannot share their global context).
To prevent accidental pollution of the global environment a limited environment is provided: * Outside of request handling the limited environment will forget any globals you set * Within request handling globals will persist per context (i.e. a handler) and request; also the `REQ` global will provide access to persistent state per request (across handlers). * Objects already present in the global environment will be accessible and can be modified. * The global environment can be accessed explicitly via `_G`. * Also everything in the environment is confined to the `lua_State` (see above), as data can't be shared between different `lua_State`s. This should mostly work just fine; you only need to be careful when providing data to your handlers from a parent block; those variables should be declared `local` or your handler won't see them.
This section describe how to translate concepts from the main config to Lua. You can write the whole config in Lua or only parts and include them (for example with [`include_lua`](core_config.html#core_config__includes)).
The following Lua snippet saved as "debug.lua" could for example be included with `include_lua "debug.lua"`. local function mydebug(vr) local url_fields = { "raw", "raw_path", "raw_orig_path", "scheme", "authority", "path", "query", "host" } local phys_fields = { "path", "doc_root", "pathinfo" } if vr:handle_direct() then vr.resp.status = 200 vr.resp.headers["Content-Type"] = "text/plain" vr.out:add("Hello World!\n\n") vr.out:add("http method: " .. vr.req.http_method .. "\n") vr.out:add("http version: " .. vr.req.http_version .. "\n") for k, v in vr.env:pairs() do vr.out:add("Env['" .. k .. "'] = '" .. v .. "'\n") end vr.out:add("\n") for k, v in pairs(url_fields) do vr.out:add("vr.req.uri['" .. v .. "'] = '" .. vr.req.uri[v] .. "'\n") end vr.out:add("\n") for k, v in pairs(phys_fields) do vr.out:add("vr.phys['" .. v .. "'] = '" .. vr.phys[v] .. "'\n") end vr.out:add("\n") for k, v in vr.req.headers:pairs() do vr.out:add("vr.req.headers['" .. k .. "'] = '" .. v .. "'\n") end end end actions = mydebug
* Boolean: Lua supports `true` and `false` directly * Integers: Lua has its own number type (usually a [`double`](https://en.wikipedia.org/wiki/Double-precision_floating-point_format)), and doesn't know any of the suffixes. * Strings: Lua supports strings directly. Check the Lua reference for the various quoting styles. * Lists and Key-Value-Lists: Lua has a "table" type; it can contain sequential lists and associative mappings. Use `{1, 2, 3}` to create simple lists, `{a=1, b=2}` to create unique mappings (which get converted to Key-Value-Lists) or `{{"a",1},{"a",2}}` to explicitly create Key-Value-Lists (where a key can be used more than once and the order matters). Don't mix sequential lists and associative mappings. If you get a List (possible a Key-Value-List) value from lighttpd it is represented as sequential list but has a special `__index` meta-table method supporting strings and `nil` as lookup parameter, i.e. you can treat a Key-Value-List like an associative mapping in Lua (see for example the options handling in [contrib/secdownload.lua](https://git.lighttpd.net/lighttpd/lighttpd2.git/tree/contrib/secdownload.lua)). * Expressions and variables just are the usual Lua things; there is no direct access to the lighttpd config variables (yet). * Action blocks: you can make an action from a list of actions using the [list action](plugin_core.html#plugin_core__action_list) (`act = action.list(act1, act2)`)
Action context is given by prefixing the function name with `action.`, and setup context by prefixing with `setup.`. Don't try to call setups in request handling. Also each Lua function can act as an action (see the debug.lua example above), taking a virtual request object as parameter. Includes are not supported, neither is the debug `__print` (there are other logging methods available).
Conditions are the ugliest part: there is no way translating native Lua if statements into the lighttpd config, so they need to be constructed manually. Only the long names of the condition variables are available in Lua. The condition operators are all given names and appended to the condition variable, and then called with the value to compare with. | op | Lua name | op | Lua name | |----|-----------|----|----------| | == | `:eq` | != | `:ne` | | <= | `:le` | < | `:lt` | | >= | `:ge` | > | `:gt` | | =~ | `:match` | !~ | `:nomatch` | | =^ | `:prefix` | !^ | `:notprefix` | | =$ | `:suffix` | !$ | `:notsuffix` | | =/ | `:ip` | !/ | `:notip` | {:.table .table-striped } Boolean condition variables are called with `:is()` or `:isnot()`. The result of such call (a "condition") is then passed as first parameter to [`action.when`](plugin_core#plugin_core__action_when).
Translating `if req.env["REMOTE_USER"] != "admin" { auth.deny; }` to Lua: actions = action.when(request.environment["REMOTE_USER"]:ne("admin"), action.auth.deny())
Translating `if !phys.exists { auth.deny; }` to Lua: actions = action.when(physical.exists:isnot(), action.auth.deny())
This section documents the object types you need to handle requests; you will probably start from the Virtual Request object you get as parameter in your handler. Object fields should be accessed with `.field` or `["field"]`, for example: ``` e = vr.env e["XXX"] = "abc" ``` Fields tagged with (ro) are read only; that does not mean the fields value can't be modified, you only cannot overwrite the field with another object. Readonly string / number properties are really read only though. Call object methods with `:method(...)`: ``` vr:print("Hello World") ``` **Note**: The `obj:method(par1, par2, ...)` syntax is just another way to say `obj["method"](obj, par1, par2, ...)` (but `obj` is only evaluated once), so field and method names live in the same namespace. This means that our container types cannot provide access to fields which have the same names as the methods (and the methods starting with "__" are not listed here), so you have to use explicit access methods to read generic fields in such containers (write is not a problem as we don't allow writing methods). All container types should provide a `get` and a `set` method to provide "clean" access to the container contents. ### pairs() Some objects may provide a `:pairs()` method to loop through the fields (not the methods); this works for simple things like ``` for k, v in vr.env:pairs() do vr:print("env['" .. k .. "'] = '" .. v .. "'") end ``` lua expects that the `:pairs` method returns a `next, obj, startkey` tuple and loops through the list with `k = startkey; while k, v = next(obj, k) do ... end`; but the `next()` method is supposed to use `k` as previous key and to return the next one. Our `next` methods will keep the current position in an internal object (associated with the `next` function as upvalue), and will advance on every call ignoring the `obj` and `k` parameter.
`liHandlerResult` enumeration values: * `lighty.HANDLER_GO_ON` * `lighty.HANDLER_COMEBACK` * `lighty.HANDLER_WAIT_FOR_EVENT` * `lighty.HANDLER_ERROR`
* `lighty.print` (and `lighty.error` and `print`): print parameters via lua "tostring" method as ERROR in global server context * `lighty.warning`: print parameters via lua "tostring" method as WARNING in global server context * `lighty.info`: print parameters via lua "tostring" method as INFO in global server context * `lighty.debug`: print parameters via lua "tostring" method as DEBUG in global server context * `lighty.filter_in(class)`: creates a new action, which adds a incoming filter from `class:new(vr)` if called at runtime * `lighty.filter_out(class)`: creates a new action, which adds a outgoing filter from `class:new(vr)` if called at runtime * `lighty.md5(str)`: calculates the md5 checksum of the string `str` (returns the digest as string in hexadecimal) * `lighty.sha1(str)`: calculates the sha1 checksum of the string `str` (returns the digest as string in hexadecimal) * `lighty.sha256(str)`: calculates the sha256 checksum of the string `str` (returns the digest as string in hexadecimal) * `lighty.path_simplify(str)`: return simplified path
lighty.print("Hello World!")
local MyFilterclass = { } MyFilterClass.__index = MyFilterClass function MyFilterClass:new(vr) local o = { } setmetatable(o, self) return o -- return nil if you want to skip the filter this time end function MyFilterClass:handle(vr, outq, inq) ... end actions = lighty.filter_out(MyFilterClass)
Fields: * `con`(ro): Connection * `in`(ro): Chunk Queue, read request post content * `out`(ro): Chunk Queue, write response content * `env`(ro): Environment, (fast)cgi environment * `req`(ro): Request, data from request header * `resp`(ro): Response, response header data * `phys`(ro): Physical, paths and filenames * `is_handled`(ro): whether vrequest is already handled * `has_response`(ro): whether the response headers (and status) is available Methods: * `error(...)` and `print(...)`: print parameters via lua `tostring` method as ERROR in Virtual Request context * `warning(...)`: print parameters via lua `tostring` method as WARNING in Virtual Request context * `info(...)`: print parameters via lua `tostring` method as INFO in Virtual Request context * `debug(...)`: print parameters via lua `tostring` method as DEBUG in Virtual Request context * `handle_direct()`: handle vrequest (i.e. provide headers and body); returns true if not already handled. * `enter_action(act)`: push a new action on the action stack (return HANDLER_WAIT_FOR_EVENT to rerun after the pushed actions are done, HANDLER_GO_ON if you are done) * `st, res, errno, msg = stat(filename)`: async stat(filename). Following results are possible * st is the stat result, res == HANDLER_GO_ON, if the file was found. errno and msg are NIL. In all other cases st is NIL and res != HANDLER_GO_ON. * res == HANDLER_WAIT_FOR_EVENT: stat() is in progress, just try again later (and return HANDLER_WAIT_FOR_EVENT in the meantime) * res == HANDLER_ERROR: if stat() failed, errno contains the errno and msg the error message for the errno code. * `add_filter_in(obj)`: adds `obj` as lua incoming filter (needs to respond to `obj:handle(vr, outq, inq)` and optionally `obj:finished()`); returns a Filter object * `add_filter_out(obj)`: adds `obj` as lua outgoing filter (needs to respond to `obj:handle(vr, outq, inq)` and optionally `obj:finished()`); returns a Filter object
* `local`: address of local socket * `remote`: address of remote host
Fields are the keys in the environment, so it behaves like a lua table; if you use keys starting with "__" or keys with the name of one of the methods below, you have to use the `get` method to read them, for example: x = env["set"] -- doesn't work, returns the set method instead x = env:get("set") -- use this instead x = env[y] -- don't do this, as y may be a special key like "set" x = env:get(y) -- just do it the safe way if you are not sure Methods: * `get(k)`: safe way for `env[k]` * `set(k, v)`: safe way for `env[k] = v` * `unset(k)`: safe way for `env[k] = nil` * `weak_set(k, v)`: don't override old value, safe way for `env[k] = env[k] or v` * `pairs()`: use to loop through keys: `for k, v in env:pairs() do ... end` * `clear()`: remove all entries
Fields: * `is_closed`: whether the ChunkQueue is closed Methods: * `add(s)`: appends a string to the queue * `add({filename="/..."})`: appends a file to the queue (only regular files allowed) * `reset()`: removes all chunks, resets counters * `steal_all(from)`: steal all chunks from another queue (useful in a filter if you decide to pass all data through it) * `skip_all()`: skips all chunks (removes all chunks but does **not** reset counters)
Fields: * `headers`(ro): HTTP Headers * `http_method`(ro): HTTP method string ("GET", "POST", "HEAD", ...) * `http_version`(ro): HTTP version string ("HTTP/1.0", "HTTP/1.1") * `content_length`(ro): Numeric value of Content-Length header (not updated automatically if someone changes the header value), -1 if not specified * `uri`: Request URI
Fields: * `raw`: Request uri as it was in the HTTP Request Line (or a rewrite result) * `raw_path`: not decoded path with querystring (will be the same as `raw` for most requests, unless someone does something like `GET http://example.com/test?abc HTTP/1.1`) * `raw_orig_path`: same as raw_path, but saved before any rewrite happened * `scheme`: "http" or "https" * `authority`: complete host name header (or authority in an absolute url), e.g. "user@www.example.com.:8080" * `path`: decoded and simplified path name, without authority, scheme, query-string; e.g. "/index.php" * `host`: simple hostname, without auth information, without port, without trailing dot; e.g. "www.example.com" * `query`: The querystring, e.g. "a=1&b=2"
Fields: * `headers`(ro): HTTP Headers * `status`: HTTP status code
Fields: * `path`: physical path * `doc_root`: document root * `pathinfo`: pathinfo
Same restriction as Environment for fields. Methods: * `get(k)`: joins all header values for the key `k` with ", " (as the rfc allows it) * `set(k, v)`: removes all headers with key `k` and, if v is not nil, appends new "k: v" header * `append(k, v)`: appends ", v" to last header value with key k if it already exists, `insert(k, v)` otherwise * `insert(k, v)`: appends new "k: v" header to list * `unset(k)`: removes all headers with key `k` * `pairs()`: loops through all headers. Please note that the keys are not unique! * `list(k)`: loops through all headers with key `k` * `clear()`: remove all headers
Represents a "liFilter". Fields: * `in`(ro): Chunk Queue, incoming stream * `out`(ro): Chunk Queue, outgoing stream
Represents "struct stat". Most fields should be self explaining (`man 2 stat` ([debian manpage](https://manpages.debian.org/stat.2.en.html)) if you don't know them). Fields: * `is_file`(ro): S_ISREG(mode) * `is_dir`(ro): S_ISDIR(mode) * `is_char`(ro): S_ISCHR(mode) * `is_block`(ro): S_ISBLK(mode) * `is_socket`(ro): S_ISSOCK(mode) * `is_link`(ro): S_ISLNK(mode) * `is_fifo`(ro): S_ISFIFO(mode) * `mode`(ro) * `mtime`(ro) * `ctime`(ro) * `atime`(ro) * `uid`(ro) * `gid`(ro) * `size`(ro) * `ino`(ro) * `dev`(ro)