描述: | 提供Lua钩子到httpd请求处理的各个部分 |
---|---|
状态: | 延期 |
模块标识符: | lua_module |
源文件: | mod_lua.c |
兼容性: | 2.3及更高版本 |
这个模块允许用Lua编程语言编写的脚本扩展服务器。可用的扩展点(挂钩)
mod_lua
包括许多可用于本机编译的Apache HTTP Server模块的挂钩,例如将请求映射到文件,生成动态响应,访问控制,身份验证和授权
可以在Lua网站上找到有关Lua编程语言的更多信息 。
此模块具有对httpd的强大控制权,这既有优势又有潜在的安全风险。这是不建议您使用这个模块,与您不信任,因为它可以被滥用来改变的httpd的内部工作的用户共享一台服务器上。
基本的模块加载指令是
LoadModule lua_module modules/mod_lua.so
mod_lua
提供一个名为的处理程序lua-script
,可以与SetHandler
或
AddHandler
指令一起使用:
<Files "*.lua"> SetHandler lua-script </Files>
这将导致通过调用该文件的
功能mod_lua
来处理对文件结尾的请求。
.lua
handle
要获得更大的灵活性,请参阅LuaMapHandler
。
在Apache HTTP Server API中,处理程序是一种特定的挂钩,负责生成响应。包括一个处理程序模块的实例是mod_proxy
,mod_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.OK
,apache2.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结构相同的字段,其中许多字段可写且可读。(表字段的内容可以更改,但是字段本身不能设置为其他表。)
名称 | 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/gif 或text/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。这通常由AddHandler or 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 GET 或POST 。 |
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 200 或404 。 |
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 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: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
可(至少)包含以下内容。
mod_proxy
mod_authz_core
(其他HTTP状态代码尚未实现。)
通过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
尽管可以免费获得标准query
和run
功能,但是建议您尽可能使用准备好的语句,以优化性能(如果您的数据库句柄可以长期生存)并最大程度地降低SQL注入攻击的风险。run
并且query
仅在没有变量插入到语句(静态语句)中时使用。使用动态语句时,请使用db:prepare
或db:prepared
。
描述: | 将授权提供程序功能插入 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 stat|forever|never |
默认: | LuaCodeCache stat |
内容: | 服务器配置,虚拟主机,目录,.htaccess |
覆写: | 所有 |
状态: | 延期 |
模块: | mod_lua |
指定内存中代码缓存的行为。默认值为stat,它在每次需要该文件时统计顶级脚本(不包含任何脚本),如果修改后的时间表明该文件比已加载的脚本新,则重新加载该脚本。其他值导致它永久保留文件缓存(不统计和替换)或从不缓存文件。
一般而言,统计数据或永远对生产有利,而统计数据或永不对发展有利。
LuaCodeCache stat LuaCodeCache forever LuaCodeCache never
描述: | 为请求处理的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”控制此脚本相对于其他模块运行的时间。
描述: | 为请求处理的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”控制此脚本相对于其他模块运行的时间。
描述: | 为请求处理的check_user_id阶段提供一个钩子 |
---|---|
句法: | LuaHookCheckUserID /path/to/lua/script.lua hook_function_name [early|late] |
内容: | 服务器配置,虚拟主机,目录,.htaccess |
覆写: | 所有 |
状态: | 延期 |
模块: | mod_lua |
兼容性: | 2.3.15和更高版本支持可选的第三个参数 |
...
可选参数“ early”或“ late”控制此脚本相对于其他模块运行的时间。
描述: | 为请求处理的修正阶段提供一个挂钩 |
---|---|
句法: | LuaHookFixups /path/to/lua/script.lua hook_function_name |
内容: | 服务器配置,虚拟主机,目录,.htaccess |
覆写: | 所有 |
状态: | 延期 |
模块: | mod_lua |
就像LuaHookTranslateName一样,但是在修正阶段执行
描述: | 为请求处理的insert_filter阶段提供一个钩子 |
---|---|
句法: | LuaHookInsertFilter /path/to/lua/script.lua hook_function_name |
内容: | 服务器配置,虚拟主机,目录,.htaccess |
覆写: | 所有 |
状态: | 延期 |
模块: | mod_lua |
尚未实现
描述: | 为请求处理的访问日志阶段提供一个挂钩 |
---|---|
句法: | 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
描述: | 为请求处理的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 /path/to/lua/script.lua hook_function_name [early|late] |
Copyright 2020 The Apache Software Foundation. |