lolly/internal/lua/api_var.go
xfy ad177e9640 docs(lua): 为 Lua API 模块添加标准化 godoc 注释
为所有 Lua API 文件添加完整的包级和函数级文档注释:
- api_balancer: 负载均衡 API(set_current_peer, set_more_tries 等)
- api_ctx: 请求上下文存储 API(ngx.ctx)
- api_location: 子请求捕获 API(ngx.location.capture)
- api_log: 日志输出 API(ngx.log)
- api_req: 请求对象 API
- api_resp: 响应对象 API
- api_shared_dict: 共享字典 API
- api_socket_tcp: TCP socket API
- api_timer: 定时器 API
- api_var: 变量 API
- engine: Lua 引擎核心
- context: 请求上下文管理
- coroutine: 协程调度器
- middleware: 中间件集成
- filter_writer: 响应过滤器
- cache: Lua 脚本缓存
- shared_dict: 共享字典实现
- socket_manager: socket 连接管理

注释格式遵循 Go 官方风格,包含功能说明、参数说明和注意事项。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 10:59:17 +08:00

325 lines
9.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package lua 提供 ngx.var API 实现。
//
// 该文件实现 nginx 变量访问的 Lua API兼容 OpenResty/ngx_lua 语义。
// 支持:
// - 标准 nginx 变量request_method、request_uri、uri、query_string 等
// - 请求头变量http_host、http_user_agent、http_content_type 等
// - 客户端信息remote_addr、server_addr 等
// - arg_ 前缀变量arg_name 用于访问查询参数
// - http_ 前缀变量http_xxx 用于访问任意请求头
// - 自定义变量存储:支持通过 store map 读写自定义变量
//
// 实现说明:
// - 通过元表__index/__newindex实现动态变量读写
// - 读取优先级:自定义变量 > fasthttp 内置变量
// - 写入始终存储到自定义变量 store 中
//
// 作者xfy
package lua
import (
"strconv"
"github.com/valyala/fasthttp"
glua "github.com/yuin/gopher-lua"
)
// argPrefix 是 arg_ 变量的前缀,用于获取查询参数
const argPrefix = "arg_"
// ngxVarAPI 封装 nginx 变量访问 API。
//
// 支持读取 fasthttp 请求中的标准 nginx 变量,
// 以及读写自定义变量(存储于 store map 中)。
type ngxVarAPI struct {
// ctx 关联的 fasthttp 请求上下文
ctx *fasthttp.RequestCtx
// store 自定义变量存储,支持 Lua 脚本读写自定义变量
store map[string]string
}
// newNgxVarAPI 创建 ngx.var API 实例。
//
// 参数:
// - ctx: fasthttp 请求上下文
//
// 返回值:
// - *ngxVarAPI: 初始化的 API 实例
func newNgxVarAPI(ctx *fasthttp.RequestCtx) *ngxVarAPI {
return &ngxVarAPI{
ctx: ctx,
store: make(map[string]string),
}
}
// RegisterNgxVarAPI 在 Lua 状态机中注册 ngx.var API
// 使用元表实现动态读写ngx.var.key 和 ngx.var[key]
func RegisterNgxVarAPI(L *glua.LState, api *ngxVarAPI, ngxTable *glua.LTable) {
// 创建 ngx.var 表(使用元表实现动态访问)
ngxVar := L.NewTable()
// 创建元表
mt := L.NewTable()
// __index 元方法:读取变量
mt.RawSetString("__index", L.NewFunction(api.luaVarIndex))
// __newindex 元方法:设置变量
mt.RawSetString("__newindex", L.NewFunction(api.luaVarNewIndex))
// 设置元表
L.SetMetatable(ngxVar, mt)
// 将 ngx.var 添加到 ngx 表
ngxTable.RawSetString("var", ngxVar)
}
// luaVarIndex 实现 ngx.var[key] 读取
// Lua 调用: local value = ngx.var.key 或 ngx.var[key]
func (api *ngxVarAPI) luaVarIndex(L *glua.LState) int {
// 第一个参数是表本身ngx.var
// 第二个参数是键名
key := L.CheckString(2)
// 1. 先查自定义变量存储
if value, ok := api.store[key]; ok {
L.Push(glua.LString(value))
return 1
}
// 2. 从 fasthttp RequestCtx 获取变量
// 某些变量需要返回数值类型(如 request_length
lv := api.getVariableLua(key)
if lv != nil {
L.Push(lv)
return 1
}
// 3. 未找到变量,返回 nil
L.Push(glua.LNil)
return 1
}
// luaVarNewIndex 实现 ngx.var[key] = value 写入
// Lua 调用: ngx.var.key = value 或 ngx.var[key] = value
// 注意Lua 的 nil 会被转换为空字符串存储
func (api *ngxVarAPI) luaVarNewIndex(L *glua.LState) int {
// 第一个参数是表本身ngx.var
// 第二个参数是键名
// 第三个参数是值
key := L.CheckString(2)
value := L.OptString(3, "")
// 存储到自定义变量存储nil 会转换为空字符串)
api.store[key] = value
return 0
}
// getVariableLua 从 fasthttp RequestCtx 获取变量值,返回 Lua 类型
// 支持常见的 nginx 变量,某些变量返回数值类型
func (api *ngxVarAPI) getVariableLua(name string) glua.LValue {
if api.ctx == nil {
return nil
}
switch name {
// HTTP 请求相关 - 数值类型
case "request_length":
return glua.LNumber(api.ctx.Request.Header.ContentLength())
// HTTP 请求相关 - 字符串类型
case "request_method":
return glua.LString(string(api.ctx.Method()))
case "request_uri":
return glua.LString(string(api.ctx.RequestURI()))
case "uri":
return glua.LString(string(api.ctx.URI().Path()))
case "document_uri":
return glua.LString(string(api.ctx.URI().Path()))
case "query_string", "args":
return glua.LString(string(api.ctx.URI().QueryString()))
case "server_protocol", "protocol":
return glua.LString(string(api.ctx.Request.Header.Protocol()))
case "scheme":
return glua.LString(string(api.ctx.URI().Scheme()))
case "request_time":
// 简化实现,返回空字符串
return glua.LString("")
// 请求头相关
case "http_host":
return glua.LString(string(api.ctx.Host()))
case "http_user_agent", "http_user-agent":
return glua.LString(string(api.ctx.UserAgent()))
case "http_referer":
return glua.LString(string(api.ctx.Referer()))
case "http_accept":
return glua.LString(string(api.ctx.Request.Header.Peek("Accept")))
case "http_accept_encoding", "http_accept-encoding":
return glua.LString(string(api.ctx.Request.Header.Peek("Accept-Encoding")))
case "http_accept_language", "http_accept-language":
return glua.LString(string(api.ctx.Request.Header.Peek("Accept-Language")))
case "http_connection":
return glua.LString(string(api.ctx.Request.Header.Peek("Connection")))
case "http_content_type", "http_content-type":
return glua.LString(string(api.ctx.Request.Header.ContentType()))
case "http_content_length", "http_content-length":
return glua.LString(string(api.ctx.Request.Header.Peek("Content-Length")))
// 客户端信息
case "remote_addr":
return glua.LString(api.ctx.RemoteAddr().String())
case "remote_port":
addr := api.ctx.RemoteAddr()
if addr != nil {
// 简化处理,实际可能需要解析端口
return glua.LString("")
}
return glua.LString("")
case "binary_remote_addr":
return glua.LString("")
// 服务器信息
case "server_addr":
addr := api.ctx.LocalAddr()
if addr != nil {
return glua.LString(addr.String())
}
return glua.LString("")
case "server_port":
return glua.LString("")
case "server_name":
return glua.LString(string(api.ctx.Host()))
// URI 参数
case argPrefix:
// 获取所有参数
return glua.LString(string(api.ctx.URI().QueryString()))
default:
// 检查是否是 arg_ 开头的参数
if len(name) > len(argPrefix) && name[:len(argPrefix)] == argPrefix {
paramName := name[4:]
return glua.LString(string(api.ctx.QueryArgs().Peek(paramName)))
}
// 检查是否是 http_ 开头的请求头
if len(name) > 5 && name[:5] == "http_" {
headerName := name[5:]
return glua.LString(string(api.ctx.Request.Header.Peek(headerName)))
}
return nil
}
}
// getVariable 从 fasthttp RequestCtx 获取变量值(字符串形式)
// 用于 Go 层调用
func (api *ngxVarAPI) getVariable(name string) string {
if api.ctx == nil {
return ""
}
switch name {
case "request_method":
return string(api.ctx.Method())
case "request_uri":
return string(api.ctx.RequestURI())
case "uri":
return string(api.ctx.URI().Path())
case "document_uri":
return string(api.ctx.URI().Path())
case "query_string", "args":
return string(api.ctx.URI().QueryString())
case "server_protocol", "protocol":
return string(api.ctx.Request.Header.Protocol())
case "scheme":
return string(api.ctx.URI().Scheme())
case "request_length":
return strconv.Itoa(api.ctx.Request.Header.ContentLength())
case "request_time":
return ""
case "http_host":
return string(api.ctx.Host())
case "http_user_agent", "http_user-agent":
return string(api.ctx.UserAgent())
case "http_referer":
return string(api.ctx.Referer())
case "http_accept":
return string(api.ctx.Request.Header.Peek("Accept"))
case "http_accept_encoding", "http_accept-encoding":
return string(api.ctx.Request.Header.Peek("Accept-Encoding"))
case "http_accept_language", "http_accept-language":
return string(api.ctx.Request.Header.Peek("Accept-Language"))
case "http_connection":
return string(api.ctx.Request.Header.Peek("Connection"))
case "http_content_type", "http_content-type":
return string(api.ctx.Request.Header.ContentType())
case "http_content_length", "http_content-length":
return string(api.ctx.Request.Header.Peek("Content-Length"))
case "remote_addr":
return api.ctx.RemoteAddr().String()
case "remote_port":
return ""
case "binary_remote_addr":
return ""
case "server_addr":
addr := api.ctx.LocalAddr()
if addr != nil {
return addr.String()
}
return ""
case "server_port":
return ""
case "server_name":
return string(api.ctx.Host())
case argPrefix:
return string(api.ctx.URI().QueryString())
default:
if len(name) > len(argPrefix) && name[:len(argPrefix)] == argPrefix {
paramName := name[4:]
return string(api.ctx.QueryArgs().Peek(paramName))
}
if len(name) > 5 && name[:5] == "http_" {
headerName := name[5:]
return string(api.ctx.Request.Header.Peek(headerName))
}
return ""
}
}
// SetVariable 设置自定义变量Go 层调用)
func (api *ngxVarAPI) SetVariable(name, value string) {
api.store[name] = value
}
// GetVariable 获取变量值Go 层调用)
func (api *ngxVarAPI) GetVariable(name string) (string, bool) {
// 先查自定义变量
if value, ok := api.store[name]; ok {
return value, true
}
// 再查 fasthttp 变量
value := api.getVariable(name)
return value, value != ""
}
// RegisterSchedulerUnsafeVarAPI 为 Scheduler LState 注册不安全的 ngx.var API
func RegisterSchedulerUnsafeVarAPI(L *glua.LState, ngx *glua.LTable) {
ngxVar := L.NewTable()
mt := L.NewTable()
mt.RawSetString("__index", L.NewFunction(luaSchedulerUnsafeVarIndex))
mt.RawSetString("__newindex", L.NewFunction(luaSchedulerUnsafeVarNewIndex))
L.SetMetatable(ngxVar, mt)
ngx.RawSetString("var", ngxVar)
}
func luaSchedulerUnsafeVarIndex(L *glua.LState) int {
L.RaiseError("API ngx.var not available in timer callback context")
return 0
}
func luaSchedulerUnsafeVarNewIndex(L *glua.LState) int {
L.RaiseError("API ngx.var not available in timer callback context")
return 0
}