新增 internal/variable 包,提供 Nginx 风格的变量展开功能: - 支持 $remote_addr、$host、$uri、$args 等 30+ 内置变量 - 使用 sync.Pool 优化 VariableContext 分配 - 支持 set_response_info 存储响应状态信息 添加 github.com/google/uuid 依赖用于请求 ID 生成。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
320 lines
8.1 KiB
Go
320 lines
8.1 KiB
Go
// builtin.go - 内置变量定义
|
||
//
|
||
// 提供 18 个 nginx 风格的内置变量,用于日志、代理和重写规则。
|
||
//
|
||
// 作者: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"
|
||
)
|
||
|
||
// 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 - 服务器名称
|
||
// 注意:这个变量需要从 VariableContext 获取
|
||
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 状态码
|
||
// 需要从 VariableContext 获取
|
||
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 - 响应体大小
|
||
// 需要从 VariableContext 获取
|
||
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 - 请求处理时间
|
||
// 需要从 VariableContext 获取
|
||
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(ctx *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(ctx *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
|
||
}
|