From f2352ab9cc98f64c7298d2311e31994cffabbdb1 Mon Sep 17 00:00:00 2001 From: xfy Date: Tue, 7 Apr 2026 15:36:09 +0800 Subject: [PATCH] =?UTF-8?q?docs(config,stream,logging,handler,proxy,cache,?= =?UTF-8?q?server,ssl,middleware):=20=E4=B8=BA=E6=A0=B8=E5=BF=83=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E6=B7=BB=E5=8A=A0=E8=AF=A6=E7=BB=86=20GoDoc=20?= =?UTF-8?q?=E6=96=87=E6=A1=A3=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - config: 为 Config 和所有子配置结构添加完整文档,包含使用示例和注意事项 - stream: 为负载均衡器和服务器添加详细的参数、返回值和功能说明 - logging: 为日志格式化和输出函数添加文档,说明支持的变量替换 - handler: 为路由器、静态文件和 sendfile 处理器添加文档 - proxy: 为健康检查器和代理功能添加完整文档 - cache/server/ssl/middleware: 补充相关模块的文档注释 - config.example.yaml: 添加可信代理配置、加密套件示例,更新压缩级别说明 Co-Authored-By: Claude --- config.example.yaml | 15 +- internal/app/app_test.go | 12 +- internal/cache/cache_test.go | 10 + internal/cache/file_cache.go | 51 +- internal/config/config.go | 1157 +++++++++++++++-- internal/config/defaults.go | 20 +- internal/config/defaults_test.go | 15 +- internal/config/validate_test.go | 36 +- internal/handler/router.go | 66 +- internal/handler/router_test.go | 14 +- internal/handler/sendfile.go | 77 +- internal/handler/sendfile_test.go | 15 + internal/handler/static.go | 71 +- internal/handler/static_test.go | 18 +- internal/http3/adapter_test.go | 10 + internal/http3/server_test.go | 10 + internal/loadbalance/balancer_test.go | 11 +- internal/loadbalance/consistent_hash.go | 11 +- internal/logging/logging.go | 41 + internal/logging/logging_test.go | 11 + internal/middleware/accesslog/accesslog.go | 19 + .../middleware/accesslog/accesslog_test.go | 9 + .../middleware/compression/compression.go | 40 + .../compression/compression_test.go | 10 + .../compression/gzip_static_test.go | 12 +- internal/middleware/middleware_test.go | 10 + internal/middleware/rewrite/rewrite_test.go | 10 + internal/middleware/security/access_test.go | 10 + internal/middleware/security/auth_test.go | 10 + internal/middleware/security/headers_test.go | 10 + .../middleware/security/ratelimit_test.go | 11 + .../security/sliding_window_test.go | 11 + internal/netutil/ip_test.go | 9 + internal/netutil/url_test.go | 9 + internal/proxy/health.go | 36 +- internal/proxy/health_test.go | 16 +- internal/proxy/proxy.go | 34 +- internal/proxy/proxy_test.go | 19 +- internal/proxy/websocket_test.go | 14 + internal/server/pool.go | 25 +- internal/server/pool_test.go | 11 + internal/server/server.go | 8 +- internal/server/server_test.go | 13 + internal/server/status_test.go | 13 + internal/server/upgrade_test.go | 12 + internal/ssl/ocsp_test.go | 12 +- internal/ssl/ssl.go | 22 +- internal/ssl/ssl_test.go | 10 + internal/stream/stream.go | 254 +++- internal/stream/stream_test.go | 10 + 50 files changed, 2114 insertions(+), 246 deletions(-) diff --git a/config.example.yaml b/config.example.yaml index c875cab..3e325ad 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -54,7 +54,12 @@ server: # protocols: # TLS 版本(有效值: TLSv1.2, TLSv1.3) # - "TLSv1.2" # - "TLSv1.3" - # ciphers: [] # 加密套件(仅 TLS 1.2 有效) + # ciphers: # 加密套件(仅 TLS 1.2 有效,TLS 1.3 使用内置套件) + # - ECDHE-ECDSA-AES256-GCM-SHA384 + # - ECDHE-RSA-AES256-GCM-SHA384 + # - ECDHE-ECDSA-CHACHA20-POLY1305 + # - ECDHE-RSA-CHACHA20-POLY1305 + # # 拒绝不安全套件:含 RC4、DES、3DES、CBC 的配置将报错 # ocsp_stapling: false # OCSP Stapling # hsts: # HTTP Strict Transport Security # max_age: 31536000 # 过期时间(秒) @@ -68,6 +73,7 @@ server: allow: [] # 允许的 IP/CIDR 列表 deny: [] # 拒绝的 IP/CIDR 列表 default: "allow" # 默认动作(有效值: allow, deny) + trusted_proxies: [] # 可信代理 CIDR 列表,用于 X-Forwarded-For 解析 # 速率限制 rate_limit: @@ -91,7 +97,7 @@ server: # 安全头部 headers: x_frame_options: "DENY" # 防止点击劫持(有效值: DENY, SAMEORIGIN, 空表示禁用) - x_content_type_options: "nosniff" # 防止 MIME 嗅探 + x_content_type_options: "nosniff" # 防止 MIME 嗅探(有效值:nosniff,空表示禁用) referrer_policy: "strict-origin-when-cross-origin" # 引用策略(有效值: no-referrer, no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url) # content_security_policy: "default-src 'self'" # 内容安全策略 CSP # permissions_policy: "geolocation=(), microphone=()" # 权限策略 @@ -105,12 +111,12 @@ server: # 响应压缩配置 compression: type: "gzip" # 压缩类型(有效值: gzip, brotli, both,空表示禁用) - level: 6 # 压缩级别(范围 1-9,值越大压缩率越高但速度越慢) + level: 6 # 压缩级别(范围 0-9,0=不压缩,1=最快,9=最高压缩率) min_size: 1024 # 最小压缩大小(字节,小于此值不压缩) gzip_static: false # 启用预压缩文件支持(自动查找 .gz/.br 文件) gzip_static_extensions: # 预压缩文件扩展名 - - ".gz" - ".br" + - ".gz" types: # 可压缩的 MIME 类型 - "text/html" - "text/css" @@ -202,7 +208,6 @@ performance: max_entries: 10000 # 最大缓存条目 max_size: 268435456 # 内存上限(字节,256MB) inactive: 20s # 未访问淘汰时间 - lru_eviction: true # 启用 LRU 淘汰 transport: # HTTP Transport 连接池 max_idle_conns: 100 # 最大空闲连接 max_idle_conns_per_host: 32 # 每主机空闲连接 diff --git a/internal/app/app_test.go b/internal/app/app_test.go index 26bb34d..366ef24 100644 --- a/internal/app/app_test.go +++ b/internal/app/app_test.go @@ -1,4 +1,14 @@ -// Package app 提供应用程序的启动和运行逻辑。 +// Package app 提供应用程序功能的测试。 +// +// 该文件测试应用程序模块的各项功能,包括: +// - 应用创建和配置 +// - 信号处理(SIGTERM、SIGHUP、SIGUSR1等) +// - 配置重载 +// - 日志重开 +// - 版本输出 +// - 优雅关闭 +// +// 作者:xfy package app import ( diff --git a/internal/cache/cache_test.go b/internal/cache/cache_test.go index 2766ff7..743d359 100644 --- a/internal/cache/cache_test.go +++ b/internal/cache/cache_test.go @@ -1,3 +1,13 @@ +// Package cache 提供缓存功能的测试。 +// +// 该文件测试缓存模块的各项功能,包括: +// - 文件缓存创建和配置 +// - 代理缓存规则和匹配 +// - 缓存设置和获取 +// - 过期和淘汰策略 +// - 路径匹配功能 +// +// 作者:xfy package cache import ( diff --git a/internal/cache/file_cache.go b/internal/cache/file_cache.go index 68a7ee9..00279d5 100644 --- a/internal/cache/file_cache.go +++ b/internal/cache/file_cache.go @@ -205,6 +205,9 @@ func (c *FileCache) removeEntry(entry *FileEntry) { } // evictIfNeeded 根据限制淘汰条目。 +// +// 检查当前缓存是否超过条目数或内存大小限制, +// 如果超过则调用 evictLRU 淘汰最久未使用的条目。 func (c *FileCache) evictIfNeeded() { // 按条目数淘汰 for c.lruList.Len() > int(c.maxEntries) && c.maxEntries > 0 { @@ -218,6 +221,9 @@ func (c *FileCache) evictIfNeeded() { } // evictLRU 淘汰最久未使用的条目。 +// +// 从 LRU 链表尾部移除条目并删除。 +// 如果链表为空则不执行任何操作。 func (c *FileCache) evictLRU() { if c.lruList.Len() == 0 { return @@ -257,10 +263,17 @@ func (c *FileCache) Stats() FileCacheStats { // FileCacheStats 文件缓存统计。 type FileCacheStats struct { - Entries int64 + // Entries 当前缓存条目数量 + Entries int64 + + // MaxEntries 最大缓存条目数限制 MaxEntries int64 - Size int64 - MaxSize int64 + + // Size 当前缓存使用的内存大小(字节) + Size int64 + + // MaxSize 最大内存使用限制(字节) + MaxSize int64 } // ProxyCacheRule 代理缓存规则。 @@ -423,7 +436,20 @@ func (c *ProxyCache) MatchRule(path, method string, status int) *ProxyCacheRule return nil } -// pathMatch 路径匹配(支持前缀和精确匹配)。 +// pathMatch 检查路径是否匹配指定模式。 +// +// 支持以下匹配模式: +// - "*":匹配所有路径 +// - 以 "*" 结尾:前缀匹配(如 "/api/*" 匹配 "/api/xxx") +// - 以 "/" 结尾:目录前缀匹配 +// - 其他:精确匹配 +// +// 参数: +// - pattern: 匹配模式,支持通配符 +// - path: 待检查的路径 +// +// 返回值: +// - bool: true 表示匹配,false 表示不匹配 func pathMatch(pattern, path string) bool { if pattern == "*" { return true @@ -442,6 +468,13 @@ func pathMatch(pattern, path string) bool { } // contains 检查字符串切片是否包含某值。 +// +// 参数: +// - slice: 字符串切片 +// - val: 待查找的值 +// +// 返回值: +// - bool: true 表示包含,false 表示不包含 func contains(slice []string, val string) bool { for _, s := range slice { if s == val { @@ -452,6 +485,13 @@ func contains(slice []string, val string) bool { } // containsInt 检查整数切片是否包含某值。 +// +// 参数: +// - slice: 整数切片 +// - val: 待查找的值 +// +// 返回值: +// - bool: true 表示包含,false 表示不包含 func containsInt(slice []int, val int) bool { for _, i := range slice { if i == val { @@ -489,6 +529,9 @@ func (c *ProxyCache) Stats() ProxyCacheStats { // ProxyCacheStats 代理缓存统计。 type ProxyCacheStats struct { + // Entries 当前缓存条目数量 Entries int + + // Pending 正在等待缓存生成的请求数量 Pending int } diff --git a/internal/config/config.go b/internal/config/config.go index 847020f..5b6ebe3 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -27,263 +27,1158 @@ import ( ) // Config 根配置结构,支持单服务器和多虚拟主机两种模式。 +// +// 包含服务器配置、日志配置、性能配置和监控配置等模块。 +// 是配置文件的顶级结构体,所有其他配置都作为其子结构。 +// +// 注意事项: +// - 必须配置 server 或 servers 中的至少一个 +// - 加载后会自动进行配置验证 +// - Stream 配置为可选,用于 TCP/UDP 层代理 +// - HTTP/3 配置为可选,需 SSL 配置配合才能生效 +// +// 使用示例: +// +// cfg, err := config.Load("config.yaml") +// if err != nil { +// log.Fatal(err) +// } +// server := cfg.Server +// // 或使用多虚拟主机 +// for _, s := range cfg.Servers { +// // 处理每个服务器配置 +// } type Config struct { - Server ServerConfig `yaml:"server"` // 单服务器模式配置 - Servers []ServerConfig `yaml:"servers"` // 多虚拟主机模式配置 - Stream []StreamConfig `yaml:"stream"` // TCP/UDP Stream 代理配置 - HTTP3 HTTP3Config `yaml:"http3"` // HTTP/3 (QUIC) 配置 - Logging LoggingConfig `yaml:"logging"` // 日志配置 - Performance PerformanceConfig `yaml:"performance"` // 性能配置 - Monitoring MonitoringConfig `yaml:"monitoring"` // 监控配置 + // Server 单服务器模式配置 + // 用于单一服务监听场景,与 Servers 二选一配置 + Server ServerConfig `yaml:"server"` + + // Servers 多虚拟主机模式配置 + // 用于同时监听多个地址或提供不同服务场景 + Servers []ServerConfig `yaml:"servers"` + + // Stream TCP/UDP Stream 代理配置 + // 用于四层网络代理,如数据库、缓存等 TCP 服务 + Stream []StreamConfig `yaml:"stream"` + + // HTTP3 HTTP/3 (QUIC) 配置 + // 启用 HTTP/3 协议支持,需要配合 SSL 配置使用 + HTTP3 HTTP3Config `yaml:"http3"` + + // Logging 日志配置 + // 控制访问日志和错误日志的输出格式与位置 + Logging LoggingConfig `yaml:"logging"` + + // Performance 性能配置 + // 包含 Goroutine 池、文件缓存、连接池等性能优化选项 + Performance PerformanceConfig `yaml:"performance"` + + // Monitoring 监控配置 + // 包含状态端点等监控相关配置 + Monitoring MonitoringConfig `yaml:"monitoring"` } // HTTP3Config HTTP/3 (QUIC) 配置。 +// +// HTTP/3 基于 QUIC 协议,提供更快的连接建立和更低的延迟。 +// 需要服务器配置 SSL/TLS 证书才能正常工作。 +// +// 注意事项: +// - 必须配置有效的 SSL 证书 +// - UDP 监听地址不能与 HTTP/1.1 或 HTTP/2 冲突 +// - 0-RTT 特性可能带来重放攻击风险,需评估安全性 +// - 部分网络环境可能限制 UDP 流量 +// +// 使用示例: +// +// http3: +// enabled: true +// listen: ":443" +// max_streams: 1000 +// idle_timeout: 30s +// enable_0rtt: true type HTTP3Config struct { - Enabled bool `yaml:"enabled"` // 是否启用 HTTP/3 - Listen string `yaml:"listen"` // UDP 监听地址,如 ":443" - MaxStreams int `yaml:"max_streams"` // 最大并发流 - IdleTimeout time.Duration `yaml:"idle_timeout"` // 空闲超时 - Enable0RTT bool `yaml:"enable_0rtt"` // 启用 0-RTT + // Enabled 是否启用 HTTP/3 + Enabled bool `yaml:"enabled"` + + // Listen UDP 监听地址,如 ":443" + // 通常与 HTTPS 端口一致 + Listen string `yaml:"listen"` + + // MaxStreams 最大并发流 + // 控制单个连接允许的最大并发流数量 + MaxStreams int `yaml:"max_streams"` + + // IdleTimeout 空闲超时 + // 连接无活动时的最大保持时间 + IdleTimeout time.Duration `yaml:"idle_timeout"` + + // Enable0RTT 启用 0-RTT 特性 + // 允许在首次握手时发送数据,降低延迟但可能存在安全风险 + Enable0RTT bool `yaml:"enable_0rtt"` } // ServerConfig 服务器配置,包含监听地址、静态文件、代理、SSL 等设置。 +// +// 用于定义单个服务器的完整行为,包括网络监听、请求处理、 +// 安全防护和性能控制等方面。 +// +// 注意事项: +// - Listen 字段为必填项,格式为 "host:port" 或 ":port" +// - Name 字段用于虚拟主机匹配,多服务器模式下建议配置 +// - SSL 配置为可选,但生产环境强烈建议启用 +// - 超时设置需根据实际业务场景调整 +// +// 使用示例: +// +// server: +// listen: ":8080" +// name: "api.example.com" +// read_timeout: 30s +// write_timeout: 30s type ServerConfig struct { - Listen string `yaml:"listen"` // 监听地址,如 ":8080" - Name string `yaml:"name"` // 服务器名称,用于虚拟主机匹配 - Static StaticConfig `yaml:"static"` // 静态文件服务配置 - Proxy []ProxyConfig `yaml:"proxy"` // 反向代理规则列表 - SSL SSLConfig `yaml:"ssl"` // SSL/TLS 配置 - Security SecurityConfig `yaml:"security"` // 安全配置 - Rewrite []RewriteRule `yaml:"rewrite"` // URL 重写规则 - Compression CompressionConfig `yaml:"compression"` // 响应压缩配置 - // 新增字段 - ReadTimeout time.Duration `yaml:"read_timeout"` // 读取超时 - WriteTimeout time.Duration `yaml:"write_timeout"` // 写入超时 - IdleTimeout time.Duration `yaml:"idle_timeout"` // 空闲超时 - MaxConnsPerIP int `yaml:"max_conns_per_ip"` // 每 IP 最大连接数 - MaxRequestsPerConn int `yaml:"max_requests_per_conn"` // 每连接最大请求数 + // Listen 监听地址,如 ":8080" 或 "127.0.0.1:8080" + // 必填字段,决定服务器在哪个地址和端口接收请求 + Listen string `yaml:"listen"` + + // Name 服务器名称,用于虚拟主机匹配 + // 多个服务器可通过 Name 区分不同域名或服务 + Name string `yaml:"name"` + + // Static 静态文件服务配置 + // 用于提供静态资源,如 HTML、CSS、JS、图片等 + Static StaticConfig `yaml:"static"` + + // Proxy 反向代理规则列表 + // 按顺序匹配,首个匹配的规则生效 + Proxy []ProxyConfig `yaml:"proxy"` + + // SSL SSL/TLS 配置 + // HTTPS 必需配置,包含证书和加密设置 + SSL SSLConfig `yaml:"ssl"` + + // Security 安全配置 + // 包含访问控制、限流、认证等安全功能 + Security SecurityConfig `yaml:"security"` + + // Rewrite URL 重写规则 + // 在代理或静态文件服务前执行 URL 转换 + Rewrite []RewriteRule `yaml:"rewrite"` + + // Compression 响应压缩配置 + // 控制 gzip/brotli 压缩行为 + Compression CompressionConfig `yaml:"compression"` + + // ReadTimeout 读取超时 + // 读取完整请求(包括 body)的最大时间 + ReadTimeout time.Duration `yaml:"read_timeout"` + + // WriteTimeout 写入超时 + // 写入响应的最大时间 + WriteTimeout time.Duration `yaml:"write_timeout"` + + // IdleTimeout 空闲超时 + // Keep-Alive 连接的最大空闲时间 + IdleTimeout time.Duration `yaml:"idle_timeout"` + + // MaxConnsPerIP 每 IP 最大连接数 + // 防止单个 IP 占用过多连接资源 + MaxConnsPerIP int `yaml:"max_conns_per_ip"` + + // MaxRequestsPerConn 每连接最大请求数 + // 达到后连接将被优雅关闭 + MaxRequestsPerConn int `yaml:"max_requests_per_conn"` } // StaticConfig 静态文件服务配置。 +// +// 用于配置静态文件服务器的行为,包括根目录和索引文件。 +// +// 注意事项: +// - Root 路径可以是相对路径或绝对路径 +// - 索引文件按顺序查找,第一个存在的文件将被使用 +// - 目录路径需要确保有读取权限 +// +// 使用示例: +// +// static: +// root: "/var/www/html" +// index: ["index.html", "index.htm", "default.html"] type StaticConfig struct { - Root string `yaml:"root"` // 静态文件根目录 - Index []string `yaml:"index"` // 索引文件列表,默认 ["index.html", "index.htm"] + // Root 静态文件根目录 + // 所有静态文件请求都将以此目录为基础解析 + Root string `yaml:"root"` + + // Index 索引文件列表 + // 访问目录时依次查找这些文件作为默认页面 + // 默认为 ["index.html", "index.htm"] + Index []string `yaml:"index"` } // ProxyConfig 反向代理配置,支持负载均衡和健康检查。 +// +// 用于将请求转发到后端服务器,支持多种负载均衡算法 +// 和健康检查机制。 +// +// 注意事项: +// - Path 使用前缀匹配,较长路径优先匹配 +// - 至少配置一个 Target 才能正常工作 +// - 负载均衡算法支持:round_robin、weighted_round_robin、least_conn、ip_hash、consistent_hash +// - 一致性哈希需要配置 HashKey +// +// 使用示例: +// +// proxy: +// - path: "/api/" +// targets: +// - url: "http://backend1:8080" +// weight: 3 +// - url: "http://backend2:8080" +// weight: 1 +// load_balance: "weighted_round_robin" +// health_check: +// interval: 10s +// path: "/health" type ProxyConfig struct { - Path string `yaml:"path"` // 匹配路径前缀 - Targets []ProxyTarget `yaml:"targets"` // 后端目标列表 - LoadBalance string `yaml:"load_balance"` // 负载均衡算法:round_robin, weighted_round_robin, least_conn, ip_hash, consistent_hash - HashKey string `yaml:"hash_key"` // 一致性哈希键:ip, uri, header:X-Name - VirtualNodes int `yaml:"virtual_nodes"` // 一致性哈希虚拟节点数,默认 150 - HealthCheck HealthCheckConfig `yaml:"health_check"` // 健康检查配置 - Timeout ProxyTimeout `yaml:"timeout"` // 超时配置 - Headers ProxyHeaders `yaml:"headers"` // 请求/响应头修改 - Cache ProxyCacheConfig `yaml:"cache"` // 代理缓存配置 + // Path 匹配路径前缀 + // 以此前缀开头的请求将被转发到该代理 + Path string `yaml:"path"` + + // Targets 后端目标列表 + // 支持配置多个后端服务器实现负载均衡 + Targets []ProxyTarget `yaml:"targets"` + + // LoadBalance 负载均衡算法 + // 可选值:round_robin、weighted_round_robin、least_conn、ip_hash、consistent_hash + LoadBalance string `yaml:"load_balance"` + + // HashKey 一致性哈希键 + // 可选值:ip、uri、header:X-Name + HashKey string `yaml:"hash_key"` + + // VirtualNodes 一致性哈希虚拟节点数 + // 影响哈希分布的均匀性,默认为 150 + VirtualNodes int `yaml:"virtual_nodes"` + + // HealthCheck 健康检查配置 + // 定期检查后端服务健康状态 + HealthCheck HealthCheckConfig `yaml:"health_check"` + + // Timeout 超时配置 + // 控制代理连接和读写超时 + Timeout ProxyTimeout `yaml:"timeout"` + + // Headers 请求/响应头修改 + // 可以在转发前后添加、修改或删除 HTTP 头 + Headers ProxyHeaders `yaml:"headers"` + + // Cache 代理缓存配置 + // 启用后缓存后端响应减少重复请求 + Cache ProxyCacheConfig `yaml:"cache"` } // ProxyTarget 后端目标配置。 +// +// 定义单个后端服务器的地址和权重。 +// +// 注意事项: +// - URL 必须包含协议(http:// 或 https://) +// - Weight 仅在 weighted_round_robin 算法下生效 +// +// 使用示例: +// +// targets: +// - url: "http://backend1:8080" +// weight: 3 +// - url: "http://backend2:8080" +// weight: 1 type ProxyTarget struct { - URL string `yaml:"url"` // 后端地址,如 "http://backend1:8080" - Weight int `yaml:"weight"` // 权重,用于加权轮询算法 + // URL 后端地址 + // 格式:"http://host:port" 或 "https://host:port" + URL string `yaml:"url"` + + // Weight 权重 + // 用于加权轮询算法,值越大分配的请求越多 + Weight int `yaml:"weight"` } // HealthCheckConfig 健康检查配置。 +// +// 定期检查后端服务器的健康状态,自动剔除不健康的节点。 +// +// 注意事项: +// - Interval 不宜设置过小,避免增加后端负担 +// - Path 应该是轻量级的健康检查端点 +// - 超时时间应小于检查间隔 +// +// 使用示例: +// +// health_check: +// interval: 10s +// path: "/health" +// timeout: 5s type HealthCheckConfig struct { - Interval time.Duration `yaml:"interval"` // 检查间隔 - Path string `yaml:"path"` // 健康检查路径 - Timeout time.Duration `yaml:"timeout"` // 检查超时时间 + // Interval 检查间隔 + // 每次健康检查之间的时间间隔 + Interval time.Duration `yaml:"interval"` + + // Path 健康检查路径 + // 发送 HTTP GET 请求的路径 + Path string `yaml:"path"` + + // Timeout 检查超时时间 + // 超过此时间未响应视为不健康 + Timeout time.Duration `yaml:"timeout"` } // ProxyTimeout 代理超时配置。 +// +// 控制代理请求的各个阶段超时。 +// +// 注意事项: +// - Connect 超时包括 DNS 解析和 TCP 连接建立 +// - Read 和 Write 超时分别控制响应读取和请求发送 +// - 超时时间需要根据后端服务响应时间调整 +// +// 使用示例: +// +// timeout: +// connect: 5s +// read: 30s +// write: 30s type ProxyTimeout struct { - Connect time.Duration `yaml:"connect"` // 连接超时 - Read time.Duration `yaml:"read"` // 读取超时 - Write time.Duration `yaml:"write"` // 写入超时 + // Connect 连接超时 + // 建立到后端服务器的连接超时 + Connect time.Duration `yaml:"connect"` + + // Read 读取超时 + // 从后端读取响应的超时 + Read time.Duration `yaml:"read"` + + // Write 写入超时 + // 向后端发送请求的超时 + Write time.Duration `yaml:"write"` } // ProxyHeaders 代理请求/响应头配置。 +// +// 在代理转发过程中修改 HTTP 头部。 +// +// 注意事项: +// - SetRequest 添加/修改发送到后端的请求头 +// - SetResponse 添加/修改返回给客户端的响应头 +// - Remove 会删除指定的请求头(在发送到后端之前) +// +// 使用示例: +// +// headers: +// set_request: +// X-Forwarded-For: "$remote_addr" +// X-Real-IP: "$remote_addr" +// set_response: +// X-Proxy-By: "lolly" +// remove: +// - "X-Internal-Header" type ProxyHeaders struct { - SetRequest map[string]string `yaml:"set_request"` // 设置请求头 - SetResponse map[string]string `yaml:"set_response"` // 设置响应头 - Remove []string `yaml:"remove"` // 移除的头部 + // SetRequest 设置请求头 + // 发送到后端的请求中添加或覆盖的头部 + SetRequest map[string]string `yaml:"set_request"` + + // SetResponse 设置响应头 + // 返回给客户端的响应中添加或覆盖的头部 + SetResponse map[string]string `yaml:"set_response"` + + // Remove 移除的头部 + // 从发送到后端的请求中移除的头部列表 + Remove []string `yaml:"remove"` } // ProxyCacheConfig 代理缓存配置。 +// +// 缓存后端响应,减少重复请求,提高响应速度。 +// +// 注意事项: +// - 仅缓存 GET 和 HEAD 请求 +// - 后端响应中 Cache-Control 头会覆盖 MaxAge 设置 +// - CacheLock 可防止缓存击穿,但会增加首次请求延迟 +// - 谨慎缓存动态内容,避免返回过期数据 +// +// 使用示例: +// +// cache: +// enabled: true +// max_age: 5m +// cache_lock: true +// stale_while_revalidate: 1m type ProxyCacheConfig struct { - Enabled bool `yaml:"enabled"` // 是否启用缓存 - MaxAge time.Duration `yaml:"max_age"` // 缓存有效期 - CacheLock bool `yaml:"cache_lock"` // 缓存锁,防止击穿 - StaleWhileRevalidate time.Duration `yaml:"stale_while_revalidate"` // 过期缓存复用时间 + // Enabled 是否启用缓存 + Enabled bool `yaml:"enabled"` + + // MaxAge 缓存有效期 + // 缓存内容的最大存活时间 + MaxAge time.Duration `yaml:"max_age"` + + // CacheLock 缓存锁 + // 防止缓存击穿,同一资源同时只有一个后端请求 + CacheLock bool `yaml:"cache_lock"` + + // StaleWhileRevalidate 过期缓存复用时间 + // 在重新验证期间返回过期缓存的最大时间 + StaleWhileRevalidate time.Duration `yaml:"stale_while_revalidate"` } // SSLConfig SSL/TLS 配置。 +// +// 用于配置 HTTPS 服务所需的证书和加密参数。 +// 支持 TLS 1.2 和 TLS 1.3 协议,可自定义加密套件。 +// +// 注意事项: +// - Cert 和 Key 为必需字段,分别指向证书和私钥文件 +// - CertChain 可选,用于配置完整的证书链 +// - Protocols 建议使用默认值,避免使用不安全的 TLS 1.0/1.1 +// - Ciphers 仅对 TLS 1.2 有效,TLS 1.3 有固定加密套件 +// - 启用 OCSPStapling 可提升握手性能 +// +// 使用示例: +// +// ssl: +// cert: "/etc/ssl/certs/server.crt" +// key: "/etc/ssl/private/server.key" +// cert_chain: "/etc/ssl/certs/chain.crt" +// protocols: ["TLSv1.2", "TLSv1.3"] +// ocsp_stapling: true +// hsts: +// max_age: 31536000 +// include_sub_domains: true type SSLConfig struct { - Cert string `yaml:"cert"` // 证书文件路径 - Key string `yaml:"key"` // 私钥文件路径 - CertChain string `yaml:"cert_chain"` // 证书链文件路径 - Protocols []string `yaml:"protocols"` // TLS 版本,默认 ["TLSv1.2", "TLSv1.3"] - Ciphers []string `yaml:"ciphers"` // 加密套件(仅 TLS 1.2 有效) - OCSPStapling bool `yaml:"ocsp_stapling"` // OCSP Stapling 支持 - HSTS HSTSConfig `yaml:"hsts"` // HSTS 配置 + // Cert 证书文件路径 + // PEM 格式的服务器证书文件 + Cert string `yaml:"cert"` + + // Key 私钥文件路径 + // PEM 格式的私钥文件 + Key string `yaml:"key"` + + // CertChain 证书链文件路径 + // 可选,包含中间证书以支持完整证书链验证 + CertChain string `yaml:"cert_chain"` + + // Protocols TLS 版本列表 + // 默认 ["TLSv1.2", "TLSv1.3"] + Protocols []string `yaml:"protocols"` + + // Ciphers 加密套件列表 + // 仅对 TLS 1.2 有效,建议使用默认值 + Ciphers []string `yaml:"ciphers"` + + // OCSPStapling OCSP Stapling 支持 + // 启用后可在 TLS 握手时提供证书状态信息 + OCSPStapling bool `yaml:"ocsp_stapling"` + + // HSTS HSTS 配置 + // HTTP Strict Transport Security 安全策略 + HSTS HSTSConfig `yaml:"hsts"` } // HSTSConfig HTTP Strict Transport Security 配置。 +// +// 强制浏览器使用 HTTPS 访问,防止中间人攻击和协议降级攻击。 +// +// 注意事项: +// - MaxAge 单位为秒,建议至少设置为 1 年(31536000) +// - IncludeSubDomains 为 true 时策略应用于所有子域名 +// - Preload 为 true 表示申请加入浏览器预加载列表 +// - 启用前确保所有站点资源都支持 HTTPS +// +// 使用示例: +// +// hsts: +// max_age: 31536000 +// include_sub_domains: true +// preload: false type HSTSConfig struct { - MaxAge int `yaml:"max_age"` // 过期时间(秒),默认 31536000(1年) - IncludeSubDomains bool `yaml:"include_sub_domains"` // 包含子域名,默认 true - Preload bool `yaml:"preload"` // 加入 HSTS 预加载列表 + // MaxAge 过期时间(秒) + // 默认 31536000(1年),建议至少 6 个月 + MaxAge int `yaml:"max_age"` + + // IncludeSubDomains 包含子域名 + // 为 true 时策略应用于当前域名及其所有子域名 + IncludeSubDomains bool `yaml:"include_sub_domains"` + + // Preload 加入 HSTS 预加载列表 + // 申请加入浏览器内置的 HSTS 列表 + Preload bool `yaml:"preload"` } // SecurityConfig 安全配置,包含访问控制、限流、认证和安全头部。 +// +// 用于保护服务器免受各种网络攻击和滥用。 +// +// 注意事项: +// - Access 配置 IP 黑白名单控制访问来源 +// - RateLimit 配置请求频率限制防止 DDoS 攻击 +// - Auth 配置 HTTP Basic 认证保护敏感资源 +// - Headers 配置安全响应头部增强浏览器安全 +// - 各项配置可以组合使用,增强安全性 +// +// 使用示例: +// +// security: +// access: +// allow: ["192.168.1.0/24"] +// deny: ["10.0.0.0/8"] +// rate_limit: +// request_rate: 100 +// burst: 150 +// auth: +// type: "basic" +// users: +// - name: "admin" +// password: "$2y$10$..." +// headers: +// x_frame_options: "DENY" type SecurityConfig struct { - Access AccessConfig `yaml:"access"` // IP 访问控制 - RateLimit RateLimitConfig `yaml:"rate_limit"` // 速率限制 - Auth AuthConfig `yaml:"auth"` // 认证配置 - Headers SecurityHeaders `yaml:"headers"` // 安全头部 + // Access IP 访问控制 + // 配置允许或拒绝的 IP 地址/CIDR 范围 + Access AccessConfig `yaml:"access"` + + // RateLimit 速率限制 + // 控制请求频率防止滥用 + RateLimit RateLimitConfig `yaml:"rate_limit"` + + // Auth 认证配置 + // HTTP Basic 认证设置 + Auth AuthConfig `yaml:"auth"` + + // Headers 安全头部 + // 添加安全相关的 HTTP 响应头 + Headers SecurityHeaders `yaml:"headers"` } // AccessConfig IP 访问控制配置。 +// +// 通过 IP 地址或 CIDR 范围控制访问权限。 +// +// 注意事项: +// - Allow 和 Deny 列表按配置顺序匹配 +// - Default 指定未匹配时的默认动作 +// - TrustedProxies 用于正确获取客户端真实 IP +// - 支持 IPv4 和 IPv6 地址格式 +// +// 使用示例: +// +// access: +// allow: ["192.168.1.0/24", "10.0.0.0/8"] +// deny: ["192.168.1.100"] +// default: "deny" +// trusted_proxies: ["172.16.0.0/16"] type AccessConfig struct { - Allow []string `yaml:"allow"` // 允许的 IP/CIDR 列表 - Deny []string `yaml:"deny"` // 拒绝的 IP/CIDR 列表 - Default string `yaml:"default"` // 默认动作:allow 或 deny - TrustedProxies []string `yaml:"trusted_proxies"` // 可信代理 CIDR 列表,用于 X-Forwarded-For 解析 + // Allow 允许的 IP/CIDR 列表 + // 配置允许访问的 IP 地址或网段 + Allow []string `yaml:"allow"` + + // Deny 拒绝的 IP/CIDR 列表 + // 配置拒绝访问的 IP 地址或网段 + Deny []string `yaml:"deny"` + + // Default 默认动作 + // 未匹配任何规则时的处理方式:allow 或 deny + Default string `yaml:"default"` + + // TrustedProxies 可信代理 CIDR 列表 + // 用于正确解析 X-Forwarded-For 头部获取真实客户端 IP + TrustedProxies []string `yaml:"trusted_proxies"` } // RateLimitConfig 速率限制配置。 +// +// 限制请求频率防止 DDoS 攻击和资源滥用。 +// +// 注意事项: +// - RequestRate 为每秒允许的最大请求数 +// - Burst 为突发流量允许的最大请求数 +// - ConnLimit 为单个 IP 的最大并发连接数 +// - Algorithm 支持 token_bucket 和 sliding_window 两种算法 +// - SlidingWindow 仅在 sliding_window 算法下生效 +// +// 使用示例: +// +// rate_limit: +// request_rate: 100 +// burst: 150 +// conn_limit: 50 +// algorithm: "token_bucket" +// key: "ip" type RateLimitConfig struct { - RequestRate int `yaml:"request_rate"` // 每秒请求数限制 - Burst int `yaml:"burst"` // 突发流量上限 - ConnLimit int `yaml:"conn_limit"` // 连接数限制 - Key string `yaml:"key"` // 限流 key 来源:ip, header - Algorithm string `yaml:"algorithm"` // 限流算法:token_bucket, sliding_window - SlidingWindowMode string `yaml:"sliding_window_mode"` // 滑动窗口模式:approximate, precise - SlidingWindow int `yaml:"sliding_window"` // 滑动窗口大小(秒) + // RequestRate 每秒请求数限制 + // 超过此速率的请求将被拒绝 + RequestRate int `yaml:"request_rate"` + + // Burst 突发流量上限 + // 允许短时间内超出 RequestRate 的最大请求数 + Burst int `yaml:"burst"` + + // ConnLimit 连接数限制 + // 单个 IP 的最大并发连接数 + ConnLimit int `yaml:"conn_limit"` + + // Key 限流 key 来源 + // 可选值:ip、header,决定限流键的生成方式 + Key string `yaml:"key"` + + // Algorithm 限流算法 + // 可选值:token_bucket、sliding_window + Algorithm string `yaml:"algorithm"` + + // SlidingWindowMode 滑动窗口模式 + // 可选值:approximate(近似)、precise(精确) + SlidingWindowMode string `yaml:"sliding_window_mode"` + + // SlidingWindow 滑动窗口大小(秒) + // 滑动窗口算法的时间窗口大小 + SlidingWindow int `yaml:"sliding_window"` } // AuthConfig 认证配置。 +// +// 配置 HTTP Basic 认证保护敏感资源。 +// +// 注意事项: +// - Type 目前仅支持 basic +// - RequireTLS 默认为 true,强制 HTTPS 传输 +// - Algorithm 支持 bcrypt 和 argon2id +// - Users 中 Password 字段存储的是密码哈希而非明文 +// - MinPasswordLength 控制密码最小长度要求 +// +// 使用示例: +// +// auth: +// type: "basic" +// require_tls: true +// algorithm: "bcrypt" +// realm: "Secure Area" +// min_password_length: 8 +// users: +// - name: "admin" +// password: "$2y$10$..." type AuthConfig struct { - Type string `yaml:"type"` // 认证类型:basic - RequireTLS bool `yaml:"require_tls"` // 强制 HTTPS,默认 true - Algorithm string `yaml:"algorithm"` // 哈希算法:bcrypt, argon2id - Users []User `yaml:"users"` // 用户列表 - Realm string `yaml:"realm"` // 认证域 - MinPasswordLength int `yaml:"min_password_length"` // 密码最小长度 + // Type 认证类型 + // 目前仅支持 basic + Type string `yaml:"type"` + + // RequireTLS 强制 HTTPS + // 为 true 时只有通过 HTTPS 的请求才允许认证 + RequireTLS bool `yaml:"require_tls"` + + // Algorithm 哈希算法 + // 可选值:bcrypt、argon2id + Algorithm string `yaml:"algorithm"` + + // Users 用户列表 + // 配置允许访问的用户及其密码哈希 + Users []User `yaml:"users"` + + // Realm 认证域 + // 显示在浏览器认证对话框中的描述信息 + Realm string `yaml:"realm"` + + // MinPasswordLength 密码最小长度 + // 密码验证时的最小长度要求 + MinPasswordLength int `yaml:"min_password_length"` } // User 认证用户配置。 +// +// 定义单个认证用户的凭据。 +// +// 注意事项: +// - Name 为用户标识,区分大小写 +// - Password 存储的是哈希值而非明文密码 +// - 支持的哈希格式取决于 Algorithm 设置 +// +// 使用示例: +// +// users: +// - name: "admin" +// password: "$2y$10$N9qo8uLOickgx2ZMRZoMy..." type User struct { - Name string `yaml:"name"` // 用户名 - Password string `yaml:"password"` // 密码哈希 + // Name 用户名 + // 认证时使用的用户标识 + Name string `yaml:"name"` + + // Password 密码哈希 + // bcrypt 或 argon2id 哈希值,非明文密码 + Password string `yaml:"password"` } // SecurityHeaders 安全头部配置。 +// +// 配置 HTTP 安全响应头部增强浏览器安全。 +// +// 注意事项: +// - XFrameOptions 防止点击劫持攻击 +// - XContentTypeOptions 防止 MIME 类型嗅探 +// - ContentSecurityPolicy 控制资源加载策略 +// - ReferrerPolicy 控制 Referer 头发送策略 +// - PermissionsPolicy 控制浏览器功能权限 +// +// 使用示例: +// +// headers: +// x_frame_options: "DENY" +// x_content_type_options: "nosniff" +// content_security_policy: "default-src 'self'" +// referrer_policy: "strict-origin-when-cross-origin" type SecurityHeaders struct { - XFrameOptions string `yaml:"x_frame_options"` // X-Frame-Options: DENY, SAMEORIGIN - XContentTypeOptions string `yaml:"x_content_type_options"` // X-Content-Type-Options: nosniff - ContentSecurityPolicy string `yaml:"content_security_policy"` // Content-Security-Policy - ReferrerPolicy string `yaml:"referrer_policy"` // Referrer-Policy - PermissionsPolicy string `yaml:"permissions_policy"` // Permissions-Policy + // XFrameOptions X-Frame-Options 头部 + // 可选值:DENY、SAMEORIGIN,防止页面被嵌入 iframe + XFrameOptions string `yaml:"x_frame_options"` + + // XContentTypeOptions X-Content-Type-Options 头部 + // 建议值:nosniff,防止浏览器 MIME 类型嗅探 + XContentTypeOptions string `yaml:"x_content_type_options"` + + // ContentSecurityPolicy Content-Security-Policy 头部 + // 控制页面可以加载的资源来源 + ContentSecurityPolicy string `yaml:"content_security_policy"` + + // ReferrerPolicy Referrer-Policy 头部 + // 控制 Referer 头的发送策略 + ReferrerPolicy string `yaml:"referrer_policy"` + + // PermissionsPolicy Permissions-Policy 头部 + // 控制浏览器功能权限(原 Feature-Policy) + PermissionsPolicy string `yaml:"permissions_policy"` } // RewriteRule URL 重写规则。 +// +// 用于在代理或静态文件服务前修改请求 URL。 +// +// 注意事项: +// - Pattern 为正则表达式,用于匹配原始 URL +// - Replacement 为替换后的目标 URL,支持捕获组 +// - Flag 控制重写行为:last、redirect、permanent、break +// - 规则按顺序执行,匹配后根据 Flag 决定是否继续 +// +// 使用示例: +// +// rewrite: +// - pattern: "^/old/(.*)$" +// replacement: "/new/$1" +// flag: "permanent" +// - pattern: "^/api/(.*)$" +// replacement: "/v1/$1" +// flag: "last" type RewriteRule struct { - Pattern string `yaml:"pattern"` // 匹配模式(正则表达式) - Replacement string `yaml:"replacement"` // 替换目标 - Flag string `yaml:"flag"` // 标志:last, redirect, permanent, break + // Pattern 匹配模式 + // 正则表达式,用于匹配请求 URL + Pattern string `yaml:"pattern"` + + // Replacement 替换目标 + // 替换后的 URL 路径,支持 $1、$2 等捕获组引用 + Replacement string `yaml:"replacement"` + + // Flag 标志 + // 可选值: + // - last:停止后续规则匹配 + // - redirect:返回 302 临时重定向 + // - permanent:返回 301 永久重定向 + // - break:停止规则匹配但继续处理 + Flag string `yaml:"flag"` } // CompressionConfig 响应压缩配置。 +// +// 配置响应内容压缩,减少传输数据量。 +// +// 注意事项: +// - Type 支持 gzip、brotli 或 both(同时使用两种) +// - Level 压缩级别 1-9,越高压缩率越好但 CPU 消耗越大 +// - MinSize 低于此大小的响应不压缩 +// - Types 指定哪些 MIME 类型进行压缩 +// - GzipStatic 启用后优先使用预压缩文件 +// +// 使用示例: +// +// compression: +// type: "gzip" +// level: 6 +// min_size: 1024 +// types: ["text/html", "text/css", "application/json"] +// gzip_static: true +// gzip_static_extensions: [".gz"] type CompressionConfig struct { - Type string `yaml:"type"` // 压缩类型:gzip, brotli, both - Level int `yaml:"level"` // 压缩级别:1-9 - MinSize int `yaml:"min_size"` // 最小压缩大小(字节) - Types []string `yaml:"types"` // 可压缩的 MIME 类型 - GzipStatic bool `yaml:"gzip_static"` // 启用预压缩文件支持 - GzipStaticExtensions []string `yaml:"gzip_static_extensions"` // 预压缩文件扩展名 + // Type 压缩类型 + // 可选值:gzip、brotli、both + Type string `yaml:"type"` + + // Level 压缩级别 + // 范围 1-9,1 最快,9 压缩率最高 + Level int `yaml:"level"` + + // MinSize 最小压缩大小(字节) + // 低于此大小的响应不进行压缩 + MinSize int `yaml:"min_size"` + + // Types 可压缩的 MIME 类型 + // 只有这些类型的响应会被压缩 + Types []string `yaml:"types"` + + // GzipStatic 启用预压缩文件支持 + // 为 true 时优先返回 .gz 预压缩文件 + GzipStatic bool `yaml:"gzip_static"` + + // GzipStaticExtensions 预压缩文件扩展名 + // 查找预压缩文件时附加的扩展名列表 + GzipStaticExtensions []string `yaml:"gzip_static_extensions"` } // LoggingConfig 日志配置。 +// +// 配置访问日志和错误日志的输出行为。 +// +// 注意事项: +// - Format 控制日志格式:text 或 json +// - Access 配置访问日志(记录每个请求) +// - Error 配置错误日志(记录错误信息) +// - Path 为空时日志输出到标准输出/标准错误 +// +// 使用示例: +// +// logging: +// format: "json" +// access: +// path: "/var/log/lolly/access.log" +// format: "combined" +// error: +// path: "/var/log/lolly/error.log" +// level: "warn" type LoggingConfig struct { - Format string `yaml:"format"` // 全局格式:text(默认)或 json,控制启动/停止日志 - Access AccessLogConfig `yaml:"access"` // 访问日志 - Error ErrorLogConfig `yaml:"error"` // 错误日志 + // Format 全局格式 + // 可选值:text(默认)、json + Format string `yaml:"format"` + + // Access 访问日志配置 + Access AccessLogConfig `yaml:"access"` + + // Error 错误日志配置 + Error ErrorLogConfig `yaml:"error"` } // AccessLogConfig 访问日志配置。 +// +// 配置访问日志的输出位置和格式。 +// +// 注意事项: +// - Path 为日志文件路径,为空则输出到 stdout +// - Format 支持预设格式或自定义格式 +// - 常用预设格式:common、combined +// +// 使用示例: +// +// access: +// path: "/var/log/lolly/access.log" +// format: "combined" type AccessLogConfig struct { - Path string `yaml:"path"` // 日志文件路径 - Format string `yaml:"format"` // 日志格式 + // Path 日志文件路径 + // 访问日志的输出文件,为空则输出到标准输出 + Path string `yaml:"path"` + + // Format 日志格式 + // 预设格式或自定义日志格式字符串 + Format string `yaml:"format"` } // ErrorLogConfig 错误日志配置。 +// +// 配置错误日志的输出位置和级别。 +// +// 注意事项: +// - Path 为日志文件路径,为空则输出到 stderr +// - Level 控制记录的日志级别阈值 +// - 可选级别:debug、info、warn、error +// +// 使用示例: +// +// error: +// path: "/var/log/lolly/error.log" +// level: "error" type ErrorLogConfig struct { - Path string `yaml:"path"` // 日志文件路径 - Level string `yaml:"level"` // 日志级别:debug, info, warn, error + // Path 日志文件路径 + // 错误日志的输出文件,为空则输出到标准错误 + Path string `yaml:"path"` + + // Level 日志级别 + // 可选值:debug、info、warn、error + Level string `yaml:"level"` } // PerformanceConfig 性能配置。 +// +// 配置服务器性能优化相关参数。 +// +// 注意事项: +// - GoroutinePool 复用 goroutine 减少创建开销 +// - FileCache 缓存静态文件内容提升响应速度 +// - Transport 配置代理连接的连接池参数 +// +// 使用示例: +// +// performance: +// goroutine_pool: +// enabled: true +// max_workers: 1000 +// file_cache: +// max_entries: 10000 +// max_size: 1073741824 +// transport: +// max_idle_conns: 100 type PerformanceConfig struct { - GoroutinePool GoroutinePoolConfig `yaml:"goroutine_pool"` // Goroutine 池 - FileCache FileCacheConfig `yaml:"file_cache"` // 文件缓存 - Transport TransportConfig `yaml:"transport"` // HTTP Transport + // GoroutinePool Goroutine 池配置 + // 控制 worker goroutine 的复用行为 + GoroutinePool GoroutinePoolConfig `yaml:"goroutine_pool"` + + // FileCache 文件缓存配置 + // 缓存静态文件内容避免重复磁盘 IO + FileCache FileCacheConfig `yaml:"file_cache"` + + // Transport HTTP Transport 配置 + // 代理连接池的参数设置 + Transport TransportConfig `yaml:"transport"` } // GoroutinePoolConfig Goroutine 池配置。 +// +// 复用 goroutine 减少创建和销毁开销。 +// +// 注意事项: +// - Enabled 为 true 时启用 goroutine 池 +// - MaxWorkers 限制最大并发 worker 数 +// - MinWorkers 预热 worker 数量 +// - IdleTimeout 空闲 worker 回收时间 +// +// 使用示例: +// +// goroutine_pool: +// enabled: true +// max_workers: 1000 +// min_workers: 100 +// idle_timeout: 60s type GoroutinePoolConfig struct { - Enabled bool `yaml:"enabled"` // 是否启用 - MaxWorkers int `yaml:"max_workers"` // 最大 worker 数 - MinWorkers int `yaml:"min_workers"` // 最小 worker 数(预热) - IdleTimeout time.Duration `yaml:"idle_timeout"` // 空闲超时 + // Enabled 是否启用 + Enabled bool `yaml:"enabled"` + + // MaxWorkers 最大 worker 数 + // 限制同时运行的最大 goroutine 数量 + MaxWorkers int `yaml:"max_workers"` + + // MinWorkers 最小 worker 数(预热) + // 启动时预创建的 goroutine 数量 + MinWorkers int `yaml:"min_workers"` + + // IdleTimeout 空闲超时 + // 空闲 worker 超过此时间将被回收 + IdleTimeout time.Duration `yaml:"idle_timeout"` } // FileCacheConfig 文件缓存配置。 +// +// 缓存静态文件内容减少磁盘 IO。 +// +// 注意事项: +// - MaxEntries 限制最大缓存文件数量 +// - MaxSize 限制缓存总内存使用量(字节) +// - Inactive 超过此时间未访问的文件将被淘汰 +// - LRUEviction 已废弃,请使用 MaxSize +// +// 使用示例: +// +// file_cache: +// max_entries: 10000 +// max_size: 1073741824 +// inactive: 60s type FileCacheConfig struct { - MaxEntries int64 `yaml:"max_entries"` // 最大缓存条目数 - MaxSize int64 `yaml:"max_size"` // 内存上限(字节) - Inactive time.Duration `yaml:"inactive"` // 未访问淘汰时间 + // MaxEntries 最大缓存条目数 + // 缓存文件的最大数量限制 + MaxEntries int64 `yaml:"max_entries"` + + // MaxSize 内存上限(字节) + // 缓存占用的最大内存限制 + MaxSize int64 `yaml:"max_size"` + + // Inactive 未访问淘汰时间 + // 超过此时间未被访问的缓存将被清除 + Inactive time.Duration `yaml:"inactive"` + + // LRUEviction 启用 LRU 淘汰 // Deprecated: 该字段已废弃,将在未来版本中移除,请使用 MaxSize 代替 - LRUEviction bool `yaml:"lru_eviction"` // 启用 LRU 淘汰(已废弃) + LRUEviction bool `yaml:"lru_eviction"` } // TransportConfig HTTP Transport 配置。 +// +// 配置代理后端连接的连接池参数。 +// +// 注意事项: +// - MaxIdleConns 控制所有主机的总空闲连接数 +// - MaxIdleConnsPerHost 控制每个后端主机的空闲连接 +// - IdleConnTimeout 控制空闲连接的保持时间 +// - MaxConnsPerHost 限制每个后端主机的总连接数 +// +// 使用示例: +// +// transport: +// max_idle_conns: 100 +// max_idle_conns_per_host: 10 +// idle_conn_timeout: 90s +// max_conns_per_host: 100 type TransportConfig struct { - MaxIdleConns int `yaml:"max_idle_conns"` // 最大空闲连接数 - MaxIdleConnsPerHost int `yaml:"max_idle_conns_per_host"` // 每主机最大空闲连接 - IdleConnTimeout time.Duration `yaml:"idle_conn_timeout"` // 空闲连接超时 - MaxConnsPerHost int `yaml:"max_conns_per_host"` // 每主机最大连接数 + // MaxIdleConns 最大空闲连接数 + // 所有后端主机的总空闲连接上限 + MaxIdleConns int `yaml:"max_idle_conns"` + + // MaxIdleConnsPerHost 每主机最大空闲连接 + // 单个后端主机的最大空闲连接数 + MaxIdleConnsPerHost int `yaml:"max_idle_conns_per_host"` + + // IdleConnTimeout 空闲连接超时 + // 空闲连接的最大存活时间 + IdleConnTimeout time.Duration `yaml:"idle_conn_timeout"` + + // MaxConnsPerHost 每主机最大连接数 + // 单个后端主机的总连接数上限 + MaxConnsPerHost int `yaml:"max_conns_per_host"` } // MonitoringConfig 监控配置。 +// +// 配置服务状态监控和健康检查端点。 +// +// 注意事项: +// - Status 配置状态检查端点 +// - 监控端点建议限制访问 IP 防止信息泄露 +// +// 使用示例: +// +// monitoring: +// status: +// path: "/status" +// allow: ["127.0.0.1", "10.0.0.0/8"] type MonitoringConfig struct { - Status StatusConfig `yaml:"status"` // 状态端点配置 + // Status 状态端点配置 + // 服务健康状态检查端点 + Status StatusConfig `yaml:"status"` } // StatusConfig 状态监控端点配置。 +// +// 配置服务状态检查端点的路径和访问控制。 +// +// 注意事项: +// - Path 为状态端点的 URL 路径 +// - Allow 限制可访问的 IP 地址列表 +// - 生产环境建议严格限制访问来源 +// +// 使用示例: +// +// status: +// path: "/status" +// allow: ["127.0.0.1", "192.168.0.0/16"] type StatusConfig struct { - Path string `yaml:"path"` // 端点路径 - Allow []string `yaml:"allow"` // 允许访问的 IP 列表 + // Path 端点路径 + // 状态检查端点的 URL 路径 + Path string `yaml:"path"` + + // Allow 允许访问的 IP 列表 + // 可访问状态端点的 IP 地址或 CIDR + Allow []string `yaml:"allow"` } // StreamConfig TCP/UDP Stream 代理配置。 +// +// 用于四层网络代理,如数据库、Redis 等 TCP/UDP 服务。 +// +// 注意事项: +// - Listen 配置监听地址 +// - Protocol 支持 tcp 或 udp +// - Upstream 配置后端目标列表 +// - Stream 代理工作在传输层,不解析应用层协议 +// +// 使用示例: +// +// stream: +// - listen: ":3306" +// protocol: "tcp" +// upstream: +// targets: +// - addr: "mysql1:3306" +// weight: 3 +// - addr: "mysql2:3306" +// weight: 1 +// load_balance: "round_robin" type StreamConfig struct { - Listen string `yaml:"listen"` // 监听地址,如 ":3306" - Protocol string `yaml:"protocol"` // 协议:tcp 或 udp - Upstream StreamUpstream `yaml:"upstream"` // 上游配置 + // Listen 监听地址 + // TCP/UDP 监听地址,如 ":3306" 或 "0.0.0.0:6379" + Listen string `yaml:"listen"` + + // Protocol 协议类型 + // 可选值:tcp、udp + Protocol string `yaml:"protocol"` + + // Upstream 上游配置 + // 后端服务器列表和负载均衡设置 + Upstream StreamUpstream `yaml:"upstream"` } // StreamUpstream Stream 上游配置。 +// +// 配置 Stream 代理的后端服务器列表。 +// +// 注意事项: +// - Targets 配置后端服务器地址 +// - LoadBalance 配置负载均衡算法 +// +// 使用示例: +// +// upstream: +// targets: +// - addr: "backend1:3306" +// weight: 3 +// load_balance: "round_robin" type StreamUpstream struct { - Targets []StreamTarget `yaml:"targets"` // 目标列表 - LoadBalance string `yaml:"load_balance"` // 负载均衡算法 + // Targets 目标列表 + // 后端服务器地址列表 + Targets []StreamTarget `yaml:"targets"` + + // LoadBalance 负载均衡算法 + // 可选值:round_robin、least_conn + LoadBalance string `yaml:"load_balance"` } // StreamTarget Stream 目标配置。 +// +// 定义单个 Stream 后端服务器。 +// +// 注意事项: +// - Addr 为后端服务器地址 +// - Weight 在加权轮询算法下生效 +// +// 使用示例: +// +// targets: +// - addr: "mysql1:3306" +// weight: 3 +// - addr: "mysql2:3306" +// weight: 1 type StreamTarget struct { - Addr string `yaml:"addr"` // 目标地址,如 "mysql1:3306" - Weight int `yaml:"weight"` // 权重 + // Addr 目标地址 + // 后端服务器地址,如 "host:port" + Addr string `yaml:"addr"` + + // Weight 权重 + // 用于加权轮询负载均衡 + Weight int `yaml:"weight"` } // Load 从文件加载配置。 diff --git a/internal/config/defaults.go b/internal/config/defaults.go index 468b8f2..f25c051 100644 --- a/internal/config/defaults.go +++ b/internal/config/defaults.go @@ -120,10 +120,9 @@ func DefaultConfig() *Config { IdleTimeout: 60 * time.Second, }, FileCache: FileCacheConfig{ - MaxEntries: 10000, - MaxSize: 256 * 1024 * 1024, // 256MB - Inactive: 20 * time.Second, - LRUEviction: true, + MaxEntries: 10000, + MaxSize: 256 * 1024 * 1024, // 256MB + Inactive: 20 * time.Second, }, Transport: TransportConfig{ MaxIdleConns: 100, @@ -234,7 +233,12 @@ func GenerateConfigYAML(cfg *Config) ([]byte, error) { for _, proto := range cfg.Server.SSL.Protocols { fmt.Fprintf(&buf, " # - \"%s\"\n", proto) } - buf.WriteString(" # ciphers: [] # 加密套件(仅 TLS 1.2 有效)\n") + buf.WriteString(" # ciphers: # 加密套件(仅 TLS 1.2 有效,TLS 1.3 使用内置套件)\n") + buf.WriteString(" # - ECDHE-ECDSA-AES256-GCM-SHA384\n") + buf.WriteString(" # - ECDHE-RSA-AES256-GCM-SHA384\n") + buf.WriteString(" # - ECDHE-ECDSA-CHACHA20-POLY1305\n") + buf.WriteString(" # - ECDHE-RSA-CHACHA20-POLY1305\n") + buf.WriteString(" # # 拒绝不安全套件:含 RC4、DES、3DES、CBC 的配置将报错\n") fmt.Fprintf(&buf, " # ocsp_stapling: %v # OCSP Stapling\n", cfg.Server.SSL.OCSPStapling) buf.WriteString(" # hsts: # HTTP Strict Transport Security\n") fmt.Fprintf(&buf, " # max_age: %d # 过期时间(秒)\n", cfg.Server.SSL.HSTS.MaxAge) @@ -274,7 +278,7 @@ func GenerateConfigYAML(cfg *Config) ([]byte, error) { buf.WriteString(" # 安全头部\n") buf.WriteString(" headers:\n") fmt.Fprintf(&buf, " x_frame_options: \"%s\" # 防止点击劫持(有效值: DENY, SAMEORIGIN, 空表示禁用)\n", cfg.Server.Security.Headers.XFrameOptions) - fmt.Fprintf(&buf, " x_content_type_options: \"%s\" # 防止 MIME 嗅探\n", cfg.Server.Security.Headers.XContentTypeOptions) + fmt.Fprintf(&buf, " x_content_type_options: \"%s\" # 防止 MIME 嗅探(有效值:nosniff,空表示禁用)\n", cfg.Server.Security.Headers.XContentTypeOptions) fmt.Fprintf(&buf, " referrer_policy: \"%s\" # 引用策略(有效值: no-referrer, no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url)\n", cfg.Server.Security.Headers.ReferrerPolicy) buf.WriteString(" # content_security_policy: \"default-src 'self'\" # 内容安全策略 CSP\n") buf.WriteString(" # permissions_policy: \"geolocation=(), microphone=()\" # 权限策略\n") @@ -292,7 +296,7 @@ func GenerateConfigYAML(cfg *Config) ([]byte, error) { buf.WriteString(" # 响应压缩配置\n") buf.WriteString(" compression:\n") fmt.Fprintf(&buf, " type: \"%s\" # 压缩类型(有效值: gzip, brotli, both,空表示禁用)\n", cfg.Server.Compression.Type) - fmt.Fprintf(&buf, " level: %d # 压缩级别(范围 1-9,值越大压缩率越高但速度越慢)\n", cfg.Server.Compression.Level) + fmt.Fprintf(&buf, " level: %d # 压缩级别(范围 0-9,0=不压缩,1=最快,9=最高压缩率)\n", cfg.Server.Compression.Level) fmt.Fprintf(&buf, " min_size: %d # 最小压缩大小(字节,小于此值不压缩)\n", cfg.Server.Compression.MinSize) fmt.Fprintf(&buf, " gzip_static: %v # 启用预压缩文件支持(自动查找 .gz/.br 文件)\n", cfg.Server.Compression.GzipStatic) buf.WriteString(" gzip_static_extensions: # 预压缩文件扩展名\n") @@ -398,8 +402,6 @@ func GenerateConfigYAML(cfg *Config) ([]byte, error) { fmt.Fprintf(&buf, " max_entries: %d # 最大缓存条目\n", cfg.Performance.FileCache.MaxEntries) fmt.Fprintf(&buf, " max_size: %d # 内存上限(字节,%dMB)\n", cfg.Performance.FileCache.MaxSize, cfg.Performance.FileCache.MaxSize/1024/1024) fmt.Fprintf(&buf, " inactive: %ds # 未访问淘汰时间\n", int(cfg.Performance.FileCache.Inactive.Seconds())) - buf.WriteString(" # Deprecated: lru_eviction 已废弃,将在未来版本中移除\n") - fmt.Fprintf(&buf, " lru_eviction: %v # 启用 LRU 淘汰\n", cfg.Performance.FileCache.LRUEviction) buf.WriteString(" transport: # HTTP Transport 连接池\n") fmt.Fprintf(&buf, " max_idle_conns: %d # 最大空闲连接\n", cfg.Performance.Transport.MaxIdleConns) fmt.Fprintf(&buf, " max_idle_conns_per_host: %d # 每主机空闲连接\n", cfg.Performance.Transport.MaxIdleConnsPerHost) diff --git a/internal/config/defaults_test.go b/internal/config/defaults_test.go index feca27b..d61578d 100644 --- a/internal/config/defaults_test.go +++ b/internal/config/defaults_test.go @@ -1,3 +1,11 @@ +// Package config 提供默认配置生成功能的测试。 +// +// 该文件测试默认配置模块的各项功能,包括: +// - DefaultConfig 默认值验证 +// - GenerateConfigYAML YAML 生成测试 +// - 性能配置默认值测试 +// +// 作者:xfy package config import ( @@ -7,6 +15,7 @@ import ( ) func TestDefaultConfig(t *testing.T) { + // TestDefaultConfig 测试默认配置生成。 cfg := DefaultConfig() // 验证 Listen 默认值 @@ -55,6 +64,7 @@ func TestDefaultConfig(t *testing.T) { } func TestGenerateConfigYAML(t *testing.T) { + // TestGenerateConfigYAML 测试 YAML 配置生成。 cfg := DefaultConfig() yamlData, err := GenerateConfigYAML(cfg) @@ -82,6 +92,7 @@ func TestGenerateConfigYAML(t *testing.T) { } func TestDefaultConfigPerformance(t *testing.T) { + // TestDefaultConfigPerformance 测试性能配置默认值。 cfg := DefaultConfig() // 验证 GoroutinePool 默认值 @@ -108,9 +119,7 @@ func TestDefaultConfigPerformance(t *testing.T) { if cfg.Performance.FileCache.Inactive != 20*time.Second { t.Errorf("FileCache.Inactive 期望 20s, 实际 %v", cfg.Performance.FileCache.Inactive) } - if !cfg.Performance.FileCache.LRUEviction { - t.Errorf("FileCache.LRUEviction 期望 true, 实际 %v", cfg.Performance.FileCache.LRUEviction) - } + // 注意: LRUEviction 已废弃,不再验证 // 验证 Transport 默认值 if cfg.Performance.Transport.MaxIdleConns != 100 { diff --git a/internal/config/validate_test.go b/internal/config/validate_test.go index f3f5cf8..6d98cfa 100644 --- a/internal/config/validate_test.go +++ b/internal/config/validate_test.go @@ -1,4 +1,17 @@ // Package config 提供 YAML 配置文件的解析、验证和默认配置生成功能。 +// +// 该文件测试配置验证模块的各项功能,包括: +// - 服务器配置验证 +// - 代理配置验证 +// - SSL 配置验证 +// - 认证配置验证 +// - 速率限制验证 +// - 压缩配置验证 +// - 访问控制验证 +// - Stream 配置验证 +// - 性能配置验证 +// +// 作者:xfy package config import ( @@ -7,6 +20,7 @@ import ( ) func TestValidateServer(t *testing.T) { + // TestValidateServer 测试服务器配置验证。 tests := []struct { name string config ServerConfig @@ -85,6 +99,7 @@ func TestValidateServer(t *testing.T) { } func TestValidateProxy(t *testing.T) { + // TestValidateProxy 测试代理配置验证。 tests := []struct { name string config ProxyConfig @@ -176,6 +191,7 @@ func TestValidateProxy(t *testing.T) { } func TestValidateSSL(t *testing.T) { + // TestValidateSSL 测试 SSL 配置验证。 tests := []struct { name string config SSLConfig @@ -287,6 +303,7 @@ func TestValidateSSL(t *testing.T) { } func TestValidateAuth(t *testing.T) { + // TestValidateAuth 测试认证配置验证。 tests := []struct { name string config AuthConfig @@ -387,6 +404,7 @@ func TestValidateAuth(t *testing.T) { } func TestValidateRateLimit(t *testing.T) { + // TestValidateRateLimit 测试速率限制配置验证。 tests := []struct { name string config RateLimitConfig @@ -471,6 +489,7 @@ func TestValidateRateLimit(t *testing.T) { } func TestValidateCompression(t *testing.T) { + // TestValidateCompression 测试压缩配置验证。 tests := []struct { name string config CompressionConfig @@ -574,6 +593,7 @@ func TestValidateCompression(t *testing.T) { } func TestValidateAccess(t *testing.T) { + // TestValidateAccess 测试访问控制配置验证。 tests := []struct { name string config AccessConfig @@ -675,6 +695,7 @@ func TestValidateAccess(t *testing.T) { } func TestValidateStatic(t *testing.T) { + // TestValidateStatic 测试静态文件配置验证。 tests := []struct { name string config StaticConfig @@ -732,6 +753,7 @@ func TestValidateStatic(t *testing.T) { } func TestValidateSecurity(t *testing.T) { + // TestValidateSecurity 测试安全配置验证。 tests := []struct { name string config SecurityConfig @@ -813,6 +835,7 @@ func TestValidateSecurity(t *testing.T) { } func TestValidateStream(t *testing.T) { + // TestValidateStream 测试 Stream 代理配置验证。 tests := []struct { name string config StreamConfig @@ -820,7 +843,7 @@ func TestValidateStream(t *testing.T) { errMsg string }{ { - name: "valid tcp stream", + name: "有效 TCP Stream", config: StreamConfig{ Listen: ":3306", Protocol: "tcp", @@ -832,7 +855,7 @@ func TestValidateStream(t *testing.T) { wantErr: false, }, { - name: "valid udp stream", + name: "有效 UDP Stream", config: StreamConfig{ Listen: ":53", Protocol: "udp", @@ -844,7 +867,7 @@ func TestValidateStream(t *testing.T) { wantErr: false, }, { - name: "empty listen", + name: "监听地址为空", config: StreamConfig{ Listen: "", Protocol: "tcp", @@ -853,7 +876,7 @@ func TestValidateStream(t *testing.T) { errMsg: "listen 地址必填", }, { - name: "invalid protocol", + name: "无效协议类型", config: StreamConfig{ Listen: ":3306", Protocol: "http", @@ -862,7 +885,7 @@ func TestValidateStream(t *testing.T) { errMsg: "无效的协议类型", }, { - name: "no targets", + name: "无目标地址", config: StreamConfig{ Listen: ":3306", Protocol: "tcp", @@ -874,7 +897,7 @@ func TestValidateStream(t *testing.T) { errMsg: "upstream.targets 至少需要一个目标地址", }, { - name: "empty target addr", + name: "目标地址为空", config: StreamConfig{ Listen: ":3306", Protocol: "tcp", @@ -908,6 +931,7 @@ func TestValidateStream(t *testing.T) { } func TestValidatePerformance(t *testing.T) { + // TestValidatePerformance 测试性能配置验证。 tests := []struct { name string config PerformanceConfig diff --git a/internal/handler/router.go b/internal/handler/router.go index b107107..73176e9 100644 --- a/internal/handler/router.go +++ b/internal/handler/router.go @@ -29,39 +29,91 @@ type Router struct { router *router.Router } -// NewRouter 创建路由器 +// NewRouter 创建路由器。 +// +// 初始化并返回一个新的 Router 实例,底层使用 fasthttp/router 实现。 +// +// 返回值: +// - *Router: 新创建的路由器实例 +// +// 使用示例: +// +// r := handler.NewRouter() +// r.GET("/api", myHandler) func NewRouter() *Router { return &Router{ router: router.New(), } } -// GET 注册 GET 路由 +// GET 注册 GET 路由。 +// +// 将指定路径映射到对应的处理器函数。 +// +// 参数: +// - path: 路由路径,支持参数化路径如 /user/{id} +// - handler: 请求处理函数 func (r *Router) GET(path string, handler fasthttp.RequestHandler) { r.router.GET(path, handler) } -// POST 注册 POST 路由 +// POST 注册 POST 路由。 +// +// 将指定路径映射到对应的处理器函数。 +// +// 参数: +// - path: 路由路径,支持参数化路径 +// - handler: 请求处理函数 func (r *Router) POST(path string, handler fasthttp.RequestHandler) { r.router.POST(path, handler) } -// PUT 注册 PUT 路由 +// PUT 注册 PUT 路由。 +// +// 将指定路径映射到对应的处理器函数。 +// +// 参数: +// - path: 路由路径 +// - handler: 请求处理函数 func (r *Router) PUT(path string, handler fasthttp.RequestHandler) { r.router.PUT(path, handler) } -// DELETE 注册 DELETE 路由 +// DELETE 注册 DELETE 路由。 +// +// 将指定路径映射到对应的处理器函数。 +// +// 参数: +// - path: 路由路径 +// - handler: 请求处理函数 func (r *Router) DELETE(path string, handler fasthttp.RequestHandler) { r.router.DELETE(path, handler) } -// HEAD 注册 HEAD 路由 +// HEAD 注册 HEAD 路由。 +// +// 将指定路径映射到对应的处理器函数。 +// +// 参数: +// - path: 路由路径 +// - handler: 请求处理函数 func (r *Router) HEAD(path string, handler fasthttp.RequestHandler) { r.router.HEAD(path, handler) } -// Handler 返回路由处理器 +// Handler 返回路由处理器。 +// +// 获取可用于 fasthttp.Server 的请求处理器。 +// 该处理器会根据注册的路由规则分发请求。 +// +// 返回值: +// - fasthttp.RequestHandler: 请求处理器函数 +// +// 使用示例: +// +// server := &fasthttp.Server{ +// Handler: r.Handler(), +// } func (r *Router) Handler() fasthttp.RequestHandler { return r.router.Handler } diff --git a/internal/handler/router_test.go b/internal/handler/router_test.go index f5a77ea..ce7b910 100644 --- a/internal/handler/router_test.go +++ b/internal/handler/router_test.go @@ -1,4 +1,16 @@ -// Package handler 提供路由器的测试。 +// Package handler 提供路由器功能的测试。 +// +// 该文件测试路由器模块的各项功能,包括: +// - GET 路由注册 +// - POST 路由注册 +// - PUT 路由注册 +// - DELETE 路由注册 +// - HEAD 路由注册 +// - 多方法路由区分 +// - 多路由注册 +// - 未匹配路由处理 +// +// 作者:xfy package handler import ( diff --git a/internal/handler/sendfile.go b/internal/handler/sendfile.go index 31dab34..4528a87 100644 --- a/internal/handler/sendfile.go +++ b/internal/handler/sendfile.go @@ -34,7 +34,30 @@ const ( ) // SendFile 零拷贝文件传输。 -// 大文件使用系统调用直接从文件传输到 socket,避免用户空间拷贝。 +// +// 大文件使用系统调用直接从文件传输到 socket,避免用户空间拷贝, +// 从而减少 CPU 和内存开销,提升传输性能。 +// +// 参数: +// - ctx: fasthttp 请求上下文,用于获取底层连接 +// - file: 要传输的文件对象 +// - offset: 文件起始偏移量(字节) +// - length: 传输长度(字节),-1 表示传输到文件末尾 +// +// 返回值: +// - error: 传输过程中的错误 +// +// 注意事项: +// - 小于 8KB 的文件使用普通 io.Copy +// - Linux 使用 sendfile 系统调用 +// - macOS 和 Windows 使用 fallback 方式 +// +// 使用示例: +// +// file, _ := os.Open("large_file.bin") +// defer file.Close() +// info, _ := file.Stat() +// err := SendFile(ctx, file, 0, info.Size()) func SendFile(ctx *fasthttp.RequestCtx, file *os.File, offset, length int64) error { // 小文件使用普通 io.Copy if length < MinSendfileSize { @@ -58,12 +81,29 @@ func SendFile(ctx *fasthttp.RequestCtx, file *os.File, offset, length int64) err } // getNetConn 从 fasthttp.RequestCtx 获取底层 net.Conn。 +// +// 参数: +// - ctx: fasthttp 请求上下文 +// +// 返回值: +// - net.Conn: 底层网络连接,如果无法获取则返回 nil func getNetConn(ctx *fasthttp.RequestCtx) net.Conn { // fasthttp 内部使用 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 { @@ -82,6 +122,17 @@ func copyFile(ctx *fasthttp.RequestCtx, file *os.File, offset, length int64) err } // platformSendfile 平台特定的 sendfile 实现。 +// +// 根据运行平台选择合适的零拷贝传输方式。 +// +// 参数: +// - conn: 目标网络连接 +// - file: 源文件对象 +// - offset: 文件起始偏移量 +// - length: 传输长度 +// +// 返回值: +// - error: 传输错误或不支持时返回 ENOTSUP func platformSendfile(conn net.Conn, file *os.File, offset, length int64) error { switch runtime.GOOS { case "linux": @@ -98,6 +149,17 @@ func platformSendfile(conn net.Conn, file *os.File, offset, length int64) error } // linuxSendfile Linux sendfile 系统调用。 +// +// 使用 Linux 特有的 sendfile 系统调用实现零拷贝传输。 +// +// 参数: +// - conn: 目标网络连接 +// - fileFd: 源文件描述符 +// - offset: 文件起始偏移量 +// - length: 传输长度 +// +// 返回值: +// - error: 系统调用错误 func linuxSendfile(conn net.Conn, fileFd uintptr, offset, length int64) error { socketFd, err := getSocketFd(conn) if err != nil { @@ -124,6 +186,19 @@ func linuxSendfile(conn net.Conn, fileFd uintptr, offset, length int64) error { } // getSocketFd 获取 socket 文件描述符。 +// +// 从网络连接中提取底层的文件描述符,用于 sendfile 系统调用。 +// +// 参数: +// - conn: 网络连接对象 +// +// 返回值: +// - uintptr: 文件描述符 +// - error: 不支持的连接类型或获取失败时返回错误 +// +// 支持的连接类型: +// - *net.TCPConn +// - *net.UnixConn func getSocketFd(conn net.Conn) (uintptr, error) { switch c := conn.(type) { case *net.TCPConn: diff --git a/internal/handler/sendfile_test.go b/internal/handler/sendfile_test.go index e3177b0..3a1602e 100644 --- a/internal/handler/sendfile_test.go +++ b/internal/handler/sendfile_test.go @@ -1,3 +1,18 @@ +// Package handler 提供 Sendfile 功能的测试。 +// +// 该文件测试 Sendfile 模块的各项功能,包括: +// - 最小 Sendfile 大小 +// - 平台 Sendfile 行为 +// - 文件复制功能 +// - 非 Linux 平台行为 +// - 套接字文件描述符获取 +// - 小文件发送 +// - 带偏移量发送 +// - 零长度文件 +// - 网络连接获取 +// - 错误处理 +// +// 作者:xfy package handler import ( diff --git a/internal/handler/static.go b/internal/handler/static.go index 7365ce5..34fd721 100644 --- a/internal/handler/static.go +++ b/internal/handler/static.go @@ -32,6 +32,11 @@ import ( // StaticHandler 静态文件处理器。 // // 提供静态文件服务,支持目录索引、文件缓存和零拷贝传输。 +// +// 注意事项: +// - 自动处理目录遍历攻击防护(拒绝包含 ".." 的路径) +// - 并发安全,可在多个 goroutine 中使用 +// - 大文件(>= 8KB)自动启用零拷贝传输 type StaticHandler struct { // root 静态文件根目录 root string @@ -49,7 +54,21 @@ type StaticHandler struct { gzipStatic *compression.GzipStatic } -// NewStaticHandler 创建静态文件处理器 +// NewStaticHandler 创建静态文件处理器。 +// +// 初始化并返回一个新的静态文件处理器实例。 +// +// 参数: +// - root: 静态文件根目录路径 +// - index: 索引文件列表,当请求目录时依次查找(如 ["index.html", "index.htm"]) +// - useSendfile: 是否启用零拷贝传输(大文件优化) +// +// 返回值: +// - *StaticHandler: 新创建的静态文件处理器 +// +// 使用示例: +// +// handler := handler.NewStaticHandler("/var/www", []string{"index.html"}, true) func NewStaticHandler(root string, index []string, useSendfile bool) *StaticHandler { return &StaticHandler{ root: root, @@ -58,19 +77,54 @@ func NewStaticHandler(root string, index []string, useSendfile bool) *StaticHand } } -// SetFileCache 设置文件缓存 +// SetFileCache 设置文件缓存。 +// +// 为静态文件处理器启用文件缓存功能。 +// 缓存可以显著提升小文件的访问性能。 +// +// 参数: +// - fc: 文件缓存实例 +// +// 注意事项: +// - 仅对小于 1MB 的文件启用缓存 +// - 缓存会自动检测文件修改并更新 func (h *StaticHandler) SetFileCache(fc *cache.FileCache) { h.fileCache = fc } -// SetGzipStatic 设置预压缩文件支持 +// SetGzipStatic 设置预压缩文件支持。 +// +// 启用后,对于匹配扩展名的请求,优先发送 .gz 预压缩文件。 +// +// 参数: +// - enabled: 是否启用预压缩支持 +// - extensions: 需要支持预压缩的文件扩展名列表(如 [".html", ".css", ".js"]) +// +// 使用示例: +// +// handler.SetGzipStatic(true, []string{".html", ".css", ".js"}) func (h *StaticHandler) SetGzipStatic(enabled bool, extensions []string) { if enabled { h.gzipStatic = compression.NewGzipStatic(true, h.root, extensions) } } -// Handle 处理静态文件请求 +// Handle 处理静态文件请求。 +// +// 根据请求路径查找并返回对应的静态文件。 +// 支持目录索引文件、缓存查找和零拷贝传输。 +// +// 参数: +// - ctx: fasthttp 请求上下文 +// +// 处理流程: +// 1. 安全检查:防止目录遍历攻击 +// 2. 检查文件/目录是否存在 +// 3. 如果是目录,尝试查找索引文件 +// 4. 尝试发送预压缩文件 +// 5. 尝试从缓存获取 +// 6. 大文件使用零拷贝传输 +// 7. 读取文件并存入缓存 func (h *StaticHandler) Handle(ctx *fasthttp.RequestCtx) { path := string(ctx.Path()) @@ -107,7 +161,14 @@ func (h *StaticHandler) Handle(ctx *fasthttp.RequestCtx) { h.serveFile(ctx, filePath, info) } -// serveFile 提供文件服务,支持缓存和零拷贝传输 +// serveFile 提供文件服务,支持缓存和零拷贝传输。 +// +// 内部方法,负责实际的文件发送逻辑。 +// +// 参数: +// - ctx: fasthttp 请求上下文 +// - filePath: 文件绝对路径 +// - info: 文件信息(用于判断文件大小和修改时间) func (h *StaticHandler) serveFile(ctx *fasthttp.RequestCtx, filePath string, info os.FileInfo) { // 尝试发送预压缩文件 if h.gzipStatic != nil { diff --git a/internal/handler/static_test.go b/internal/handler/static_test.go index 18795d6..9b94417 100644 --- a/internal/handler/static_test.go +++ b/internal/handler/static_test.go @@ -1,4 +1,20 @@ -// Package handler 提供静态文件处理器的测试。 +// Package handler 提供静态文件处理器功能的测试。 +// +// 该文件测试静态文件处理器模块的各项功能,包括: +// - 正常文件访问 +// - 嵌套路径文件 +// - 目录索引文件 +// - 路径遍历安全检查 +// - 索引文件优先级 +// - 构造函数 +// - 文件缓存设置 +// - Gzip 静态文件设置 +// - HEAD 请求处理 +// - 预压缩文件支持 +// - 大文件处理 +// - 符号链接处理 +// +// 作者:xfy package handler import ( diff --git a/internal/http3/adapter_test.go b/internal/http3/adapter_test.go index ba5d794..a9d3e01 100644 --- a/internal/http3/adapter_test.go +++ b/internal/http3/adapter_test.go @@ -1,3 +1,13 @@ +// Package http3 提供 HTTP/3 适配器功能的测试。 +// +// 该文件测试 HTTP/3 适配器模块的各项功能,包括: +// - 适配器创建和初始化 +// - HTTP 请求到 fasthttp 请求的转换 +// - fasthttp 响应到 HTTP 响应的转换 +// - 请求方法、URI、头部、请求体的转换 +// - 完整请求响应流程 +// +// 作者:xfy package http3 import ( diff --git a/internal/http3/server_test.go b/internal/http3/server_test.go index cb28b8e..f59ffe7 100644 --- a/internal/http3/server_test.go +++ b/internal/http3/server_test.go @@ -1,3 +1,13 @@ +// Package http3 提供 HTTP/3 服务器功能的测试。 +// +// 该文件测试 HTTP/3 服务器模块的各项功能,包括: +// - 服务器创建和配置验证 +// - Alt-Svc 头部生成 +// - 服务器统计信息获取 +// - 运行状态检查 +// - 服务器停止和优雅停止 +// +// 作者:xfy package http3 import ( diff --git a/internal/loadbalance/balancer_test.go b/internal/loadbalance/balancer_test.go index 1ac5c06..04c299e 100644 --- a/internal/loadbalance/balancer_test.go +++ b/internal/loadbalance/balancer_test.go @@ -1,4 +1,13 @@ -// Package loadbalance provides load balancing algorithms for the Lolly HTTP server. +// Package loadbalance 提供负载均衡算法的测试。 +// +// 该文件测试负载均衡模块的各项功能,包括: +// - 轮询算法 +// - 加权轮询算法 +// - 最少连接算法 +// - IP 哈希算法 +// - 一致性哈希算法 +// +// 作者:xfy package loadbalance import ( diff --git a/internal/loadbalance/consistent_hash.go b/internal/loadbalance/consistent_hash.go index 2779ac5..64caf49 100644 --- a/internal/loadbalance/consistent_hash.go +++ b/internal/loadbalance/consistent_hash.go @@ -163,9 +163,14 @@ func (c *ConsistentHash) GetVirtualNodes() int { // Stats 返回一致性哈希统计信息。 type ConsistentHashStats struct { - VirtualNodes int // 虚拟节点数 - CircleSize int // 哈希环大小 - SortedHashes int // 排序哈希值数量 + // VirtualNodes 每个目标的虚拟节点数量 + VirtualNodes int + + // CircleSize 哈希环中的节点总数 + CircleSize int + + // SortedHashes 排序后的哈希值数量 + SortedHashes int } // GetStats 返回统计信息。 diff --git a/internal/logging/logging.go b/internal/logging/logging.go index e78c182..cbdec54 100644 --- a/internal/logging/logging.go +++ b/internal/logging/logging.go @@ -78,6 +78,17 @@ func New(cfg *config.LoggingConfig) *Logger { } // getOutput 获取输出目标(stdout/stderr/文件)。 +// +// 根据路径返回对应的输出 writer: +// - "stdout" 或空字符串:返回 os.Stdout +// - "stderr":返回 os.Stderr +// - 其他:尝试打开文件,失败返回 os.Stdout +// +// 参数: +// - path: 输出路径 +// +// 返回值: +// - io.Writer: 输出 writer func getOutput(path string) io.Writer { path = strings.TrimSpace(path) if path == "" || path == "stdout" { @@ -128,6 +139,26 @@ func (l *Logger) LogAccess(ctx *fasthttp.RequestCtx, status int, size int64, dur } // formatAccessLog 根据模板格式化访问日志。 +// +// 支持以下变量替换: +// - $remote_addr: 客户端地址 +// - $remote_user: 认证用户 +// - $request: 请求方法和路径 +// - $status: HTTP 状态码 +// - $body_bytes_sent: 响应体大小 +// - $request_time: 请求处理时间 +// - $http_referer: Referer 头 +// - $http_user_agent: User-Agent 头 +// - $time: 当前时间 +// +// 参数: +// - ctx: FastHTTP 请求上下文 +// - status: HTTP 状态码 +// - size: 响应体大小 +// - duration: 请求处理时间 +// +// 返回值: +// - string: 格式化后的日志字符串 func (l *Logger) formatAccessLog(ctx *fasthttp.RequestCtx, status int, size int64, duration time.Duration) string { // 获取认证用户名,无认证时为 "-" remoteUser := "-" @@ -208,6 +239,16 @@ func Debug() *zerolog.Event { } // parseLevel 解析日志级别。 +// +// 将字符串级别转换为 zerolog.Level。 +// 支持的级别:debug, info, warn, error(不区分大小写)。 +// 未知级别默认返回 info。 +// +// 参数: +// - level: 日志级别字符串 +// +// 返回值: +// - zerolog.Level: 解析后的日志级别 func parseLevel(level string) zerolog.Level { switch strings.ToLower(level) { case "debug": diff --git a/internal/logging/logging_test.go b/internal/logging/logging_test.go index 46e2839..3e56616 100644 --- a/internal/logging/logging_test.go +++ b/internal/logging/logging_test.go @@ -1,3 +1,14 @@ +// Package logging 提供日志功能的测试。 +// +// 该文件测试日志模块的各项功能,包括: +// - 日志级别解析 +// - 日志记录器创建 +// - 访问日志记录 +// - 错误日志记录 +// - 自定义格式 +// - 文件输出 +// +// 作者:xfy package logging import ( diff --git a/internal/middleware/accesslog/accesslog.go b/internal/middleware/accesslog/accesslog.go index 06f51fe..26bcd99 100644 --- a/internal/middleware/accesslog/accesslog.go +++ b/internal/middleware/accesslog/accesslog.go @@ -23,10 +23,17 @@ import ( // AccessLog 访问日志中间件,记录请求方法、路径、状态码、响应大小和处理时间。 type AccessLog struct { + // logger 日志记录器实例,用于输出访问日志 logger *logging.Logger } // New 创建访问日志中间件。 +// +// 参数: +// - cfg: 日志配置,包含输出路径、格式等设置 +// +// 返回值: +// - *AccessLog: 访问日志中间件实例 func New(cfg *config.LoggingConfig) *AccessLog { return &AccessLog{ logger: logging.New(cfg), @@ -34,11 +41,20 @@ func New(cfg *config.LoggingConfig) *AccessLog { } // Name 返回中间件名称。 +// +// 返回值: +// - string: 中间件名称 "accesslog" func (a *AccessLog) Name() string { return "accesslog" } // Process 包装 handler,在请求处理后记录访问日志。 +// +// 参数: +// - next: 下一个请求处理器 +// +// 返回值: +// - fasthttp.RequestHandler: 包装后的请求处理器 func (a *AccessLog) Process(next fasthttp.RequestHandler) fasthttp.RequestHandler { return func(ctx *fasthttp.RequestCtx) { start := time.Now() @@ -49,6 +65,9 @@ func (a *AccessLog) Process(next fasthttp.RequestHandler) fasthttp.RequestHandle } // Close 关闭日志文件。 +// +// 返回值: +// - error: 关闭失败时返回错误,成功返回 nil func (a *AccessLog) Close() error { return a.logger.Close() } diff --git a/internal/middleware/accesslog/accesslog_test.go b/internal/middleware/accesslog/accesslog_test.go index 84decfe..5d2fb79 100644 --- a/internal/middleware/accesslog/accesslog_test.go +++ b/internal/middleware/accesslog/accesslog_test.go @@ -1,3 +1,12 @@ +// Package accesslog 提供访问日志功能的测试。 +// +// 该文件测试访问日志模块的各项功能,包括: +// - 访问日志中间件创建 +// - 请求处理和响应记录 +// - 请求持续时间记录 +// - 日志格式化 +// +// 作者:xfy package accesslog import ( diff --git a/internal/middleware/compression/compression.go b/internal/middleware/compression/compression.go index 235dc9a..96042c6 100644 --- a/internal/middleware/compression/compression.go +++ b/internal/middleware/compression/compression.go @@ -56,6 +56,13 @@ type CompressionMiddleware struct { } // New 创建压缩中间件。 +// +// 参数: +// - cfg: 压缩配置,包含算法类型、压缩级别、最小压缩大小等 +// +// 返回值: +// - *CompressionMiddleware: 压缩中间件实例 +// - error: 配置无效时返回错误 func New(cfg *config.CompressionConfig) (*CompressionMiddleware, error) { if cfg == nil { cfg = &config.CompressionConfig{ @@ -139,6 +146,12 @@ func (m *CompressionMiddleware) Name() string { } // Process 应用压缩中间件。 +// +// 参数: +// - next: 下一个请求处理器 +// +// 返回值: +// - fasthttp.RequestHandler: 包装后的请求处理器 func (m *CompressionMiddleware) Process(next fasthttp.RequestHandler) fasthttp.RequestHandler { return func(ctx *fasthttp.RequestCtx) { // 检查客户端是否支持压缩 @@ -203,6 +216,12 @@ func (m *CompressionMiddleware) Process(next fasthttp.RequestHandler) fasthttp.R } // isCompressible 检查 MIME 类型是否可压缩。 +// +// 参数: +// - contentType: 内容类型(MIME 类型) +// +// 返回值: +// - bool: 是否可压缩 func (m *CompressionMiddleware) isCompressible(contentType string) bool { // 移除 charset 等参数 ct := contentType @@ -227,6 +246,12 @@ func (m *CompressionMiddleware) isCompressible(contentType string) bool { } // compressGzip 使用 gzip 压缩数据。 +// +// 参数: +// - data: 待压缩的原始数据 +// +// 返回值: +// - []byte: 压缩后的数据 func (m *CompressionMiddleware) compressGzip(data []byte) []byte { w := m.gzipPool.Get().(*gzip.Writer) defer m.gzipPool.Put(w) @@ -240,6 +265,12 @@ func (m *CompressionMiddleware) compressGzip(data []byte) []byte { } // compressBrotli 使用 brotli 压缩数据。 +// +// 参数: +// - data: 待压缩的原始数据 +// +// 返回值: +// - []byte: 压缩后的数据 func (m *CompressionMiddleware) compressBrotli(data []byte) []byte { w := m.brotliPool.Get().(*brotli.Writer) defer m.brotliPool.Put(w) @@ -253,16 +284,25 @@ func (m *CompressionMiddleware) compressBrotli(data []byte) []byte { } // Types 返回可压缩的 MIME 类型列表。 +// +// 返回值: +// - []string: 可压缩的 MIME 类型列表 func (m *CompressionMiddleware) Types() []string { return m.types } // Level 返回压缩级别。 +// +// 返回值: +// - int: 压缩级别(1-9) func (m *CompressionMiddleware) Level() int { return m.level } // MinSize 返回最小压缩大小。 +// +// 返回值: +// - int: 最小压缩大小(字节) func (m *CompressionMiddleware) MinSize() int { return m.minSize } diff --git a/internal/middleware/compression/compression_test.go b/internal/middleware/compression/compression_test.go index d8755b3..56cab8f 100644 --- a/internal/middleware/compression/compression_test.go +++ b/internal/middleware/compression/compression_test.go @@ -1,3 +1,13 @@ +// Package compression 提供压缩功能的测试。 +// +// 该文件测试压缩中间件模块的各项功能,包括: +// - 压缩中间件创建 +// - gzip 和 brotli 压缩 +// - 可压缩类型检查 +// - 压缩级别配置 +// - 响应处理 +// +// 作者:xfy package compression import ( diff --git a/internal/middleware/compression/gzip_static_test.go b/internal/middleware/compression/gzip_static_test.go index 60d3d15..5bec097 100644 --- a/internal/middleware/compression/gzip_static_test.go +++ b/internal/middleware/compression/gzip_static_test.go @@ -1,4 +1,14 @@ -// Package compression 提供 gzip_static 预压缩文件测试。 +// Package compression 提供 gzip_static 预压缩文件功能的测试。 +// +// 该文件测试 gzip_static 模块的各项功能,包括: +// - Brotli 和 Gzip 文件优先级 +// - Gzip 回退机制 +// - Accept-Encoding 头解析 +// - 扩展名检查 +// - 路径遍历防护 +// - Vary 头设置 +// +// 作者:xfy package compression import ( diff --git a/internal/middleware/middleware_test.go b/internal/middleware/middleware_test.go index dd581ae..b47179e 100644 --- a/internal/middleware/middleware_test.go +++ b/internal/middleware/middleware_test.go @@ -1,3 +1,13 @@ +// Package middleware 提供中间件链功能的测试。 +// +// 该文件测试中间件链模块的各项功能,包括: +// - 中间件链创建 +// - 空链处理 +// - 单中间件包装 +// - 多中间件执行顺序 +// - 中间件修改响应 +// +// 作者:xfy package middleware import ( diff --git a/internal/middleware/rewrite/rewrite_test.go b/internal/middleware/rewrite/rewrite_test.go index 8e25a39..3de16bf 100644 --- a/internal/middleware/rewrite/rewrite_test.go +++ b/internal/middleware/rewrite/rewrite_test.go @@ -1,3 +1,13 @@ +// Package rewrite 提供 URL 重写功能的测试。 +// +// 该文件测试 URL 重写模块的各项功能,包括: +// - 重写规则解析 +// - 正则表达式匹配 +// - 重定向和重写 +// - 规则链执行 +// - ReDoS 防护 +// +// 作者:xfy package rewrite import ( diff --git a/internal/middleware/security/access_test.go b/internal/middleware/security/access_test.go index 6a6af12..64ef5cf 100644 --- a/internal/middleware/security/access_test.go +++ b/internal/middleware/security/access_test.go @@ -1,3 +1,13 @@ +// Package security 提供访问控制功能的测试。 +// +// 该文件测试访问控制模块的各项功能,包括: +// - 访问控制器创建 +// - IP 地址检查 +// - 允许列表和拒绝列表 +// - CIDR 解析 +// - 列表更新和统计 +// +// 作者:xfy package security import ( diff --git a/internal/middleware/security/auth_test.go b/internal/middleware/security/auth_test.go index 93b5aac..a315280 100644 --- a/internal/middleware/security/auth_test.go +++ b/internal/middleware/security/auth_test.go @@ -1,3 +1,13 @@ +// Package security 提供基本认证功能的测试。 +// +// 该文件测试基本认证模块的各项功能,包括: +// - 基本认证创建和配置 +// - 用户认证验证 +// - 密码哈希(bcrypt/argon2id) +// - 用户添加和删除 +// - 凭据提取 +// +// 作者:xfy package security import ( diff --git a/internal/middleware/security/headers_test.go b/internal/middleware/security/headers_test.go index 9a74948..1c15d76 100644 --- a/internal/middleware/security/headers_test.go +++ b/internal/middleware/security/headers_test.go @@ -1,3 +1,13 @@ +// Package security 提供安全头部功能的测试。 +// +// 该文件测试安全头部模块的各项功能,包括: +// - 安全头部中间件创建 +// - 各种安全头部设置(CSP、HSTS、X-Frame-Options等) +// - 配置更新 +// - 默认和严格配置 +// - HSTS 值格式化 +// +// 作者:xfy package security import ( diff --git a/internal/middleware/security/ratelimit_test.go b/internal/middleware/security/ratelimit_test.go index 3afff7d..89e39b4 100644 --- a/internal/middleware/security/ratelimit_test.go +++ b/internal/middleware/security/ratelimit_test.go @@ -1,3 +1,14 @@ +// Package security 提供速率限制功能的测试。 +// +// 该文件测试速率限制模块的各项功能,包括: +// - 速率限制器创建 +// - 令牌桶算法 +// - 令牌补充机制 +// - 计数器重置 +// - 连接数限制 +// - 统计信息获取 +// +// 作者:xfy package security import ( diff --git a/internal/middleware/security/sliding_window_test.go b/internal/middleware/security/sliding_window_test.go index df3440b..ebb155a 100644 --- a/internal/middleware/security/sliding_window_test.go +++ b/internal/middleware/security/sliding_window_test.go @@ -1,3 +1,14 @@ +// Package security 提供滑动窗口速率限制功能的测试。 +// +// 该文件测试滑动窗口限流模块的各项功能,包括: +// - 滑动窗口限流器创建 +// - 精确和近似模式 +// - 请求允许和拒绝 +// - 计数器重置 +// - 过期清理 +// - 统计信息获取 +// +// 作者:xfy package security import ( diff --git a/internal/netutil/ip_test.go b/internal/netutil/ip_test.go index 5193fa6..908f23a 100644 --- a/internal/netutil/ip_test.go +++ b/internal/netutil/ip_test.go @@ -1,3 +1,12 @@ +// Package netutil 提供网络工具功能的测试。 +// +// 该文件测试网络工具模块的 IP 相关功能,包括: +// - 客户端 IP 提取 +// - X-Forwarded-For 头解析 +// - X-Real-IP 头解析 +// - 远程地址解析 +// +// 作者:xfy package netutil import ( diff --git a/internal/netutil/url_test.go b/internal/netutil/url_test.go index 635f44b..9a0bbc2 100644 --- a/internal/netutil/url_test.go +++ b/internal/netutil/url_test.go @@ -1,3 +1,12 @@ +// Package netutil 提供 URL 工具功能的测试。 +// +// 该文件测试网络工具模块的 URL 相关功能,包括: +// - 目标 URL 解析 +// - 默认端口添加 +// - TLS 检测 +// - 主机提取 +// +// 作者:xfy package netutil import "testing" diff --git a/internal/proxy/health.go b/internal/proxy/health.go index 5fcb804..d4902e0 100644 --- a/internal/proxy/health.go +++ b/internal/proxy/health.go @@ -1,9 +1,16 @@ -// Package proxy provides reverse proxy functionality for the Lolly HTTP server. +// Package proxy 提供反向代理功能,支持 HTTP、WebSocket 和流式代理。 // // 此文件实现了针对后端目标的健康检查功能,支持 // 主动健康检查(定期 HTTP 探测)和被动健康检查 // (基于观察到的失败标记目标为不健康)。 // +// 主要功能: +// - 定期向后端发送健康检查请求 +// - 根据响应状态更新目标健康状态 +// - 支持被动健康检查(基于请求失败) +// +// 作者:xfy +// //go:generate go test -v ./... package proxy @@ -27,7 +34,7 @@ import ( // 返回 2xx 状态码的目标被标记为健康; // 超时、连接失败或非 2xx 响应将其标记为不健康。 // -// Example usage: +// 使用示例: // // targets := []*loadbalance.Target{ // {URL: "http://backend1:8080"}, @@ -46,13 +53,26 @@ import ( // checker.Start() // defer checker.Stop() type HealthChecker struct { - targets []*loadbalance.Target + // targets 需要检查的后端目标列表 + targets []*loadbalance.Target + + // interval 健康检查间隔时间 interval time.Duration - timeout time.Duration - path string - stopCh chan struct{} - running atomic.Bool - client *fasthttp.Client + + // timeout 单次检查超时时间 + timeout time.Duration + + // path 健康检查请求路径 + path string + + // stopCh 停止信号通道 + stopCh chan struct{} + + // running 运行状态标志 + running atomic.Bool + + // client HTTP 客户端,用于发送健康检查请求 + client *fasthttp.Client } // NewHealthChecker 使用指定的目标和配置创建一个新的 HealthChecker。 diff --git a/internal/proxy/health_test.go b/internal/proxy/health_test.go index a6a03c1..9aba9dd 100644 --- a/internal/proxy/health_test.go +++ b/internal/proxy/health_test.go @@ -1,4 +1,18 @@ -// Package proxy 提供健康检查的测试。 +// Package proxy 提供健康检查功能的测试。 +// +// 该文件测试健康检查模块的各项功能,包括: +// - 健康检查器创建 +// - 默认值应用 +// - 自定义配置 +// - 负值配置处理 +// - 零值配置处理 +// - 启动和停止控制 +// - 目标健康检查 +// - 超时处理 +// - 连接失败处理 +// - 标记不健康 +// +// 作者:xfy package proxy import ( diff --git a/internal/proxy/proxy.go b/internal/proxy/proxy.go index 0aab829..48ddb18 100644 --- a/internal/proxy/proxy.go +++ b/internal/proxy/proxy.go @@ -47,15 +47,33 @@ import ( ) // Proxy 表示反向代理实例,负责将 HTTP 请求转发到后端目标。 +// // 它为每个后端目标管理连接池,并提供负载均衡功能。 +// +// 注意事项: +// - 所有公开方法均为并发安全 +// - 使用前需确保 targets 中至少有一个健康目标 type Proxy struct { - targets []*loadbalance.Target - clients map[string]*fasthttp.HostClient // key: target URL - balancer loadbalance.Balancer - config *config.ProxyConfig - cache *cache.ProxyCache // 代理缓存(可选) - healthChecker *HealthChecker // 健康检查器(用于被动检查) - mu sync.RWMutex + // targets 后端目标列表,用于负载均衡选择 + targets []*loadbalance.Target + + // clients 每个目标对应的 HostClient 连接池映射(key: target URL) + clients map[string]*fasthttp.HostClient + + // balancer 负载均衡器实例 + balancer loadbalance.Balancer + + // config 代理配置,包含超时、请求头等设置 + config *config.ProxyConfig + + // cache 代理缓存实例,用于缓存响应(可选) + cache *cache.ProxyCache + + // healthChecker 健康检查器,用于被动健康检查 + healthChecker *HealthChecker + + // mu 保护并发访问的读写锁 + mu sync.RWMutex } // NewProxy 使用给定的配置和后台目标创建一个新的反向代理实例。 @@ -171,7 +189,7 @@ func createHostClient(targetURL string, timeout config.ProxyTimeout, transportCf MaxIdleConnDuration: maxIdleConnDuration, MaxConns: maxConns, MaxConnWaitTimeout: timeout.Connect, - RetryIf: nil, // Disable automatic retries + RetryIf: nil, // 禁用自动重试 DisablePathNormalizing: false, SecureErrorLogMessage: false, } diff --git a/internal/proxy/proxy_test.go b/internal/proxy/proxy_test.go index 1863e93..434c9db 100644 --- a/internal/proxy/proxy_test.go +++ b/internal/proxy/proxy_test.go @@ -1,6 +1,21 @@ -// Package proxy provides reverse proxy functionality for the Lolly HTTP server. +// Package proxy 提供反向代理功能的测试。 // -//go:generate go test -v ./... +// 该文件测试代理模块的各项功能,包括: +// - 代理创建和配置 +// - 目标选择 +// - 请求转发 +// - 请求头修改 +// - 响应头修改 +// - 客户端 IP 提取 +// - 目标更新 +// - WebSocket 请求检测 +// - 负载均衡器创建 +// - HostClient 创建 +// - 健康检查器设置 +// - 代理缓存功能 +// - 被动健康检查 +// +// 作者:xfy package proxy import ( diff --git a/internal/proxy/websocket_test.go b/internal/proxy/websocket_test.go index a258a10..4b7f9f9 100644 --- a/internal/proxy/websocket_test.go +++ b/internal/proxy/websocket_test.go @@ -1,3 +1,17 @@ +// Package proxy 提供 WebSocket 代理功能的测试。 +// +// 该文件测试 WebSocket 代理模块的各项功能,包括: +// - 桥接器创建 +// - 桥接器关闭 +// - 空连接处理 +// - 连接关闭错误检测 +// - 目标地址拨号 +// - URL 解析 +// - 数据复制 +// - 双向数据转发 +// - 超时错误处理 +// +// 作者:xfy package proxy import ( diff --git a/internal/server/pool.go b/internal/server/pool.go index 09709bc..551b230 100644 --- a/internal/server/pool.go +++ b/internal/server/pool.go @@ -206,7 +206,7 @@ func (p *GoroutinePool) startWorker() { task(nil) case <-idleTimer.C: - // 穴闲超时,退出 worker(保持最小数量) + // 空闲超时,退出 worker(保持最小数量) atomic.AddInt32(&p.idleWorkers, -1) if atomic.LoadInt32(&p.workers) > p.minWorkers { return @@ -243,12 +243,23 @@ func (p *GoroutinePool) Stats() PoolStats { // // 用于监控池的运行状态,包括 worker 数量和队列状态。 type PoolStats struct { - Workers int32 // 当前活跃 worker 数量 - IdleWorkers int32 // 当前空闲 worker 数量 - MaxWorkers int32 // 最大 worker 数量上限 - MinWorkers int32 // 最小 worker 数量下限 - QueueLen int32 // 当前队列中的任务数 - QueueCap int32 // 队列容量上限 + // Workers 当前活跃 worker 数量 + Workers int32 + + // IdleWorkers 当前空闲 worker 数量 + IdleWorkers int32 + + // MaxWorkers 最大 worker 数量上限 + MaxWorkers int32 + + // MinWorkers 最小 worker 数量下限 + MinWorkers int32 + + // QueueLen 当前队列中的任务数 + QueueLen int32 + + // QueueCap 队列容量上限 + QueueCap int32 } // WrapHandler 使用 Goroutine 池包装 fasthttp 处理器。 diff --git a/internal/server/pool_test.go b/internal/server/pool_test.go index f0b3b41..63f1095 100644 --- a/internal/server/pool_test.go +++ b/internal/server/pool_test.go @@ -1,3 +1,14 @@ +// Package server 提供 Goroutine 池功能的测试。 +// +// 该文件测试 Goroutine 池的各项功能,包括: +// - 池的创建和配置 +// - 启动和停止控制 +// - 任务提交和执行 +// - 并发提交处理 +// - Handler 包装功能 +// - 统计信息收集 +// +// 作者:xfy package server import ( diff --git a/internal/server/server.go b/internal/server/server.go index 3104801..7beb581 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -678,7 +678,13 @@ func (s *Server) getProxyCacheStats() ProxyCacheStats { return total } -// registerStaticHandler registers static file handler. +// registerStaticHandler 注册静态文件处理器。 +// +// 为路由器注册静态文件服务,支持文件缓存和预压缩文件。 +// +// 参数: +// - router: 路由器实例,用于注册路由规则 +// - cfg: 服务器配置,包含静态文件和压缩设置 func (s *Server) registerStaticHandler(router *handler.Router, cfg *config.ServerConfig) { staticHandler := handler.NewStaticHandler( cfg.Static.Root, diff --git a/internal/server/server_test.go b/internal/server/server_test.go index 9f17f8e..a6a97c7 100644 --- a/internal/server/server_test.go +++ b/internal/server/server_test.go @@ -1,3 +1,16 @@ +// Package server 提供 HTTP 服务器功能的测试。 +// +// 该文件测试服务器模块的各项功能,包括: +// - 服务器创建和初始化 +// - 启动和停止控制 +// - 优雅关闭 +// - 中间件链构建 +// - 请求统计追踪 +// - 监听器管理 +// - TLS 配置 +// - 代理缓存统计 +// +// 作者:xfy package server import ( diff --git a/internal/server/status_test.go b/internal/server/status_test.go index 4f676b6..6349a73 100644 --- a/internal/server/status_test.go +++ b/internal/server/status_test.go @@ -1,3 +1,16 @@ +// Package server 提供状态处理器功能的测试。 +// +// 该文件测试状态处理器模块的各项功能,包括: +// - 状态处理器创建 +// - CIDR 和单 IP 白名单配置 +// - 无效 IP 格式处理 +// - 访问控制逻辑 +// - 客户端 IP 提取 +// - 状态信息收集 +// - Goroutine 池统计 +// - 文件缓存统计 +// +// 作者:xfy package server import ( diff --git a/internal/server/upgrade_test.go b/internal/server/upgrade_test.go index f3c2fe9..4ca1671 100644 --- a/internal/server/upgrade_test.go +++ b/internal/server/upgrade_test.go @@ -1,3 +1,15 @@ +// Package server 提供优雅升级功能的测试。 +// +// 该文件测试优雅升级模块的各项功能,包括: +// - UpgradeManager 的创建和配置 +// - 子进程检测 +// - PID 文件的读写 +// - 监听器继承 +// - 进程通知机制 +// - 平滑关闭等待 +// - 错误处理场景 +// +// 作者:xfy package server import ( diff --git a/internal/ssl/ocsp_test.go b/internal/ssl/ocsp_test.go index f0d3df1..473c4b3 100644 --- a/internal/ssl/ocsp_test.go +++ b/internal/ssl/ocsp_test.go @@ -1,3 +1,13 @@ +// Package ssl 提供 OCSP(在线证书状态协议)功能的测试。 +// +// 该文件测试 OCSP 模块的各项功能,包括: +// - OCSP 管理器创建和配置 +// - OCSP 响应获取 +// - 证书状态检查 +// - 过期响应处理 +// - OCSP 配置默认值 +// +// 作者:xfy package ssl import ( @@ -380,7 +390,7 @@ func TestOCSPManagerRegisterCertificate(t *testing.T) { // If it fails, that's also OK - graceful degradation } -// generateTestCertWithOCSP generates a self-signed certificate for testing. +// generateTestCertWithOCSP 生成用于测试的自签名证书。 // If ocspServer is provided, it will be included in the certificate. func generateTestCertWithOCSP(t *testing.T, ocspServer []string) ([]byte, []byte) { t.Helper() diff --git a/internal/ssl/ssl.go b/internal/ssl/ssl.go index ff148af..1258830 100644 --- a/internal/ssl/ssl.go +++ b/internal/ssl/ssl.go @@ -190,7 +190,7 @@ func NewMultiTLSManager(configs map[string]*config.SSLConfig, defaultCfg *config configs: make(map[string]*tls.Config), } - // Load each certificate + // 加载每个证书 for name, cfg := range configs { tlsCfg, err := createTLSConfig(cfg) if err != nil { @@ -199,7 +199,7 @@ func NewMultiTLSManager(configs map[string]*config.SSLConfig, defaultCfg *config manager.configs[name] = tlsCfg } - // Load default config if provided + // 如果提供了默认配置,则加载 if defaultCfg != nil { tlsCfg, err := createTLSConfig(defaultCfg) if err != nil { @@ -236,7 +236,7 @@ func (m *TLSManager) GetTLSConfigForHost(host string) *tls.Config { m.mu.RLock() defer m.mu.RUnlock() - // Remove port from host if present + // 从主机名中移除端口(如果存在) for i := 0; i < len(host); i++ { if host[i] == ':' { host = host[:i] @@ -261,14 +261,14 @@ func (m *TLSManager) GetCertificate() func(*tls.ClientHelloInfo) (*tls.Certifica m.mu.RLock() defer m.mu.RUnlock() - // Look for matching server name + // 查找匹配的服务器名称 if cfg, ok := m.configs[hello.ServerName]; ok { if len(cfg.Certificates) > 0 { return &cfg.Certificates[0], nil } } - // Fall back to default + // 回退到默认配置 if m.defaultCfg != nil && len(m.defaultCfg.Certificates) > 0 { return &m.defaultCfg.Certificates[0], nil } @@ -331,7 +331,7 @@ func (m *TLSManager) getConfigForClientWithOCSP(hello *tls.ClientHelloInfo) (*tl m.mu.RLock() defer m.mu.RUnlock() - // Get the base config + // 获取基础配置 var baseCfg *tls.Config if hello.ServerName != "" { if cfg, ok := m.configs[hello.ServerName]; ok { @@ -347,13 +347,13 @@ func (m *TLSManager) getConfigForClientWithOCSP(hello *tls.ClientHelloInfo) (*tl return baseCfg, nil } - // Create a copy of the config with OCSP response attached + // 创建配置副本并附加 OCSP 响应 cfgCopy := baseCfg.Clone() // Attach OCSP response to the certificate cert := &cfgCopy.Certificates[0] if len(cert.Certificate) > 0 { - // Parse the leaf certificate to get serial number + // 解析叶子证书以获取序列号 leafCert, err := x509.ParseCertificate(cert.Certificate[0]) if err == nil { serial := leafCert.SerialNumber.String() @@ -487,7 +487,7 @@ func extractPEMBlock(data []byte) ([]byte, []byte) { return nil, nil } - // Extract and decode the PEM block + // 提取并解码 PEM 块 blockData := data[start : start+end+len(endMarker)] rest := data[start+end+len(endMarker):] @@ -629,7 +629,7 @@ func parseCipherSuites(ciphers []string) ([]uint16, error) { if !ok { return nil, fmt.Errorf("unknown cipher suite: %s", c) } - // Check for insecure cipher suites + // 检查不安全的加密套件 if isInsecureCipher(id) { return nil, fmt.Errorf("insecure cipher suite %s is not allowed", c) } @@ -735,7 +735,7 @@ func ValidateKey(keyPath string) error { return fmt.Errorf("failed to read key: %w", err) } - // Key validation happens during tls.LoadX509KeyPair + // 密钥验证在 tls.LoadX509KeyPair 期间进行 // This is a preliminary check that the file exists and is readable return nil } diff --git a/internal/ssl/ssl_test.go b/internal/ssl/ssl_test.go index fba82bb..7b769aa 100644 --- a/internal/ssl/ssl_test.go +++ b/internal/ssl/ssl_test.go @@ -1,3 +1,13 @@ +// Package ssl 提供 SSL/TLS 功能的测试。 +// +// 该文件测试 SSL 模块的各项功能,包括: +// - TLS 管理器创建和配置 +// - TLS 版本和加密套件解析 +// - 证书验证 +// - 多证书管理 +// - TLS 配置获取 +// +// 作者:xfy package ssl import ( diff --git a/internal/stream/stream.go b/internal/stream/stream.go index 498ef3b..5a3e82f 100644 --- a/internal/stream/stream.go +++ b/internal/stream/stream.go @@ -42,17 +42,34 @@ type Balancer interface { Select(targets []*Target) *Target } -// roundRobin 简单轮询。 +// roundRobin 简单轮询负载均衡器。 +// +// 使用原子计数器实现线程安全的轮询选择,每次选择后计数器递增, +// 确保请求均匀分布到所有健康目标。 type roundRobin struct { counter uint64 } // newRoundRobin 创建轮询均衡器。 +// +// 返回实现了简单轮询算法的 Balancer 接口实例。 +// +// 返回值: +// - Balancer: 轮询负载均衡器实例 func newRoundRobin() Balancer { return &roundRobin{} } // Select 选择下一个目标。 +// +// 从健康目标列表中按轮询顺序选择一个目标。 +// 只选择标记为健康的目标,如果无健康目标则返回 nil。 +// +// 参数: +// - targets: 目标服务器列表 +// +// 返回值: +// - *Target: 选中的目标服务器,无可用目标时返回 nil func (r *roundRobin) Select(targets []*Target) *Target { // 过滤健康目标 healthy := make([]*Target, 0) @@ -68,15 +85,32 @@ func (r *roundRobin) Select(targets []*Target) *Target { return healthy[idx%uint64(len(healthy))] } -// leastConn 最少连接。 +// leastConn 最少连接负载均衡器。 +// +// 跟踪每个目标服务器的活跃连接数,总是选择连接数最少的目标。 +// 适合连接持续时间差异较大的场景。 type leastConn struct{} // newLeastConn 创建最少连接均衡器。 +// +// 返回实现了最少连接算法的 Balancer 接口实例。 +// +// 返回值: +// - Balancer: 最少连接负载均衡器实例 func newLeastConn() Balancer { return &leastConn{} } // Select 选择连接最少的目标。 +// +// 遍历所有健康目标,选择当前连接数最少的目标服务器。 +// 只选择标记为健康的目标,如果无健康目标则返回 nil。 +// +// 参数: +// - targets: 目标服务器列表 +// +// 返回值: +// - *Target: 选中的目标服务器,无可用目标时返回 nil func (l *leastConn) Select(targets []*Target) *Target { var selected *Target var minConns int64 = -1 @@ -93,17 +127,35 @@ func (l *leastConn) Select(targets []*Target) *Target { return selected } -// weightedRoundRobin 加权轮询。 +// weightedRoundRobin 加权轮询负载均衡器。 +// +// 根据目标服务器的权重分配请求,权重高的目标获得更多请求。 +// 使用原子计数器确保线程安全,支持不同权重的目标混合使用。 type weightedRoundRobin struct { counter uint64 } // newWeightedRoundRobin 创建加权轮询均衡器。 +// +// 返回实现了加权轮询算法的 Balancer 接口实例。 +// +// 返回值: +// - Balancer: 加权轮询负载均衡器实例 func newWeightedRoundRobin() Balancer { return &weightedRoundRobin{} } // Select 基于权重分布选择目标。 +// +// 根据目标服务器的权重进行轮询选择,权重决定被选中的概率。 +// 权重为 0 或负数的目标按权重 1 处理。 +// 只选择标记为健康的目标,如果无健康目标则返回 nil。 +// +// 参数: +// - targets: 目标服务器列表 +// +// 返回值: +// - *Target: 选中的目标服务器,无可用目标时返回 nil func (w *weightedRoundRobin) Select(targets []*Target) *Target { healthy := make([]*Target, 0) for _, t := range targets { @@ -145,20 +197,48 @@ func (w *weightedRoundRobin) Select(targets []*Target) *Target { return healthy[len(healthy)-1] } -// ipHash IP 哈希。 +// ipHash IP 哈希负载均衡器。 +// +// 基于客户端 IP 地址计算哈希值,将同一 IP 的请求分配到固定目标。 +// 适合需要会话保持的场景,确保相同客户端总是被路由到同一服务器。 type ipHash struct{} // newIPHash 创建 IP 哈希均衡器。 +// +// 返回实现了 IP 哈希算法的 Balancer 接口实例。 +// +// 返回值: +// - Balancer: IP 哈希负载均衡器实例 func newIPHash() Balancer { return &ipHash{} } // Select 默认选择(IP Hash 需要具体 IP)。 +// +// 当无客户端 IP 时,使用空字符串进行哈希计算。 +// 通常应使用 SelectByIP 方法指定客户端 IP。 +// +// 参数: +// - targets: 目标服务器列表 +// +// 返回值: +// - *Target: 选中的目标服务器,无可用目标时返回 nil func (i *ipHash) Select(targets []*Target) *Target { return i.SelectByIP(targets, "") } // SelectByIP 基于客户端 IP 哈希选择目标。 +// +// 使用 FNV-64a 算法计算客户端 IP 的哈希值,对目标数取模选择目标。 +// 同一 IP 总是映射到同一目标(除非目标列表变化)。 +// 只选择标记为健康的目标,如果无健康目标则返回 nil。 +// +// 参数: +// - targets: 目标服务器列表 +// - clientIP: 客户端 IP 地址字符串 +// +// 返回值: +// - *Target: 选中的目标服务器,无可用目标时返回 nil func (i *ipHash) SelectByIP(targets []*Target, clientIP string) *Target { healthy := make([]*Target, 0) for _, t := range targets { @@ -397,6 +477,13 @@ func (s *Server) Start() error { } // acceptLoop 接受连接循环。 +// +// 在单独的 goroutine 中运行,持续接受 TCP 连接。 +// 当服务器停止时,该函数返回。 +// +// 参数: +// - addr: 监听地址 +// - listener: TCP 监听器实例 func (s *Server) acceptLoop(addr string, listener net.Listener) { for s.running.Load() { conn, err := listener.Accept() @@ -413,6 +500,16 @@ func (s *Server) acceptLoop(addr string, listener net.Listener) { } // handleConnection 处理单个连接。 +// +// 处理客户端连接的完整生命周期: +// 1. 选择上游目标 +// 2. 建立到目标服务器的连接 +// 3. 在客户端和目标之间双向转发数据 +// 4. 处理连接关闭和错误 +// +// 参数: +// - clientConn: 客户端连接 +// - addr: 监听地址 func (s *Server) handleConnection(clientConn net.Conn, addr string) { defer func() { _ = clientConn.Close() @@ -491,6 +588,9 @@ func (h *HealthChecker) Start() { } // check 执行健康检查。 +// +// 尝试 TCP 连接到所有目标,根据连接结果更新健康状态。 +// 连接成功标记为健康,连接失败标记为不健康。 func (h *HealthChecker) check() { for _, target := range h.upstream.targets { conn, err := net.DialTimeout("tcp", target.addr, h.timeout) @@ -549,34 +649,71 @@ func (s *Server) Stats() Stats { // Stats Stream 服务器统计。 type Stats struct { + // Connections 当前活跃连接数量 Connections int64 - Listeners int - Upstreams int + + // Listeners 当前监听器数量(TCP + UDP) + Listeners int + + // Upstreams 上游配置数量 + Upstreams int } -// udpSession UDP 会话,管理客户端到后端的映射 +// udpSession UDP 会话,管理客户端到后端的映射。 +// +// 每个 UDP 会话代表一个客户端与一个后端目标的映射关系。 +// 会话包含客户端地址、后端连接、最后活跃时间等信息。 +// 会话在空闲超时后自动清理。 type udpSession struct { + // clientAddr 客户端 UDP 地址 clientAddr *net.UDPAddr + // targetConn 到后端目标的连接 targetConn net.Conn + // lastActive 最后活跃时间 lastActive time.Time - mu sync.RWMutex - srv *udpServer - closeOnce sync.Once + // mu 保护 lastActive 的读写锁 + mu sync.RWMutex + // srv 所属的 UDP 服务器 + srv *udpServer + // closeOnce 确保只关闭一次 + closeOnce sync.Once } -// udpServer UDP 服务器,管理多个客户端会话 +// udpServer UDP 服务器,管理多个客户端会话。 +// +// 负责监听 UDP 端口,管理客户端会话的生命周期。 +// 支持会话自动过期清理和优雅停止。 type udpServer struct { - conn *net.UDPConn + // conn UDP 连接 + conn *net.UDPConn + // sessions 客户端会话映射,键为 sessionKey sessions map[string]*udpSession - mu sync.RWMutex - running atomic.Bool + // mu 保护 sessions 的读写锁 + mu sync.RWMutex + // running 运行状态标志 + running atomic.Bool + // upstream 上游配置 upstream *Upstream - timeout time.Duration - stopCh chan struct{} - wg sync.WaitGroup + // timeout 会话空闲超时时间 + timeout time.Duration + // stopCh 停止信号通道 + stopCh chan struct{} + // wg 等待 goroutine 结束 + wg sync.WaitGroup } -// newUDPServer 创建新的 UDP 服务器 +// newUDPServer 创建新的 UDP 服务器。 +// +// 根据 UDP 连接、上游配置和超时时间创建 UDP 服务器实例。 +// 如果超时时间小于等于 0,使用默认 60 秒。 +// +// 参数: +// - conn: UDP 连接 +// - upstream: 上游配置 +// - timeout: 会话空闲超时时间 +// +// 返回值: +// - *udpServer: 创建的 UDP 服务器实例 func newUDPServer(conn *net.UDPConn, upstream *Upstream, timeout time.Duration) *udpServer { if timeout <= 0 { timeout = 60 * time.Second // 默认 60 秒超时 @@ -590,12 +727,29 @@ func newUDPServer(conn *net.UDPConn, upstream *Upstream, timeout time.Duration) } } -// sessionKey 从 UDP 地址生成会话键 +// sessionKey 从 UDP 地址生成会话键。 +// +// 使用 UDP 地址的字符串表示作为会话映射的唯一键。 +// +// 参数: +// - addr: UDP 地址 +// +// 返回值: +// - string: 会话键字符串 func sessionKey(addr *net.UDPAddr) string { return addr.String() } -// getSession 获取现有会话(线程安全) +// getSession 获取现有会话(线程安全)。 +// +// 根据客户端地址查找现有会话,如果找到则更新最后活跃时间。 +// 如果会话不存在返回 nil。 +// +// 参数: +// - clientAddr: 客户端 UDP 地址 +// +// 返回值: +// - *udpSession: 找到的会话,不存在时返回 nil func (s *udpServer) getSession(clientAddr *net.UDPAddr) *udpSession { s.mu.RLock() defer s.mu.RUnlock() @@ -613,7 +767,21 @@ func (s *udpServer) getSession(clientAddr *net.UDPAddr) *udpSession { return session } -// getOrCreateSession 获取或创建会话(线程安全) +// getOrCreateSession 获取或创建会话(线程安全)。 +// +// 首先尝试获取现有会话,如果不存在则创建新会话: +// 1. 选择后端目标 +// 2. 建立到目标的 UDP 连接 +// 3. 创建会话并启动响应监听 goroutine +// +// 使用双重检查锁定模式避免重复创建会话。 +// +// 参数: +// - clientAddr: 客户端 UDP 地址 +// +// 返回值: +// - *udpSession: 获取或创建的会话 +// - error: 创建失败时返回错误 func (s *udpServer) getOrCreateSession(clientAddr *net.UDPAddr) (*udpSession, error) { // 先尝试获取现有会话 session := s.getSession(clientAddr) @@ -669,7 +837,12 @@ func (s *udpServer) getOrCreateSession(clientAddr *net.UDPAddr) (*udpSession, er return session, nil } -// removeSession 移除会话(线程安全) +// removeSession 移除会话(线程安全)。 +// +// 关闭会话并删除会话映射。 +// +// 参数: +// - clientAddr: 客户端 UDP 地址 func (s *udpServer) removeSession(clientAddr *net.UDPAddr) { s.mu.Lock() defer s.mu.Unlock() @@ -681,7 +854,10 @@ func (s *udpServer) removeSession(clientAddr *net.UDPAddr) { } } -// close 关闭会话 +// close 关闭会话。 +// +// 使用 sync.Once 确保会话只关闭一次。 +// 关闭后端目标连接。 func (sess *udpSession) close() { sess.closeOnce.Do(func() { if sess.targetConn != nil { @@ -690,7 +866,14 @@ func (sess *udpSession) close() { }) } -// handleBackendResponse 处理后端响应并转发回客户端 +// handleBackendResponse 处理后端响应并转发回客户端。 +// +// 在单独的 goroutine 中运行,持续监听后端响应: +// 1. 读取后端数据 +// 2. 转发到客户端 +// 3. 处理超时和错误 +// +// 当会话超时、连接错误或服务器停止时返回。 func (sess *udpSession) handleBackendResponse() { defer sess.srv.wg.Done() @@ -734,7 +917,14 @@ func (sess *udpSession) handleBackendResponse() { } } -// serve 启动 UDP 服务循环 +// serve 启动 UDP 服务循环。 +// +// 在单独的 goroutine 中运行,持续监听 UDP 数据报: +// 1. 接收客户端数据报 +// 2. 获取或创建会话 +// 3. 转发数据到后端 +// +// 当服务器停止时返回。 func (s *udpServer) serve() { s.running.Store(true) @@ -772,7 +962,10 @@ func (s *udpServer) serve() { } } -// startCleanupTicker 启动定期清理过期会话的 ticker +// startCleanupTicker 启动定期清理过期会话的 ticker。 +// +// 每 10 秒执行一次清理,移除空闲超时的会话。 +// 当服务器停止时返回。 func (s *udpServer) startCleanupTicker() { ticker := time.NewTicker(10 * time.Second) defer ticker.Stop() @@ -787,7 +980,10 @@ func (s *udpServer) startCleanupTicker() { } } -// cleanupExpiredSessions 清理过期会话 +// cleanupExpiredSessions 清理过期会话。 +// +// 遍历所有会话,移除空闲时间超过 timeout 的会话。 +// 必须在持有写锁的情况下调用。 func (s *udpServer) cleanupExpiredSessions() { s.mu.Lock() defer s.mu.Unlock() @@ -805,7 +1001,9 @@ func (s *udpServer) cleanupExpiredSessions() { } } -// stop 停止 UDP 服务器 +// stop 停止 UDP 服务器。 +// +// 设置停止标志,关闭所有会话,等待 goroutine 结束,关闭连接。 func (s *udpServer) stop() { s.running.Store(false) close(s.stopCh) diff --git a/internal/stream/stream_test.go b/internal/stream/stream_test.go index 333a487..21041d0 100644 --- a/internal/stream/stream_test.go +++ b/internal/stream/stream_test.go @@ -1,3 +1,13 @@ +// Package stream 提供 TCP/UDP 流代理功能的测试。 +// +// 该文件测试流代理模块的各项功能,包括: +// - 服务器创建和初始化 +// - 上游配置和负载均衡 +// - TCP 和 UDP 监听 +// - 健康检查 +// - 连接统计 +// +// 作者:xfy package stream import (