lolly/internal/config/config.go
xfy ee4f7a5ca9 refactor(config): 移除已废弃的配置字段
移除以下已标记废弃的字段:
- AuthConfig.MinPasswordLength (auth)
- FileCacheConfig.LRUEviction (file_cache)
- TransportConfig.MaxIdleConns (transport)

同时清理相关的验证逻辑、默认值设置、测试代码和文档。

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-08 09:50:32 +08:00

1465 lines
41 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package config 提供 YAML 配置文件的解析、验证和默认配置生成功能。
//
// 该文件包含配置结构体定义和加载/保存功能,包括:
// - 根配置和服务器配置结构体
// - SSL、安全、代理、压缩等子配置结构体
// - 配置文件的加载、保存和验证方法
//
// 主要用途:
//
// 用于定义和管理服务器的完整配置,支持单服务器和多虚拟主机两种模式。
//
// 注意事项:
// - 配置文件使用 YAML 格式
// - 所有配置项都有合理的默认值
// - 配置加载后会自动验证
//
// 作者xfy
package config
import (
"errors"
"fmt"
"os"
"time"
"gopkg.in/yaml.v3"
)
// 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 单服务器模式配置
// 用于单一服务监听场景,与 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 是否启用 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 监听地址,如 ":8080" 或 "127.0.0.1:8080"
// 必填字段,决定服务器在哪个地址和端口接收请求
Listen string `yaml:"listen"`
// Name 服务器名称,用于虚拟主机匹配
// 多个服务器可通过 Name 区分不同域名或服务
Name string `yaml:"name"`
// Static 静态文件服务配置列表
// 支持多个静态目录,按 path 前缀匹配
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"`
// ClientMaxBodySize 客户端请求体大小限制
// 限制请求体最大字节数,超过返回 413 错误
// 支持单位b, kb, mb, gb 或纯数字表示字节
// 默认值为 1MB
ClientMaxBodySize string `yaml:"client_max_body_size"`
}
// StaticConfig 静态文件服务配置。
//
// 用于配置静态文件服务器的行为,包括路径匹配、根目录和索引文件。
//
// 注意事项:
// - Path 为路径前缀,匹配的请求将被该静态处理器处理
// - Root 路径可以是相对路径或绝对路径
// - 索引文件按顺序查找,第一个存在的文件将被使用
// - 目录路径需要确保有读取权限
//
// 使用示例:
//
// static:
// - path: "/"
// root: "/var/www/html"
// index: ["index.html", "index.htm"]
// - path: "/assets/"
// root: "/var/www/assets"
type StaticConfig struct {
// Path 匹配路径前缀
// 以此前缀开头的请求将被该静态处理器处理
// 默认为 "/",匹配所有路径
Path string `yaml:"path"`
// Root 静态文件根目录
// 所有静态文件请求都将以此目录为基础解析
Root string `yaml:"root"`
// Index 索引文件列表
// 访问目录时依次查找这些文件作为默认页面
// 默认为 ["index.html", "index.htm"]
Index []string `yaml:"index"`
// TryFiles 按顺序尝试查找的文件列表
// 支持 $uri 和 $uri/ 占位符,用于 SPA 部署
// 示例: ["$uri", "$uri/", "/index.html"]
TryFiles []string `yaml:"try_files"`
// TryFilesPass 内部重定向是否触发中间件
// 默认为 false内部重定向不触发中间件
// 设置为 true 时try_files 回退会重新进入中间件链
TryFilesPass bool `yaml:"try_files_pass"`
}
// 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 匹配路径前缀
// 以此前缀开头的请求将被转发到该代理
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"`
// ClientMaxBodySize 请求体大小限制
// 限制此代理路径的请求体最大字节数,覆盖全局配置
// 支持单位b, kb, mb, gb 或纯数字表示字节
ClientMaxBodySize string `yaml:"client_max_body_size"`
// NextUpstream 故障转移配置
// 配置后端故障时的自动重试行为
NextUpstream NextUpstreamConfig `yaml:"next_upstream"`
}
// 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 后端地址
// 格式:"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 检查间隔
// 每次健康检查之间的时间间隔
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 连接超时
// 建立到后端服务器的连接超时
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 设置请求头
// 发送到后端的请求中添加或覆盖的头部
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 是否启用缓存
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"`
}
// NextUpstreamConfig 故障转移配置,定义后端失败时的自动重试行为。
//
// 当后端返回特定错误状态码或连接失败时,自动尝试下一个可用后端。
//
// 注意事项:
// - Tries 为 1 时禁用故障转移
// - 空 NextUpstream 使用默认值Tries=1禁用故障转移
// - 建议根据后端数量合理设置 Tries 值
//
// 使用示例:
//
// next_upstream:
// tries: 3
// http_codes: [502, 503, 504]
type NextUpstreamConfig struct {
// Tries 最大尝试次数
// 包括第一次尝试在内的总请求次数
Tries int `yaml:"tries"`
// HTTPCodes 触发重试的 HTTP 状态码列表
// 后端返回这些状态码时自动尝试下一个
HTTPCodes []int `yaml:"http_codes"`
}
// 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 证书文件路径
// 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 过期时间(秒)
// 默认 315360001年建议至少 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 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"`
// ErrorPage 自定义错误页面配置
// 允许为特定 HTTP 状态码配置自定义错误页面
ErrorPage ErrorPageConfig `yaml:"error_page"`
}
// 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 允许的 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 每秒请求数限制
// 超过此速率的请求将被拒绝
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 认证类型
// 目前仅支持 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"`
}
// User 认证用户配置。
//
// 定义单个认证用户的凭据。
//
// 注意事项:
// - Name 为用户标识,区分大小写
// - Password 存储的是哈希值而非明文密码
// - 支持的哈希格式取决于 Algorithm 设置
//
// 使用示例:
//
// users:
// - name: "admin"
// password: "$2y$10$N9qo8uLOickgx2ZMRZoMy..."
type User struct {
// 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 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"`
}
// ErrorPageConfig 自定义错误页面配置。
//
// 允许为特定 HTTP 状态码配置自定义错误页面。
// 错误页面文件在启动时预加载到内存中,运行时不进行文件 I/O。
//
// 注意事项:
// - 错误页面文件路径可以是相对路径或绝对路径
// - 所有错误页面加载失败时会阻止服务器启动
// - 部分错误页面加载失败会记录警告但允许启动
// - 支持可选的响应状态码覆盖
//
// 使用示例:
//
// error_page:
// pages:
// 404: "/var/www/errors/404.html"
// 500: "/var/www/errors/500.html"
// 503: "/var/www/errors/503.html"
// default: "/var/www/errors/error.html"
// response_code: 200 # 可选:覆盖响应状态码
type ErrorPageConfig struct {
// Pages 状态码到错误页面文件的映射
// key 为 HTTP 状态码(如 404, 500value 为文件路径
Pages map[int]string `yaml:"pages"`
// Default 默认错误页面
// 当特定状态码没有配置时使用
Default string `yaml:"default"`
// ResponseCode 响应状态码覆盖
// 如果不为 0所有错误页面响应将使用此状态码
// 例如设置为 200 时,即使发生错误也返回 200 OK
ResponseCode int `yaml:"response_code"`
}
// 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 匹配模式
// 正则表达式,用于匹配请求 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 压缩类型
// 可选值gzip、brotli、both
Type string `yaml:"type"`
// Level 压缩级别
// 范围 1-91 最快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 全局格式
// 可选值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 日志文件路径
// 访问日志的输出文件,为空则输出到标准输出
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 日志文件路径
// 错误日志的输出文件,为空则输出到标准错误
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 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 是否启用
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 超过此时间未访问的文件将被淘汰
//
// 使用示例:
//
// file_cache:
// max_entries: 10000
// max_size: 1073741824
// inactive: 60s
type FileCacheConfig struct {
// MaxEntries 最大缓存条目数
// 缓存文件的最大数量限制
MaxEntries int64 `yaml:"max_entries"`
// MaxSize 内存上限(字节)
// 缓存占用的最大内存限制
MaxSize int64 `yaml:"max_size"`
// Inactive 未访问淘汰时间
// 超过此时间未被访问的缓存将被清除
Inactive time.Duration `yaml:"inactive"`
}
// TransportConfig HTTP Transport 配置。
//
// 配置代理后端连接的连接池参数。
//
// 注意事项:
// - MaxIdleConnsPerHost 控制每个后端主机的空闲连接
// - IdleConnTimeout 控制空闲连接的保持时间
// - MaxConnsPerHost 限制每个后端主机的总连接数
//
// 使用示例:
//
// transport:
// max_idle_conns_per_host: 10
// idle_conn_timeout: 90s
// max_conns_per_host: 100
type TransportConfig struct {
// 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 状态端点配置
// 服务健康状态检查端点
Status StatusConfig `yaml:"status"`
// Pprof pprof 性能分析端点配置
// 用于收集 CPU、内存等性能数据支持 PGO 优化
Pprof PprofConfig `yaml:"pprof"`
}
// PprofConfig pprof 性能分析端点配置。
//
// 配置 pprof 端点用于收集运行时性能数据。
// 收集的 profile 可用于 PGO (Profile-Guided Optimization) 构建。
//
// 注意事项:
// - 生产环境仅在收集 profile 时启用,完成后关闭
// - 建议严格限制访问 IP防止性能数据泄露
// - CPU profile 收集需要代表性 workload
//
// 使用示例:
//
// pprof:
// enabled: true
// path: "/debug/pprof"
// allow: ["127.0.0.1"]
type PprofConfig struct {
// Enabled 是否启用 pprof 端点
Enabled bool `yaml:"enabled"`
// Path 端点路径前缀
// 默认为 "/debug/pprof"
Path string `yaml:"path"`
// Allow 允许访问的 IP 列表
// 可访问 pprof 端点的 IP 地址或 CIDR
Allow []string `yaml:"allow"`
}
// StatusConfig 状态监控端点配置。
//
// 配置服务状态检查端点的路径和访问控制。
//
// 注意事项:
// - Path 为状态端点的 URL 路径
// - Allow 限制可访问的 IP 地址列表
// - 生产环境建议严格限制访问来源
//
// 使用示例:
//
// status:
// path: "/status"
// allow: ["127.0.0.1", "192.168.0.0/16"]
type StatusConfig struct {
// 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 监听地址
// 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 目标列表
// 后端服务器地址列表
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 目标地址
// 后端服务器地址,如 "host:port"
Addr string `yaml:"addr"`
// Weight 权重
// 用于加权轮询负载均衡
Weight int `yaml:"weight"`
}
// Load 从文件加载配置。
//
// 读取指定路径的 YAML 配置文件,解析并验证配置内容。
//
// 参数:
// - path: 配置文件路径
//
// 返回值:
// - *Config: 解析后的配置对象
// - error: 读取、解析或验证失败时的错误信息
//
// 注意事项:
// - 加载后会自动调用 Validate 进行配置验证
// - 文件不存在或格式错误都会返回错误
func Load(path string) (*Config, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("读取配置文件失败: %w", err)
}
var cfg Config
if err := yaml.Unmarshal(data, &cfg); err != nil {
return nil, fmt.Errorf("解析配置文件失败: %w", err)
}
if err := Validate(&cfg); err != nil {
return nil, fmt.Errorf("配置验证失败: %w", err)
}
return &cfg, nil
}
// LoadFromString 从 YAML 字符串加载配置。
//
// 解析 YAML 格式的配置字符串,适用于从环境变量或命令行参数加载配置。
//
// 参数:
// - yamlStr: YAML 格式的配置字符串
//
// 返回值:
// - *Config: 解析后的配置对象
// - error: 解析或验证失败时的错误信息
//
// 注意事项:
// - 加载后会自动调用 Validate 进行配置验证
func LoadFromString(yamlStr string) (*Config, error) {
var cfg Config
if err := yaml.Unmarshal([]byte(yamlStr), &cfg); err != nil {
return nil, fmt.Errorf("解析配置失败: %w", err)
}
if err := Validate(&cfg); err != nil {
return nil, fmt.Errorf("配置验证失败: %w", err)
}
return &cfg, nil
}
// Save 保存配置到文件。
//
// 将配置对象序列化为 YAML 格式并写入指定文件。
//
// 参数:
// - cfg: 配置对象
// - path: 目标文件路径
//
// 返回值:
// - error: 序列化或写入失败时的错误信息
//
// 注意事项:
// - 文件权限设为 0644
func Save(cfg *Config, path string) error {
data, err := yaml.Marshal(cfg)
if err != nil {
return fmt.Errorf("序列化配置失败: %w", err)
}
if err := os.WriteFile(path, data, 0644); err != nil {
return fmt.Errorf("写入配置文件失败: %w", err)
}
return nil
}
// HasServers 检查是否为多虚拟主机模式。
//
// 返回值:
// - bool: 如果配置了 servers 列表且非空,返回 true
func (c *Config) HasServers() bool {
return len(c.Servers) > 0
}
// HasDefaultServer 检查是否有默认服务器配置。
//
// 返回值:
// - bool: 如果 server.listen 已配置,返回 true
func (c *Config) HasDefaultServer() bool {
return c.Server.Listen != ""
}
// GetDefaultServer 获取默认服务器配置。
//
// 用于在虚拟主机模式下获取默认服务器的配置作为 fallback。
//
// 返回值:
// - *ServerConfig: 默认服务器配置,如未配置则返回 nil
func (c *Config) GetDefaultServer() *ServerConfig {
if c.HasDefaultServer() {
return &c.Server
}
return nil
}
// Validate 配置验证入口。
//
// 验证配置的完整性和有效性,检查是否至少配置了一个服务器,
// 并递归验证所有服务器配置。
//
// 参数:
// - cfg: 配置对象
//
// 返回值:
// - error: 验证失败时的错误信息,包含具体字段路径
//
// 验证规则:
// - 必须配置 server 或 servers 中的至少一个
// - 所有服务器配置必须通过 validateServer 验证
func Validate(cfg *Config) error {
// 至少需要一种服务器配置
if !cfg.HasDefaultServer() && !cfg.HasServers() {
return errors.New("至少需要配置 server 或 servers")
}
// 验证默认服务器
if cfg.HasDefaultServer() {
if err := validateServer(&cfg.Server, true); err != nil {
return err
}
}
// 验证所有虚拟主机
for i := range cfg.Servers {
if err := validateServer(&cfg.Servers[i], false); err != nil {
return fmt.Errorf("servers[%d]: %w", i, err)
}
}
// 验证 Stream 配置
for i := range cfg.Stream {
if err := validateStream(&cfg.Stream[i]); err != nil {
return fmt.Errorf("stream[%d]: %w", i, err)
}
}
// 验证日志配置
if err := validateLogging(&cfg.Logging); err != nil {
return err
}
// 验证性能配置
if err := validatePerformance(&cfg.Performance); err != nil {
return fmt.Errorf("performance: %w", err)
}
return nil
}