GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing httpd.apache.org — mod_lua - Apache HTTP Server Version 2.4 Apache Module mod_lua
SummaryThis module allows the server to be extended with scripts written in the
Lua programming language. The extension points (hooks) available with
More information on the Lua programming language can be found at the the Lua website. WarningThis module holds a great deal of power over httpd, which is both a strength and a potential security risk. It is not recommended that you use this module on a server that is shared with users you do not trust, as it can be abused to change the internal workings of httpd. Topics
Directives
Bugfix checklistBasic Configuration ¶The basic module loading directive is LoadModule lua_module modules/mod_lua.so
<Files "*.lua">
SetHandler lua-script
</Files>
This will cause For more flexibility, see Writing Handlers ¶ In the Apache HTTP Server API, the handler is a specific kind of hook
responsible for generating the response. Examples of modules that include a
handler are
example.lua This handler function just prints out the uri or form encoded arguments to a plaintext page. This means (and in fact encourages) that you can have multiple handlers (or hooks, or filters) in the same script. Writing Authorization Providers ¶
The authz provider is normally called before authentication. If it needs to
know the authenticated user name (or if the user will be authenticated at
all), the provider must return The following authz provider function takes two arguments, one ip address and one user name. It will allow access from the given ip address without authentication, or if the authenticated user matches the second argument: authz_provider.lua The following configuration registers this function as provider
LuaAuthzProvider foo authz_provider.lua authz_check_foo <Location "/"> Require foo 10.1.2.3 john_doe </Location> Writing Hooks ¶Hook functions are how modules (and Lua scripts) participate in the processing of requests. Each type of hook exposed by the server exists for a specific purpose, such as mapping requests to the file system, performing access control, or setting mime types:
Hook functions are passed the request object as their only argument
(except for LuaAuthzProvider, which also gets passed the arguments from
the Require directive).
They can return any value, depending on the hook, but most commonly
they'll return OK, DONE, or DECLINED, which you can write in Lua as
translate_name.lua translate_name2.lua Data Structures ¶
Built in functions ¶The request_rec object has (at least) the following methods: r:flush() -- flushes the output buffer.
-- Returns true if the flush was successful, false otherwise.
while we_have_stuff_to_send do
r:puts("Bla bla bla\n") -- print something to client
r:flush() -- flush the buffer (send to client)
r.usleep(500000) -- fake processing time for 0.5 sec. and repeat
end
r:add_output_filter(filter_name) -- add an output filter:
r:add_output_filter("fooFilter") -- add the fooFilter to the output stream
r:sendfile(filename) -- sends an entire file to the client, using sendfile if supported by the current platform:
if use_sendfile_thing then
r:sendfile("/var/www/large_file.img")
end
r:parseargs() -- returns two tables; one standard key/value table for regular GET data,
-- and one for multi-value data (fx. foo=1&foo=2&foo=3):
local GET, GETMULTI = r:parseargs()
r:puts("Your name is: " .. GET['name'] or "Unknown")
r:parsebody([sizeLimit]) -- parse the request body as a POST and return two lua tables,
-- just like r:parseargs().
-- An optional number may be passed to specify the maximum number
-- of bytes to parse. Default is 8192 bytes:
local POST, POSTMULTI = r:parsebody(1024*1024)
r:puts("Your name is: " .. POST['name'] or "Unknown")
r:puts("hello", " world", "!") -- print to response body, self explanatory
r:write("a single string") -- print to response body, self explanatory
r:escape_html("<html>test</html>") -- Escapes HTML code and returns the escaped result
r:base64_encode(string) -- Encodes a string using the Base64 encoding standard:
local encoded = r:base64_encode("This is a test") -- returns VGhpcyBpcyBhIHRlc3Q=
r:base64_decode(string) -- Decodes a Base64-encoded string:
local decoded = r:base64_decode("VGhpcyBpcyBhIHRlc3Q=") -- returns 'This is a test'
r:md5(string) -- Calculates and returns the MD5 digest of a string (binary safe):
local hash = r:md5("This is a test") -- returns ce114e4501d2f4e2dcea3e17b546f339
r:sha1(string) -- Calculates and returns the SHA1 digest of a string (binary safe):
local hash = r:sha1("This is a test") -- returns a54d88e06612d820bc3be72877c74f257b561b19
r:escape(string) -- URL-Escapes a string: local url = "http://foo.bar/1 2 3 & 4 + 5" local escaped = r:escape(url) -- returns 'http%3a%2f%2ffoo.bar%2f1+2+3+%26+4+%2b+5' r:unescape(string) -- Unescapes an URL-escaped string: local url = "http%3a%2f%2ffoo.bar%2f1+2+3+%26+4+%2b+5" local unescaped = r:unescape(url) -- returns 'http://foo.bar/1 2 3 & 4 + 5' r:construct_url(string) -- Constructs an URL from an URI local url = r:construct_url(r.uri) r.mpm_query(number) -- Queries the server for MPM information using ap_mpm_query:
local mpm = r.mpm_query(14)
if mpm == 1 then
r:puts("This server uses the Event MPM")
end
r:expr(string) -- Evaluates an expr string. if r:expr("%{HTTP_HOST} =~ /^www/") then r:puts("This host name starts with www") end r:scoreboard_process(a) -- Queries the server for information about the process at position
r:scoreboard_worker(a, b) -- Queries for information about the worker thread, r:clock() -- Returns the current time with microsecond precision r:requestbody(filename) -- Reads and returns the request body of a request.
-- If 'filename' is specified, it instead saves the
-- contents to that file:
local input = r:requestbody()
r:puts("You sent the following request body to me:\n")
r:puts(input)
r:add_input_filter(filter_name) -- Adds 'filter_name' as an input filter r.module_info(module_name) -- Queries the server for information about a module
local mod = r.module_info("mod_lua.c")
if mod then
for k, v in pairs(mod.commands) do
r:puts( ("%s: %s\n"):format(k,v)) -- print out all directives accepted by this module
end
end
r:loaded_modules() -- Returns a list of modules loaded by httpd:
for k, module in pairs(r:loaded_modules()) do
r:puts("I have loaded module " .. module .. "\n")
end
r:runtime_dir_relative(filename) -- Compute the name of a run-time file (e.g., shared memory "file")
-- relative to the appropriate run-time directory.
r:server_info() -- Returns a table containing server information, such as
-- the name of the httpd executable file, mpm used etc.
r:set_document_root(file_path) -- Sets the document root for the request to file_path r:set_context_info(prefix, docroot) -- Sets the context prefix and context document root for a request r:os_escape_path(file_path) -- Converts an OS path to a URL in an OS dependent way r:escape_logitem(string) -- Escapes a string for logging r.strcmp_match(string, pattern) -- Checks if 'string' matches 'pattern' using strcmp_match (globs).
-- fx. whether 'www.example.com' matches '*.example.com':
local match = r.strcmp_match("foobar.com", "foo*.com")
if match then
r:puts("foobar.com matches foo*.com")
end
r:set_keepalive() -- Sets the keepalive status for a request. Returns true if possible, false otherwise. r:make_etag() -- Constructs and returns the etag for the current request. r:send_interim_response(clear) -- Sends an interim (1xx) response to the client.
-- if 'clear' is true, available headers will be sent and cleared.
r:custom_response(status_code, string) -- Construct and set a custom response for a given status code.
-- This works much like the ErrorDocument directive:
r:custom_response(404, "Baleted!")
r.exists_config_define(string) -- Checks whether a configuration definition exists or not:
if r.exists_config_define("FOO") then
r:puts("httpd was probably run with -DFOO, or it was defined in the configuration")
end
r:state_query(string) -- Queries the server for state information r:stat(filename [,wanted]) -- Runs stat() on a file, and returns a table with file information:
local info = r:stat("/var/www/foo.txt")
if info then
r:puts("This file exists and was last modified at: " .. info.modified)
end
r:regex(string, pattern [,flags]) -- Runs a regular expression match on a string, returning captures if matched:
local matches = r:regex("foo bar baz", [[foo (\w+) (\S*)]])
if matches then
r:puts("The regex matched, and the last word captured ($2) was: " .. matches[2])
end
-- Example ignoring case sensitivity:
local matches = r:regex("FOO bar BAz", [[(foo) bar]], 1)
-- Flags can be a bitwise combination of:
-- 0x01: Ignore case
-- 0x02: Multiline search
r.usleep(number_of_microseconds) -- Puts the script to sleep for a given number of microseconds. r:dbacquire(dbType[, dbParams]) -- Acquires a connection to a database and returns a database class.
-- See 'Database connectivity' for details.
r:ivm_set("key", value) -- Set an Inter-VM variable to hold a specific value.
-- These values persist even though the VM is gone or not being used,
-- and so should only be used if MaxConnectionsPerChild is > 0
-- Values can be numbers, strings and booleans, and are stored on a
-- per process basis (so they won't do much good with a prefork mpm)
r:ivm_get("key") -- Fetches a variable set by ivm_set. Returns the contents of the variable
-- if it exists or nil if no such variable exists.
-- An example getter/setter that saves a global variable outside the VM:
function handle(r)
-- First VM to call this will get no value, and will have to create it
local foo = r:ivm_get("cached_data")
if not foo then
foo = do_some_calcs() -- fake some return value
r:ivm_set("cached_data", foo) -- set it globally
end
r:puts("Cached data is: ", foo)
end
r:htpassword(string [,algorithm [,cost]]) -- Creates a password hash from a string.
-- algorithm: 0 = APMD5 (default), 1 = SHA, 2 = BCRYPT, 3 = CRYPT.
-- cost: only valid with BCRYPT algorithm (default = 5).
r:mkdir(dir [,mode]) -- Creates a directory and sets mode to optional mode parameter. r:mkrdir(dir [,mode]) -- Creates directories recursive and sets mode to optional mode parameter. r:rmdir(dir) -- Removes a directory. r:touch(file [,mtime]) -- Sets the file modification time to current time or to optional mtime msec value. r:get_direntries(dir) -- Returns a table with all directory entries.
function handle(r)
local dir = r.context_document_root
for _, f in ipairs(r:get_direntries(dir)) do
local info = r:stat(dir .. "/" .. f)
if info then
local mtime = os.date(fmt, info.mtime / 1000000)
local ftype = (info.filetype == 2) and "[dir] " or "[file]"
r:puts( ("%s %s %10i %s\n"):format(ftype, mtime, info.size, f) )
end
end
end
r.date_parse_rfc(string) -- Parses a date/time string and returns seconds since epoche. r:getcookie(key) -- Gets a HTTP cookie r:setcookie{
key = [key],
value = [value],
expires = [expiry],
secure = [boolean],
httponly = [boolean],
path = [path],
domain = [domain]
} -- Sets a HTTP cookie, for instance:
r:setcookie{
key = "cookie1",
value = "HDHfa9eyffh396rt",
expires = os.time() + 86400,
secure = true
}
r:wsupgrade() -- Upgrades a connection to WebSockets if possible (and requested):
if r:wsupgrade() then -- if we can upgrade:
r:wswrite("Welcome to websockets!") -- write something to the client
r:wsclose() -- goodbye!
end
r:wsread() -- Reads a WebSocket frame from a WebSocket upgraded connection (see above):
local line, isFinal = r:wsread() -- isFinal denotes whether this is the final frame.
-- If it isn't, then more frames can be read
r:wswrite("You wrote: " .. line)
r:wswrite(line) -- Writes a frame to a WebSocket client:
r:wswrite("Hello, world!")
r:wsclose() -- Closes a WebSocket request and terminates it for httpd:
if r:wsupgrade() then
r:wswrite("Write something: ")
local line = r:wsread() or "nothing"
r:wswrite("You wrote: " .. line);
r:wswrite("Goodbye!")
r:wsclose()
end
Logging Functions ¶-- examples of logging messages
r:trace1("This is a trace log message") -- trace1 through trace8 can be used
r:debug("This is a debug log message")
r:info("This is an info log message")
r:notice("This is a notice log message")
r:warn("This is a warn log message")
r:err("This is an err log message")
r:alert("This is an alert log message")
r:crit("This is a crit log message")
r:emerg("This is an emerg log message")
apache2 Package ¶A package named
(Other HTTP status codes are not yet implemented.) Modifying contents with Lua filters ¶
Filter functions implemented via function filter(r)
-- Our first yield is to signal that we are ready to receive buckets.
-- Before this yield, we can set up our environment, check for conditions,
-- and, if we deem it necessary, decline filtering a request altogether:
if something_bad then
return -- This would skip this filter.
end
-- Regardless of whether we have data to prepend, a yield MUST be called here.
-- Note that only output filters can prepend data. Input filters must use the
-- final stage to append data to the content.
coroutine.yield([optional header to be prepended to the content])
-- After we have yielded, buckets will be sent to us, one by one, and we can
-- do whatever we want with them and then pass on the result.
-- Buckets are stored in the global variable 'bucket', so we create a loop
-- that checks if 'bucket' is not nil:
while bucket ~= nil do
local output = mangle(bucket) -- Do some stuff to the content
coroutine.yield(output) -- Return our new content to the filter chain
end
-- Once the buckets are gone, 'bucket' is set to nil, which will exit the
-- loop and land us here. Anything extra we want to append to the content
-- can be done by doing a final yield here. Both input and output filters
-- can append data to the content in this phase.
coroutine.yield([optional footer to be appended to the content])
end
Database connectivity ¶Mod_lua implements a simple database feature for querying and running commands on the most popular database engines (mySQL, PostgreSQL, FreeTDS, ODBC, SQLite, Oracle) as well as mod_dbd.
The
It should be one of The example below shows how to acquire a database handle and return information from a table: function handle(r)
-- Acquire a database handle
local database, err = r:dbacquire("mysql", "server=localhost,user=someuser,pass=somepass,dbname=mydb")
if not err then
-- Select some information from it
local results, err = database:select(r, "SELECT `name`, `age` FROM `people` WHERE 1")
if not err then
local rows = results(0) -- fetch all rows synchronously
for k, row in pairs(rows) do
r:puts( string.format("Name: %s, Age: %s<br/>", row[1], row[2]) )
end
else
r:puts("Database query error: " .. err)
end
database:close()
else
r:puts("Could not connect to the database: " .. err)
end
end
To utilize local database = r:dbacquire("mod_dbd")
Database object and contained functionsThe database object returned by Normal select and query from a database: -- Run a statement and return the number of rows affected: local affected, errmsg = database:query(r, "DELETE FROM `tbl` WHERE 1") -- Run a statement and return a result set that can be used synchronously or async: local result, errmsg = database:select(r, "SELECT * FROM `people` WHERE 1") Using prepared statements (recommended): -- Create and run a prepared statement:
local statement, errmsg = database:prepare(r, "DELETE FROM `tbl` WHERE `age` > %u")
if not errmsg then
local result, errmsg = statement:query(20) -- run the statement with age > 20
end
-- Fetch a prepared statement from a DBDPrepareSQL directive:
local statement, errmsg = database:prepared(r, "someTag")
if not errmsg then
local result, errmsg = statement:select("John Doe", 123) -- inject the values "John Doe" and 123 into the statement
end
Escaping values, closing databases etc: -- Escape a value for use in a statement: local escaped = database:escape(r, [["'|blabla]]) -- Close a database connection and free up handles: database:close() -- Check whether a database connection is up and running: local connected = database:active() Working with result setsThe result set returned by -- fetch a result set using a regular query: local result, err = db:select(r, "SELECT * FROM `tbl` WHERE 1") local rows = result(0) -- Fetch ALL rows synchronously local row = result(-1) -- Fetch the next available row, asynchronously local row = result(1234) -- Fetch row number 1234, asynchronously local row = result(-1, true) -- Fetch the next available row, using row names as key indexes. One can construct a function that returns an iterative function to iterate over all rows in a synchronous or asynchronous way, depending on the async argument: function rows(resultset, async)
local a = 0
local function getnext()
a = a + 1
local row = resultset(-1)
return row and a or nil, row
end
if not async then
return pairs(resultset(0))
else
return getnext, self
end
end
local statement, err = db:prepare(r, "SELECT * FROM `tbl` WHERE `age` > %u")
if not err then
-- fetch rows asynchronously:
local result, err = statement:select(20)
if not err then
for index, row in rows(result, true) do
....
end
end
-- fetch rows synchronously:
local result, err = statement:select(20)
if not err then
for index, row in rows(result, false) do
....
end
end
end
Closing a database connectionDatabase handles should be closed using -- Method 1: Manually close a handle
local database = r:dbacquire("mod_dbd")
database:close() -- All done
-- Method 2: Letting the garbage collector close it
local database = r:dbacquire("mod_dbd")
database = nil -- throw away the reference
collectgarbage() -- close the handle via GC
Precautions when working with databasesAlthough the standard LuaAuthzProvider Directive ¶
After a lua function has been registered as authorization provider, it can be used
with the LuaRoot "/usr/local/apache2/lua" LuaAuthzProvider foo authz.lua authz_check_foo <Location "/"> Require foo johndoe </Location> require "apache2"
function authz_check_foo(r, who)
if r.user ~= who then return apache2.AUTHZ_DENIED
return apache2.AUTHZ_GRANTED
end
LuaCodeCache Directive ¶
Specify the behavior of the in-memory code cache. The default is stat, which stats the top level script (not any included ones) each time that file is needed, and reloads it if the modified time indicates it is newer than the one it has already loaded. The other values cause it to keep the file cached forever (don't stat and replace) or to never cache the file. In general stat or forever is good for production, and stat or never for development. Examples:LuaCodeCache stat LuaCodeCache forever LuaCodeCache never LuaHookAccessChecker Directive ¶
Add your hook to the access_checker phase. An access checker hook function usually returns OK, DECLINED, or HTTP_FORBIDDEN. OrderingThe optional arguments "early" or "late" control when this script runs relative to other modules. LuaHookAuthChecker Directive ¶
Invoke a lua function in the auth_checker phase of processing a request. This can be used to implement arbitrary authentication and authorization checking. A very simple example: require 'apache2'
-- fake authcheck hook
-- If request has no auth info, set the response header and
-- return a 401 to ask the browser for basic auth info.
-- If request has auth info, don't actually look at it, just
-- pretend we got userid 'foo' and validated it.
-- Then check if the userid is 'foo' and accept the request.
function authcheck_hook(r)
-- look for auth info
auth = r.headers_in['Authorization']
if auth ~= nil then
-- fake the user
r.user = 'foo'
end
if r.user == nil then
r:debug("authcheck: user is nil, returning 401")
r.err_headers_out['WWW-Authenticate'] = 'Basic realm="WallyWorld"'
return 401
elseif r.user == "foo" then
r:debug('user foo: OK')
else
r:debug("authcheck: user='" .. r.user .. "'")
r.err_headers_out['WWW-Authenticate'] = 'Basic realm="WallyWorld"'
return 401
end
return apache2.OK
end
OrderingThe optional arguments "early" or "late" control when this script runs relative to other modules. LuaHookCheckUserID Directive ¶
... OrderingThe optional arguments "early" or "late" control when this script runs relative to other modules. LuaHookFixups Directive ¶
Just like LuaHookTranslateName, but executed at the fixups phase LuaHookInsertFilter Directive ¶
Not Yet Implemented LuaHookLog Directive ¶
This simple logging hook allows you to run a function when httpd enters the
logging phase of a request. With it, you can append data to your own logs,
manipulate data before the regular log is written, or prevent a log entry
from being created. To prevent the usual logging from happening, simply return
Example: LuaHookLog "/path/to/script.lua" logger -- /path/to/script.lua --
function logger(r)
-- flip a coin:
-- If 1, then we write to our own Lua log and tell httpd not to log
-- in the main log.
-- If 2, then we just sanitize the output a bit and tell httpd to
-- log the sanitized bits.
if math.random(1,2) == 1 then
-- Log stuff ourselves and don't log in the regular log
local f = io.open("/foo/secret.log", "a")
if f then
f:write("Something secret happened at " .. r.uri .. "\n")
f:close()
end
return apache2.DONE -- Tell httpd not to use the regular logging functions
else
r.uri = r.uri:gsub("somesecretstuff", "") -- sanitize the URI
return apache2.OK -- tell httpd to log it.
end
end
LuaHookMapToStorage Directive ¶
Like LuaHookMapToStorage "/path/to/lua/script.lua" check_cache require"apache2"
cached_files = {}
function read_file(filename)
local input = io.open(filename, "r")
if input then
local data = input:read("*a")
cached_files[filename] = data
file = cached_files[filename]
input:close()
end
return cached_files[filename]
end
function check_cache(r)
if r.filename:match("%.png$") then -- Only match PNG files
local file = cached_files[r.filename] -- Check cache entries
if not file then
file = read_file(r.filename) -- Read file into cache
end
if file then -- If file exists, write it out
r.status = 200
r:write(file)
r:info(("Sent %s to client from cache"):format(r.filename))
return apache2.DONE -- skip default handler for PNG files
end
end
return apache2.DECLINED -- If we had nothing to do, let others serve this.
end
LuaHookPreTranslate Directive ¶
Just like LuaHookTranslateName, but executed at the pre_translate phase, where the URI-path is not percent decoded. LuaHookTranslateName Directive ¶
Add a hook (at APR_HOOK_MIDDLE) to the translate name phase of request processing. The hook function receives a single argument, the request_rec, and should return a status code, which is either an HTTP error code, or the constants defined in the apache2 module: apache2.OK, apache2.DECLINED, or apache2.DONE. For those new to hooks, basically each hook will be invoked until one of them returns apache2.OK. If your hook doesn't want to do the translation it should just return apache2.DECLINED. If the request should stop processing, then return apache2.DONE. Example: # httpd.conf LuaHookTranslateName "/scripts/conf/hooks.lua" silly_mapper -- /scripts/conf/hooks.lua --
require "apache2"
function silly_mapper(r)
if r.uri == "/" then
r.filename = "/var/www/home.lua"
return apache2.OK
else
return apache2.DECLINED
end
end
ContextThis directive is not valid in OrderingThe optional arguments "early" or "late" control when this script runs relative to other modules. LuaHookTypeChecker Directive ¶
This directive provides a hook for the type_checker phase of the request processing. This phase is where requests are assigned a content type and a handler, and thus can be used to modify the type and handler based on input: LuaHookTypeChecker "/path/to/lua/script.lua" type_checker function type_checker(r)
if r.uri:match("%.to_gif$") then -- match foo.png.to_gif
r.content_type = "image/gif" -- assign it the image/gif type
r.handler = "gifWizard" -- tell the gifWizard module to handle this
r.filename = r.uri:gsub("%.to_gif$", "") -- fix the filename requested
return apache2.OK
end
return apache2.DECLINED
end
LuaInherit Directive ¶
By default, if LuaHook* directives are used in overlapping Directory or Location configuration sections, the scripts defined in the more specific section are run after those defined in the more generic section (LuaInherit parent-first). You can reverse this order, or make the parent context not apply at all. In previous 2.3.x releases, the default was effectively to ignore LuaHook* directives from parent configuration sections. LuaInputFilter Directive ¶
Provides a means of adding a Lua function as an input filter.
As with output filters, input filters work as coroutines,
first yielding before buffers are sent, then yielding whenever
a bucket needs to be passed down the chain, and finally (optionally)
yielding anything that needs to be appended to the input data. The
global variable LuaInputFilter myInputFilter "/www/filter.lua" input_filter <Files "*.lua"> SetInputFilter myInputFilter </Files> --[[
Example input filter that converts all POST data to uppercase.
]]--
function input_filter(r)
print("luaInputFilter called") -- debug print
coroutine.yield() -- Yield and wait for buckets
while bucket do -- For each bucket, do...
local output = string.upper(bucket) -- Convert all POST data to uppercase
coroutine.yield(output) -- Send converted data down the chain
end
-- No more buckets available.
coroutine.yield("&filterSignature=1234") -- Append signature at the end
end
The input filter supports denying/skipping a filter if it is deemed unwanted: function input_filter(r)
if not good then
return -- Simply deny filtering, passing on the original content instead
end
coroutine.yield() -- wait for buckets
... -- insert filter stuff here
end
See "Modifying contents with Lua filters" for more information. LuaMapHandler Directive ¶
This directive matches a uri pattern to invoke a specific handler function in a specific file. It uses PCRE regular expressions to match the uri, and supports interpolating match groups into both the file path and the function name. Be careful writing your regular expressions to avoid security issues. Examples:LuaMapHandler "/(\w+)/(\w+)" "/scripts/$1.lua" "handle_$2" This would match uri's such as /photos/show?id=9 to the file /scripts/photos.lua and invoke the handler function handle_show on the lua vm after loading that file. LuaMapHandler "/bingo" "/scripts/wombat.lua" This would invoke the "handle" function, which is the default if no specific function name is provided. LuaOutputFilter Directive ¶
Provides a means of adding a Lua function as an output filter.
As with input filters, output filters work as coroutines,
first yielding before buffers are sent, then yielding whenever
a bucket needs to be passed down the chain, and finally (optionally)
yielding anything that needs to be appended to the input data. The
global variable LuaOutputFilter myOutputFilter "/www/filter.lua" output_filter <Files "*.lua"> SetOutputFilter myOutputFilter </Files> --[[
Example output filter that escapes all HTML entities in the output
]]--
function output_filter(r)
coroutine.yield("(Handled by myOutputFilter)<br/>\n") -- Prepend some data to the output,
-- yield and wait for buckets.
while bucket do -- For each bucket, do...
local output = r:escape_html(bucket) -- Escape all output
coroutine.yield(output) -- Send converted data down the chain
end
-- No more buckets available.
end
As with the input filter, the output filter supports denying/skipping a filter if it is deemed unwanted: function output_filter(r)
if not r.content_type:match("text/html") then
return -- Simply deny filtering, passing on the original content instead
end
coroutine.yield() -- wait for buckets
... -- insert filter stuff here
end
Lua filters with
|
| Description: | Add a directory to lua's package.cpath |
|---|---|
| Syntax: | LuaPackageCPath /path/to/include/?.soa |
| Context: | server config, virtual host, directory, .htaccess |
| Override: | All |
| Status: | Extension |
| Module: | mod_lua |
Add a path to lua's shared library search path. Follows the same conventions as lua. This just munges the package.cpath in the lua vms.
| Description: | Add a directory to lua's package.path |
|---|---|
| Syntax: | LuaPackagePath /path/to/include/?.lua |
| Context: | server config, virtual host, directory, .htaccess |
| Override: | All |
| Status: | Extension |
| Module: | mod_lua |
Add a path to lua's module search path. Follows the same conventions as lua. This just munges the package.path in the lua vms.
LuaPackagePath "/scripts/lib/?.lua" LuaPackagePath "/scripts/lib/?/init.lua"
| Description: | Provide a hook for the quick handler of request processing |
|---|---|
| Syntax: | LuaQuickHandler /path/to/script.lua hook_function_name |
| Context: | server config, virtual host |
| Override: | All |
| Status: | Extension |
| Module: | mod_lua |
This phase is run immediately after the request has been mapped to a virtual host,
and can be used to either do some request processing before the other phases kick
in, or to serve a request without the need to translate, map to storage et cetera.
As this phase is run before anything else, directives such as <Location> or <Directory> are void in this phase, just as
URIs have not been properly parsed yet.
This directive is not valid in <Directory>, <Files>, or htaccess
context.
| Description: | Specify the base path for resolving relative paths for mod_lua directives |
|---|---|
| Syntax: | LuaRoot /path/to/a/directory |
| Context: | server config, virtual host, directory, .htaccess |
| Override: | All |
| Status: | Extension |
| Module: | mod_lua |
Specify the base path which will be used to evaluate all
relative paths within mod_lua. If not specified they
will be resolved relative to the current working directory,
which may not always work well for a server.
| Description: | One of once, request, conn, thread -- default is once |
|---|---|
| Syntax: | LuaScope once|request|conn|thread|server [min] [max] |
| Default: | LuaScope once |
| Context: | server config, virtual host, directory, .htaccess |
| Override: | All |
| Status: | Extension |
| Module: | mod_lua |
Specify the life cycle scope of the Lua interpreter which will be used by handlers in this "Directory." The default is "once"
min and max arguments
specify the minimum and maximum number of Lua states to keep in the
pool.
Generally speaking, the thread and server scopes
execute roughly 2-3 times faster than the rest, because they don't have to
spawn new Lua states on every request (especially with the event MPM, as
even keepalive requests will use a new thread for each request). If you are
satisfied that your scripts will not have problems reusing a state, then
the thread or server scopes should be used for
maximum performance. While the thread scope will provide the
fastest responses, the server scope will use less memory, as
states are pooled, allowing f.x. 1000 threads to share only 100 Lua states,
thus using only 10% of the memory required by the thread scope.