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:
xfy 2026-04-20 10:59:53 +08:00
parent 088785bce2
commit 2458ac1ed1
35 changed files with 713 additions and 94 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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,

View File

@ -1,3 +1,21 @@
// Package config 提供 YAML 配置文件的解析、验证和默认配置生成功能。
//
// 该文件包含配置加载器相关的核心逻辑,包括:
// - 多文件配置合并include 指令支持)
// - DAG-safe 循环检测(防止配置文件循环引用)
// - Glob 模式展开(支持通配符路径)
// - 深度限制(防止无限递归)
//
// 主要用途:
//
// 用于加载主配置文件及其引用的子配置文件,合并为完整配置。
//
// 注意事项:
// - 最大 include 深度为 10 层
// - 循环引用会返回错误
// - DAG 共享子配置允许(同一文件可被多处引用)
//
// 作者xfy
package config
import (

View File

@ -121,6 +121,9 @@ type PartialLoadError struct {
}
// Error 实现 error 接口。
//
// 返回值:
// - string: 格式化的错误消息,包含失败的数量
func (e *PartialLoadError) Error() string {
return fmt.Sprintf("部分错误页面加载失败: %d 个错误", len(e.Errors))
}

View File

@ -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:

View File

@ -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)

View File

@ -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 }

View File

@ -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 {

View File

@ -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)

View File

@ -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()

View File

@ -482,6 +482,12 @@ type Settings struct {
}
// DefaultSettings 返回默认 HTTP/2 设置。
//
// 返回包含 HTTP/2 协议推荐的默认参数设置,包括头部表大小、
// 并发流数量、初始窗口大小、帧大小等。
//
// 返回值:
// - Settings: 默认 HTTP/2 配置参数
func DefaultSettings() Settings {
return Settings{
HeaderTableSize: 4096,

View File

@ -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{

View File

@ -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

View File

@ -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))

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -26,7 +26,7 @@ import (
//
// 拦截 HTTP 错误响应4xx 和 5xx并使用预加载的自定义错误页面内容替换响应。
type ErrorIntercept struct {
// manager 错误页面管理器
// manager 错误页面管理器,用于获取预加载的错误页面内容
manager *handler.ErrorPageManager
}

View File

@ -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
}

View File

@ -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 ""

View File

@ -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)

View File

@ -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,

View File

@ -1,4 +1,18 @@
// Package mimeutil 提供 MIME 类型检测工具。
// Package mimeutil 提供 MIME 类型检测工具函数。
//
// 该文件实现了基于文件扩展名的 MIME 类型检测,
// 补充 Go 标准库 mime 包中缺失或错误的映射。
//
// 主要功能:
// - 本地 MIME 映射:避免 mime.AddExtensionType 的全局副作用
// - 自动回退:未覆盖的扩展名回退到标准库
// - 大小写处理:自动将扩展名转为小写再查找
//
// 注意事项:
// - 使用包本地映射而非全局修改,确保多线程安全
// - 部分扩展名(如 .otf、.webmGo 标准库返回错误类型,已在此纠正
//
// 作者xfy
package mimeutil
import (

View File

@ -1,8 +1,12 @@
// Package netutil 提供网络相关的通用工具函数。
//
// 该包包含 URL 解析、客户端 IP 提取等网络操作的工具函数,
// 该文件包含 URL 解析和主机地址提取的工具函数,
// 供 proxy、middleware、server 等模块共享使用。
//
// 主要功能:
// - ParseTargetURL: 解析目标 URL提取主机地址和 TLS 标志
// - ExtractHost: 简化版 URL 解析,始终添加默认端口
//
// 作者xfy
package netutil

View File

@ -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{}

View File

@ -16,6 +16,15 @@ import (
type UpgradeManager struct{}
// NewUpgradeManager 创建空的升级管理器 stub。
//
// Windows 平台不支持优雅升级(热升级)功能,该函数返回
// 一个空的 UpgradeManager 实例,所有方法均为空操作。
//
// 参数:
// - server: 服务器实例Windows 上不使用)
//
// 返回值:
// - *UpgradeManager: 空操作的管理器实例
func NewUpgradeManager(server *Server) *UpgradeManager {
return &UpgradeManager{}
}

View File

@ -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,

View File

@ -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
}

View File

@ -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),

View File

@ -1,3 +1,17 @@
// Package testutil 提供测试辅助工具函数。
//
// 该文件包含请求上下文创建相关的辅助函数,用于单元测试:
// - 创建测试用的 fasthttp.RequestCtx
// - 支持 method、path、body、header 配置
//
// 主要用途:
//
// 简化单元测试中请求上下文的创建,避免重复代码。
//
// 注意事项:
// - 仅用于测试,不应在生产代码中使用
//
// 作者xfy
package testutil
import "github.com/valyala/fasthttp"

View File

@ -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
}

View File

@ -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 {

View File

@ -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)