Lua API
Lua 可以用于生成配置(类似于 include_shell 的快捷方式)或编写实际的响应处理程序。
使用 Lua 生成配置不会产生任何性能影响;在这种情况下,Lua 只在启动时运行以生成配置,并且不涉及 Lua 处理请求。
由于 lua_State 本身不是线程安全的,您有两种方式使用 Lua 配置
-
include_lua和lua.plugin:使用全局服务器锁,但在所有 worker 中共享同一个lua_State -
lua_handler:不带锁,每个 worker 都有自己的lua_State(并且它们不能共享其全局上下文)。
Lua 配置
本节描述如何将主配置中的概念转换为 Lua。您可以将整个配置或部分配置用 Lua 编写,并将其包含进来(例如使用 include_lua)。
示例 - debug.lua
以下保存为“debug.lua”的 Lua 片段可以例如通过 include_lua "debug.lua" 包含。
function mydebug(vr)
local url_fields = { "raw", "raw_path", "raw_orig_path", "scheme", "authority", "path", "query", "host" }
local phys_fields = { "path", "doc_root", "pathinfo" }
if vr:handle_direct() then
vr.resp.status = 200
vr.resp.headers["Content-Type"] = "text/plain"
vr.out:add("Hello World!\n\n")
vr.out:add("http method: " .. vr.req.http_method .. "\n")
vr.out:add("http version: " .. vr.req.http_version .. "\n")
for k, v in vr.env:pairs() do
vr.out:add("Env['" .. k .. "'] = '" .. v .. "'\n")
end
vr.out:add("\n")
for k, v in pairs(url_fields) do
vr.out:add("vr.req.uri['" .. v .. "'] = '" .. vr.req.uri[v] .. "'\n")
end
vr.out:add("\n")
for k, v in pairs(phys_fields) do
vr.out:add("vr.phys['" .. v .. "'] = '" .. vr.phys[v] .. "'\n")
end
vr.out:add("\n")
for k, v in vr.req.headers:pairs() do
vr.out:add("vr.req.headers['" .. k .. "'] = '" .. v .. "'\n")
end
end
end
actions = mydebug
值
- 布尔值:Lua 直接支持
true和false - 整数:Lua 有自己的数字类型(通常是
double),并且不知道任何后缀。 - 字符串:Lua 直接支持字符串。请查阅 Lua 参考手册以了解各种引用样式。
- 列表和键值对列表:Lua 有一个“table”类型;它可以包含顺序列表和关联映射。使用
{1, 2, 3}创建简单列表,使用{a=1, b=2}创建唯一映射(将转换为键值对列表),或使用{{"a",1},{"a",2}}显式创建键值对列表(其中一个键可以多次使用,并且顺序很重要)。
不要混合使用顺序列表和关联映射。
如果您从 lighttpd 获取一个列表(可能是键值对列表)值,它会表示为顺序列表,但具有一个特殊的__index元表方法,支持字符串和nil作为查找参数,即您可以在 Lua 中将键值对列表视为关联映射(例如参阅 contrib/secdownload.lua 中的选项处理)。 - 表达式和变量只是普通的 Lua 内容;目前(尚未)无法直接访问 lighttpd 配置变量。
- 动作块:您可以使用列表动作 (
act = action.list(act1, act2)) 从动作列表中创建一个动作。
函数调用
动作上下文通过函数名前缀 action. 给出,设置上下文通过前缀 setup. 给出。不要尝试在请求处理中调用设置。
此外,每个 Lua 函数都可以作为动作(参阅上面的 debug.lua 示例),将一个虚拟请求对象作为参数。
不支持包含,也不支持调试 __print(有其他日志方法可用)。
条件
条件是最糟糕的部分:无法将原生 Lua 的 if 语句转换为 lighttpd 配置,因此需要手动构建它们。
在 Lua 中,只有条件变量的长名称可用。所有条件运算符都已命名并附加到条件变量中,然后用作比较值来调用。
| 运算符 | Lua 名称 | 运算符 | Lua 名称 |
|---|---|---|---|
| == | :eq |
!= | :ne |
| <= | :le |
< | :lt |
| >= | :ge |
> | :gt |
| =~ | :match |
!~ | :nomatch |
| =^ | :prefix |
!^ | :notprefix |
| =$ | :suffix |
!$ | :notsuffix |
| =/ | :ip |
!/ | :notip |
布尔条件变量使用 :is() 或 :isnot() 调用。
此类调用(一个“条件”)的结果将作为第一个参数传递给 action.when。
示例 - 仅限管理员
将 if req.env["REMOTE_USER"] != "admin" { auth.deny; } 转换为 Lua
actions = action.when(request.environment["REMOTE_USER"]:ne("admin"), action.auth.deny())
示例 - 仅限物理文件
将 if !phys.exists { auth.deny; } 转换为 Lua
actions = action.when(physical.exists:isnot(), action.auth.deny())
API
本节介绍了处理请求所需的对象类型;您可能会从处理程序中作为参数获得的虚拟请求对象开始。
对象字段应使用 .field 或 ["field"] 访问,例如
e = vr.env e["XXX"] = "abc"
标记为 (ro) 的字段是只读的;这并不意味着字段值不能被修改,只是您不能用另一个对象覆盖该字段。不过,只读字符串/数字属性确实是只读的。
使用 :method(...) 调用对象方法
vr:print("Hello World")
注意:obj:method(par1, par2, ...) 语法只是 obj["method"](obj, par1, par2, ...) 的另一种表达方式(但 obj 只会评估一次),因此字段名和方法名位于相同的命名空间中。
这意味着我们的容器类型无法访问与方法同名的字段(以“__”开头的方法未在此处列出),因此您必须使用显式访问方法来读取此类容器中的通用字段(写入不是问题,因为我们不允许写入方法)。
所有容器类型都应提供 get 和 set 方法,以提供“干净”的容器内容访问方式。
pairs()
某些对象可能会提供 :pairs() 方法来遍历字段(而非方法);这适用于简单的情况,例如
for k, v in vr.env:pairs() do vr:print("env['" .. k .. "'] = '" .. v .. "'") end
Lua 期望 :pairs 方法返回一个 next, obj, startkey 元组,并使用 k = startkey; while k, v = next(obj, k) do ... end 循环遍历列表;但 next() 方法应使用 k 作为上一个键并返回下一个键。
我们的 next 方法将在一个内部对象中(作为 next 函数的 upvalue 关联)保持当前位置,并且在每次调用时都会忽略 obj 和 k 参数并向前推进。
全局常量
liHandlerResult 枚举值
lighty.HANDLER_GO_ONlighty.HANDLER_COMEBACKlighty.HANDLER_WAIT_FOR_EVENTlighty.HANDLER_ERROR
全局方法
-
lighty.print(以及lighty.error和print):通过 Lua 的“tostring”方法在全局服务器上下文中以 ERROR 级别打印参数 -
lighty.warning:通过 Lua 的“tostring”方法在全局服务器上下文中以 WARNING 级别打印参数 -
lighty.info:通过 Lua 的“tostring”方法在全局服务器上下文中以 INFO 级别打印参数 -
lighty.debug:通过 Lua 的“tostring”方法在全局服务器上下文中以 DEBUG 级别打印参数 -
lighty.filter_in(class):创建一个新动作,如果在运行时调用,它会从class:new(vr)添加一个入站过滤器 -
lighty.filter_out(class):创建一个新动作,如果在运行时调用,它会从class:new(vr)添加一个出站过滤器 -
lighty.md5(str):计算字符串str的 md5 校验和(以十六进制字符串形式返回摘要) -
lighty.sha1(str):计算字符串str的 sha1 校验和(以十六进制字符串形式返回摘要) -
lighty.sha256(str):计算字符串str的 sha256 校验和(以十六进制字符串形式返回摘要) -
lighty.path_simplify(str):返回简化路径
示例
lighty.print("Hello World!")
示例
local MyFilterclass = { }
MyFilterClass.__index = MyFilterClass
function MyFilterClass:new(vr)
local o = { }
setmetatable(o, self)
return o -- return nil if you want to skip the filter this time
end
function MyFilterClass:handle(vr, outq, inq) ... end
actions = lighty.filter_out(MyFilterClass)
虚拟请求
字段
-
con(ro):连接 -
in(ro):数据块队列,读取请求的 post 内容 -
out(ro):数据块队列,写入响应内容 -
env(ro):环境,(fast)cgi 环境 -
req(ro):请求,来自请求头的数据 -
resp(ro):响应,响应头数据 -
phys(ro):物理,路径和文件名 -
is_handled(ro):vrequest 是否已处理 -
has_response(ro):响应头(和状态)是否可用
方法
-
print(...):通过 Lua 的tostring方法在虚拟请求上下文中以 ERROR 级别打印参数 -
warning(...):通过 Lua 的tostring方法在虚拟请求上下文中以 WARNING 级别打印参数 -
info(...):通过 Lua 的tostring方法在虚拟请求上下文中以 INFO 级别打印参数 -
debug(...):通过 Lua 的tostring方法在虚拟请求上下文中以 DEBUG 级别打印参数 -
handle_direct():处理 vrequest(即提供头和正文);如果尚未处理,则返回 true。 -
enter_action(act):将新动作推送到动作堆栈(如果推送的动作完成后需要重新运行,则返回 HANDLER_WAIT_FOR_EVENT;如果完成,则返回 HANDLER_GO_ON) -
st, res, errno, msg = stat(filename):异步 stat(filename)。可能的结果如下- st 是 stat 结果,如果找到文件,则 res == HANDLER_GO_ON。errno 和 msg 为 NIL。在所有其他情况下,st 为 NIL 且 res != HANDLER_GO_ON。
- res == HANDLER_WAIT_FOR_EVENT:stat() 正在进行中,稍后再试(同时返回 HANDLER_WAIT_FOR_EVENT)
- res == HANDLER_ERROR:如果 stat() 失败,errno 包含错误号,msg 包含错误号对应的错误消息。
-
add_filter_in(obj):将obj添加为 Lua 入站过滤器(需要响应obj:handle(vr, outq, inq)和可选的obj:finished());返回一个 Filter 对象 -
add_filter_out(obj):将obj添加为 Lua 出站过滤器(需要响应obj:handle(vr, outq, inq)和可选的obj:finished());返回一个 Filter 对象
连接
-
local:本地套接字地址 -
remote:远程主机地址
环境
字段是环境中的键,因此它表现得像一个 Lua 表;如果您使用以“__”开头的键或与下面某个方法同名的键,则必须使用 get 方法来读取它们,例如
x = env["set"] -- doesn't work, returns the set method instead
x = env:get("set") -- use this instead
x = env[y] -- don't do this, as y may be a special key like "set"
x = env:get(y) -- just do it the safe way if you are not sure
方法
-
get(k):env[k]的安全方式 -
set(k, v):env[k] = v的安全方式 -
unset(k):env[k] = nil的安全方式 -
weak_set(k, v):不覆盖旧值,env[k] = env[k] or v的安全方式 -
pairs():用于遍历键:for k, v in env:pairs() do ... end -
clear():删除所有条目
数据块队列
字段
-
is_closed:ChunkQueue 是否已关闭
方法
-
add(s):将字符串附加到队列 -
add({filename="/..."}):将文件附加到队列(只允许常规文件) -
reset():移除所有数据块,重置计数器 -
steal_all(from):从另一个队列窃取所有数据块(在过滤器中很有用,如果您决定将所有数据通过它传递) -
skip_all():跳过所有数据块(移除所有数据块,但不重置计数器)
请求
字段
-
headers(ro):HTTP 头 -
http_method(ro):HTTP 方法字符串(“GET”,“POST”,“HEAD”,…) -
http_version(ro):HTTP 版本字符串(“HTTP/1.0”,“HTTP/1.1”) -
content_length(ro):Content-Length 头的数值(如果有人更改了头值,则不会自动更新),未指定则为 -1 -
uri:请求 URI
请求 URI
字段
-
raw:HTTP 请求行中的原始请求 URI(或重写结果) -
raw_path:未解码的带查询字符串的路径(对于大多数请求将与raw相同,除非有人执行类似GET http://example.com/test?abc HTTP/1.1的操作) -
raw_orig_path:与 raw_path 相同,但在任何重写发生之前保存 -
scheme:“http”或“https” -
authority:完整的 hostname 头(或绝对 URL 中的 authority 部分),例如 “user@www.example.com.:8080” -
path:已解码和简化的路径名,不含 authority、scheme、query-string;例如 “/index.php” -
host:简单主机名,不含认证信息、端口、末尾点;例如 “www.example.com” -
query:查询字符串,例如 “a=1&b=2”
响应
字段
-
headers(ro):HTTP 头 -
status:HTTP 状态码
物理
字段
-
path:物理路径 -
doc_root:文档根目录 -
pathinfo:pathinfo
HTTP 头
字段与 Environment 具有相同的限制。
方法
-
get(k):将键k的所有头值用 “, “ 连接起来(RFC 允许这样做) -
set(k, v):移除所有键为k的头,如果 v 不为 nil,则附加新的 “k: v” 头 -
append(k, v):如果键 k 的最后一个头值已存在,则附加 “, v”;否则,使用insert(k, v) -
insert(k, v):将新的 “k: v” 头附加到列表 -
unset(k):移除所有键为k的头 -
pairs():遍历所有头。请注意,键不是唯一的! -
list(k):遍历所有键为k的头 -
clear():移除所有头
过滤器
表示一个“liFilter”。
字段
-
in(ro):数据块队列,入站流 -
out(ro):数据块队列,出站流
Stat 结构体
表示“struct stat”。大多数字段应该是自解释的(如果您不了解,可以查阅 man 2 stat (debian 手册页))。
字段
-
is_file(ro):S_ISREG(mode) -
is_dir(ro):S_ISDIR(mode) -
is_char(ro):S_ISCHR(mode) -
is_block(ro):S_ISBLK(mode) -
is_socket(ro):S_ISSOCK(mode) -
is_link(ro):S_ISLNK(mode) -
is_fifo(ro):S_ISFIFO(mode) -
mode(ro) -
mtime(ro) -
ctime(ro) -
atime(ro) -
uid(ro) -
gid(ro) -
size(ro) -
ino(ro) -
dev(ro)