<-
Apache HTTP 服务器 2.4 > Apache模块mod_lua

Apache模块mod_lua

描述:提供Lua钩子到httpd请求处理的各个部分
状态:延期
模块标识符:lua_module
源文件:mod_lua.c
兼容性:2.3及更高版本

摘要

这个模块允许用Lua编程语言编写的脚本扩展服务器。可用的扩展点(挂钩) mod_lua包括许多可用于本机编译的Apache HTTP Server模块的挂钩,例如将请求映射到文件,生成动态响应,访问控制,身份验证和授权

可以在Lua网站上找到有关Lua编程语言的更多信息 。

警告

此模块具有对httpd的强大控制权,这既有优势又有潜在的安全风险。这是建议您使用这个模块,与您不信任,因为它可以被滥用来改变的httpd的内部工作的用户共享一台服务器上。

支持Apache!

主题

指令

错误修正清单

也可以看看

最佳

基本配置

基本的模块加载指令是

LoadModule lua_module modules/mod_lua.so

mod_lua提供一个名为的处理程序lua-script,可以与SetHandlerAddHandler指令一起使用:

<Files "*.lua">
    SetHandler lua-script
</Files>

这将导致通过调用该文件的 功能mod_lua来处理对文件结尾的请求。 .luahandle

要获得更大的灵活性,请参阅LuaMapHandler

最佳

编写处理程序

在Apache HTTP Server API中,处理程序是一种特定的挂钩,负责生成响应。包括一个处理程序模块的实例是mod_proxymod_cgi,和mod_status

mod_lua总是希望为处理程序调用Lua函数,而不仅仅是评估脚本主体的CGI样式。处理程序函数如下所示:

example.lua
-- example handler require "string" --[[ This is the default method name for Lua handlers, see the optional function-name in the LuaMapHandler directive to choose a different entry point. --]] function handle(r) r.content_type = "text/plain" if r.method == 'GET' then r:puts("Hello Lua World!\n") for k, v in pairs( r:parseargs() ) do r:puts( string.format("%s: %s\n", k, v) ) end elseif r.method == 'POST' then r:puts("Hello Lua World!\n") for k, v in pairs( r:parsebody() ) do r:puts( string.format("%s: %s\n", k, v) ) end elseif r.method == 'PUT' then -- use our own Error contents r:puts("Unsupported HTTP method " .. r.method) r.status = 405 return apache2.OK else -- use the ErrorDocument return 501 end return apache2.OK end

该处理程序函数仅将uri或表单编码的参数输出到纯文本页面。

这意味着(实际上是鼓励)您可以在同一脚本中拥有多个处理程序(或挂钩或过滤器)。

最佳

编写授权提供者

mod_authz_core提供了高级的授权接口,比直接用于相关的挂钩要容易得多。Require指令的第一个参数 给出负责的授权提供者的名称。对于任何 Require一行, mod_authz_core将调用给定名称的授权提供程序,并将其余行作为参数传递。然后,提供程序将检查授权并将结果作为返回值传递。

通常在身份验证之前调用authz提供程序。如果需要知道经过身份验证的用户名(或者完全要对用户进行身份验证),则提供程序必须返回apache2.AUTHZ_DENIED_NO_USER。这将导致身份验证继续进行,并再次调用authz提供程序。

以下authz提供程序功能使用两个参数,一个IP地址和一个用户名。它将允许从给定的ip地址进行访问而无需身份验证,或者通过身份验证的用户与第二个参数匹配:

authz_provider.lua
require 'apache2' function authz_check_foo(r, ip, user) if r.useragent_ip == ip then return apache2.AUTHZ_GRANTED elseif r.user == nil then return apache2.AUTHZ_DENIED_NO_USER elseif r.user == user then return apache2.AUTHZ_GRANTED else return apache2.AUTHZ_DENIED end end

以下配置将此功能注册为提供程序 foo并将其配置为URL /

LuaAuthzProvider foo authz_provider.lua authz_check_foo
<Location "/">
  Require foo 10.1.2.3 john_doe
</Location>
最佳

书写钩

挂钩函数是模块(和Lua脚本)如何参与请求的处理。服务器公开的每种挂钩类型都有特定的用途,例如将请求映射到文件系统,执行访问控制或设置mime类型:

钩相 mod_lua指令 描述
快速处理程序 LuaQuickHandler 这是将请求映射到主机或虚拟主机后将调用的第一个钩子
翻译名称 LuaHookTranslateName 此阶段将请求的URI转换为系统上的文件名。诸如mod_alias和 等模块mod_rewrite将在此阶段运行。
映射到存储 LuaHookMapToStorage 此阶段将文件映射到其物理,缓存或外部/代理存储。可以由代理或缓存模块使用
检查访问 LuaHookAccessChecker 此阶段检查客户端是否有权访问资源。请在验证用户身份之前运行此阶段。
检查用户ID LuaHookCheckUserID 此阶段用于检查协商的用户标识
检查授权 LuaHookAuthChecker 要么 LuaAuthzProvider 此阶段根据协商的凭证(例如用户ID,客户端证书等)授权用户。
检查类型 LuaHookTypeChecker 此阶段检查请求的文件,并为其分配内容类型和处理程序
修正 LuaHookFixups 这是内容处理程序运行之前的最后一个“修复所有内容”阶段。对请求的任何最新更改都应在此处进行。
内容处理程序 fx。.lua文件或通过LuaMapHandler 这是处理内容的地方。读取,解析文件,运行某些文件,并将结果发送到客户端
记录中 LuaHookLog 处理完请求后,它将进入几个日志记录阶段,将请求记录在错误或访问日志中。Mod_lua可以加入该程序的开头并控制日志记录输出。

挂钩函数作为唯一的参数传递给请求对象(LuaAuthzProvider除外,该函数也从Require指令传递参数)。他们可以返回任何值,取决于挂钩,但最常见的,他们会返回OK,DONE,或下降,你可以在Lua写的 apache2.OKapache2.DONE或者 apache2.DECLINED,或者HTTP状态代码。

translate_name.lua
-- example hook that rewrites the URI to a filesystem path. require 'apache2' function translate_name(r) if r.uri == "/translate-name" then r.filename = r.document_root .. "/find_me.txt" return apache2.OK end -- we don't care about this URL, give another module a chance return apache2.DECLINED end
translate_name2.lua
--[[ example hook that rewrites one URI to another URI. It returns a apache2.DECLINED to give other URL mappers a chance to work on the substitution, including the core translate_name hook which maps based on the DocumentRoot. Note: Use the early/late flags in the directive to make it run before or after mod_alias. --]] require 'apache2' function translate_name(r) if r.uri == "/translate-name" then r.uri = "/find_me.txt" return apache2.DECLINED end return apache2.DECLINED end
最佳

数据结构

request_rec

request_rec作为用户数据映射。它具有一个元表,可让您使用它做有用的事情。在大多数情况下,它具有与request_rec结构相同的字段,其中许多字段可写且可读。(表字段的内容可以更改,但是字段本身不能设置为其他表。)

名称 a型 可写 描述
allowoverrides 没有 AllowOverride选项应用于当前请求。
ap_auth_type 没有 如果进行了身份验证检查,则将其设置为身份验证类型(fx basic
args 从请求(fx foo=bar&name=johnsmith)中提取的查询字符串参数
assbackwards 布尔值 没有 如果这是HTTP / 0.9样式请求,则设置为true(例如GET /foo(无标头))
auth_name 没有 用于授权的领域名称(如果适用)。
banner 没有 服务器横幅fx Apache HTTP Server/2.4.3 openssl/0.9.8c
basic_auth_pw 没有 与此请求一起发送的基本身份验证密码(如果有)
canonical_filename 没有 请求的规范文件名
content_encoding 没有 当前请求的内容编码
content_type 在type_check阶段确定的当前请求的内容类型(fx image/giftext/html
context_prefix 没有
context_document_root 没有
document_root 没有 主机的文档根目录
err_headers_out 没有 响应的MIME标头环境,即使在错误时也可以打印,并在内部重定向中保持不变。适用于迭代的只读lua表可作为r:err_headers_out_table()获得。
filename 该请求映射到的文件名fx /www/example.com/foo.txt。可以在请求的转换名称或映射到存储阶段中更改此设置,以允许默认处理程序(或脚本处理程序)为请求的文件提供不同的文件。
handler 应该为该请求提供服务的处理程序的名称, lua-script如果要由mod_lua提供服务,则为fx。这通常由AddHandleror SetHandler 指令设置 ,但也可以通过mod_lua设置,以允许另一个处理程序处理特定的请求,否则该请求将无法处理。
headers_in 来自请求的MIME标头环境。其中包含诸如之类的标题Host, User-Agent, Referer。适用于迭代的只读lua表可作为r:headers_in_table()获得。
headers_out 响应的MIME标头环境。适用于迭代的只读lua表可作为r:headers_out_table()获得。
hostname 没有 主机名,由Host:标题或完整URI设置。
is_https 布尔值 没有 此请求是否通过HTTPS完成
is_initial_req 布尔值 没有 该请求是初始请求还是子请求
limit_req_body 没有 此请求的请求主体的大小限制;如果没有限制,则为0。
log_id 没有 标识访问和错误日​​志中的请求的ID。
method 没有 请求方法fx GETPOST
notes 可以从一个模块传递到另一个模块的注释列表。r:notes_table()提供了适合迭代的只读lua表。
options 没有 选项指令应用于当前请求。
path_info 没有 从此请求中提取的PATH_INFO。
port 没有 请求使用的服务器端口。
protocol 没有 使用的协议,fx HTTP/1.1
proxyreq 表示这是否是代理请求。通常在请求的post_read_request / translate_name阶段中设置此值。
range 没有 Range:标头的内容。
remaining 没有 要从请求主体读取的剩余字节数。
server_built 没有 服务器可执行文件的构建时间。
server_name 没有 此请求的服务器名称。
some_auth_required 布尔值 没有 此请求是否需要某种授权。
subprocess_env 为此请求设置的环境变量。适用于迭代的只读lua表可作为r:subprocess_env_table()获得。
started 没有 从该时期(1970年1月1日)开始(重新)启动服务器的时间(以秒为单位)
status 此请求的(当前)HTTP返回代码,fx 200404
the_request 没有 客户端发送的请求字符串fx GET /foo/bar HTTP/1.1
unparsed_uri 没有 请求的未解析的URI
uri httpd解析后的URI
user 如果进行了身份验证检查,则将其设置为已验证用户的名称。
useragent_ip 没有 发出请求的用户代理的IP
最佳

内置功能

request_rec对象具有(至少)以下方法:

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 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 process a:

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: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
最佳

记录功能

-- 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软件包

一个名为的程序包apache2可(至少)包含以下内容。

apache2.OK
内部常量确定。如果处理程序处理了请求,则应返回此值。
apache2.DECLINED
内部常量DECLINED。如果处理程序不打算处理请求,则应返回此值。
apache2.DONE
内部常量DONE。
apache2.version
Apache HTTP服务器版本字符串
apache2.HTTP_MOVED_TEMPORARILY
HTTP状态码
apache2.PROXYREQ_NONE,apache2.PROXYREQ_PROXY,apache2.PROXYREQ_REVERSE,apache2.PROXYREQ_RESPONSE
内部常量使用 mod_proxy
apache2.AUTHZ_DENIED,apache2.AUTHZ_GRANTED,apache2.AUTHZ_NEUTRAL,apache2.AUTHZ_GENERAL_ERROR,apache2.AUTHZ_DENIED_NO_USER
内部常量使用 mod_authz_core

(其他HTTP状态代码尚未实现。)

最佳

用Lua过滤器修改内容

通过LuaInputFilter 或实施的过滤器功能LuaOutputFilter被设计为三级非阻塞功能,使用协程在存储桶沿着过滤器链发送时暂停和恢复功能。该功能的核心结构是:

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
最佳

数据库连接

Mod_lua实现了一种简单的数据库功能,用于在最流行的数据库引擎(mySQL,PostgreSQL,FreeTDS,ODBC,SQLite,Oracle)以及mod_dbd上查询和运行命令。

下面的示例显示如何获取数据库句柄并从表返回信息:

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

要使用mod_dbd,请将其指定mod_dbd 为数据库类型,或将该字段留空:

local database = r:dbacquire("mod_dbd")

数据库对象和包含的功能

返回的数据库对象dbacquire具有以下方法:

从数据库正常选择和查询:

-- 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")

使用准备好的语句(推荐):

-- 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

转义值,关闭数据库等:

-- 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()

处理结果集

db:select通过db:prepare指定的行号,由或通过创建的准备好的语句函数返回的结果集或返回的结果集可用于同步或异步获取行:
result(0)以同步方式获取所有行,并返回行表。
result(-1)异步获取集合中的下一个可用行。异步
result(N)获取行号N

-- 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.

可以构造一个函数,该函数返回一个迭代函数,以异步或异步方式遍历所有行,具体取决于async参数:

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

关闭数据库连接

数据库句柄应在database:close()不再需要时使用来关闭。如果不手动关闭它们,它们最终将被垃圾回收并由mod_lua关闭,但是如果您将关闭保留为mod_lua,则最终可能与数据库的连接数过多。本质上,以下两个度量是相同的:

-- 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

使用数据库时的注意事项

尽管可以免费获得标准queryrun功能,但是建议您尽可能使用准备好的语句,以优化性能(如果您的数据库句柄可以长期生存)并最大程度地降低SQL注入攻击的风险。run并且query仅在没有变量插入到语句(静态语句)中时使用。使用动态语句时,请使用db:preparedb:prepared

最佳

LuaAuthzProvider 指令

描述:将授权提供程序功能插入 mod_authz_core
句法:LuaAuthzProvider provider_name /path/to/lua/script.lua function_name
内容:服务器配置
状态:延期
模块:mod_lua
兼容性:2.4.3及更高版本

将lua函数注册为授权提供程序后,可以将其与Require伪指令一起使用:

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 指令

描述:配置编译后的代码缓存。
句法:LuaCodeCache stat|forever|never
默认:LuaCodeCache stat
内容:服务器配置,虚拟主机,目录,.htaccess
覆写:所有
状态:延期
模块:mod_lua

指定内存中代码缓存的行为。默认值为stat,它在每次需要该文件时统计顶级脚本(不包含任何脚本),如果修改后的时间表明该文件比已加载的脚本新,则重新加载该脚本。其他值导致它永久保留文件缓存(不统计和替换)或从不缓存文件。

一般而言,统计数据或永远对生产有利,而统计数据或永不对发展有利。

例子:

LuaCodeCache stat
LuaCodeCache forever
LuaCodeCache never
最佳

LuaHookAccessChecker 指令

描述:为请求处理的access_checker阶段提供一个挂钩
句法:LuaHookAccessChecker /path/to/lua/script.lua hook_function_name [early|late]
内容:服务器配置,虚拟主机,目录,.htaccess
覆写:所有
状态:延期
模块:mod_lua
兼容性:2.3.15和更高版本支持可选的第三个参数

将您的钩子添加到access_checker阶段。访问检查器挂钩函数通常返回OK,DECLINED或HTTP_FORBIDDEN。

定购

可选参数“ early”或“ late”控制此脚本相对于其他模块运行的时间。

最佳

LuaHookAuthChecker 指令

描述:为请求处理的auth_checker阶段提供一个钩子
句法:LuaHookAuthChecker /path/to/lua/script.lua hook_function_name [early|late]
内容:服务器配置,虚拟主机,目录,.htaccess
覆写:所有
状态:延期
模块:mod_lua
兼容性:2.3.15和更高版本支持可选的第三个参数

在处理请求的auth_checker阶段调用lua函数。这可用于实现任意身份验证和授权检查。一个非常简单的例子:

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

定购

可选参数“ early”或“ late”控制此脚本相对于其他模块运行的时间。

最佳

LuaHookCheckUserID 指令

描述:为请求处理的check_user_id阶段提供一个钩子
句法:LuaHookCheckUserID /path/to/lua/script.lua hook_function_name [early|late]
内容:服务器配置,虚拟主机,目录,.htaccess
覆写:所有
状态:延期
模块:mod_lua
兼容性:2.3.15和更高版本支持可选的第三个参数

...

定购

可选参数“ early”或“ late”控制此脚本相对于其他模块运行的时间。

最佳

LuaHookFixups 指令

描述:为请求处理的修正阶段提供一个挂钩
句法:LuaHookFixups /path/to/lua/script.lua hook_function_name
内容:服务器配置,虚拟主机,目录,.htaccess
覆写:所有
状态:延期
模块:mod_lua

就像LuaHookTranslateName一样,但是在修正阶段执行

最佳

LuaHookInsertFilter 指令

描述:为请求处理的insert_filter阶段提供一个钩子
句法:LuaHookInsertFilter /path/to/lua/script.lua hook_function_name
内容:服务器配置,虚拟主机,目录,.htaccess
覆写:所有
状态:延期
模块:mod_lua

尚未实现

最佳

LuaHookLog 指令

描述:为请求处理的访问日志阶段提供一个挂钩
句法:LuaHookLog /path/to/lua/script.lua log_function_name
内容:服务器配置,虚拟主机,目录,.htaccess
覆写:所有
状态:延期
模块:mod_lua

这个简单的日志挂钩可让您在httpd进入请求的日志记录阶段时运行功能。使用它,您可以将数据追加到自己的日志中,可以在写入常规日志之前处理数据,或者阻止创建日志条目。为了防止发生通常的日志记录,只需返回 apache2.DONE您的日志记录处理程序,否则返回 apache2.OK告诉httpd正常记录。

例:

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 指令

描述:为请求处理的map_to_storage阶段提供一个钩子
句法:LuaHookMapToStorage /path/to/lua/script.lua hook_function_name
内容:服务器配置,虚拟主机,目录,.htaccess
覆写:所有
状态:延期
模块:mod_lua

类似于LuaHookTranslateName但在请求的映射到存储阶段执行。诸如mod_cache之类的模块在此阶段运行,这为此处的操作提供了一个有趣的示例:

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
最佳

LuaHookTranslateName 指令

描述:为请求处理的翻译名称阶段提供一个挂钩
句法:LuaHookTranslateName /path/to/lua/script.lua hook_function_name [early|late]

Copyright 2020 The Apache Software Foundation.
基于
Apache License, Version 2.0 许可证.