// Package config 提供 YAML 配置文件的解析、验证和默认配置生成功能。 // // 该文件包含根配置结构体和加载/保存功能,包括: // - 根配置结构体 // - 配置文件的加载、保存和验证方法 // // 主要用途: // // 用于定义和管理服务器的完整配置,支持单服务器和多虚拟主机两种模式。 // // 注意事项: // - 配置文件使用 YAML 格式 // - 所有配置项都有合理的默认值 // - 配置加载后会自动验证 // // 作者:xfy package config import ( "errors" "fmt" "os" "path/filepath" "strconv" "strings" "gopkg.in/yaml.v3" ) // 默认配置常量。 const ( // DefaultPprofPath pprof 端点的默认路径。 DefaultPprofPath = "/debug/pprof" ) // ServerMode 服务器运行模式类型。 // // 定义服务器的工作模式,支持显式配置或自动推断。 type ServerMode string // ServerMode 枚举值。 const ( // ServerModeSingle 单服务器模式 - 只运行一个服务器实例。 ServerModeSingle ServerMode = "single" // ServerModeVHost 虚拟主机模式 - 多个服务器共享相同的监听地址。 ServerModeVHost ServerMode = "vhost" // ServerModeMultiServer 多服务器模式 - 多个服务器监听不同的地址。 ServerModeMultiServer ServerMode = "multi_server" // ServerModeAuto 自动模式 - 根据配置自动推断运行模式。 ServerModeAuto ServerMode = "auto" ) // Config 根配置结构,支持单服务器和多虚拟主机两种模式。 // // 包含服务器配置、日志配置、性能配置和监控配置等模块。 // 是配置文件的顶级结构体,所有其他配置都作为其子结构。 // // 注意事项: // - 必须配置 servers 列表中的至少一个 // - 加载后会自动进行配置验证 // - Stream 配置为可选,用于 TCP/UDP 层代理 // - HTTP/3 配置为可选,需 SSL 配置配合才能生效 // // 使用示例: // // cfg, err := config.Load("config.yaml") // if err != nil { // log.Fatal(err) // } // // 使用多虚拟主机模式 // for _, s := range cfg.Servers { // // 处理每个服务器配置 // } type Config struct { Mode ServerMode `yaml:"mode"` Variables VariablesConfig `yaml:"variables"` Logging LoggingConfig `yaml:"logging"` Servers []ServerConfig `yaml:"servers"` Stream []StreamConfig `yaml:"stream"` Monitoring MonitoringConfig `yaml:"monitoring"` HTTP3 HTTP3Config `yaml:"http3"` Resolver ResolverConfig `yaml:"resolver"` Performance PerformanceConfig `yaml:"performance"` Shutdown ShutdownConfig `yaml:"shutdown"` Include []IncludeConfig `yaml:"include"` // 配置引入,支持从其他文件引入配置片段 } // parseSize 解析大小字符串(支持 k, m 单位)。 func parseSize(s string) (int, error) { s = strings.TrimSpace(s) if s == "" { return 0, strconv.ErrSyntax } // 提取单位 unit := strings.ToLower(s[len(s)-1:]) multiplier := 1 numStr := s switch unit { case "k": multiplier = 1024 numStr = s[:len(s)-1] case "m": multiplier = 1024 * 1024 numStr = s[:len(s)-1] } value, err := strconv.Atoi(numStr) if err != nil { return 0, err } return value * multiplier, nil } // 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 len(cfg.Include) > 0 { if err := processIncludes(&cfg, filepath.Dir(path), 0); err != nil { return nil, fmt.Errorf("处理配置引入失败: %w", err) } } if err := Validate(&cfg); err != nil { return nil, fmt.Errorf("配置验证失败: %w", err) } return &cfg, nil } const maxIncludeDepth = 10 func processIncludes(cfg *Config, baseDir string, depth int) error { if depth >= maxIncludeDepth { return fmt.Errorf("配置引入嵌套深度超过 %d 层,可能存在循环引入", maxIncludeDepth) } for _, inc := range cfg.Include { pattern := inc.Path if !filepath.IsAbs(pattern) { pattern = filepath.Join(baseDir, pattern) } matches, err := filepath.Glob(pattern) if err != nil { return fmt.Errorf("展开引入路径 %q 失败: %w", inc.Path, err) } if len(matches) == 0 { return fmt.Errorf("引入路径 %q 未匹配到任何文件", inc.Path) } for _, match := range matches { info, err := os.Stat(match) if err != nil { return fmt.Errorf("读取引入文件 %q 失败: %w", match, err) } if info.IsDir() { continue } data, err := os.ReadFile(match) if err != nil { return fmt.Errorf("读取引入文件 %q 失败: %w", match, err) } var included Config if err := yaml.Unmarshal(data, &included); err != nil { return fmt.Errorf("解析引入文件 %q 失败: %w", match, err) } if len(included.Include) > 0 { if err := processIncludes(&included, filepath.Dir(match), depth+1); err != nil { return err } } cfg.Servers = append(cfg.Servers, included.Servers...) cfg.Stream = append(cfg.Stream, included.Stream...) for k, v := range included.Variables.Set { if _, exists := cfg.Variables.Set[k]; !exists { if cfg.Variables.Set == nil { cfg.Variables.Set = make(map[string]string) } cfg.Variables.Set[k] = v } } } } cfg.Include = nil return 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, 0o644); err != nil { return fmt.Errorf("写入配置文件失败: %w", err) } return nil } // HasServers 检查是否为多虚拟主机模式。 // // 返回值: // - bool: 如果配置了 servers 列表且非空,返回 true func (c *Config) HasServers() bool { return len(c.Servers) > 0 } // GetDefaultServerFromList 从 servers 列表中获取默认服务器配置。 // // 遍历 servers 列表,返回第一个 Default 标记为 true 的服务器。 // 用于在虚拟主机模式下获取默认服务器的配置作为 fallback。 // // 返回值: // - *ServerConfig: 默认服务器配置,如无则返回 nil func (c *Config) GetDefaultServerFromList() *ServerConfig { for i := range c.Servers { if c.Servers[i].Default { return &c.Servers[i] } } return nil } // GetMode 获取服务器运行模式。 // // 如果 Mode 显式设置(非 auto),返回设置的值。 // 如果 Mode 是 auto 或未设置,根据配置自动推断: // - servers 数量 == 1 → single // - servers 数量 > 1 且所有 listen 地址相同 → vhost // - servers 数量 > 1 且 listen 地址不同 → multi_server // // 返回值: // - ServerMode: 推断后的服务器运行模式 func (c *Config) GetMode() ServerMode { // 如果显式设置了非 auto 模式,直接返回 if c.Mode != "" && c.Mode != ServerModeAuto { return c.Mode } // 自动推断模式 serverCount := len(c.Servers) // servers 为空 → auto(配置验证会确保至少有一个服务器) if serverCount == 0 { return ServerModeAuto } // servers 数量 == 1 → single if serverCount == 1 { return ServerModeSingle } // servers 数量 > 1,检查 listen 地址 firstListen := c.Servers[0].Listen allSameListen := true for i := 1; i < serverCount; i++ { if c.Servers[i].Listen != firstListen { allSameListen = false break } } // 所有 listen 地址相同 → vhost,否则 → multi_server if allSameListen { return ServerModeVHost } return ServerModeMultiServer } // Validate 配置验证入口。 // // 验证配置的完整性和有效性,检查是否至少配置了一个服务器, // 并递归验证所有服务器配置。 // // 参数: // - cfg: 配置对象 // // 返回值: // - error: 验证失败时的错误信息,包含具体字段路径 // // 验证规则: // - 必须配置 servers 数组且至少包含一个服务器 // - 所有服务器配置必须通过 validateServer 验证 func Validate(cfg *Config) error { // 必须配置 servers 且至少包含一个服务器 if !cfg.HasServers() { return errors.New("必须配置 servers 且至少包含一个服务器") } // 验证模式 if err := validateMode(cfg.Mode); err != nil { return err } // 验证监听地址冲突(multi_server 模式) if err := validateListenConflicts(cfg.Servers, cfg.GetMode()); err != nil { return err } // 验证 default 服务器唯一性 if err := validateDefaultServer(cfg.Servers); 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) } // 验证 Resolver 配置 if err := cfg.Resolver.Validate(); err != nil { return fmt.Errorf("resolver: %w", err) } // 验证变量配置 if err := validateVariables(&cfg.Variables); err != nil { return fmt.Errorf("variables: %w", err) } // 验证关闭配置 if err := validateShutdown(&cfg.Shutdown); err != nil { return err } return nil } // validateShutdown 验证关闭配置。 func validateShutdown(cfg *ShutdownConfig) error { if cfg.GracefulTimeout < 0 { return errors.New("shutdown.graceful_timeout 不能为负数") } if cfg.FastTimeout < 0 { return errors.New("shutdown.fast_timeout 不能为负数") } // 0 值表示使用默认值,在应用层处理 return nil }