docs: 为其余模块添加标准化 godoc 注释
为剩余模块添加完整文档注释: - app: 应用生命周期管理 - cache: 文件缓存 - config: 配置加载器 - handler: 静态文件处理和错误页面 - http2/http3: HTTP/2 和 HTTP/3 适配器 - loadbalance: 负载均衡算法和均衡器 - middleware: bodylimit、compression、rewrite、security - mimeutil: MIME 类型检测 - netutil: URL 处理工具 - resolver: DNS 解析器 - server: 服务器升级处理 - ssl: SSL/TLS 和 OCSP - stream: 流处理 - testutil: 测试工具 - variable: 变量池和 SSL 变量 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
088785bce2
commit
2458ac1ed1
@ -72,7 +72,15 @@ type App struct {
|
||||
listeners []net.Listener
|
||||
}
|
||||
|
||||
// NewApp 创建应用程序。
|
||||
// NewApp 创建应用程序实例。
|
||||
//
|
||||
// 该函数初始化 App 结构体,保存配置文件路径供后续加载使用。
|
||||
//
|
||||
// 参数:
|
||||
// - cfgPath: 配置文件路径(YAML 格式),用于加载服务器配置
|
||||
//
|
||||
// 返回值:
|
||||
// - *App: 初始化的应用程序实例,包含配置路径等信息
|
||||
func NewApp(cfgPath string) *App {
|
||||
return &App{
|
||||
cfgPath: cfgPath,
|
||||
@ -89,7 +97,18 @@ func (a *App) SetLogFile(path string) {
|
||||
a.logFile = path
|
||||
}
|
||||
|
||||
// Run 应用程序入口。
|
||||
// Run 应用程序入口函数。
|
||||
//
|
||||
// 根据参数决定行为:生成配置、打印版本或启动应用。
|
||||
//
|
||||
// 参数:
|
||||
// - cfgPath: 配置文件路径
|
||||
// - genConfig: 是否仅生成默认配置并退出
|
||||
// - outputPath: 配置输出路径,为空时输出到 stdout
|
||||
// - showVersion: 是否仅打印版本信息并退出
|
||||
//
|
||||
// 返回值:
|
||||
// - int: 退出码,0 表示正常退出,1 表示异常
|
||||
func Run(cfgPath string, genConfig bool, outputPath string, showVersion bool) int {
|
||||
if genConfig {
|
||||
return generateConfig(outputPath)
|
||||
|
||||
@ -52,7 +52,15 @@ type App struct {
|
||||
resv resolver.Resolver
|
||||
}
|
||||
|
||||
// NewApp 创建应用程序。
|
||||
// NewApp 创建应用程序实例(Windows 版本)。
|
||||
//
|
||||
// 该函数初始化 App 结构体,保存配置文件路径供后续加载使用。
|
||||
//
|
||||
// 参数:
|
||||
// - cfgPath: 配置文件路径(YAML 格式),用于加载服务器配置
|
||||
//
|
||||
// 返回值:
|
||||
// - *App: 初始化的应用程序实例,包含配置路径等信息
|
||||
func NewApp(cfgPath string) *App {
|
||||
return &App{cfgPath: cfgPath}
|
||||
}
|
||||
@ -67,7 +75,18 @@ func (a *App) SetLogFile(path string) {
|
||||
a.logFile = path
|
||||
}
|
||||
|
||||
// Run 应用程序入口。
|
||||
// Run 应用程序入口函数(Windows 版本)。
|
||||
//
|
||||
// 根据参数决定行为:生成配置、打印版本或启动应用。
|
||||
//
|
||||
// 参数:
|
||||
// - cfgPath: 配置文件路径
|
||||
// - genConfig: 是否仅生成默认配置并退出
|
||||
// - outputPath: 配置输出路径,为空时输出到 stdout
|
||||
// - showVersion: 是否仅打印版本信息并退出
|
||||
//
|
||||
// 返回值:
|
||||
// - int: 退出码,0 表示正常退出,1 表示异常
|
||||
func Run(cfgPath string, genConfig bool, outputPath string, showVersion bool) int {
|
||||
if genConfig {
|
||||
return generateConfig(outputPath)
|
||||
|
||||
13
internal/cache/file_cache.go
vendored
13
internal/cache/file_cache.go
vendored
@ -318,7 +318,18 @@ type pendingRequest struct {
|
||||
err error // 生成结果
|
||||
}
|
||||
|
||||
// NewProxyCache 创建代理缓存。
|
||||
// NewProxyCache 创建代理缓存实例。
|
||||
//
|
||||
// 根据指定的缓存规则、锁开关和过期复用时间创建 ProxyCache。
|
||||
// 启用 cacheLock 可防止缓存击穿,多个并发请求共享同一个生成过程。
|
||||
//
|
||||
// 参数:
|
||||
// - rules: 代理缓存规则列表,定义可缓存的路径、方法、状态码等
|
||||
// - cacheLock: 是否启用缓存生成锁,防止多个请求同时生成缓存
|
||||
// - staleTime: 过期缓存可复用的额外时间,设为 0 表示不启用
|
||||
//
|
||||
// 返回值:
|
||||
// - *ProxyCache: 初始化的代理缓存实例
|
||||
func NewProxyCache(rules []ProxyCacheRule, cacheLock bool, staleTime time.Duration) *ProxyCache {
|
||||
return &ProxyCache{
|
||||
rules: rules,
|
||||
|
||||
@ -1,3 +1,21 @@
|
||||
// Package config 提供 YAML 配置文件的解析、验证和默认配置生成功能。
|
||||
//
|
||||
// 该文件包含配置加载器相关的核心逻辑,包括:
|
||||
// - 多文件配置合并(include 指令支持)
|
||||
// - DAG-safe 循环检测(防止配置文件循环引用)
|
||||
// - Glob 模式展开(支持通配符路径)
|
||||
// - 深度限制(防止无限递归)
|
||||
//
|
||||
// 主要用途:
|
||||
//
|
||||
// 用于加载主配置文件及其引用的子配置文件,合并为完整配置。
|
||||
//
|
||||
// 注意事项:
|
||||
// - 最大 include 深度为 10 层
|
||||
// - 循环引用会返回错误
|
||||
// - DAG 共享子配置允许(同一文件可被多处引用)
|
||||
//
|
||||
// 作者:xfy
|
||||
package config
|
||||
|
||||
import (
|
||||
|
||||
@ -121,6 +121,9 @@ type PartialLoadError struct {
|
||||
}
|
||||
|
||||
// Error 实现 error 接口。
|
||||
//
|
||||
// 返回值:
|
||||
// - string: 格式化的错误消息,包含失败的数量
|
||||
func (e *PartialLoadError) Error() string {
|
||||
return fmt.Sprintf("部分错误页面加载失败: %d 个错误", len(e.Errors))
|
||||
}
|
||||
|
||||
@ -22,10 +22,12 @@ const (
|
||||
// 小于该值的文件使用普通 io.Copy,避免系统调用开销。
|
||||
MinSendfileSize = 8 * 1024
|
||||
|
||||
// sendfile 最大重试次数
|
||||
// sendfileMaxRetries sendfile 系统调用最大重试次数。
|
||||
// 用于处理 EAGAIN/EWOULDBLOCK 等临时性错误。
|
||||
sendfileMaxRetries = 100
|
||||
|
||||
// sendfile 重试等待时间
|
||||
// sendfileRetryDelay sendfile 重试间隔等待时间。
|
||||
// 短暂等待以允许 socket 缓冲区恢复。
|
||||
sendfileRetryDelay = 1 * time.Millisecond
|
||||
)
|
||||
|
||||
@ -70,11 +72,30 @@ func SendFile(ctx *fasthttp.RequestCtx, file *os.File, offset, length int64) err
|
||||
}
|
||||
|
||||
// getNetConn 从 fasthttp.RequestCtx 获取底层 net.Conn。
|
||||
//
|
||||
// 用于获取网络连接以便提取 socket 文件描述符。
|
||||
//
|
||||
// 参数:
|
||||
// - ctx: fasthttp 请求上下文
|
||||
//
|
||||
// 返回值:
|
||||
// - net.Conn: 底层网络连接,如果无法获取则返回 nil
|
||||
func getNetConn(ctx *fasthttp.RequestCtx) net.Conn {
|
||||
return ctx.Conn()
|
||||
}
|
||||
|
||||
// copyFile 普通文件拷贝(fallback)。
|
||||
//
|
||||
// 使用 io.Copy 进行文件传输,适用于不支持 sendfile 的场景或小文件。
|
||||
//
|
||||
// 参数:
|
||||
// - ctx: fasthttp 请求上下文,作为写入目标
|
||||
// - file: 源文件对象
|
||||
// - offset: 文件起始偏移量
|
||||
// - length: 传输长度,0 表示拷贝到文件末尾
|
||||
//
|
||||
// 返回值:
|
||||
// - error: 拷贝过程中的错误
|
||||
func copyFile(ctx *fasthttp.RequestCtx, file *os.File, offset, length int64) error {
|
||||
if offset > 0 {
|
||||
if _, err := file.Seek(offset, io.SeekStart); err != nil {
|
||||
@ -95,6 +116,15 @@ func copyFile(ctx *fasthttp.RequestCtx, file *os.File, offset, length int64) err
|
||||
//
|
||||
// 使用 Linux 特有的 sendfile 系统调用实现零拷贝传输。
|
||||
// 正确处理临时错误(EAGAIN、EINTR)和连接断开(EPIPE、ECONNRESET)。
|
||||
//
|
||||
// 参数:
|
||||
// - conn: 目标网络连接(必须是 TCPConn 或 UnixConn)
|
||||
// - fileFd: 源文件的文件描述符
|
||||
// - offset: 文件起始偏移量(未使用,由内核自动处理)
|
||||
// - length: 传输长度(字节)
|
||||
//
|
||||
// 返回值:
|
||||
// - error: 传输过程中的错误,nil 表示成功
|
||||
func linuxSendfile(conn net.Conn, fileFd uintptr, _, length int64) error {
|
||||
socketFd, err := getSocketFd(conn)
|
||||
if err != nil {
|
||||
@ -156,6 +186,14 @@ func linuxSendfile(conn net.Conn, fileFd uintptr, _, length int64) error {
|
||||
// getSocketFd 获取 socket 文件描述符。
|
||||
//
|
||||
// 从网络连接中提取底层的文件描述符,用于 sendfile 系统调用。
|
||||
// 支持 TCPConn 和 UnixConn 两种连接类型。
|
||||
//
|
||||
// 参数:
|
||||
// - conn: 网络连接对象
|
||||
//
|
||||
// 返回值:
|
||||
// - uintptr: socket 文件描述符,失败时返回 0
|
||||
// - error: 获取失败时的错误,不支持的连接类型返回 ENOTSUP
|
||||
func getSocketFd(conn net.Conn) (uintptr, error) {
|
||||
switch c := conn.(type) {
|
||||
case *net.TCPConn:
|
||||
|
||||
@ -18,6 +18,7 @@ import (
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
// TestMinSendfileSize 验证 MinSendfileSize 常量值。
|
||||
func TestMinSendfileSize(t *testing.T) {
|
||||
if MinSendfileSize != 8*1024 {
|
||||
t.Errorf("Expected MinSendfileSize 8KB, got %d", MinSendfileSize)
|
||||
|
||||
@ -128,7 +128,9 @@ func TestGetSocketFd_UnsupportedType(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// mockConn 是一个不实现 TCPConn/UnixConn 的连接
|
||||
// mockConn 是一个不实现 TCPConn/UnixConn 的模拟连接。
|
||||
//
|
||||
// 用于测试 getSocketFd 对不支持连接类型的处理。
|
||||
type mockConn struct{}
|
||||
|
||||
func (m *mockConn) Read([]byte) (n int, err error) { return 0, nil }
|
||||
|
||||
@ -14,7 +14,14 @@ import (
|
||||
"rua.plus/lolly/internal/cache"
|
||||
)
|
||||
|
||||
// setupStaticTestDir 创建临时测试目录。
|
||||
// setupStaticTestDir 创建临时测试目录并填充测试文件。
|
||||
//
|
||||
// 创建包含 index.html、style.css、large.json、nested/file.js 的目录结构,
|
||||
// 用于静态文件基准测试。
|
||||
//
|
||||
// 返回值:
|
||||
// - string: 临时测试目录路径
|
||||
// - func(): 清理函数,调用后删除临时目录
|
||||
func setupStaticTestDir() (string, func()) {
|
||||
dir, err := os.MkdirTemp("", "static_bench_*")
|
||||
if err != nil {
|
||||
|
||||
@ -26,13 +26,27 @@ import (
|
||||
"rua.plus/lolly/internal/testutil"
|
||||
)
|
||||
|
||||
// newTestHandler 创建测试用的静态文件处理器
|
||||
// newTestHandler 创建测试用的静态文件处理器。
|
||||
//
|
||||
// 参数:
|
||||
// - t: 测试上下文,用于标记 helper
|
||||
// - root: 静态文件根目录
|
||||
//
|
||||
// 返回值:
|
||||
// - *StaticHandler: 配置好索引文件的静态文件处理器(禁用 sendfile)
|
||||
func newTestHandler(t *testing.T, root string) *StaticHandler {
|
||||
t.Helper()
|
||||
return NewStaticHandler(root, "/", []string{"index.html", "index.htm"}, false) // 测试时禁用 sendfile
|
||||
}
|
||||
|
||||
// newTestContext 创建测试用的 fasthttp 请求上下文
|
||||
// newTestContext 创建测试用的 fasthttp 请求上下文。
|
||||
//
|
||||
// 参数:
|
||||
// - t: 测试上下文,用于标记 helper
|
||||
// - path: 请求路径
|
||||
//
|
||||
// 返回值:
|
||||
// - *fasthttp.RequestCtx: 初始化好的请求上下文
|
||||
func newTestContext(t *testing.T, path string) *fasthttp.RequestCtx {
|
||||
t.Helper()
|
||||
return testutil.NewRequestCtx("GET", path)
|
||||
|
||||
@ -337,7 +337,13 @@ type AdapterConfig struct {
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
// DefaultAdapterConfig 返回默认配置。
|
||||
// DefaultAdapterConfig 返回适配器的默认配置。
|
||||
//
|
||||
// 返回包含默认缓冲区大小(4096 字节)、最大请求体大小(64KB)
|
||||
// 和超时时间(30 秒)的配置实例。
|
||||
//
|
||||
// 返回值:
|
||||
// - *AdapterConfig: 初始化的适配器默认配置实例
|
||||
func DefaultAdapterConfig() *AdapterConfig {
|
||||
return &AdapterConfig{
|
||||
BufferSize: 4096,
|
||||
@ -352,7 +358,17 @@ type ConfigurableAdapter struct {
|
||||
config *AdapterConfig
|
||||
}
|
||||
|
||||
// NewConfigurableAdapter 创建可配置适配器。
|
||||
// NewConfigurableAdapter 创建可配置的 HTTP/2 适配器。
|
||||
//
|
||||
// 根据传入的配置参数创建适配器实例,如果配置为 nil 则使用默认配置。
|
||||
// 适用于需要自定义缓冲区大小、请求体大小限制或超时时间的场景。
|
||||
//
|
||||
// 参数:
|
||||
// - handler: fasthttp 请求处理器,用于处理 HTTP/2 请求
|
||||
// - config: 适配器配置,为 nil 时使用默认配置
|
||||
//
|
||||
// 返回值:
|
||||
// - *ConfigurableAdapter: 初始化的可配置适配器实例
|
||||
func NewConfigurableAdapter(handler fasthttp.RequestHandler, config *AdapterConfig) *ConfigurableAdapter {
|
||||
if config == nil {
|
||||
config = DefaultAdapterConfig()
|
||||
|
||||
@ -482,6 +482,12 @@ type Settings struct {
|
||||
}
|
||||
|
||||
// DefaultSettings 返回默认 HTTP/2 设置。
|
||||
//
|
||||
// 返回包含 HTTP/2 协议推荐的默认参数设置,包括头部表大小、
|
||||
// 并发流数量、初始窗口大小、帧大小等。
|
||||
//
|
||||
// 返回值:
|
||||
// - Settings: 默认 HTTP/2 配置参数
|
||||
func DefaultSettings() Settings {
|
||||
return Settings{
|
||||
HeaderTableSize: 4096,
|
||||
|
||||
@ -37,7 +37,14 @@ type Adapter struct {
|
||||
bufferPool sync.Pool
|
||||
}
|
||||
|
||||
// NewAdapter 创建新的适配器。
|
||||
// NewAdapter 创建 HTTP/3 适配器实例。
|
||||
//
|
||||
// 初始化用于将 fasthttp.RequestHandler 适配为标准库 http.Handler
|
||||
// 的适配器。内部使用 sync.Pool 复用 RequestCtx 和缓冲区对象,
|
||||
// 以降低内存分配开销。
|
||||
//
|
||||
// 返回值:
|
||||
// - *Adapter: 初始化的 HTTP/3 适配器实例
|
||||
func NewAdapter() *Adapter {
|
||||
return &Adapter{
|
||||
ctxPool: sync.Pool{
|
||||
|
||||
@ -1,22 +1,29 @@
|
||||
// Package loadbalance 负载均衡包,提供多种负载均衡算法实现。
|
||||
// Package loadbalance 提供负载均衡算法实现。
|
||||
//
|
||||
// 该文件包含负载均衡相关的核心逻辑,包括:
|
||||
// - 轮询算法(Round Robin)
|
||||
// - 加权轮询算法(Weighted Round Robin)
|
||||
// - 最少连接算法(Least Connections)
|
||||
// - IP 哈希算法(IP Hash)
|
||||
// - 一致性哈希算法(Consistent Hash)
|
||||
// 该文件包含负载均衡算法相关的辅助逻辑,包括:
|
||||
// - ValidAlgorithms 有效算法列表定义
|
||||
// - IsValidAlgorithm 算法有效性校验函数
|
||||
//
|
||||
// 主要用途:
|
||||
// 用于校验和枚举系统支持的负载均衡算法类型,供配置解析和路由选择使用。
|
||||
//
|
||||
// 用于在后端服务器之间分发请求,提高服务可用性和性能。
|
||||
// 注意事项:
|
||||
// - 新增算法时必须同步更新 ValidAlgorithms 列表
|
||||
// - 空字符串被视为有效值,表示使用默认算法
|
||||
//
|
||||
// 作者:xfy
|
||||
package loadbalance
|
||||
|
||||
import "slices"
|
||||
|
||||
// ValidAlgorithms 是支持的负载均衡算法列表。
|
||||
// ValidAlgorithms 定义系统支持的负载均衡算法名称列表。
|
||||
//
|
||||
// 该列表用于配置校验,包含以下算法:
|
||||
// - round_robin: 简单轮询,按顺序均匀分配请求
|
||||
// - weighted_round_robin: 加权轮询,按权重比例分配请求
|
||||
// - least_conn: 最少连接,选择活跃连接数最少的目标
|
||||
// - ip_hash: IP 哈希,基于客户端 IP 实现会话保持
|
||||
// - consistent_hash: 一致性哈希,使用虚拟节点实现均匀分布
|
||||
var ValidAlgorithms = []string{
|
||||
"round_robin",
|
||||
"weighted_round_robin",
|
||||
@ -25,7 +32,17 @@ var ValidAlgorithms = []string{
|
||||
"consistent_hash",
|
||||
}
|
||||
|
||||
// IsValidAlgorithm 检查算法是否有效。
|
||||
// IsValidAlgorithm 检查给定的算法名称是否有效。
|
||||
//
|
||||
// 该函数用于配置解析阶段校验算法参数的合法性。
|
||||
// 空字符串被视为有效值,表示由调用方选择默认算法。
|
||||
//
|
||||
// 参数:
|
||||
// - alg: 算法名称字符串,如 "round_robin"、"least_conn" 等
|
||||
//
|
||||
// 返回值:
|
||||
// - true: 算法有效(在 ValidAlgorithms 列表中或为空字符串)
|
||||
// - false: 算法无效(不在 ValidAlgorithms 列表中且非空)
|
||||
func IsValidAlgorithm(alg string) bool {
|
||||
if alg == "" {
|
||||
return true
|
||||
|
||||
@ -75,13 +75,22 @@ type Balancer interface {
|
||||
}
|
||||
|
||||
// RoundRobin 实现简单的轮询负载均衡。
|
||||
// 它按顺序将请求均匀分配到所有健康目标上。
|
||||
//
|
||||
// 它按顺序将请求均匀分配到所有健康目标上,适合后端服务器性能相近的场景。
|
||||
//
|
||||
// 并发安全:counter 使用 atomic 操作,支持多 goroutine 并发调用。
|
||||
type RoundRobin struct {
|
||||
// counter 原子地为每个请求递增
|
||||
// counter 请求计数器,原子递增,用于确定轮询位置
|
||||
counter uint64
|
||||
}
|
||||
|
||||
// NewRoundRobin 创建一个新的轮询负载均衡器。
|
||||
//
|
||||
// 该函数初始化一个无状态的 RoundRobin 实例,内部 counter 从零开始。
|
||||
// 适合后端服务器性能相近、无需权重区分的场景。
|
||||
//
|
||||
// 返回值:
|
||||
// - *RoundRobin: 初始化的轮询均衡器实例
|
||||
func NewRoundRobin() *RoundRobin {
|
||||
return &RoundRobin{}
|
||||
}
|
||||
@ -100,13 +109,22 @@ func (r *RoundRobin) Select(targets []*Target) *Target {
|
||||
}
|
||||
|
||||
// WeightedRoundRobin 实现权重轮询负载均衡。
|
||||
// 权重越高的目标接收成比例更多的请求。
|
||||
//
|
||||
// 权重越高的目标接收成比例更多的请求,适合后端服务器性能差异较大的场景。
|
||||
//
|
||||
// 并发安全:counter 使用 atomic 操作,支持多 goroutine 并发调用。
|
||||
type WeightedRoundRobin struct {
|
||||
// counter 原子地为每个请求递增
|
||||
// counter 请求计数器,原子递增,用于确定权重分布位置
|
||||
counter uint64
|
||||
}
|
||||
|
||||
// NewWeightedRoundRobin 创建一个新的权重轮询负载均衡器。
|
||||
//
|
||||
// 该函数初始化一个 WeightedRoundRobin 实例,内部 counter 从零开始。
|
||||
// 权重越高的目标接收成比例更多的请求,适合后端服务器性能差异较大的场景。
|
||||
//
|
||||
// 返回值:
|
||||
// - *WeightedRoundRobin: 初始化的权重轮询均衡器实例
|
||||
func NewWeightedRoundRobin() *WeightedRoundRobin {
|
||||
return &WeightedRoundRobin{}
|
||||
}
|
||||
@ -155,10 +173,16 @@ func (w *WeightedRoundRobin) Select(targets []*Target) *Target {
|
||||
}
|
||||
|
||||
// LeastConnections 实现最少连接负载均衡。
|
||||
// 它选择活跃连接数最少的目标。
|
||||
//
|
||||
// 它选择活跃连接数最少的目标,适合请求处理时间差异较大的场景。
|
||||
//
|
||||
// 该算法无状态设计,不维护内部计数器,直接读取目标连接数。
|
||||
type LeastConnections struct{}
|
||||
|
||||
// NewLeastConnections 创建一个新的最少连接负载均衡器。
|
||||
//
|
||||
// 返回值:
|
||||
// - 初始化的 LeastConnections 实例
|
||||
func NewLeastConnections() *LeastConnections {
|
||||
return &LeastConnections{}
|
||||
}
|
||||
@ -187,10 +211,15 @@ func (l *LeastConnections) Select(targets []*Target) *Target {
|
||||
}
|
||||
|
||||
// IPHash 实现基于 IP 哈希的负载均衡。
|
||||
// 它将来自同一客户端 IP 的请求始终路由到同一目标。
|
||||
//
|
||||
// 它将来自同一客户端 IP 的请求始终路由到同一目标,实现会话保持。
|
||||
// 适合需要会话粘性的场景,如无状态后端的有状态请求处理。
|
||||
type IPHash struct{}
|
||||
|
||||
// NewIPHash 创建一个新的 IP 哈希负载均衡器。
|
||||
//
|
||||
// 返回值:
|
||||
// - 初始化的 IPHash 实例
|
||||
func NewIPHash() *IPHash {
|
||||
return &IPHash{}
|
||||
}
|
||||
@ -219,8 +248,16 @@ func (i *IPHash) SelectByIP(targets []*Target, clientIP string) *Target {
|
||||
return healthy[idx]
|
||||
}
|
||||
|
||||
// filterHealthy 返回仅包含健康目标的新切片。
|
||||
// 这是负载均衡实现使用的辅助函数。
|
||||
// filterHealthy 从目标列表中筛选出所有健康的目标,返回新切片。
|
||||
//
|
||||
// 该函数遍历输入切片,仅保留 Healthy 状态为 true 的目标。
|
||||
// 返回的切片容量与输入相同,避免多次内存分配。
|
||||
//
|
||||
// 参数:
|
||||
// - targets: 原始目标列表
|
||||
//
|
||||
// 返回值:
|
||||
// - 仅包含健康目标的新切片,当无健康目标时返回空切片
|
||||
func filterHealthy(targets []*Target) []*Target {
|
||||
healthy := make([]*Target, 0, len(targets))
|
||||
for _, t := range targets {
|
||||
@ -243,8 +280,17 @@ func DecrementConnections(t *Target) {
|
||||
atomic.AddInt64(&t.Connections, -1)
|
||||
}
|
||||
|
||||
// filterHealthyAndExclude 返回仅包含健康目标且不在排除列表中的新切片。
|
||||
// 这是 SelectExcluding 使用的辅助函数。
|
||||
// filterHealthyAndExclude 从目标列表中筛选出健康且不在排除列表中的目标,返回新切片。
|
||||
//
|
||||
// 该函数用于 SelectExcluding 方法,同时处理健康过滤和排除逻辑。
|
||||
// 排除判断基于目标的 URL 进行匹配。
|
||||
//
|
||||
// 参数:
|
||||
// - targets: 原始目标列表
|
||||
// - excluded: 需要排除的目标列表(nil 值会被安全忽略)
|
||||
//
|
||||
// 返回值:
|
||||
// - 包含健康且非排除目标的新切片
|
||||
func filterHealthyAndExclude(targets []*Target, excluded []*Target) []*Target {
|
||||
// 构建排除集合(使用 URL 作为键)
|
||||
excludeSet := make(map[string]bool, len(excluded))
|
||||
|
||||
@ -1,4 +1,21 @@
|
||||
// Package loadbalance 提供负载均衡算法的基准测试。
|
||||
//
|
||||
// 该文件测试各负载均衡算法在不同规模下的性能表现,包括:
|
||||
// - 轮询算法(RoundRobin)在不同目标数量下的吞吐量
|
||||
// - 加权轮询算法(WeightedRoundRobin)在等权重/变权重场景下的性能
|
||||
// - 一致性哈希算法(ConsistentHash)在不同虚拟节点数下的选择和重建开销
|
||||
// - 一致性哈希排除选择(SelectExcludingByKey)的性能
|
||||
// - 最少连接(LeastConnections)和 IP 哈希(IPHash)的基准性能
|
||||
// - 所有算法的横向对比测试
|
||||
//
|
||||
// 主要用途:
|
||||
// 用于评估负载均衡算法在不同规模下的性能特征,指导算法选型和参数调优。
|
||||
//
|
||||
// 注意事项:
|
||||
// - 基准测试使用并行模式(RunParallel),结果受 CPU 核心数影响
|
||||
// - 测试前需确保目标均为健康状态
|
||||
//
|
||||
// 作者:xfy
|
||||
package loadbalance
|
||||
|
||||
import (
|
||||
@ -7,7 +24,13 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// generateTargets 生成指定数量的健康目标用于基准测试。
|
||||
// generateTargets 生成指定数量的健康目标,用于基准测试。
|
||||
//
|
||||
// 参数:
|
||||
// - count: 目标数量
|
||||
//
|
||||
// 返回值:
|
||||
// - 包含 count 个健康目标的切片,权重均为 1
|
||||
func generateTargets(count int) []*Target {
|
||||
targets := make([]*Target, count)
|
||||
for i := 0; i < count; i++ {
|
||||
@ -20,7 +43,14 @@ func generateTargets(count int) []*Target {
|
||||
return targets
|
||||
}
|
||||
|
||||
// generateWeightedTargets 生成带权重的目标用于基准测试。
|
||||
// generateWeightedTargets 生成带指定权重的健康目标,用于基准测试。
|
||||
//
|
||||
// 参数:
|
||||
// - count: 目标数量
|
||||
// - weights: 权重列表,长度不足时默认权重为 1
|
||||
//
|
||||
// 返回值:
|
||||
// - 包含 count 个健康目标的切片,按 weights 分配权重
|
||||
func generateWeightedTargets(count int, weights []int) []*Target {
|
||||
targets := make([]*Target, count)
|
||||
for i := 0; i < count; i++ {
|
||||
@ -37,7 +67,12 @@ func generateWeightedTargets(count int, weights []int) []*Target {
|
||||
return targets
|
||||
}
|
||||
|
||||
// BenchmarkRoundRobinSelect 基准测试轮询算法。
|
||||
// BenchmarkRoundRobinSelect 基准测试轮询算法在不同目标数量下的性能。
|
||||
//
|
||||
// 测试用例:
|
||||
// - 3 个目标:小规模场景
|
||||
// - 50 个目标:中等规模场景
|
||||
// - 200 个目标:大规模场景
|
||||
func BenchmarkRoundRobinSelect(b *testing.B) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
@ -63,7 +98,12 @@ func BenchmarkRoundRobinSelect(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkWeightedRoundRobin 基准测试加权轮询算法。
|
||||
// BenchmarkWeightedRoundRobin 基准测试加权轮询算法在等权重和变权重场景下的性能。
|
||||
//
|
||||
// 测试用例:
|
||||
// - 等权重:所有目标权重相同(1:1:1)
|
||||
// - 变权重:目标权重差异较大(1:5:10)
|
||||
// - 不同规模:3/50/200 个目标
|
||||
func BenchmarkWeightedRoundRobin(b *testing.B) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
@ -92,7 +132,13 @@ func BenchmarkWeightedRoundRobin(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkConsistentHashSelect 基准测试一致性哈希算法。
|
||||
// BenchmarkConsistentHashSelect 基准测试一致性哈希算法在不同虚拟节点数下的选择性能。
|
||||
//
|
||||
// 测试用例:
|
||||
// - 10 个目标 + 50/150/200 个虚拟节点
|
||||
// - 50/100 个目标 + 150 个虚拟节点
|
||||
//
|
||||
// 验证虚拟节点数量对哈希环遍历性能的影响。
|
||||
func BenchmarkConsistentHashSelect(b *testing.B) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
@ -187,6 +233,10 @@ func BenchmarkConsistentHashSelectExcluding(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkLeastConnSelect 基准测试最少连接算法在不同目标数量下的性能。
|
||||
//
|
||||
// 测试用例:
|
||||
// - 3/50/200 个目标,连接数按递增方式分配(模拟负载差异)
|
||||
func BenchmarkLeastConnSelect(b *testing.B) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
|
||||
@ -17,7 +17,16 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// createHealthyTarget 创建一个带有健康状态的目标(辅助函数)
|
||||
// createHealthyTarget 创建并返回一个指定 URL 和健康状态的 Target 实例。
|
||||
//
|
||||
// 该辅助函数简化测试中 Target 的创建过程,自动初始化 Healthy 状态。
|
||||
//
|
||||
// 参数:
|
||||
// - url: 目标 URL 地址
|
||||
// - healthy: 健康状态,true 表示健康,false 表示不健康
|
||||
//
|
||||
// 返回值:
|
||||
// - 初始化完成的 Target 指针
|
||||
func createHealthyTarget(url string, healthy bool) *Target {
|
||||
t := &Target{URL: url}
|
||||
t.Healthy.Store(healthy)
|
||||
|
||||
@ -176,13 +176,20 @@ func (bl *BodyLimit) Process(next fasthttp.RequestHandler) fasthttp.RequestHandl
|
||||
}
|
||||
|
||||
// limitedBodyReader 包装请求体读取器以限制最大读取字节数。
|
||||
//
|
||||
// 当读取的字节数超过限制时,返回错误并在上下文中设置 413 状态码。
|
||||
type limitedBodyReader struct {
|
||||
// original 原始读取器
|
||||
original interface {
|
||||
Read(p []byte) (n int, err error)
|
||||
}
|
||||
// ctx 请求上下文,用于设置错误响应
|
||||
ctx *fasthttp.RequestCtx
|
||||
// limit 最大允许读取的字节数
|
||||
limit int64
|
||||
// read 已读取的字节数
|
||||
read int64
|
||||
// done 是否已达到限制
|
||||
done bool
|
||||
}
|
||||
|
||||
@ -269,13 +276,15 @@ func ParseSize(sizeStr string) (int64, error) {
|
||||
return int64(value * multiplier), nil
|
||||
}
|
||||
|
||||
// FormatSize 将字节数格式化为人类可读的字符串。
|
||||
// formatSize 将字节数格式化为人类可读的字符串。
|
||||
//
|
||||
// 根据大小自动选择合适的单位(b、kb、mb、gb)。
|
||||
//
|
||||
// 参数:
|
||||
// - size: 字节数
|
||||
//
|
||||
// 返回值:
|
||||
// - string: 格式化后的字符串,如 "1mb", "10kb" 等
|
||||
// - string: 格式化后的字符串,如 "1.00mb"、"10.00kb"
|
||||
func formatSize(size int64) string {
|
||||
const (
|
||||
KB = 1024
|
||||
|
||||
@ -1,15 +1,25 @@
|
||||
// Package compression 提供 gzip_static 预压缩文件支持。
|
||||
// Package compression 提供预压缩静态文件支持。
|
||||
//
|
||||
// 该文件实现预压缩文件的检测和发送,避免实时压缩开销。
|
||||
//
|
||||
// 主要功能:
|
||||
// - 检测 .gz 和 .br 预压缩文件
|
||||
// - 按优先级选择编码(brotli > gzip)
|
||||
// - 客户端 Accept-Encoding 协商
|
||||
// - 扩展名过滤,仅对文本类资源启用预压缩
|
||||
//
|
||||
// 主要用途:
|
||||
//
|
||||
// 用于发送预压缩的 .gz 文件,减少服务器 CPU 开销,提升响应速度。
|
||||
// 用于发送预压缩的静态资源文件,减少服务器 CPU 开销,提升响应速度。
|
||||
//
|
||||
// 使用场景:
|
||||
// - 静态资源预先压缩(如 CSS、JS、HTML 文件)
|
||||
// - 构建时生成 .gz 文件
|
||||
// - 运行时直接发送预压缩文件
|
||||
// - 构建时生成 .gz 或 .br 文件
|
||||
// - 运行时直接发送预压缩文件,避免实时压缩
|
||||
//
|
||||
// 注意事项:
|
||||
// - 需要客户端支持对应的 Content-Encoding
|
||||
// - 预压缩文件需要与源文件放在同一目录
|
||||
//
|
||||
// 作者:xfy
|
||||
package compression
|
||||
@ -23,23 +33,32 @@ import (
|
||||
"rua.plus/lolly/internal/mimeutil"
|
||||
)
|
||||
|
||||
// GzipStatic 预压缩文件支持。
|
||||
// GzipStatic 预压缩文件支持中间件。
|
||||
//
|
||||
// 检查是否存在预压缩的 .gz 或 .br 文件,如果存在且客户端支持对应编码,
|
||||
// 则直接发送,避免实时压缩的 CPU 开销。
|
||||
type GzipStatic struct {
|
||||
root string
|
||||
// root 静态文件根目录路径
|
||||
root string
|
||||
// precompressedExtensions 预压缩文件扩展名列表(如 .br, .gz)
|
||||
precompressedExtensions []string
|
||||
extensions []string
|
||||
enabled bool
|
||||
// extensions 支持预压缩的源文件扩展名列表(如 .html, .css, .js)
|
||||
extensions []string
|
||||
// enabled 是否启用预压缩支持
|
||||
enabled bool
|
||||
}
|
||||
|
||||
// NewGzipStatic 创建预压缩文件处理器。
|
||||
//
|
||||
// 根据配置创建预压缩文件处理器实例,设置默认扩展名列表。
|
||||
//
|
||||
// 参数:
|
||||
// - enabled: 是否启用预压缩支持
|
||||
// - root: 静态文件根目录
|
||||
// - extensions: 支持预压缩的文件扩展名,为空则使用默认值
|
||||
// - root: 静态文件根目录路径
|
||||
// - extensions: 支持预压缩的文件扩展名列表,为空则使用默认值
|
||||
//
|
||||
// 返回值:
|
||||
// - *GzipStatic: 创建的预压缩文件处理器
|
||||
func NewGzipStatic(enabled bool, root string, extensions []string) *GzipStatic {
|
||||
if len(extensions) == 0 {
|
||||
extensions = []string{".html", ".css", ".js", ".json", ".xml", ".svg", ".txt"}
|
||||
@ -117,7 +136,13 @@ func (g *GzipStatic) ServeFile(ctx *fasthttp.RequestCtx, filePath string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// matchExtension 检查文件扩展名是否匹配。
|
||||
// matchExtension 检查文件扩展名是否在支持的预压缩扩展名列表中。
|
||||
//
|
||||
// 参数:
|
||||
// - filePath: 文件路径
|
||||
//
|
||||
// 返回值:
|
||||
// - bool: true 表示文件扩展名匹配支持列表
|
||||
func (g *GzipStatic) matchExtension(filePath string) bool {
|
||||
ext := strings.ToLower(filepath.Ext(filePath))
|
||||
for _, e := range g.extensions {
|
||||
@ -129,18 +154,32 @@ func (g *GzipStatic) matchExtension(filePath string) bool {
|
||||
}
|
||||
|
||||
// Enabled 返回是否启用预压缩。
|
||||
//
|
||||
// 返回值:
|
||||
// - bool: true 表示已启用预压缩支持
|
||||
func (g *GzipStatic) Enabled() bool {
|
||||
return g.enabled
|
||||
}
|
||||
|
||||
// Extensions 返回支持的扩展名列表。
|
||||
// Extensions 返回支持的源文件扩展名列表。
|
||||
//
|
||||
// 返回值:
|
||||
// - []string: 支持预压缩的源文件扩展名列表
|
||||
func (g *GzipStatic) Extensions() []string {
|
||||
return g.extensions
|
||||
}
|
||||
|
||||
// supportsEncoding 检查客户端是否支持指定编码。
|
||||
//
|
||||
// 简单检查,忽略 q-value,固定优先级由遍历顺序决定。
|
||||
// 通过检查 Accept-Encoding 请求头中是否包含指定编码名称。
|
||||
// 忽略 q-value(质量因子),固定优先级由遍历顺序决定。
|
||||
//
|
||||
// 参数:
|
||||
// - acceptEncoding: Accept-Encoding 请求头值
|
||||
// - ext: 预压缩文件扩展名(如 ".br"、".gz")
|
||||
//
|
||||
// 返回值:
|
||||
// - bool: true 表示客户端支持该编码
|
||||
func supportsEncoding(acceptEncoding []byte, ext string) bool {
|
||||
if len(acceptEncoding) == 0 {
|
||||
return false
|
||||
|
||||
@ -26,7 +26,7 @@ import (
|
||||
//
|
||||
// 拦截 HTTP 错误响应(4xx 和 5xx),并使用预加载的自定义错误页面内容替换响应。
|
||||
type ErrorIntercept struct {
|
||||
// manager 错误页面管理器
|
||||
// manager 错误页面管理器,用于获取预加载的错误页面内容
|
||||
manager *handler.ErrorPageManager
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,21 @@
|
||||
// Package rewrite 提供 URL 重写中间件,支持正则表达式匹配和多种重写标志。
|
||||
//
|
||||
// 该文件包含 URL 重写相关的核心功能,包括:
|
||||
// - 正则表达式匹配和替换
|
||||
// - 多种重写标志(last、redirect、permanent、break)
|
||||
// - 变量展开支持
|
||||
// - ReDoS 安全防护
|
||||
//
|
||||
// 主要用途:
|
||||
//
|
||||
// 用于实现类似 nginx rewrite 模块的 URL 重写功能,支持灵活的 URL 变换规则。
|
||||
//
|
||||
// 注意事项:
|
||||
// - 重写规则按配置顺序执行,FlagLast 规则会重新从第一条规则开始匹配
|
||||
// - 最大迭代次数限制防止无限循环
|
||||
// - 正则表达式会进行安全性检查,防止 ReDoS 攻击
|
||||
//
|
||||
// 作者:xfy
|
||||
package rewrite
|
||||
|
||||
import (
|
||||
@ -18,17 +35,29 @@ const MaxRewriteIterations = 10
|
||||
type Flag int
|
||||
|
||||
const (
|
||||
// FlagLast 继续匹配其他规则(nginx行为:重新从第一条规则开始匹配)。
|
||||
// FlagLast 继续匹配其他规则(nginx 行为:重新从第一条规则开始匹配)。
|
||||
// 匹配到规则后会重新从第一条规则开始遍历,用于多规则链式重写。
|
||||
FlagLast Flag = iota
|
||||
// FlagRedirect 返回 302 临时重定向。
|
||||
// 客户端收到 302 响应后重新请求新 URL,不会继续匹配后续规则。
|
||||
FlagRedirect
|
||||
// FlagPermanent 返回 301 永久重定向。
|
||||
// 客户端收到 301 响应后永久重定向到新 URL,不会继续匹配后续规则。
|
||||
FlagPermanent
|
||||
// FlagBreak 停止匹配规则。
|
||||
// 修改请求路径后终止重写流程,直接进入下一个处理器。
|
||||
FlagBreak
|
||||
)
|
||||
|
||||
// parseFlag 解析配置中的标志字符串。
|
||||
// parseFlag 解析配置中的标志字符串为 Flag 枚举值。
|
||||
//
|
||||
// 将配置字符串转换为对应的 Flag 类型,用于控制重写后的行为。
|
||||
//
|
||||
// 参数:
|
||||
// - s: 标志字符串,支持 "last"、"redirect"、"permanent"、"break"
|
||||
//
|
||||
// 返回值:
|
||||
// - Flag: 对应的标志枚举值,无法识别时返回 FlagLast
|
||||
func parseFlag(s string) Flag {
|
||||
switch strings.ToLower(s) {
|
||||
case "redirect":
|
||||
@ -44,11 +73,11 @@ func parseFlag(s string) Flag {
|
||||
|
||||
// Rule 编译后的重写规则。
|
||||
type Rule struct {
|
||||
// pattern 正则匹配模式
|
||||
// pattern 正则匹配模式,用于匹配请求路径
|
||||
pattern *regexp.Regexp
|
||||
// replacement 替换字符串,支持 $1、$2 等捕获组
|
||||
// replacement 替换字符串,支持 $1、$2 等捕获组和变量展开
|
||||
replacement string
|
||||
// flag 执行标志,控制重写后行为
|
||||
// flag 执行标志,控制重写后的行为(last/redirect/permanent/break)
|
||||
flag Flag
|
||||
}
|
||||
|
||||
@ -58,7 +87,16 @@ type Middleware struct {
|
||||
rules []Rule
|
||||
}
|
||||
|
||||
// New 创建重写中间件。
|
||||
// New 创建 URL 重写中间件。
|
||||
//
|
||||
// 编译配置中的重写规则,验证正则表达式安全性后返回中间件实例。
|
||||
//
|
||||
// 参数:
|
||||
// - rules: 重写规则配置列表,包含模式、替换和标志
|
||||
//
|
||||
// 返回值:
|
||||
// - *Middleware: 编译后的重写中间件
|
||||
// - error: 正则表达式无效或不安全时返回错误
|
||||
func New(rules []config.RewriteRule) (*Middleware, error) {
|
||||
compiled := make([]Rule, 0, len(rules))
|
||||
for _, r := range rules {
|
||||
@ -83,6 +121,13 @@ func New(rules []config.RewriteRule) (*Middleware, error) {
|
||||
// validateRegexSafety 验证正则表达式的安全性,防止 ReDoS 攻击。
|
||||
//
|
||||
// 检测可能导致灾难性回溯的危险模式,如嵌套量词。
|
||||
// 限制模式最大长度 1000 字符。
|
||||
//
|
||||
// 参数:
|
||||
// - pattern: 待验证的正则表达式字符串
|
||||
//
|
||||
// 返回值:
|
||||
// - error: 检测到不安全模式时返回错误
|
||||
func validateRegexSafety(pattern string) error {
|
||||
// 限制模式长度
|
||||
if len(pattern) > 1000 {
|
||||
@ -112,6 +157,15 @@ func (m *Middleware) Name() string {
|
||||
}
|
||||
|
||||
// Process 应用重写规则。
|
||||
//
|
||||
// 对请求路径执行正则匹配和替换,根据标志控制后续行为。
|
||||
// 支持迭代重写(FlagLast 会重新从第一条规则开始匹配)。
|
||||
//
|
||||
// 参数:
|
||||
// - next: 下一个请求处理器
|
||||
//
|
||||
// 返回值:
|
||||
// - fasthttp.RequestHandler: 包装后的请求处理器
|
||||
func (m *Middleware) Process(next fasthttp.RequestHandler) fasthttp.RequestHandler {
|
||||
return func(ctx *fasthttp.RequestCtx) {
|
||||
path := string(ctx.Path())
|
||||
@ -123,7 +177,7 @@ func (m *Middleware) Process(next fasthttp.RequestHandler) fasthttp.RequestHandl
|
||||
ruleIndex := 0
|
||||
|
||||
for ruleIndex < len(m.rules) {
|
||||
// 检查迭代次数是否超过限制(在任何重写操作之前检查)
|
||||
// 步骤1: 检查迭代次数是否超过限制(防止无限循环)
|
||||
if iterationCount >= MaxRewriteIterations {
|
||||
ctx.Error("Internal Server Error", fasthttp.StatusInternalServerError)
|
||||
return
|
||||
@ -132,23 +186,26 @@ func (m *Middleware) Process(next fasthttp.RequestHandler) fasthttp.RequestHandl
|
||||
rule := m.rules[ruleIndex]
|
||||
|
||||
if rule.pattern.MatchString(path) {
|
||||
// 执行正则替换
|
||||
// 步骤2: 执行正则替换
|
||||
newPath := rule.pattern.ReplaceAllString(path, rule.replacement)
|
||||
|
||||
// 对替换结果进行变量展开
|
||||
// 步骤3: 对替换结果进行变量展开
|
||||
vc := variable.NewContext(ctx)
|
||||
newPath = vc.Expand(newPath)
|
||||
variable.ReleaseContext(vc)
|
||||
|
||||
// 步骤4: 根据标志决定后续行为
|
||||
switch rule.flag {
|
||||
case FlagRedirect:
|
||||
// 302 临时重定向
|
||||
ctx.Redirect(newPath, fasthttp.StatusFound)
|
||||
return
|
||||
case FlagPermanent:
|
||||
// 301 永久重定向
|
||||
ctx.Redirect(newPath, fasthttp.StatusMovedPermanently)
|
||||
return
|
||||
case FlagBreak:
|
||||
// 修改路径后停止匹配,不增加迭代计数(不触发循环检测)
|
||||
// 修改路径后停止匹配,直接进入处理器
|
||||
ctx.Request.SetRequestURI(newPath)
|
||||
next(ctx)
|
||||
return
|
||||
@ -165,7 +222,7 @@ func (m *Middleware) Process(next fasthttp.RequestHandler) fasthttp.RequestHandl
|
||||
ruleIndex++
|
||||
}
|
||||
|
||||
// 如果路径被修改过,需要重新设置
|
||||
// 步骤5: 如果路径被修改过,需要重新设置
|
||||
if path != originalPath {
|
||||
ctx.Request.SetRequestURI(path)
|
||||
}
|
||||
@ -175,6 +232,9 @@ func (m *Middleware) Process(next fasthttp.RequestHandler) fasthttp.RequestHandl
|
||||
}
|
||||
|
||||
// Rules 返回编译后的规则列表(用于调试)。
|
||||
//
|
||||
// 返回值:
|
||||
// - []Rule: 编译后的重写规则列表
|
||||
func (m *Middleware) Rules() []Rule {
|
||||
return m.rules
|
||||
}
|
||||
|
||||
@ -47,9 +47,15 @@ import (
|
||||
)
|
||||
|
||||
// AuthRequest 实现外部认证子请求中间件。
|
||||
//
|
||||
// 将认证委托给外部服务,根据认证服务的响应状态码决定是否允许原请求继续。
|
||||
// 支持完整 URL 和相对路径两种认证服务地址。
|
||||
type AuthRequest struct {
|
||||
// client 用于向认证服务发送子请求的 HTTP 客户端
|
||||
client *fasthttp.HostClient
|
||||
// config 认证子请求配置
|
||||
config config.AuthRequestConfig
|
||||
// mu 读写锁,保护并发访问 client 和 config
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
@ -107,12 +113,24 @@ func NewAuthRequest(cfg config.AuthRequestConfig) (*AuthRequest, error) {
|
||||
return ar, nil
|
||||
}
|
||||
|
||||
// isFullURL 检查 URI 是否为完整 URL。
|
||||
// isFullURL 检查 URI 是否为完整 URL(以 http:// 或 https:// 开头)。
|
||||
//
|
||||
// 参数:
|
||||
// - uri: 待检查的 URI 字符串
|
||||
//
|
||||
// 返回值:
|
||||
// - bool: true 表示为完整 URL
|
||||
func isFullURL(uri string) bool {
|
||||
return strings.HasPrefix(uri, "http://") || strings.HasPrefix(uri, "https://")
|
||||
}
|
||||
|
||||
// initClient 初始化用于认证子请求的 HTTP 客户端。
|
||||
//
|
||||
// 解析认证服务 URL 并创建独立连接池的 HTTP 客户端。
|
||||
// 调用者需持有写锁。
|
||||
//
|
||||
// 返回值:
|
||||
// - error: URL 解析失败时返回错误
|
||||
func (a *AuthRequest) initClient() error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -182,12 +200,23 @@ func parseAuthURL(url string) (string, bool, error) {
|
||||
}
|
||||
|
||||
// Name 返回中间件名称。
|
||||
//
|
||||
// 返回值:
|
||||
// - string: 中间件标识名 "auth_request"
|
||||
func (a *AuthRequest) Name() string {
|
||||
return "auth_request"
|
||||
}
|
||||
|
||||
// Process 实现中间件处理逻辑。
|
||||
//
|
||||
// 向认证服务发送子请求,根据响应决定是否允许原请求继续。
|
||||
// 配置未启用时直接透传。
|
||||
//
|
||||
// 参数:
|
||||
// - next: 下一个请求处理器
|
||||
//
|
||||
// 返回值:
|
||||
// - fasthttp.RequestHandler: 包装后的请求处理器
|
||||
func (a *AuthRequest) Process(next fasthttp.RequestHandler) fasthttp.RequestHandler {
|
||||
if !a.config.Enabled {
|
||||
return next
|
||||
@ -294,7 +323,17 @@ func (a *AuthRequest) doAuthRequest(ctx *fasthttp.RequestCtx) (bool, int, error)
|
||||
}
|
||||
}
|
||||
|
||||
// expandVars 展开字符串中的变量。
|
||||
// expandVars 展开字符串中的变量(如 $request_uri)。
|
||||
//
|
||||
// 使用 variable 包展开请求上下文中的变量引用。
|
||||
// 如果字符串中不包含变量标记,则直接返回原字符串。
|
||||
//
|
||||
// 参数:
|
||||
// - ctx: FastHTTP 请求上下文
|
||||
// - template: 包含变量引用的模板字符串
|
||||
//
|
||||
// 返回值:
|
||||
// - string: 展开后的字符串
|
||||
func (a *AuthRequest) expandVars(ctx *fasthttp.RequestCtx, template string) string {
|
||||
if template == "" {
|
||||
return ""
|
||||
|
||||
@ -152,6 +152,19 @@ type SlidingWindowLimiterWrapper struct {
|
||||
}
|
||||
|
||||
// NewSlidingWindowLimiterWrapper 创建滑动窗口限流器包装。
|
||||
//
|
||||
// 该函数基于配置创建一个滑动窗口限流器,并将其包装为实现
|
||||
// middleware.Middleware 接口的结构体。滑动窗口模式提供更精确
|
||||
// 的限流控制,适用于需要平滑流量分布的场景。
|
||||
//
|
||||
// 参数:
|
||||
// - cfg: 限流配置,包含速率和键类型
|
||||
// - window: 滑动窗口持续时间
|
||||
// - precise: 是否使用精确模式(true)或粗略模式(false)
|
||||
//
|
||||
// 返回值:
|
||||
// - *SlidingWindowLimiterWrapper: 限流器包装实例
|
||||
// - error: 键类型无效时返回错误
|
||||
func NewSlidingWindowLimiterWrapper(cfg *config.RateLimitConfig, window time.Duration, precise bool) (*SlidingWindowLimiterWrapper, error) {
|
||||
keyFunc, err := parseKeyFunc(cfg.Key)
|
||||
if err != nil {
|
||||
@ -626,15 +639,18 @@ func (m *connLimiterMiddleware) Process(next fasthttp.RequestHandler) fasthttp.R
|
||||
}
|
||||
|
||||
// 连接数原子操作辅助函数
|
||||
|
||||
// loadInt64 原子加载 int64 值。
|
||||
func loadInt64(ptr *int64) int64 {
|
||||
return atomic.LoadInt64(ptr)
|
||||
}
|
||||
|
||||
// addInt64 原子添加 int64 增量。
|
||||
|
||||
func addInt64(ptr *int64, delta int64) {
|
||||
atomic.AddInt64(ptr, delta)
|
||||
}
|
||||
|
||||
// 验证接口实现
|
||||
// 验证接口实现
|
||||
var (
|
||||
_ middleware.Middleware = (*RateLimiter)(nil)
|
||||
|
||||
@ -7,8 +7,14 @@
|
||||
// 用于更精确地控制请求速率,相比令牌桶算法提供更平滑的限流效果。
|
||||
//
|
||||
// 算法特点:
|
||||
// - 近似模式:O(1) 时间复杂度,内存占用低
|
||||
// - 精确模式:O(n) 时间复杂度,限流更精确
|
||||
// - 近似模式:O(1) 时间复杂度,内存占用低,适合高并发场景
|
||||
// - 精确模式:O(n) 时间复杂度,记录每个请求时间戳,限流更精确
|
||||
// - 分段锁结构:采用 16 个分段锁桶,减少锁竞争
|
||||
//
|
||||
// 注意事项:
|
||||
// - 近似模式使用时间戳数组进行估算,可能存在精度偏差
|
||||
// - 精确模式在限流键较多时内存占用较大
|
||||
// - 建议定期调用 Cleanup 清理过期计数器
|
||||
//
|
||||
// 作者:xfy
|
||||
package security
|
||||
@ -20,20 +26,27 @@ import (
|
||||
)
|
||||
|
||||
// limiterBucket 分段锁桶,每个桶持有部分键的计数器。
|
||||
//
|
||||
// 使用分段锁减少全局锁竞争,提高并发性能。
|
||||
type limiterBucket struct {
|
||||
// counters 限流键到计数器的映射
|
||||
counters map[string]*windowCounter
|
||||
// mu 读写锁,保护 counters 的并发访问
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// SlidingWindowLimiter 滑动窗口限流器。
|
||||
//
|
||||
// 使用滑动窗口算法限制请求速率,支持近似和精确两种模式。
|
||||
// 采用16个分段锁桶结构,减少锁竞争,提高并发性能。
|
||||
// 采用 16 个分段锁桶结构,减少锁竞争,提高并发性能。
|
||||
type SlidingWindowLimiter struct {
|
||||
// buckets 分段锁桶数组,固定 16 个桶
|
||||
buckets [16]*limiterBucket
|
||||
// window 滑动窗口大小
|
||||
window time.Duration
|
||||
// limit 窗口内最大请求数
|
||||
limit int
|
||||
// precise 是否使用精确模式
|
||||
precise bool
|
||||
}
|
||||
|
||||
@ -51,19 +64,29 @@ func (s *SlidingWindowLimiter) getBucket(key string) *limiterBucket {
|
||||
return s.buckets[h.Sum64()%16]
|
||||
}
|
||||
|
||||
// windowCounter 窗口计数器。
|
||||
// windowCounter 滑动窗口计数器。
|
||||
//
|
||||
// 记录窗口内的请求时间戳,用于精确或近似限流计算。
|
||||
type windowCounter struct {
|
||||
// timestamps 请求时间戳列表
|
||||
timestamps []time.Time
|
||||
// count 当前窗口内的请求计数
|
||||
count int64
|
||||
// mu 互斥锁,保护并发访问
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// NewSlidingWindowLimiter 创建滑动窗口限流器。
|
||||
//
|
||||
// 初始化 16 个分段锁桶并配置窗口参数。
|
||||
//
|
||||
// 参数:
|
||||
// - window: 窗口大小(如 1s、1m)
|
||||
// - window: 滑动窗口大小(如 1s、1m)
|
||||
// - limit: 窗口内最大请求数
|
||||
// - precise: 是否使用精确模式
|
||||
// - precise: 是否使用精确模式(true 为精确,false 为近似)
|
||||
//
|
||||
// 返回值:
|
||||
// - *SlidingWindowLimiter: 创建的限流器实例
|
||||
func NewSlidingWindowLimiter(window time.Duration, limit int, precise bool) *SlidingWindowLimiter {
|
||||
s := &SlidingWindowLimiter{
|
||||
window: window,
|
||||
|
||||
@ -1,4 +1,18 @@
|
||||
// Package mimeutil 提供 MIME 类型检测工具。
|
||||
// Package mimeutil 提供 MIME 类型检测工具函数。
|
||||
//
|
||||
// 该文件实现了基于文件扩展名的 MIME 类型检测,
|
||||
// 补充 Go 标准库 mime 包中缺失或错误的映射。
|
||||
//
|
||||
// 主要功能:
|
||||
// - 本地 MIME 映射:避免 mime.AddExtensionType 的全局副作用
|
||||
// - 自动回退:未覆盖的扩展名回退到标准库
|
||||
// - 大小写处理:自动将扩展名转为小写再查找
|
||||
//
|
||||
// 注意事项:
|
||||
// - 使用包本地映射而非全局修改,确保多线程安全
|
||||
// - 部分扩展名(如 .otf、.webm)Go 标准库返回错误类型,已在此纠正
|
||||
//
|
||||
// 作者:xfy
|
||||
package mimeutil
|
||||
|
||||
import (
|
||||
|
||||
@ -1,8 +1,12 @@
|
||||
// Package netutil 提供网络相关的通用工具函数。
|
||||
//
|
||||
// 该包包含 URL 解析、客户端 IP 提取等网络操作的工具函数,
|
||||
// 该文件包含 URL 解析和主机地址提取的工具函数,
|
||||
// 供 proxy、middleware、server 等模块共享使用。
|
||||
//
|
||||
// 主要功能:
|
||||
// - ParseTargetURL: 解析目标 URL,提取主机地址和 TLS 标志
|
||||
// - ExtractHost: 简化版 URL 解析,始终添加默认端口
|
||||
//
|
||||
// 作者:xfy
|
||||
package netutil
|
||||
|
||||
|
||||
@ -125,6 +125,19 @@ type DNSCacheEntry struct {
|
||||
}
|
||||
|
||||
// New 创建新的 DNS 解析器。
|
||||
//
|
||||
// 该函数根据配置创建一个 DNSResolver 实例。如果配置中
|
||||
// Enabled 为 false,则返回空操作的 noopResolver。
|
||||
// 对于启用的解析器,会自动设置合理的默认值:
|
||||
// - Valid: 30 秒(缓存有效期)
|
||||
// - Timeout: 5 秒(查询超时)
|
||||
// - IPv4: 默认启用(除非显式禁用)
|
||||
//
|
||||
// 参数:
|
||||
// - cfg: 解析器配置,包含 DNS 服务器地址、缓存大小等
|
||||
//
|
||||
// 返回值:
|
||||
// - Resolver: DNS 解析器接口实现,禁用时返回 noopResolver
|
||||
func New(cfg *config.ResolverConfig) Resolver {
|
||||
if !cfg.Enabled {
|
||||
return &noopResolver{}
|
||||
|
||||
@ -16,6 +16,15 @@ import (
|
||||
type UpgradeManager struct{}
|
||||
|
||||
// NewUpgradeManager 创建空的升级管理器 stub。
|
||||
//
|
||||
// Windows 平台不支持优雅升级(热升级)功能,该函数返回
|
||||
// 一个空的 UpgradeManager 实例,所有方法均为空操作。
|
||||
//
|
||||
// 参数:
|
||||
// - server: 服务器实例(Windows 上不使用)
|
||||
//
|
||||
// 返回值:
|
||||
// - *UpgradeManager: 空操作的管理器实例
|
||||
func NewUpgradeManager(server *Server) *UpgradeManager {
|
||||
return &UpgradeManager{}
|
||||
}
|
||||
|
||||
@ -81,6 +81,15 @@ type OCSPConfig struct {
|
||||
}
|
||||
|
||||
// DefaultOCSPConfig 返回默认的 OCSP 配置。
|
||||
//
|
||||
// 该函数提供一组适用于大多数生产环境的默认 OCSP Stapling 设置:
|
||||
// - Enabled: true(默认启用 OCSP Stapling)
|
||||
// - RefreshInterval: 1 小时(响应刷新间隔)
|
||||
// - Timeout: 10 秒(HTTP 请求超时)
|
||||
// - MaxRetries: 3(失败重试次数)
|
||||
//
|
||||
// 返回值:
|
||||
// - *OCSPConfig: 包含默认值的 OCSP 配置指针
|
||||
func DefaultOCSPConfig() *OCSPConfig {
|
||||
return &OCSPConfig{
|
||||
Enabled: true,
|
||||
|
||||
@ -377,7 +377,7 @@ func (m *TLSManager) getConfigForClientWithOCSP(hello *tls.ClientHelloInfo) (*tl
|
||||
baseCfg = m.defaultCfg
|
||||
}
|
||||
|
||||
// If no OCSP manager or no certificates, return base config
|
||||
// 无 OCSP 管理器或无证书时,返回基础配置
|
||||
if m.ocspManager == nil || len(baseCfg.Certificates) == 0 {
|
||||
return baseCfg, nil
|
||||
}
|
||||
@ -385,7 +385,7 @@ func (m *TLSManager) getConfigForClientWithOCSP(hello *tls.ClientHelloInfo) (*tl
|
||||
// 创建配置副本并附加 OCSP 响应
|
||||
cfgCopy := baseCfg.Clone()
|
||||
|
||||
// Attach OCSP response to the certificate
|
||||
// 将 OCSP 响应附加到证书
|
||||
cert := &cfgCopy.Certificates[0]
|
||||
if len(cert.Certificate) > 0 {
|
||||
// 解析叶子证书以获取序列号
|
||||
@ -394,7 +394,7 @@ func (m *TLSManager) getConfigForClientWithOCSP(hello *tls.ClientHelloInfo) (*tl
|
||||
serial := leafCert.SerialNumber.String()
|
||||
ocspResp := m.ocspManager.GetOCSPResponse(serial)
|
||||
if ocspResp != nil {
|
||||
// Attach OCSP response to certificate
|
||||
// 将 OCSP 响应附加到证书
|
||||
cert.OCSPStaple = ocspResp
|
||||
}
|
||||
}
|
||||
@ -526,8 +526,8 @@ func extractPEMBlock(data []byte) ([]byte, []byte) {
|
||||
blockData := data[start : start+end+len(endMarker)]
|
||||
rest := data[start+end+len(endMarker):]
|
||||
|
||||
// Decode PEM to DER (simplified - actual implementation would use encoding/pem)
|
||||
// For now, we return the raw block data
|
||||
// 注意:此处为简化实现,直接返回原始 PEM 块数据
|
||||
// 生产环境建议使用 encoding/pem 进行完整解码
|
||||
return blockData, rest
|
||||
}
|
||||
|
||||
|
||||
@ -371,7 +371,14 @@ type HealthCheckSpec struct {
|
||||
Enabled bool
|
||||
}
|
||||
|
||||
// NewServer 创建 Stream 服务器。
|
||||
// NewServer 创建 Stream 代理服务器实例。
|
||||
//
|
||||
// 初始化 TCP/UDP 第四层代理服务器,包含监听器、上游配置和
|
||||
// 会话管理的空映射。创建后需通过 AddUpstream、ListenTCP/UDP
|
||||
// 等方法配置后再启动。
|
||||
//
|
||||
// 返回值:
|
||||
// - *Server: 初始化的 Stream 代理服务器实例
|
||||
func NewServer() *Server {
|
||||
return &Server{
|
||||
listeners: make(map[string]net.Listener),
|
||||
|
||||
@ -1,3 +1,17 @@
|
||||
// Package testutil 提供测试辅助工具函数。
|
||||
//
|
||||
// 该文件包含请求上下文创建相关的辅助函数,用于单元测试:
|
||||
// - 创建测试用的 fasthttp.RequestCtx
|
||||
// - 支持 method、path、body、header 配置
|
||||
//
|
||||
// 主要用途:
|
||||
//
|
||||
// 简化单元测试中请求上下文的创建,避免重复代码。
|
||||
//
|
||||
// 注意事项:
|
||||
// - 仅用于测试,不应在生产代码中使用
|
||||
//
|
||||
// 作者:xfy
|
||||
package testutil
|
||||
|
||||
import "github.com/valyala/fasthttp"
|
||||
|
||||
@ -37,7 +37,11 @@ var (
|
||||
|
||||
// GetStats 获取池统计信息的副本。
|
||||
//
|
||||
// 返回当前统计信息的快照,线程安全。
|
||||
// 返回当前统计信息的快照,包含获取次数、放回次数、新建次数和活跃数量。
|
||||
// 该方法是线程安全的,可在多个 goroutine 中同时调用。
|
||||
//
|
||||
// 返回值:
|
||||
// - PoolStats: 统计信息快照,包含 Gets、Puts、NewCount 和 Active 字段
|
||||
func GetStats() PoolStats {
|
||||
return PoolStats{
|
||||
Gets: gets.Load(),
|
||||
@ -48,6 +52,9 @@ func GetStats() PoolStats {
|
||||
}
|
||||
|
||||
// GetPool 获取底层的 sync.Pool(用于测试和调试)。
|
||||
//
|
||||
// 返回值:
|
||||
// - *sync.Pool: 变量池实例,可用于直接操作池中的对象
|
||||
func GetPool() *sync.Pool {
|
||||
return &pool
|
||||
}
|
||||
|
||||
@ -150,6 +150,15 @@ func GetSSLClientVerify(ctx *fasthttp.RequestCtx) string {
|
||||
}
|
||||
|
||||
// GetSSLClientSerial 获取客户端证书序列号。
|
||||
//
|
||||
// 从请求上下文中读取 mTLS 客户端证书的序列号。
|
||||
// 证书信息由连接处理器通过 SetSSLClientInfoInContext 预先设置。
|
||||
//
|
||||
// 参数:
|
||||
// - ctx: fasthttp 请求上下文
|
||||
//
|
||||
// 返回值:
|
||||
// - string: 证书序列号字符串,未提供证书时返回空字符串
|
||||
func GetSSLClientSerial(ctx *fasthttp.RequestCtx) string {
|
||||
if v := ctx.UserValue(VarSSLClientSerial); v != nil {
|
||||
if s, ok := v.(string); ok {
|
||||
@ -160,6 +169,14 @@ func GetSSLClientSerial(ctx *fasthttp.RequestCtx) string {
|
||||
}
|
||||
|
||||
// GetSSLClientSubject 获取客户端证书主题。
|
||||
//
|
||||
// 从请求上下文中读取 mTLS 客户端证书的主题(Distinguished Name)。
|
||||
//
|
||||
// 参数:
|
||||
// - ctx: fasthttp 请求上下文
|
||||
//
|
||||
// 返回值:
|
||||
// - string: 证书主题字符串,未提供证书时返回空字符串
|
||||
func GetSSLClientSubject(ctx *fasthttp.RequestCtx) string {
|
||||
if v := ctx.UserValue(VarSSLClientSubject); v != nil {
|
||||
if s, ok := v.(string); ok {
|
||||
@ -170,6 +187,14 @@ func GetSSLClientSubject(ctx *fasthttp.RequestCtx) string {
|
||||
}
|
||||
|
||||
// GetSSLClientIssuer 获取客户端证书颁发者。
|
||||
//
|
||||
// 从请求上下文中读取 mTLS 客户端证书的颁发者(Distinguished Name)。
|
||||
//
|
||||
// 参数:
|
||||
// - ctx: fasthttp 请求上下文
|
||||
//
|
||||
// 返回值:
|
||||
// - string: 证书颁发者字符串,未提供证书时返回空字符串
|
||||
func GetSSLClientIssuer(ctx *fasthttp.RequestCtx) string {
|
||||
if v := ctx.UserValue(VarSSLClientIssuer); v != nil {
|
||||
if s, ok := v.(string); ok {
|
||||
@ -180,6 +205,14 @@ func GetSSLClientIssuer(ctx *fasthttp.RequestCtx) string {
|
||||
}
|
||||
|
||||
// GetSSLClientFingerprint 获取客户端证书指纹。
|
||||
//
|
||||
// 从请求上下文中读取 mTLS 客户端证书的 SHA1 指纹。
|
||||
//
|
||||
// 参数:
|
||||
// - ctx: fasthttp 请求上下文
|
||||
//
|
||||
// 返回值:
|
||||
// - string: 证书 SHA1 指纹(十六进制大写),未提供证书时返回空字符串
|
||||
func GetSSLClientFingerprint(ctx *fasthttp.RequestCtx) string {
|
||||
if v := ctx.UserValue(VarSSLClientFingerprint); v != nil {
|
||||
if s, ok := v.(string); ok {
|
||||
@ -190,6 +223,14 @@ func GetSSLClientFingerprint(ctx *fasthttp.RequestCtx) string {
|
||||
}
|
||||
|
||||
// GetSSLClientNotBefore 获取客户端证书生效时间。
|
||||
//
|
||||
// 从请求上下文中读取 mTLS 客户端证书的生效时间(ISO8601 格式)。
|
||||
//
|
||||
// 参数:
|
||||
// - ctx: fasthttp 请求上下文
|
||||
//
|
||||
// 返回值:
|
||||
// - string: 证书生效时间,格式为 "2006-01-02T15:04:05Z",未提供证书时返回空字符串
|
||||
func GetSSLClientNotBefore(ctx *fasthttp.RequestCtx) string {
|
||||
if v := ctx.UserValue(VarSSLClientNotBefore); v != nil {
|
||||
if s, ok := v.(string); ok {
|
||||
@ -200,6 +241,14 @@ func GetSSLClientNotBefore(ctx *fasthttp.RequestCtx) string {
|
||||
}
|
||||
|
||||
// GetSSLClientNotAfter 获取客户端证书过期时间。
|
||||
//
|
||||
// 从请求上下文中读取 mTLS 客户端证书的过期时间(ISO8601 格式)。
|
||||
//
|
||||
// 参数:
|
||||
// - ctx: fasthttp 请求上下文
|
||||
//
|
||||
// 返回值:
|
||||
// - string: 证书过期时间,格式为 "2006-01-02T15:04:05Z",未提供证书时返回空字符串
|
||||
func GetSSLClientNotAfter(ctx *fasthttp.RequestCtx) string {
|
||||
if v := ctx.UserValue(VarSSLClientNotAfter); v != nil {
|
||||
if s, ok := v.(string); ok {
|
||||
@ -210,6 +259,14 @@ func GetSSLClientNotAfter(ctx *fasthttp.RequestCtx) string {
|
||||
}
|
||||
|
||||
// GetSSLClientEmail 获取客户端证书邮箱。
|
||||
//
|
||||
// 从请求上下文中读取 mTLS 客户端证书中包含的邮箱地址。
|
||||
//
|
||||
// 参数:
|
||||
// - ctx: fasthttp 请求上下文
|
||||
//
|
||||
// 返回值:
|
||||
// - string: 证书中的第一个邮箱地址,未提供证书或无邮箱时返回空字符串
|
||||
func GetSSLClientEmail(ctx *fasthttp.RequestCtx) string {
|
||||
if v := ctx.UserValue(VarSSLClientEmail); v != nil {
|
||||
if s, ok := v.(string); ok {
|
||||
|
||||
@ -78,6 +78,16 @@ func SetGlobalVariables(vars map[string]string) {
|
||||
}
|
||||
|
||||
// GetGlobalVariable 获取全局变量值。
|
||||
//
|
||||
// 线程安全地查询全局自定义变量存储。
|
||||
// 使用读锁保护,可在多个 goroutine 中并发调用。
|
||||
//
|
||||
// 参数:
|
||||
// - name: 变量名称
|
||||
//
|
||||
// 返回值:
|
||||
// - string: 变量值,不存在时返回空字符串
|
||||
// - bool: 变量是否存在,true 表示存在,false 表示不存在
|
||||
func GetGlobalVariable(name string) (string, bool) {
|
||||
globalVariablesLock.RLock()
|
||||
defer globalVariablesLock.RUnlock()
|
||||
@ -89,6 +99,12 @@ func GetGlobalVariable(name string) (string, bool) {
|
||||
}
|
||||
|
||||
// GetAllGlobalVariables 获取所有全局变量的副本。
|
||||
//
|
||||
// 线程安全地返回全局自定义变量存储的完整快照。
|
||||
// 返回的是副本,外部修改不会影响全局存储。
|
||||
//
|
||||
// 返回值:
|
||||
// - map[string]string: 所有全局变量的键值对副本,未初始化时返回 nil
|
||||
func GetAllGlobalVariables() map[string]string {
|
||||
globalVariablesLock.RLock()
|
||||
defer globalVariablesLock.RUnlock()
|
||||
@ -279,14 +295,14 @@ func (vc *Context) Get(name string) (string, bool) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
// EphemeralGet returns []byte view valid only within request scope.
|
||||
// EphemeralGet 获取请求作用域内的变量值(返回 []byte)。
|
||||
//
|
||||
// WARNING: The returned []byte becomes INVALID after request completes.
|
||||
// SAFETY: Use only for immediate consumption (logging, header write).
|
||||
// For persistent storage, use PersistentGet().
|
||||
// 警告:返回的 []byte 在请求结束后失效。
|
||||
// 安全用法:仅用于即时消费场景(如写入日志、响应头)。
|
||||
// 如需持久化存储,请使用 PersistentGet()。
|
||||
//
|
||||
// This method provides zero-copy access to variable values by using
|
||||
// GetterBytes functions registered in BuiltinVariable.
|
||||
// 该方法通过 BuiltinVariable 中注册的 GetterBytes 函数
|
||||
// 提供零拷贝访问变量值。
|
||||
func (vc *Context) EphemeralGet(name string) []byte {
|
||||
// 1. 先查自定义变量(需要转换为 []byte)
|
||||
if v, ok := vc.store[name]; ok {
|
||||
@ -409,10 +425,10 @@ func (vc *Context) EphemeralGet(name string) []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
// PersistentGet returns string for cross-request storage.
|
||||
// PersistentGet 获取持久化字符串变量值。
|
||||
//
|
||||
// Use this method when you need to store the variable value beyond
|
||||
// the current request scope (e.g., in a database, cache, or long-lived struct).
|
||||
// 当需要跨请求存储变量值时使用此方法
|
||||
//(如保存到数据库、缓存或长期存活的结构体中)。
|
||||
func (vc *Context) PersistentGet(name string) string {
|
||||
// 直接调用 Get,它返回 string
|
||||
v, _ := vc.Get(name)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user