feat(config): 完善 Resolver/SSL 默认值及 YAML 输出

- 添加 Resolver/SessionTickets/ClientVerify 默认配置
- GenerateConfigYAML 输出改为非注释格式
- 新增 Resolver、SSL 默认值、YAML 可加载性测试

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
xfy 2026-04-09 14:44:01 +08:00
parent be56cae64f
commit 83b2a35715
2 changed files with 146 additions and 25 deletions

View File

@ -66,6 +66,19 @@ func DefaultConfig() *Config {
PushEnabled: false,
H2CEnabled: false,
},
SessionTickets: SessionTicketsConfig{
Enabled: false,
KeyFile: "",
RotateInterval: 1 * time.Hour,
RetainKeys: 3,
},
ClientVerify: ClientVerifyConfig{
Enabled: false,
Mode: "none",
ClientCA: "",
VerifyDepth: 1,
CRL: "",
},
},
Security: SecurityConfig{
Access: AccessConfig{
@ -157,6 +170,15 @@ func DefaultConfig() *Config {
IdleTimeout: 60 * time.Second,
Enable0RTT: false,
},
Resolver: ResolverConfig{
Enabled: false,
Addresses: []string{"8.8.8.8:53", "8.8.4.4:53"},
Valid: 30 * time.Second,
Timeout: 5 * time.Second,
IPv4: true,
IPv6: false,
CacheSize: 1024,
},
Variables: VariablesConfig{
Set: map[string]string{},
},
@ -328,8 +350,8 @@ func GenerateConfigYAML(cfg *Config) ([]byte, error) {
fmt.Fprintf(&buf, " x_frame_options: \"%s\" # 防止点击劫持(有效值: DENY, SAMEORIGIN, 空表示禁用)\n", cfg.Server.Security.Headers.XFrameOptions)
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")
buf.WriteString(" content_security_policy: \"\" # 内容安全策略 CSP(空表示禁用)\n")
buf.WriteString(" permissions_policy: \"\" # 权限策略(空表示禁用)\n")
buf.WriteString("\n")
buf.WriteString(" # 自定义错误页面\n")
buf.WriteString(" error_page:\n")
@ -338,13 +360,13 @@ func GenerateConfigYAML(cfg *Config) ([]byte, error) {
buf.WriteString(" response_code: 0 # 响应状态码覆盖0 表示使用原始状态码)\n")
buf.WriteString("\n")
buf.WriteString(" # 外部认证子请求配置(将认证委托给外部服务)\n")
buf.WriteString(" # auth_request:\n")
buf.WriteString(" # enabled: false # 是否启用外部认证\n")
buf.WriteString(" # uri: \"/auth\" # 认证服务地址(支持相对路径或完整 URL\n")
buf.WriteString(" # method: \"GET\" # 认证请求方法(有效值: GET, POST, HEAD\n")
buf.WriteString(" # auth_timeout: 5s # 认证请求超时时间\n")
buf.WriteString(" # headers: {} # 自定义认证请求头,如 {X-Original-Uri: \"$request_uri\"}\n")
buf.WriteString(" # forward_headers: [] # 需要转发的原请求头,默认包含 Cookie, Authorization, X-Forwarded-For\n")
buf.WriteString(" auth_request:\n")
buf.WriteString(" enabled: false # 是否启用外部认证\n")
buf.WriteString(" uri: \"\" # 认证服务地址(支持相对路径或完整 URL\n")
buf.WriteString(" method: \"GET\" # 认证请求方法(有效值: GET, POST, HEAD\n")
buf.WriteString(" auth_timeout: 5s # 认证请求超时时间\n")
buf.WriteString(" headers: {} # 自定义认证请求头,如 {X-Original-Uri: \"$request_uri\"}\n")
buf.WriteString(" forward_headers: [] # 需要转发的原请求头,默认包含 Cookie, Authorization, X-Forwarded-For\n")
buf.WriteString("\n")
// rewrite 配置示例
@ -520,26 +542,24 @@ func GenerateConfigYAML(cfg *Config) ([]byte, error) {
// resolver 配置
buf.WriteString("# DNS 解析器配置(用于后端域名动态解析)\n")
buf.WriteString("# resolver:\n")
fmt.Fprintf(&buf, "# enabled: %v # 是否启用 DNS 解析器\n", cfg.Resolver.Enabled)
buf.WriteString("# addresses: # DNS 服务器地址列表\n")
buf.WriteString("# - \"8.8.8.8:53\"\n")
buf.WriteString("# - \"8.8.4.4:53\"\n")
fmt.Fprintf(&buf, "# valid: %ds # 缓存有效期TTL建议 30s-300s\n", int(cfg.Resolver.Valid.Seconds()))
fmt.Fprintf(&buf, "# timeout: %ds # DNS 查询超时\n", int(cfg.Resolver.Timeout.Seconds()))
fmt.Fprintf(&buf, "# ipv4: %v # 是否查询 IPv4 地址\n", cfg.Resolver.IPv4)
fmt.Fprintf(&buf, "# ipv6: %v # 是否查询 IPv6 地址\n", cfg.Resolver.IPv6)
fmt.Fprintf(&buf, "# cache_size: %d # 缓存最大条目数0 表示不限制)\n", cfg.Resolver.CacheSize)
buf.WriteString("resolver:\n")
fmt.Fprintf(&buf, " enabled: %v # 是否启用 DNS 解析器\n", cfg.Resolver.Enabled)
buf.WriteString(" addresses: # DNS 服务器地址列表\n")
buf.WriteString(" - \"8.8.8.8:53\"\n")
buf.WriteString(" - \"8.8.4.4:53\"\n")
fmt.Fprintf(&buf, " valid: %ds # 缓存有效期TTL建议 30s-300s\n", int(cfg.Resolver.Valid.Seconds()))
fmt.Fprintf(&buf, " timeout: %ds # DNS 查询超时\n", int(cfg.Resolver.Timeout.Seconds()))
fmt.Fprintf(&buf, " ipv4: %v # 是否查询 IPv4 地址\n", cfg.Resolver.IPv4)
fmt.Fprintf(&buf, " ipv6: %v # 是否查询 IPv6 地址\n", cfg.Resolver.IPv6)
fmt.Fprintf(&buf, " cache_size: %d # 缓存最大条目数0 表示不限制)\n", cfg.Resolver.CacheSize)
buf.WriteString("\n")
// variables 配置
buf.WriteString("# 自定义变量配置(全局变量,应用于所有虚拟主机)\n")
buf.WriteString("# variables:\n")
buf.WriteString("# set: # 自定义变量集合\n")
buf.WriteString("# app_name: \"lolly\" # 可在日志格式中通过 $var_app_name 引用\n")
buf.WriteString("# version: \"1.0.0\"\n")
buf.WriteString("# 注意:变量名只允许字母、数字、下划线,不能与内置变量冲突\n")
buf.WriteString("# 不能以 arg_、http_、cookie_ 开头(这些是动态变量前缀)\n")
buf.WriteString("variables:\n")
buf.WriteString(" set: {} # 自定义变量集合,如 {app_name: \"lolly\"}\n")
buf.WriteString(" # 注意:变量名只允许字母、数字、下划线,不能与内置变量冲突\n")
buf.WriteString(" # 不能以 arg_、http_、cookie_ 开头(这些是动态变量前缀)\n")
return buf.Bytes(), nil
}

View File

@ -131,3 +131,104 @@ func TestDefaultConfigPerformance(t *testing.T) {
t.Errorf("Transport.MaxConnsPerHost 期望 0 (不限制), 实际 %d", cfg.Performance.Transport.MaxConnsPerHost)
}
}
func TestDefaultConfigResolver(t *testing.T) {
// TestDefaultConfigResolver 测试 Resolver 默认值。
cfg := DefaultConfig()
// 验证 Resolver 默认值
if cfg.Resolver.Enabled {
t.Error("Resolver.Enabled 期望 false")
}
if len(cfg.Resolver.Addresses) != 2 {
t.Errorf("Resolver.Addresses 期望 2 个 DNS 服务器,实际 %d", len(cfg.Resolver.Addresses))
}
if cfg.Resolver.Valid != 30*time.Second {
t.Errorf("Resolver.Valid 期望 30s实际 %v", cfg.Resolver.Valid)
}
if cfg.Resolver.Timeout != 5*time.Second {
t.Errorf("Resolver.Timeout 期望 5s实际 %v", cfg.Resolver.Timeout)
}
if !cfg.Resolver.IPv4 {
t.Error("Resolver.IPv4 期望 true")
}
if cfg.Resolver.IPv6 {
t.Error("Resolver.IPv6 期望 false")
}
if cfg.Resolver.CacheSize != 1024 {
t.Errorf("Resolver.CacheSize 期望 1024实际 %d", cfg.Resolver.CacheSize)
}
}
func TestDefaultConfigSSLDefaults(t *testing.T) {
// TestDefaultConfigSSLDefaults 测试 SSL 相关默认值。
cfg := DefaultConfig()
// 验证 SessionTickets 默认值
if cfg.Server.SSL.SessionTickets.Enabled {
t.Error("SessionTickets.Enabled 期望 false")
}
if cfg.Server.SSL.SessionTickets.RetainKeys != 3 {
t.Errorf("SessionTickets.RetainKeys 期望 3实际 %d", cfg.Server.SSL.SessionTickets.RetainKeys)
}
// 验证 ClientVerify 默认值
if cfg.Server.SSL.ClientVerify.Enabled {
t.Error("ClientVerify.Enabled 期望 false")
}
if cfg.Server.SSL.ClientVerify.Mode != "none" {
t.Errorf("ClientVerify.Mode 期望 none实际 %s", cfg.Server.SSL.ClientVerify.Mode)
}
if cfg.Server.SSL.ClientVerify.VerifyDepth != 1 {
t.Errorf("ClientVerify.VerifyDepth 期望 1实际 %d", cfg.Server.SSL.ClientVerify.VerifyDepth)
}
}
func TestGenerateConfigYAMLContainsAllSections(t *testing.T) {
// TestGenerateConfigYAMLContainsAllSections 测试 YAML 包含所有配置块。
cfg := DefaultConfig()
yamlData, err := GenerateConfigYAML(cfg)
if err != nil {
t.Fatalf("GenerateConfigYAML 失败: %v", err)
}
yamlStr := string(yamlData)
// 验证包含非注释的配置块
requiredSections := []string{
"resolver:",
"variables:",
"content_security_policy:",
"permissions_policy:",
"auth_request:",
}
for _, section := range requiredSections {
// 检查配置块存在
if !strings.Contains(yamlStr, section) {
t.Errorf("配置块 %s 缺失", section)
}
}
}
func TestGenerateConfigYAMLLoadable(t *testing.T) {
// TestGenerateConfigYAMLLoadable 测试生成的 YAML 可以被加载。
cfg := DefaultConfig()
yamlData, err := GenerateConfigYAML(cfg)
if err != nil {
t.Fatalf("GenerateConfigYAML 失败: %v", err)
}
// 尝试加载生成的 YAML
loadedCfg, err := LoadFromString(string(yamlData))
if err != nil {
t.Fatalf("生成的 YAML 无法加载: %v", err)
}
// 验证关键字段匹配
if loadedCfg.Server.Listen != cfg.Server.Listen {
t.Errorf("Server.Listen 不匹配: 期望 %s, 实际 %s", cfg.Server.Listen, loadedCfg.Server.Listen)
}
if loadedCfg.Resolver.Enabled != cfg.Resolver.Enabled {
t.Errorf("Resolver.Enabled 不匹配")
}
}