lolly/internal/variable/builtin.go
xfy 0731dc46e4 refactor(variable): 重命名核心类型移除冗余前缀
VariableContext → Context
VariableStore → Store
ReleaseVariableContext → ReleaseContext (别名保留向后兼容)
提取硬编码字符串为命名常量 sslProtocolNone

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 09:40:17 +08:00

327 lines
8.5 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 variable 提供 nginx 风格的内置变量支持,用于日志、代理和重写规则。
//
// 包含 18 个内置变量常量、动态变量获取函数($arg_name、$http_name、$cookie_name
// 以及 Context 池管理。
//
// 作者xfy
package variable
import (
"strconv"
"time"
"github.com/google/uuid"
"github.com/valyala/fasthttp"
)
// 内置变量常量
const (
VarHost = "host"
VarRemoteAddr = "remote_addr"
VarRemotePort = "remote_port"
VarRequestURI = "request_uri"
VarURI = "uri"
VarArgs = "args"
VarRequestMethod = "request_method"
VarScheme = "scheme"
VarServerName = "server_name"
VarServerPort = "server_port"
VarStatus = "status"
VarBodyBytesSent = "body_bytes_sent"
VarRequestTime = "request_time"
VarTimeLocal = "time_local"
VarTimeISO8601 = "time_iso8601"
VarRequestID = "request_id"
// 上游变量
VarUpstreamAddr = "upstream_addr"
VarUpstreamStatus = "upstream_status"
VarUpstreamResponseTime = "upstream_response_time"
VarUpstreamConnectTime = "upstream_connect_time"
VarUpstreamHeaderTime = "upstream_header_time"
)
// init 注册所有内置变量
func init() {
// 1. $host - 请求 Host 头
RegisterBuiltin(&BuiltinVariable{
Name: VarHost,
Description: "请求的主机名Host 头)",
Getter: func(ctx *fasthttp.RequestCtx) string {
return string(ctx.Host())
},
})
// 2. $remote_addr - 客户端 IP
RegisterBuiltin(&BuiltinVariable{
Name: VarRemoteAddr,
Description: "客户端 IP 地址",
Getter: func(ctx *fasthttp.RequestCtx) string {
addr := ctx.RemoteAddr()
if addr == nil {
return "-"
}
return addr.String()
},
})
// 3. $remote_port - 客户端端口
RegisterBuiltin(&BuiltinVariable{
Name: VarRemotePort,
Description: "客户端端口",
Getter: func(ctx *fasthttp.RequestCtx) string {
addr := ctx.RemoteAddr()
if addr == nil {
return "-"
}
// 解析地址获取端口
s := addr.String()
for i := len(s) - 1; i >= 0; i-- {
if s[i] == ':' {
return s[i+1:]
}
}
return "-"
},
})
// 4. $request_uri - 原始请求 URI
RegisterBuiltin(&BuiltinVariable{
Name: VarRequestURI,
Description: "原始请求 URI包含查询参数",
Getter: func(ctx *fasthttp.RequestCtx) string {
return string(ctx.RequestURI())
},
})
// 5. $uri - 解码后的 URI 路径
RegisterBuiltin(&BuiltinVariable{
Name: VarURI,
Description: "URI 路径(不包含查询参数)",
Getter: func(ctx *fasthttp.RequestCtx) string {
return string(ctx.Path())
},
})
// 6. $args - 查询参数字符串
RegisterBuiltin(&BuiltinVariable{
Name: VarArgs,
Description: "查询参数字符串",
Getter: func(ctx *fasthttp.RequestCtx) string {
return string(ctx.QueryArgs().QueryString())
},
})
// 7. $request_method - 请求方法
RegisterBuiltin(&BuiltinVariable{
Name: VarRequestMethod,
Description: "HTTP 请求方法",
Getter: func(ctx *fasthttp.RequestCtx) string {
return string(ctx.Method())
},
})
// 8. $scheme - 协议
RegisterBuiltin(&BuiltinVariable{
Name: VarScheme,
Description: "协议http 或 https",
Getter: func(ctx *fasthttp.RequestCtx) string {
if ctx.IsTLS() {
return "https"
}
return "http"
},
})
// 9. $server_name - 服务器名称
// 注意:这个变量需要从 Context 获取
RegisterBuiltin(&BuiltinVariable{
Name: VarServerName,
Description: "服务器名称",
Getter: func(ctx *fasthttp.RequestCtx) string {
// 从 UserValue 获取,由外部设置
if v := ctx.UserValue(VarServerName); v != nil {
if s, ok := v.(string); ok {
return s
}
}
return "-"
},
})
// 10. $server_port - 服务器端口
RegisterBuiltin(&BuiltinVariable{
Name: VarServerPort,
Description: "服务器端口",
Getter: func(ctx *fasthttp.RequestCtx) string {
addr := ctx.LocalAddr()
if addr == nil {
return "-"
}
s := addr.String()
for i := len(s) - 1; i >= 0; i-- {
if s[i] == ':' {
return s[i+1:]
}
}
return "-"
},
})
// 11. $status - HTTP 状态码
// 需要从 Context 获取
RegisterBuiltin(&BuiltinVariable{
Name: VarStatus,
Description: "HTTP 响应状态码",
Getter: func(ctx *fasthttp.RequestCtx) string {
if v := ctx.UserValue(VarStatus); v != nil {
if i, ok := v.(int); ok {
return strconv.Itoa(i)
}
}
return "-"
},
})
// 12. $body_bytes_sent - 响应体大小
// 需要从 Context 获取
RegisterBuiltin(&BuiltinVariable{
Name: VarBodyBytesSent,
Description: "发送的响应体字节数",
Getter: func(ctx *fasthttp.RequestCtx) string {
if v := ctx.UserValue(VarBodyBytesSent); v != nil {
if i, ok := v.(int64); ok {
return strconv.FormatInt(i, 10)
}
}
return "0"
},
})
// 13. $request_time - 请求处理时间
// 需要从 Context 获取
RegisterBuiltin(&BuiltinVariable{
Name: VarRequestTime,
Description: "请求处理时间(秒)",
Getter: func(ctx *fasthttp.RequestCtx) string {
if v := ctx.UserValue(VarRequestTime); v != nil {
if i, ok := v.(int64); ok {
return formatRequestTime(i)
}
}
return "0.000"
},
})
// 14. $time_local - 本地时间
RegisterBuiltin(&BuiltinVariable{
Name: VarTimeLocal,
Description: "本地时间格式02/Jan/2024:15:04:05 +0800",
Getter: func(_ *fasthttp.RequestCtx) string {
return time.Now().Format("02/Jan/2006:15:04:05 +0800")
},
})
// 15. $time_iso8601 - ISO8601 时间
RegisterBuiltin(&BuiltinVariable{
Name: VarTimeISO8601,
Description: "ISO8601 格式时间",
Getter: func(_ *fasthttp.RequestCtx) string {
return time.Now().Format(time.RFC3339)
},
})
// 16. $request_id - 唯一请求 ID
RegisterBuiltin(&BuiltinVariable{
Name: VarRequestID,
Description: "唯一请求标识符",
Getter: func(ctx *fasthttp.RequestCtx) string {
// 先从 UserValue 获取,如果没有则生成
if v := ctx.UserValue(VarRequestID); v != nil {
if s, ok := v.(string); ok {
return s
}
}
return uuid.New().String()
},
})
}
// formatRequestTime 格式化请求处理时间
func formatRequestTime(ns int64) string {
// 转换为秒保留3位小数
sec := float64(ns) / 1e9
return strconv.FormatFloat(sec, 'f', 3, 64)
}
// GetArgVariable 获取查询参数变量(动态变量 $arg_name
func GetArgVariable(ctx *fasthttp.RequestCtx, name string) string {
return string(ctx.URI().QueryArgs().Peek(name))
}
// GetHTTPVariable 获取 HTTP 头变量(动态变量 $http_name
func GetHTTPVariable(ctx *fasthttp.RequestCtx, name string) string {
// 将下划线转换为连字符,并规范化头名
headerName := normalizeHeaderName(name)
return string(ctx.Request.Header.Peek(headerName))
}
// GetCookieVariable 获取 Cookie 变量(动态变量 $cookie_name
func GetCookieVariable(ctx *fasthttp.RequestCtx, name string) string {
return string(ctx.Request.Header.Cookie(name))
}
// normalizeHeaderName 规范化 HTTP 头名
func normalizeHeaderName(name string) string {
// 简单处理:将 _ 替换为 -,并首字母大写
if name == "" {
return name
}
var result []byte
upper := true
for i := 0; i < len(name); i++ {
c := name[i]
if c == '_' {
result = append(result, '-')
upper = true
} else if upper {
if c >= 'a' && c <= 'z' {
result = append(result, c-'a'+'A')
} else {
result = append(result, c)
}
upper = false
} else {
result = append(result, c)
}
}
return string(result)
}
// SetResponseInfoInContext 在 fasthttp.RequestCtx 中设置响应信息
// 用于在 builtin getter 中获取 status、body_bytes_sent、request_time
func SetResponseInfoInContext(ctx *fasthttp.RequestCtx, status int, bodySize int64, durationNs int64) {
ctx.SetUserValue(VarStatus, status)
ctx.SetUserValue(VarBodyBytesSent, bodySize)
ctx.SetUserValue(VarRequestTime, durationNs)
}
// SetServerNameInContext 在 fasthttp.RequestCtx 中设置服务器名称
func SetServerNameInContext(ctx *fasthttp.RequestCtx, name string) {
ctx.SetUserValue(VarServerName, name)
}
// SetRequestIDInContext 在 fasthttp.RequestCtx 中设置请求 ID
func SetRequestIDInContext(ctx *fasthttp.RequestCtx, id string) {
ctx.SetUserValue(VarRequestID, id)
}
// BuiltinVarNames 返回所有内置变量名称列表
func BuiltinVarNames() []string {
names := make([]string, 0, len(builtinVars))
for name := range builtinVars {
names = append(names, name)
}
return names
}