From 2a463b3cd73c32ee9dcd508248d0194923f435f4 Mon Sep 17 00:00:00 2001
From: Stefan Fritsch
The basic module loading directive is
-- LoadModule lua_module modules/mod_lua.so -+
LoadModule lua_module modules/mod_lua.so
mod_lua
provides a handler named lua-script
,
-which can be used with an AddHandler
directive:
SetHandler
or
+AddHandler
directive:
--AddHandler lua-script .lua -+
<Files *.lua> + SetHandler lua-script +</Files>
@@ -170,8 +169,7 @@ function handle(r) return 501 end return apache2.OK -end - +end
@@ -226,18 +224,15 @@ function authz_check_foo(r, ip, user) else return apache2.AUTHZ_DENIED end -end - +end
The following configuration registers this function as provider
foo
and configures it for URL /
:
-LuaAuthzProvider foo authz_provider.lua authz_check_foo +@@ -342,8 +337,7 @@ function translate_name(r) end -- we don't care about this URL, give another module a chance return apache2.DECLINED -end - +end @@ -366,8 +360,7 @@ function translate_name(r) return apache2.DECLINED end return apache2.DECLINED -end - +endLuaAuthzProvider foo authz_provider.lua authz_check_foo <Location /> Require foo 10.1.2.3 john_doe -</Location> -+</Location>
The request_rec object has (at least) the following methods:
--r:flush() -- flushes the output buffer. +-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 -+end
-r:addoutputfilter(name|function) -- add an output filter: +-r:addoutputfilter(name|function) -- add an output filter: -r:addoutputfilter("fooFilter") -- add the fooFilter to the output stream -+r:addoutputfilter("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: +-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 -+end
-r:parseargs() -- returns two tables; one standard key/value table for regular GET data, +-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:puts("Your name is: " .. GET['name'] or "Unknown")
-r:parsebody([sizeLimit]) -- parse the request body as a POST and return two lua tables, +-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("Your name is: " .. POST['name'] or "Unknown")
-r:puts("hello", " world", "!") -- print to response body, self explanatory -+
r:puts("hello", " world", "!") -- print to response body, self explanatory-
-r:write("a single string") -- 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: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: +-r:base64_encode(string) -- Encodes a string using the Base64 encoding standard: -local encoded = r:base64_encode("This is a test") -- returns VGhpcyBpcyBhIHRlc3Q= -+local encoded = r:base64_encode("This is a test") -- returns VGhpcyBpcyBhIHRlc3Q=
-r:base64_decode(string) -- Decodes a Base64-encoded string: +-r:base64_decode(string) -- Decodes a Base64-encoded string: -local decoded = r:base64_decode("VGhpcyBpcyBhIHRlc3Q=") -- returns 'This is a test' -+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): +-r:md5(string) -- Calculates and returns the MD5 digest of a string (binary safe): -local hash = r:md5("This is a test") -- returns ce114e4501d2f4e2dcea3e17b546f339 -+local hash = r:md5("This is a test") -- returns ce114e4501d2f4e2dcea3e17b546f339
-r:sha1(string) -- Calculates and returns the SHA1 digest of a string (binary safe): +-r:sha1(string) -- Calculates and returns the SHA1 digest of a string (binary safe): -local hash = r:sha1("This is a test") -- returns a54d88e06612d820bc3be72877c74f257b561b19 -+local hash = r:sha1("This is a test") -- returns a54d88e06612d820bc3be72877c74f257b561b19
-r:escape(string) -- URL-Escapes a string: +-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' -+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: +-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' -+local unescaped = r:unescape(url) -- returns 'http://foo.bar/1 2 3 & 4 + 5'
-r:construct_url(string) -- Constructs an URL from an URI +-r:construct_url(string) -- Constructs an URL from an URI -local url = r:construct_url(r.uri) -+local url = r:construct_url(r.uri)
-r.mpm_query(number) -- Queries the server for MPM information using ap_mpm_query: +-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 -+end
-r:expr(string) -- Evaluates an expr string. +-r:expr(string) -- Evaluates an expr string. if r:expr("%{HTTP_HOST} =~ /^www/") then r:puts("This host name starts with www") -end -+end
-r:scoreboard_process(a) -- Queries the server for information about the process at position-a
: +r:scoreboard_process(a) -- Queries the server for information about the process at position+r:puts("Server 1 has PID " .. process.pid)a
: local process = r:scoreboard_process(1) -r:puts("Server 1 has PID " .. process.pid) -
-r:scoreboard_worker(a, b) -- Queries for information about the worker thread,-b
, in processa
: +r:scoreboard_worker(a, b) -- Queries for information about the worker thread,+r:puts("Server 1's thread 1 has thread ID " .. thread.tid .. " and is in " .. thread.status .. " status")b
, in processa
: local thread = r:scoreboard_worker(1, 1) -r:puts("Server 1's thread 1 has thread ID " .. thread.tid .. " and is in " .. thread.status .. " status") -
-r:clock() -- Returns the current time with microsecond precision -+
r:clock() -- Returns the current time with microsecond precision-
-r:requestbody(filename) -- Reads and returns the request body of a request. +-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:puts(input)
-r:add_input_filter(filter_name) -- Adds 'filter_name' as an input filter -+
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 +-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 -+end
-r:loaded_modules() -- Returns a list of modules loaded by httpd: +-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 -+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: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: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_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: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: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:escape_logitem(string) -- Escapes a string for logging-
-r.strcmp_match(string, pattern) -- Checks if 'string' matches 'pattern' using strcmp_match (globs). +-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 -+end
-r:set_keepalive() -- Sets the keepalive status for a request. Returns true if possible, false otherwise. -+
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: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: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. +-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:custom_response(404, "Baleted!")
-r.exists_config_define(string) -- Checks whether a configuration definition exists or not: +-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 -+end
-r:state_query(string) -- Queries the server for state information -+
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: +-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 -+end
-r:regex(string, pattern [,flags]) -- Runs a regular expression match on a string, returning captures if matched: +-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 @@ -967,23 +883,17 @@ local matches = r:regex("FOO bar BAz", [[(foo) bar]], 1) -- Flags can be a bitwise combination of: -- 0x01: Ignore case --- 0x02: Multiline search -+-- 0x02: Multiline search
-r.usleep(number_of_microseconds) -- Puts the script to sleep for a given number of microseconds. -+
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: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. +-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 @@ -1001,39 +911,27 @@ function handle(r) r:ivm_set("cached_data", foo) -- set it globally end r:puts("Cached data is: ", foo) -end -+end
-r:htpassword(string [,algorithm [,cost]]) -- Creates a password hash from a string. +-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). -+ -- cost: only valid with BCRYPT algorithm (default = 5).
-r:mkdir(dir [,mode]) -- Creates a directory and sets mode to optional mode paramter. -+
r:mkdir(dir [,mode]) -- Creates a directory and sets mode to optional mode paramter.-
-r:mkrdir(dir [,mode]) -- Creates directories recursive and sets mode to optional mode paramter. -+
r:mkrdir(dir [,mode]) -- Creates directories recursive and sets mode to optional mode paramter.-
-r:rmdir(dir) -- Removes a directory. -+
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: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. +-r:get_direntries(dir) -- Returns a table with all directory entries. function handle(r) local dir = r.context_document_root @@ -1045,52 +943,52 @@ function handle(r) r:puts( ("%s %s %10i %s\n"):format(ftype, mtime, info.size, f) ) end end -end -+end
-r.date_parse_rfc(string) -- Parses a date/time string and returns seconds since epoche. -+
r.date_parse_rfc(string) -- Parses a date/time string and returns seconds since epoche.-
-r:getcookie(key) -- Gets a HTTP cookie -+
r:getcookie(key) -- Gets a HTTP cookie-
-r:setcookie(key, value, secure, expires) -- Sets a HTTP cookie, for instance: -r:setcookie("foo", "bar and stuff", false, os.time() + 86400) -+
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): + +-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 -+end
-r:wsread() -- Reads a WebSocket frame from a WebSocket upgraded connection (see above): +-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("You wrote: " .. line)
-r:wswrite(line) -- Writes a frame to a WebSocket client: -r:wswrite("Hello, world!") -+
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: +r:wsclose() -- Closes a WebSocket request and terminates it for httpd: if r:wsupgrade() then r:wswrite("Write something: ") @@ -1098,16 +996,14 @@ if r:wsupgrade() then r:wswrite("You wrote: " .. line); r:wswrite("Goodbye!") r:wsclose() -end -+end
- -- examples of logging messages
+-- 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")
@@ -1155,8 +1051,7 @@ end function as buckets are sent down the filter chain. The core structure of such a function is: --function filter(r) +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 alltogether: @@ -1182,8 +1077,7 @@ function filter(r) -- 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 -+end
The example below shows how to acquire a database handle and return information from a table:
--function handle(r) +function handle(r) -- Acquire a database handle - local database, err = r:dbacquire("mysql", "server=localhost,user=root,dbname=mydb") + 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") @@ -1214,32 +1107,26 @@ function handle(r) else r:puts("Could not connect to the database: " .. err) end -end -+end
To utilize mod_dbd
, specify mod_dbd
as the database type, or leave the field blank:
- local database = r:dbacquire("mod_dbd") -+
local database = r:dbacquire("mod_dbd")
The database object returned by dbacquire
has the following methods:
Normal select and query from a database:
---- Run a statement and return the number of rows affected: +-- 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") -+local result, errmsg = database:select(r, "SELECT * FROM `people` WHERE 1")
Using prepared statements (recommended):
---- Create and run a prepared statement: +-- 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 @@ -1249,21 +1136,17 @@ end 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 - -+end
Escaping values, closing databases etc:
---- Escape a value for use in a statement: +-- 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() -+local connected = database:active()
result(-1)
fetches the next available row in the set, asynchronously.result(N)
fetches row number N
, asynchronously:
- --- fetch a result set using a regular query: +-- 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) +function rows(resultset, async) local a = 0 local function getnext() a = a + 1 @@ -1319,8 +1200,7 @@ if not err then .... end end -end -+end
--- Method 1: Manually close a handle +-- 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 -+collectgarbage() -- close the handle via GC
After a lua function has been registered as authorization provider, it can be used
with the Require
directive:
-LuaRoot /usr/local/apache2/lua +-LuaRoot /usr/local/apache2/lua LuaAuthzProvider foo authz.lua authz_check_foo <Location /> Require foo johndoe -</Location> -+</Location>
-require "apache2" +@@ -1412,11 +1286,9 @@ endrequire "apache2" function authz_check_foo(r, who) if r.user ~= who then return apache2.AUTHZ_DENIED return apache2.AUTHZ_GRANTED -end -+end
In general stat or forever is good for production, and stat or never for development.
--LuaCodeCache stat +@@ -1453,8 +1325,7 @@ hook function usually returns OK, DECLINED, or HTTP_FORBIDDEN. a request. This can be used to implement arbitrary authentication and authorization checking. A very simple example: -Examples:
LuaCodeCache stat LuaCodeCache forever -LuaCodeCache never -+LuaCodeCache never-require 'apache2' +require 'apache2' -- fake authcheck hook -- If request has no auth info, set the response header and @@ -1483,8 +1354,7 @@ function authcheck_hook(r) return 401 end return apache2.OK -end -+end@@ -1552,12 +1422,9 @@ processingOrdering
The optional arguments "early" or "late" control when this script runs relative to other modules.
apache2.OK
to tell httpd to log as normal.Example:
--LuaHookLog /path/to/script.lua logger -+LuaHookLog /path/to/script.lua logger---- /path/to/script.lua -- +-- /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 @@ -1577,8 +1444,7 @@ function logger(r) r.uri = r.uri:gsub("somesecretstuff", "") -- sanitize the URI return apache2.OK -- tell httpd to log it. end -end -+end
Like LuaHookTranslateName
but executed at the
map-to-storage phase of a request. Modules like mod_cache run at this phase,
which makes for an interesting example on what to do here:
- LuaHookMapToStorage /path/to/lua/script.lua check_cache -+
LuaHookMapToStorage /path/to/lua/script.lua check_cache-
-require"apache2" +@@ -1660,14 +1522,11 @@ endrequire"apache2" cached_files = {} function read_file(filename) @@ -1628,8 +1491,7 @@ function check_cache(r) end end return apache2.DECLINED -- If we had nothing to do, let others serve this. -end -+end
Example:
--# httpd.conf -LuaHookTranslateName /scripts/conf/hooks.lua silly_mapper -+
# httpd.conf +LuaHookTranslateName /scripts/conf/hooks.lua silly_mapper-
--- /scripts/conf/hooks.lua -- +-- /scripts/conf/hooks.lua -- require "apache2" function silly_mapper(r) if r.uri == "/" then @@ -1676,8 +1535,7 @@ function silly_mapper(r) else return apache2.DECLINED end -end -+end
This directive is not valid in <Directory>
, <Files>
, or htaccess
@@ -1702,12 +1560,9 @@ end
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 -+
LuaHookTypeChecker /path/to/lua/script.lua type_checker-
- function type_checker(r) +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 @@ -1716,8 +1571,7 @@ end end return apache2.DECLINED - end -+ end
bucket
holds the buckets as they are passed
onto the Lua script:
--LuaInputFilter myInputFilter /www/filter.lua input_filter -<FilesMatch "\.lua> +-LuaInputFilter myInputFilter /www/filter.lua input_filter +<Files *.lua> SetInputFilter myInputFilter -</FilesMatch> -+</Files>
---[[ +--[[ Example input filter that converts all POST data to uppercase. ]]-- function input_filter(r) @@ -1780,21 +1631,18 @@ function input_filter(r) end -- No more buckets available. coroutine.yield("&filterSignature=1234") -- Append signature at the end -end -+end
The input filter supports denying/skipping a filter if it is deemed unwanted:
--function input_filter(r) +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 -+end
See "Modifying contents with Lua @@ -1818,18 +1666,14 @@ filters" for more information. match groups into both the file path and the function name. Be careful writing your regular expressions to avoid security issues.
-- LuaMapHandler /(\w+)/(\w+) /scripts/$1.lua handle_$2 -+
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 -+
LuaMapHandler /bingo /scripts/wombat.lua
This would invoke the "handle" function, which
is the default if no specific function name is
@@ -1855,15 +1699,12 @@ global variable bucket
holds the buckets as they are passed
onto the Lua script:
-LuaOutputFilter myOutputFilter /www/filter.lua output_filter -<FilesMatch "\.lua> +-LuaOutputFilter myOutputFilter /www/filter.lua output_filter +<Files *.lua> SetOutputFilter myOutputFilter -</FilesMatch> -+</Files>
---[[ +--[[ Example output filter that escapes all HTML entities in the output ]]-- function output_filter(r) @@ -1874,22 +1715,19 @@ function output_filter(r) coroutine.yield(output) -- Send converted data down the chain end -- No more buckets available. -end -+end
As with the input filter, the output filter supports denying/skipping a filter if it is deemed unwanted:
--function output_filter(r) +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 -+end
mod_filter
When a Lua filter is used as the underlying provider via the @@ -1933,10 +1771,8 @@ information. conventions as lua. This just munges the package.path in the lua vms.
--LuaPackagePath /scripts/lib/?.lua -LuaPackagePath /scripts/lib/?/init.lua -+
LuaPackagePath /scripts/lib/?.lua +LuaPackagePath /scripts/lib/?/init.lua