diff --git a/internal/app/app.go b/internal/app/app.go index 05c4ffe..c9f3588 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -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) diff --git a/internal/app/app_windows.go b/internal/app/app_windows.go index c4f50b6..0048696 100644 --- a/internal/app/app_windows.go +++ b/internal/app/app_windows.go @@ -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) diff --git a/internal/cache/file_cache.go b/internal/cache/file_cache.go index 8ef51d2..7e184c0 100644 --- a/internal/cache/file_cache.go +++ b/internal/cache/file_cache.go @@ -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, diff --git a/internal/config/loader.go b/internal/config/loader.go index 0c5a28d..bac2cdb 100644 --- a/internal/config/loader.go +++ b/internal/config/loader.go @@ -1,3 +1,21 @@ +// Package config 提供 YAML 配置文件的解析、验证和默认配置生成功能。 +// +// 该文件包含配置加载器相关的核心逻辑,包括: +// - 多文件配置合并(include 指令支持) +// - DAG-safe 循环检测(防止配置文件循环引用) +// - Glob 模式展开(支持通配符路径) +// - 深度限制(防止无限递归) +// +// 主要用途: +// +// 用于加载主配置文件及其引用的子配置文件,合并为完整配置。 +// +// 注意事项: +// - 最大 include 深度为 10 层 +// - 循环引用会返回错误 +// - DAG 共享子配置允许(同一文件可被多处引用) +// +// 作者:xfy package config import ( diff --git a/internal/handler/errorpage.go b/internal/handler/errorpage.go index 86e8bb6..f474c5d 100644 --- a/internal/handler/errorpage.go +++ b/internal/handler/errorpage.go @@ -121,6 +121,9 @@ type PartialLoadError struct { } // Error 实现 error 接口。 +// +// 返回值: +// - string: 格式化的错误消息,包含失败的数量 func (e *PartialLoadError) Error() string { return fmt.Sprintf("部分错误页面加载失败: %d 个错误", len(e.Errors)) } diff --git a/internal/handler/sendfile_linux.go b/internal/handler/sendfile_linux.go index 7118323..45a0e00 100644 --- a/internal/handler/sendfile_linux.go +++ b/internal/handler/sendfile_linux.go @@ -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: diff --git a/internal/handler/sendfile_other_test.go b/internal/handler/sendfile_other_test.go index c8a8c9f..85a3875 100644 --- a/internal/handler/sendfile_other_test.go +++ b/internal/handler/sendfile_other_test.go @@ -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) diff --git a/internal/handler/sendfile_test.go b/internal/handler/sendfile_test.go index aac9083..ad4edc1 100644 --- a/internal/handler/sendfile_test.go +++ b/internal/handler/sendfile_test.go @@ -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 } diff --git a/internal/handler/static_bench_test.go b/internal/handler/static_bench_test.go index 902127f..fb1d2c1 100644 --- a/internal/handler/static_bench_test.go +++ b/internal/handler/static_bench_test.go @@ -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 { diff --git a/internal/handler/static_test.go b/internal/handler/static_test.go index 247cd3e..220d0ff 100644 --- a/internal/handler/static_test.go +++ b/internal/handler/static_test.go @@ -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) diff --git a/internal/http2/adapter.go b/internal/http2/adapter.go index 9fde6f4..bf5ff1d 100644 --- a/internal/http2/adapter.go +++ b/internal/http2/adapter.go @@ -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() diff --git a/internal/http2/server.go b/internal/http2/server.go index 676294f..30006db 100644 --- a/internal/http2/server.go +++ b/internal/http2/server.go @@ -482,6 +482,12 @@ type Settings struct { } // DefaultSettings 返回默认 HTTP/2 设置。 +// +// 返回包含 HTTP/2 协议推荐的默认参数设置,包括头部表大小、 +// 并发流数量、初始窗口大小、帧大小等。 +// +// 返回值: +// - Settings: 默认 HTTP/2 配置参数 func DefaultSettings() Settings { return Settings{ HeaderTableSize: 4096, diff --git a/internal/http3/adapter.go b/internal/http3/adapter.go index 86869ed..2ac530e 100644 --- a/internal/http3/adapter.go +++ b/internal/http3/adapter.go @@ -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{ diff --git a/internal/loadbalance/algorithms.go b/internal/loadbalance/algorithms.go index 1c875b4..dc193a9 100644 --- a/internal/loadbalance/algorithms.go +++ b/internal/loadbalance/algorithms.go @@ -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 diff --git a/internal/loadbalance/balancer.go b/internal/loadbalance/balancer.go index 00fa867..2174e17 100644 --- a/internal/loadbalance/balancer.go +++ b/internal/loadbalance/balancer.go @@ -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)) diff --git a/internal/loadbalance/balancer_bench_test.go b/internal/loadbalance/balancer_bench_test.go index 7082c06..3bd6ba0 100644 --- a/internal/loadbalance/balancer_bench_test.go +++ b/internal/loadbalance/balancer_bench_test.go @@ -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 diff --git a/internal/loadbalance/balancer_test.go b/internal/loadbalance/balancer_test.go index ff6ba3f..c8bdfb4 100644 --- a/internal/loadbalance/balancer_test.go +++ b/internal/loadbalance/balancer_test.go @@ -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) diff --git a/internal/middleware/bodylimit/bodylimit.go b/internal/middleware/bodylimit/bodylimit.go index c8e39fb..2bb67fc 100644 --- a/internal/middleware/bodylimit/bodylimit.go +++ b/internal/middleware/bodylimit/bodylimit.go @@ -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 diff --git a/internal/middleware/compression/gzip_static.go b/internal/middleware/compression/gzip_static.go index 61c82ae..8ebc7b2 100644 --- a/internal/middleware/compression/gzip_static.go +++ b/internal/middleware/compression/gzip_static.go @@ -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 diff --git a/internal/middleware/errorintercept/errorintercept.go b/internal/middleware/errorintercept/errorintercept.go index afe9085..c2409c9 100644 --- a/internal/middleware/errorintercept/errorintercept.go +++ b/internal/middleware/errorintercept/errorintercept.go @@ -26,7 +26,7 @@ import ( // // 拦截 HTTP 错误响应(4xx 和 5xx),并使用预加载的自定义错误页面内容替换响应。 type ErrorIntercept struct { - // manager 错误页面管理器 + // manager 错误页面管理器,用于获取预加载的错误页面内容 manager *handler.ErrorPageManager } diff --git a/internal/middleware/rewrite/rewrite.go b/internal/middleware/rewrite/rewrite.go index e8d7c05..616dc50 100644 --- a/internal/middleware/rewrite/rewrite.go +++ b/internal/middleware/rewrite/rewrite.go @@ -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 } diff --git a/internal/middleware/security/auth_request.go b/internal/middleware/security/auth_request.go index d8e6793..d9b9a33 100644 --- a/internal/middleware/security/auth_request.go +++ b/internal/middleware/security/auth_request.go @@ -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 "" diff --git a/internal/middleware/security/ratelimit.go b/internal/middleware/security/ratelimit.go index 65cdd8d..c6058f1 100644 --- a/internal/middleware/security/ratelimit.go +++ b/internal/middleware/security/ratelimit.go @@ -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) diff --git a/internal/middleware/security/sliding_window.go b/internal/middleware/security/sliding_window.go index ea20cd3..199a188 100644 --- a/internal/middleware/security/sliding_window.go +++ b/internal/middleware/security/sliding_window.go @@ -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, diff --git a/internal/mimeutil/detect.go b/internal/mimeutil/detect.go index da3684d..ccc9a0e 100644 --- a/internal/mimeutil/detect.go +++ b/internal/mimeutil/detect.go @@ -1,4 +1,18 @@ -// Package mimeutil 提供 MIME 类型检测工具。 +// Package mimeutil 提供 MIME 类型检测工具函数。 +// +// 该文件实现了基于文件扩展名的 MIME 类型检测, +// 补充 Go 标准库 mime 包中缺失或错误的映射。 +// +// 主要功能: +// - 本地 MIME 映射:避免 mime.AddExtensionType 的全局副作用 +// - 自动回退:未覆盖的扩展名回退到标准库 +// - 大小写处理:自动将扩展名转为小写再查找 +// +// 注意事项: +// - 使用包本地映射而非全局修改,确保多线程安全 +// - 部分扩展名(如 .otf、.webm)Go 标准库返回错误类型,已在此纠正 +// +// 作者:xfy package mimeutil import ( diff --git a/internal/netutil/url.go b/internal/netutil/url.go index 1193180..fd0df57 100644 --- a/internal/netutil/url.go +++ b/internal/netutil/url.go @@ -1,8 +1,12 @@ // Package netutil 提供网络相关的通用工具函数。 // -// 该包包含 URL 解析、客户端 IP 提取等网络操作的工具函数, +// 该文件包含 URL 解析和主机地址提取的工具函数, // 供 proxy、middleware、server 等模块共享使用。 // +// 主要功能: +// - ParseTargetURL: 解析目标 URL,提取主机地址和 TLS 标志 +// - ExtractHost: 简化版 URL 解析,始终添加默认端口 +// // 作者:xfy package netutil diff --git a/internal/resolver/resolver.go b/internal/resolver/resolver.go index 10ba990..808f4d0 100644 --- a/internal/resolver/resolver.go +++ b/internal/resolver/resolver.go @@ -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{} diff --git a/internal/server/upgrade_windows.go b/internal/server/upgrade_windows.go index c20f742..a27bc79 100644 --- a/internal/server/upgrade_windows.go +++ b/internal/server/upgrade_windows.go @@ -16,6 +16,15 @@ import ( type UpgradeManager struct{} // NewUpgradeManager 创建空的升级管理器 stub。 +// +// Windows 平台不支持优雅升级(热升级)功能,该函数返回 +// 一个空的 UpgradeManager 实例,所有方法均为空操作。 +// +// 参数: +// - server: 服务器实例(Windows 上不使用) +// +// 返回值: +// - *UpgradeManager: 空操作的管理器实例 func NewUpgradeManager(server *Server) *UpgradeManager { return &UpgradeManager{} } diff --git a/internal/ssl/ocsp.go b/internal/ssl/ocsp.go index 119562d..3407c2d 100644 --- a/internal/ssl/ocsp.go +++ b/internal/ssl/ocsp.go @@ -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, diff --git a/internal/ssl/ssl.go b/internal/ssl/ssl.go index a7be4ce..2c4550e 100644 --- a/internal/ssl/ssl.go +++ b/internal/ssl/ssl.go @@ -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 } diff --git a/internal/stream/stream.go b/internal/stream/stream.go index 337454a..c0f5d5e 100644 --- a/internal/stream/stream.go +++ b/internal/stream/stream.go @@ -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), diff --git a/internal/testutil/request.go b/internal/testutil/request.go index 28d21b5..9640b67 100644 --- a/internal/testutil/request.go +++ b/internal/testutil/request.go @@ -1,3 +1,17 @@ +// Package testutil 提供测试辅助工具函数。 +// +// 该文件包含请求上下文创建相关的辅助函数,用于单元测试: +// - 创建测试用的 fasthttp.RequestCtx +// - 支持 method、path、body、header 配置 +// +// 主要用途: +// +// 简化单元测试中请求上下文的创建,避免重复代码。 +// +// 注意事项: +// - 仅用于测试,不应在生产代码中使用 +// +// 作者:xfy package testutil import "github.com/valyala/fasthttp" diff --git a/internal/variable/pool.go b/internal/variable/pool.go index 0afe6c8..9dc8296 100644 --- a/internal/variable/pool.go +++ b/internal/variable/pool.go @@ -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 } diff --git a/internal/variable/ssl.go b/internal/variable/ssl.go index 10cc96a..f7ddb7c 100644 --- a/internal/variable/ssl.go +++ b/internal/variable/ssl.go @@ -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 { diff --git a/internal/variable/variable.go b/internal/variable/variable.go index fc22e06..4f6f322 100644 --- a/internal/variable/variable.go +++ b/internal/variable/variable.go @@ -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)