<-
Apache > HTTP Server > 文件 > 版本 2.4 > 模組

Apache 模組 mod_lua

可用語言:  en  |  fr 

說明在 httpd 要求處理的各個部分中提供 Lua 掛鉤
狀態擴充套件
模組識別碼lua_module
原始檔mod_lua.c
相容性2.3 及更新版本

摘要

此模組允許伺服器使用 Lua 程式語言編寫的腳本進行擴展。可供 mod_lua 使用的擴充套件點(掛鉤)包含許多可供原生編譯的 Apache HTTP Server 模組使用的掛鉤,例如將要求對應至檔案、產生動態回應、存取控制、驗證和授權

有關 Lua 程式語言的更多資訊,請見 Lua 網站

警告

此模組對 httpd 握有極大權限,這既是一個優點,也是潛在的安全風險。不建議您在與不信任的使用者共用伺服器的情況下使用此模組,因為它可能會被濫用來變更 httpd 的內部運作。

Support Apache!

主題

指令

錯誤修正清單

請參閱

top

基本設定

基本的模組載入指令為

LoadModule lua_module modules/mod_lua.so

mod_lua 提供一個名為 lua-script 的處理常式,可用於 SetHandlerAddHandler 指令

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

這將導致 mod_lua 處理以 .lua 結尾的檔案請求,並呼叫該檔案的 handle 函數。

如需更多彈性,請參閱 LuaMapHandler

top

撰寫處理常式

在 Apache HTTP Server API 中,處理常式是一種負責產生回應的特定類型鉤子。包含處理常式的模組範例包括 mod_proxymod_cgimod_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 或表單編碼的參數列印到一個純文字頁面。

這表示(實際上鼓勵)您可以在同一個指令碼中擁有多個處理常式(或鉤子或篩選器)。

top

撰寫授權提供者

mod_authz_core 提供一個高階的授權介面,其使用起來比直接使用相關的鉤子容易許多。Require 指令的第一個參數提供負責任授權提供者的名稱。對於任何 Require 行,mod_authz_core 會呼叫指定名稱的授權提供者,並將行的其餘部分作為參數傳遞。然後提供者會檢查授權,並將結果傳遞為回傳值。

認證提供程序通常在認證之前被呼叫。如果認證提供程序需要知道經過驗證的使用者名稱(或使用者是否已通過驗證),則提供程序必須傳回 apache2.AUTHZ_DENIED_NO_USER。這將導致認證繼續進行,並第二次呼叫認證提供程序。

下列認證提供程序函式會接收兩個參數,一個 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>
top

撰寫掛勾

掛勾函式是模組(和 Lua 腳本)參與處理請求的方式。伺服器公開的每種類型的掛勾都有特定的目的,例如將請求對應到檔案系統、執行存取控制或設定 MIME 類型

掛勾階段 mod_lua 指令 說明
快速處理常式 LuaQuickHandler 這是請求對應至代管或虛擬代管後將呼叫的第一個掛勾
預翻譯名稱 LuaHookPreTranslateName 此階段會在解碼發生之前將要求的 URI 翻譯成系統中的檔案名稱。mod_proxy 等模組可以在此階段運作。
翻譯名稱 LuaHookTranslateName 此階段會將要求的 URI 翻譯成系統中的檔案名稱。mod_aliasmod_rewrite 等模組在此階段運作。
對應到儲存 LuaHookMapToStorage 此階段會將檔案對應到它們的實體、快取或外部/代理儲存。它可以用於代理或快取模組
檢查存取權限 LuaHookAccessChecker 此階段會檢查客戶端是否有權限存取資源。請注意,此階段在使用者進行驗證之前執行。
檢查使用者 ID LuaHookCheckUserID 此階段用於檢查協商的使用者 ID
檢查授權 LuaHookAuthCheckerLuaAuthzProvider 此階段會根據協商的憑證(例如使用者 ID、客戶端憑證等)授權使用者。
檢查類型 LuaHookTypeChecker 此階段會檢查要求的檔案,並指派內容類型和處理常式給檔案
修正 LuaHookFixups 這是內容處理常式執行前的最後一個「修正」階段。任何最後一刻的請求變更都應該在此階段進行。
內容處理常式 例. .lua 檔案或透過 LuaMapHandler 這是處理內容的地方。此階段會讀取、剖析和執行檔案(有的會執行),然後將結果傳送給客戶端
記錄 LuaHookLog 在處理請求之後,它會進入幾個記錄階段,將請求記錄於錯誤或存取記錄中。Mod_lua 能夠掛入這個開始的位置並控制記錄輸出的內容。

掛入功能將請求物件作為其唯一參數傳遞 (除了 LuaAuthzProvider,它也會傳遞由 Require 指令傳遞的參數)。它們可以返回任何值,端視掛入功能而定,但通常會傳回 OK、DONE 或 DECLINED,你可以將它們寫為 Lua 中的 apache2.OKapache2.DONEapache2.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
top

資料結構

request_rec

request_rec 被指定為使用者資料。它有一個元資料表,讓你能夠以有用的方式來使用它。它大部分都和 request_rec 結構擁有相同的欄位,其中許多欄位都可以寫入也能夠讀取。(表格欄位的內容可以變更,但欄位本身不能設定為不同的表格。)

名稱 Lua 類型 可寫入 說明
allowoverrides 字串 套用於當前請求的 AllowOverride 選項。
ap_auth_type 字串 如果進行身份驗證檢查,這裡會設定為身分驗證類型 (例如 basic)
args 字串 從請求中萃取的查詢字串參數 (例如 foo=bar&name=johnsmith)
assbackwards 布林 如果是 HTTP/0.9 風格的請求,則設定為 true (例如 GET /foo (沒有標頭) )
auth_name 字串 用於授權的領域名稱 (如果適用)。
banner 字串 伺服器標語,例如 Apache HTTP Server/2.4.3 openssl/0.9.8c
basic_auth_pw 字串 隨這個請求一起傳送的基本授權密碼 (如果有)
canonical_filename 字串 這個請求的標準檔名
content_encoding 字串 當前請求的內容編碼
content_type 字串 當前請求的內容類型,在 type_check 階段確定 (例如 image/giftext/html)
context_prefix 字串
context_document_root 字串
document_root 字串 主機的存放區根目錄
err_headers_out 表格 回應的 MIME 標頭環境,會列印出來,即使在發生錯誤和跨內部重新導向時也會列印。可用的只讀 lua 表格適合進行重複操作,可以用做為 r:err_headers_out_table()。
filename 字串 請求對應到的檔案名稱,例如 /www/example.com/foo.txt。這可以在請求的 pre-translate-name、translate-name 或 map-to-storage 階段進行變更,以允許預設的處理常式 (或指令碼處理常式) 提供和請求不同的檔案。
handler 字串 這項處理程式的名稱應服務這項要求。例如,如果要由 mod_lua 服務,則為 lua-script。這通常是由 AddHandlerSetHandler 指令設定,但也可以透過 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 字串 要求方法,例如 GETPOST
notes 表格 可以從一個模組傳遞到另一個模組的筆記清單。以適用於反覆運算的唯讀 Lua 表格 r:notes_table() 提供。
options 字串 套用於目前要求的 Options 指令。
path_info 字串 從此要求中擷取的 PATH_INFO
port 數字 要求使用的伺服器埠
protocol 字串 使用的通訊協定,例如 HTTP/1.1
proxyreq 字串 表示這是代理程式要求還是不是。此值通常在要求的 post_read_request/pre_translate_name/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 回傳碼,例如 200404
the_request 字串 客戶端傳送的要求字串,例如 GET /foo/bar HTTP/1.1
unparsed_uri 字串 要求的未剖析 URI
uri 字串 由 httpd 剖析後的 URI
user 字串 如果已經進行驗證檢查,它會設定為驗證過的使用者名稱。
useragent_ip 字串 提出要求之使用者代理人的 IP
top

內建函式

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
top

記錄函式

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

apache2 套件

名為 apache2 的套件提供(至少)下列內容。

apache2.OK
內部常數「確定」。如果處理程式處理了要求,應傳回此常數。
apache2.DECLINED
內部常數「拒絕」。如果處理程式不會處理要求,應傳回此常數。
apache2.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 狀態碼尚未實作。)

top

修改具有 Lua 篩選器的內容

透過 LuaInputFilterLuaOutputFilter 實作的篩選器函式,設計為使用共程例程的三階段非封鎖函式,用於在置於篩選器串中後暫停和繼續運作函式。此類函式的主要結構為

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
top

資料庫連線

Mod_lua 實作一項簡單的資料庫功能,用於對最熱門的資料庫引擎(mySQL、PostgreSQL、FreeTDS、ODBC、SQLite、Oracle)以及 mod_dbd 進行查詢和執行命令。

用於作為 dbacquire 第一項參數的 dbType 區分大小寫。

它應該是其中一個:mysqlpgsqlfreetdsodbcsqlite2sqlite3oraclemod_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.

可以建構一個函式,它會傳回一個反覆函式,用於以同步或非同步的方式反覆運算所有列,這取決於非同步引數

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 注入攻擊的風險減至最低。runquery僅應在沒有插入變數至陳述式(靜態陳述式)中時使用。使用動態陳述式時,請使用 db:preparedb:prepared

top

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
top

LuaCodeCache 指令

說明設定已編譯的原始碼快取。
語法LuaCodeCache stat|forever|never
預設值LuaCodeCache stat
內容伺服器設定、虛擬主機、目錄、.htaccess
覆寫全部
狀態擴充套件
模組mod_lua

指定內部記憶體原始碼快取的行為。預設值為 stat,每次需要檔案時,stat 會統計頂層指令碼(不包括任何包含的指令碼),如果修改時間表示它較已載入的檔案新,則會重新載入。其它值會讓它永遠快取檔案(不要統計和取代)或永遠不快取檔案。

一般而言,stat 或 forever 適用於生產,stat 或 never 適用於開發。

範例

LuaCodeCache stat
LuaCodeCache forever
LuaCodeCache never
top

LuaHookAccessChecker 指令

說明提供請求處理之中存取檢查器階段的掛鉤
語法LuaHookAccessChecker /path/to/lua/script.lua hook_function_name [early|late]
內容伺服器設定、虛擬主機、目錄、.htaccess
覆寫全部
狀態擴充套件
模組mod_lua
相容性2.3.15 及更新版本支援的選用第 3 個參數

將您的掛鉤新增至存取檢查器階段。存取檢查器掛鉤函式通常會傳回 OK、DECLINED 或 HTTP_FORBIDDEN。

排序

選用參數「early」或「late」控制此指令碼相對於其它模組執行時機。

top

LuaHookAuthChecker 指令

說明提供請求處理之中驗證檢查器階段的掛鉤
語法LuaHookAuthChecker /path/to/lua/script.lua hook_function_name [early|late]
內容伺服器設定、虛擬主機、目錄、.htaccess
覆寫全部
狀態擴充套件
模組mod_lua
相容性2.3.15 及更新版本支援的選用第 3 個參數

在處理要求的驗證檢查程序中,呼叫 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」控制此指令碼相對於其它模組執行時機。

top

LuaHookCheckUserID 指令

說明提供一個程式碼掛在 check_user_id 處理請求程序
語法LuaHookCheckUserID /path/to/lua/script.lua hook_function_name [early|late]
內容伺服器設定、虛擬主機、目錄、.htaccess
覆寫全部
狀態擴充套件
模組mod_lua
相容性2.3.15 及更新版本支援的選用第 3 個參數

...

排序

選用參數「early」或「late」控制此指令碼相對於其它模組執行時機。

top

LuaHookFixups 指令

說明提供一個程式碼掛在處理請求的修復程序
語法LuaHookFixups /path/to/lua/script.lua hook_function_name
內容伺服器設定、虛擬主機、目錄、.htaccess
覆寫全部
狀態擴充套件
模組mod_lua

就像 LuaHookTranslateName,但在修復程序執行

top

LuaHookInsertFilter 指令

說明提供一個程式碼掛在處理請求的插入過濾程序
語法LuaHookInsertFilter /path/to/lua/script.lua hook_function_name
內容伺服器設定、虛擬主機、目錄、.htaccess
覆寫全部
狀態擴充套件
模組mod_lua

尚未實做

top

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
top

LuaHookMapToStorage 指令

說明提供一個程式碼掛在處理請求的對映到儲存空間程序
語法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
top

LuaHookPreTranslate 指令

說明提供一個程式碼掛在處理請求的預先轉換程序
語法LuaHookPreTranslate /path/to/lua/script.lua hook_function_name
內容伺服器設定、虛擬主機、目錄、.htaccess
覆寫全部
狀態擴充套件
模組mod_lua

就像 LuaHookTranslateName,但在預先轉換的程序執行,其中 URI 路徑並未進行百分比解碼。

top

LuaHookTranslateName 指令

說明提供一個程式碼掛在處理請求的轉換名稱程序
語法LuaHookTranslateName /path/to/lua/script.lua hook_function_name [early|late]
內容伺服器設定、虛擬主機
覆寫全部
狀態擴充套件
模組mod_lua
相容性2.3.15 及更新版本支援的選用第 3 個參數

將一個程式碼掛在(在 APR_HOOK_MIDDLE)加到處理請求的轉換名稱程序。程式碼掛入函數會收到一個單一引數,即 request_rec,並應傳回一個狀態代碼,該代碼可以是 HTTP 錯誤代碼,或是 apache2 模組中定義的常數:apache2.OK、apache2.DECLINED 或 apache2.DONE。

對於初次使用 Hooks 的開發人員,基本上每個 Hook 都會呼叫至其中一個 Hooks 回傳 apache2.OK 時才會停止。如果 Hook 不需要進行轉換,應只回傳 apache2.DECLINED。如果要求不應該繼續處理,則回傳 apache2.DONE。

範例

# 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

內容

此指令在 <Directory><Files>,或 htaccess 環境無效。

排序

選用參數「early」或「late」控制此指令碼相對於其它模組執行時機。

top

LuaHookTypeChecker 指令

說明提供請求處理類型檢查階段的 Hook。
語法LuaHookTypeChecker /path/to/lua/script.lua hook_function_name
內容伺服器設定、虛擬主機、目錄、.htaccess
覆寫全部
狀態擴充套件
模組mod_lua

此指令提供請求處理類型檢查階段的 Hook。這個階段會將內容類型和處理常式指派給請求,因此可以按照輸入修改類型和處理常式。

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
top

LuaInherit 指令

說明控制如何將父層設定區段整併成子層設定區段
語法LuaInherit none|parent-first|parent-last
預設值LuaInherit parent-first
內容伺服器設定、虛擬主機、目錄、.htaccess
覆寫全部
狀態擴充套件
模組mod_lua
相容性2.4.0 以上版本

預設情況下,如果在重疊的 Directory 或 Location 設定區段中使用 LuaHook* 指令,在特定區段定義的指令會在一般區段中定義的指令之後執行 (LuaInherit parent-first)。您可以反轉此順序,或不套用父層環境。

先前的 2.3.x 版本預設值是實際上忽略父層設定區段中的 LuaHook* 指令。

top

LuaInputFilter 指令

說明提供內容輸入過濾的 Lua 函式
語法LuaInputFilter filter_name /path/to/lua/script.lua function_name
內容伺服器設定
狀態擴充套件
模組mod_lua
相容性2.4.5 以上版本

提供一個將 Lua 函式新增為輸入過濾的機制。與輸出過濾一樣,輸入過濾以 coroutine 方式執行,在緩衝區傳送之前先讓出,然後在需要將 bucket 傳遞給下一個鏈時讓出,最後 (選擇性地) 讓出需要附加至輸入資料的任何內容。bucket 全域變數會在傳遞至 Lua 指令時儲存 bucket。

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

如果輸入過濾器判定不需要,它可以拒絕/略過過濾器

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

請參閱「使用 Lua 過濾器修改內容」以取得更多資訊。

top

LuaMapHandler 指令

說明將路徑對應至 lua 處理常式
語法LuaMapHandler uri-pattern /path/to/lua/script.lua [function-name]
內容伺服器設定、虛擬主機、目錄、.htaccess
覆寫全部
狀態擴充套件
模組mod_lua

此指令會比對 uri 模式,以在特定檔案中呼叫特定處理常式函式。它會使用 PCRE 正規表示法比對 uri,並支援將比對群組插入檔案路徑和函式名稱中。撰寫正規表示法時務必謹慎,以避免安全問題。

範例

LuaMapHandler "/(\w+)/(\w+)" "/scripts/$1.lua" "handle_$2"

這會比對 uri(例如 /photos/show?id=9)至檔案 /scripts/photos.lua,以及在載入該檔案後在 lua vm 上呼叫處理常式函式 handle_show。

LuaMapHandler "/bingo" "/scripts/wombat.lua"

這將呼叫「handle」函數,如果未提供特定函數名稱,則為預設值。

top

LuaOutputFilter 指令

說明提供 Lua 函數以作為內容輸出篩選
語法LuaOutputFilter filter_name /path/to/lua/script.lua function_name
內容伺服器設定
狀態擴充套件
模組mod_lua
相容性2.4.5 以上版本

此指令提供將 Lua 函數新增為輸出篩選器的方法。與輸入篩選器一樣,輸出篩選器以合併例程運行,在傳送緩衝區之前先讓傳回結果,然後在需要將區塊傳遞給鏈時傳回結果,最後(選用)傳回結果任何需要附加至輸入資料的內容。全域變數 bucket 在將區塊傳遞至 Lua 腳本時會暫存區塊

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

與輸入篩選器相同,輸出篩選器支援否認/略過不受歡迎的篩選項目

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

使用 mod_filter 的 Lua 篩選器

當使用 FilterProvider 指令將 Lua 篩選器用作基礎提供者時,僅當 filter-nameprovider-name 相同時,篩選才會運作。

請參閱「使用 Lua 過濾器修改內容」以取得更多資訊。

top

LuaPackageCPath 指令

說明將目錄新增至 lua 的 package.cpath
語法LuaPackageCPath /path/to/include/?.soa
內容伺服器設定、虛擬主機、目錄、.htaccess
覆寫全部
狀態擴充套件
模組mod_lua

將路徑新增至 lua 的共用函式庫搜尋路徑。遵循與 lua 相同的慣例。這只會在 lua vms 中混合 package.cpath。

top

LuaPackagePath 指令

說明將目錄新增至 lua 的 package.path
語法LuaPackagePath /path/to/include/?.lua
內容伺服器設定、虛擬主機、目錄、.htaccess
覆寫全部
狀態擴充套件
模組mod_lua

將路徑新增至 lua 的模組搜尋路徑。遵循與 lua 相同的慣例。這只會在 lua vms 中混合 package.path。

範例

LuaPackagePath "/scripts/lib/?.lua"
LuaPackagePath "/scripts/lib/?/init.lua"
top

LuaQuickHandler 指令

說明提供掛勾,以快速處理請求
語法LuaQuickHandler /path/to/script.lua hook_function_name
內容伺服器設定、虛擬主機
覆寫全部
狀態擴充套件
模組mod_lua

此階段在請求已對應至虛擬主機後立即執行,可於其他階段啟動前執行一些請求處理,或用於在不需要轉譯、對應至儲存空間等情況下服務請求。由於此階段在其他階段之前執行,因此 <Location><Directory> 等指令在此階段中會無效,因為 URI 尚未正確剖析。

內容

此指令在 <Directory><Files>,或 htaccess 環境無效。

top

LuaRoot 指令

說明指定用於解析 mod_lua 指令的相對路徑的基底路徑
語法LuaRoot /path/to/a/directory
內容伺服器設定、虛擬主機、目錄、.htaccess
覆寫全部
狀態擴充套件
模組mod_lua

指定用於評估 mod_lua 中所有相對路徑的基底路徑。如果未指定,則會相對於目前的作業目錄進行解析,而這可能無法始終順利地使用於伺服器。

top

LuaScope 指令

說明once、request、conn、thread 其一 -- 預設為 once
語法LuaScope once|request|conn|thread|server [min] [max]
預設值LuaScope 一次
內容伺服器設定、虛擬主機、目錄、.htaccess
覆寫全部
狀態擴充套件
模組mod_lua

指定此「目錄」中處理程式會使用的 Lua 直譯器生命週期範圍。預設為「once」

once
使用直譯器一次,然後捨棄。
request
在這個要求中,使用直譯器處理基於同一個檔案的任何內容,這也是要求範圍。
conn
同上一個,但附屬於 connection_rec
thread
使用直譯器處理請求的執行緒生命週期(只可用於有執行緒的 MPM)。
server
此範圍不同於其他範圍,因為伺服器範圍的生命週期相當長,而且多個執行緒會具備相同的 server_rec。為適應此架構,伺服器範圍的 Lua 狀態會儲存在 apr 資源清單中。minmax 引數指定儲存在池中的 Lua 狀態的最小和最大數值。

一般而言,threadserver 範圍的執行速度比其他範圍快約 2 至 3 倍,因為它們不必為每個請求產生新的 Lua 狀態(尤其是對於事件 MPM,因為即使是保持連線請求也會為每個請求使用新的執行緒)。如果您確信您的腳本不會在重複使用狀態時產生問題,則應使用 threadserver 範圍以獲得最佳效能。雖然 thread 範圍會提供最快速的回應,但 server 範圍會使用較少的記憶體,因為狀態會集中起來,例如允許 1,000 個執行緒只共用 100 個 Lua 狀態,因此只使用 thread 範圍所需的 10% 記憶體。

可用語言:  en  |  fr 

top

留言

注意事項
此處並非問答區。放置於此的留言應著重於改進文件或伺服器的建議,如果留言已被實作或被認為無效/離題,我們的管理員可能會將其移除。有關如何管理 Apache HTTP Server 的問題應轉由我們的 IRC 頻道、Libera.chat 上的 #httpd,或傳送至我們的郵寄清單