// Package loader converts a parsed [config.Config] into running components // by parsing each config section and registering the results into the // global registries. Components are registered in dependency order so that // when a component reads its dependencies from registries during parsing, // those dependencies are already available. package loader import ( "github.com/go-gost/core/admission" "github.com/go-gost/core/auth" "github.com/go-gost/core/bypass" "github.com/go-gost/core/chain" "github.com/go-gost/core/hop" "github.com/go-gost/core/hosts" "github.com/go-gost/core/ingress" "github.com/go-gost/core/limiter/conn" "github.com/go-gost/core/limiter/rate" "github.com/go-gost/core/limiter/traffic" "github.com/go-gost/core/logger" "github.com/go-gost/core/observer" "github.com/go-gost/core/recorder" reg "github.com/go-gost/core/registry" "github.com/go-gost/core/resolver" "github.com/go-gost/core/router" "github.com/go-gost/core/sd" "github.com/go-gost/x/config" "github.com/go-gost/x/config/parsing" admission_parser "github.com/go-gost/x/config/parsing/admission" auth_parser "github.com/go-gost/x/config/parsing/auth" bypass_parser "github.com/go-gost/x/config/parsing/bypass" chain_parser "github.com/go-gost/x/config/parsing/chain" hop_parser "github.com/go-gost/x/config/parsing/hop" hosts_parser "github.com/go-gost/x/config/parsing/hosts" ingress_parser "github.com/go-gost/x/config/parsing/ingress" limiter_parser "github.com/go-gost/x/config/parsing/limiter" logger_parser "github.com/go-gost/x/config/parsing/logger" observer_parser "github.com/go-gost/x/config/parsing/observer" quota_parser "github.com/go-gost/x/config/parsing/quota" recorder_parser "github.com/go-gost/x/config/parsing/recorder" resolver_parser "github.com/go-gost/x/config/parsing/resolver" router_parser "github.com/go-gost/x/config/parsing/router" sd_parser "github.com/go-gost/x/config/parsing/sd" service_parser "github.com/go-gost/x/config/parsing/service" quota "github.com/go-gost/x/limiter/quota" "github.com/go-gost/x/registry" ) // defaultLoader is a singleton loader used by the top-level Load function. var ( defaultLoader *loader = &loader{} ) // Load parses all config sections from cfg and registers the resulting // components into the global registries, then sets up the default logger // and TLS config. func Load(cfg *config.Config) error { return defaultLoader.Load(cfg) } // loader holds no state; its Load method is the entry point for converting // a config into running components. type loader struct{} // Load builds the default TLS config, registers all named components from // cfg into the global registries, and sets the default logger and TLS config. func (l *loader) Load(cfg *config.Config) error { if cfg == nil { return nil } logCfg := cfg.Log if logCfg == nil { logCfg = &config.LogConfig{} } logger.SetDefault(logger_parser.ParseLogger(&config.LoggerConfig{Log: logCfg})) tlsCfg, err := parsing.BuildDefaultTLSConfig(cfg.TLS) if err != nil { return err } parsing.SetDefaultTLSConfig(tlsCfg) if err := register(cfg); err != nil { return err } return nil } // named is a generic name+value pair used to buffer parsed components // before registering them. type named[T any] struct { name string v T } // registerGroup replaces all entries in r with the given entries. Old entries // are unregistered first (see unregisterAll); if any new entry fails to // register, the group is left partially updated (matching the historical // reload behavior for intra-group failures). func registerGroup[T any](entries []named[T], r reg.Registry[T]) error { unregisterAll(r) for _, e := range entries { if err := r.Register(e.name, e.v); err != nil { return err } } return nil } // unregisterAll removes every entry from r. registry.Unregister closes a value // before deleting it when it implements io.Closer, so for services this frees // the bound port (a service's Close closes its listener). func unregisterAll[T any](r reg.Registry[T]) { for name := range r.GetAll() { r.Unregister(name) } } // register parses config sections and registers them into the global // registries. Groups are processed in dependency order: leaf components // first, then hops, chains, and finally services. This ensures that // when a component reads its dependencies from registries during // parsing, those dependencies are already registered. func register(cfg *config.Config) error { if cfg == nil { return nil } // --- leaf components (no inter-group registry dependencies) --- { var entries []named[logger.Logger] for _, c := range cfg.Loggers { entries = append(entries, named[logger.Logger]{c.Name, logger_parser.ParseLogger(c)}) } if err := registerGroup(entries, registry.LoggerRegistry()); err != nil { return err } } { var entries []named[auth.Authenticator] for _, c := range cfg.Authers { entries = append(entries, named[auth.Authenticator]{c.Name, auth_parser.ParseAuther(c)}) } if err := registerGroup(entries, registry.AutherRegistry()); err != nil { return err } } { var entries []named[admission.Admission] for _, c := range cfg.Admissions { entries = append(entries, named[admission.Admission]{c.Name, admission_parser.ParseAdmission(c)}) } if err := registerGroup(entries, registry.AdmissionRegistry()); err != nil { return err } } { var entries []named[bypass.Bypass] for _, c := range cfg.Bypasses { entries = append(entries, named[bypass.Bypass]{c.Name, bypass_parser.ParseBypass(c)}) } if err := registerGroup(entries, registry.BypassRegistry()); err != nil { return err } } { var entries []named[resolver.Resolver] for _, c := range cfg.Resolvers { r, err := resolver_parser.ParseResolver(c) if err != nil { return err } entries = append(entries, named[resolver.Resolver]{c.Name, r}) } if err := registerGroup(entries, registry.ResolverRegistry()); err != nil { return err } } { var entries []named[hosts.HostMapper] for _, c := range cfg.Hosts { entries = append(entries, named[hosts.HostMapper]{c.Name, hosts_parser.ParseHostMapper(c)}) } if err := registerGroup(entries, registry.HostsRegistry()); err != nil { return err } } { var entries []named[ingress.Ingress] for _, c := range cfg.Ingresses { entries = append(entries, named[ingress.Ingress]{c.Name, ingress_parser.ParseIngress(c)}) } if err := registerGroup(entries, registry.IngressRegistry()); err != nil { return err } } { var entries []named[router.Router] for _, c := range cfg.Routers { entries = append(entries, named[router.Router]{c.Name, router_parser.ParseRouter(c)}) } if err := registerGroup(entries, registry.RouterRegistry()); err != nil { return err } } { var entries []named[sd.SD] for _, c := range cfg.SDs { entries = append(entries, named[sd.SD]{c.Name, sd_parser.ParseSD(c)}) } if err := registerGroup(entries, registry.SDRegistry()); err != nil { return err } } { var entries []named[observer.Observer] for _, c := range cfg.Observers { entries = append(entries, named[observer.Observer]{c.Name, observer_parser.ParseObserver(c)}) } if err := registerGroup(entries, registry.ObserverRegistry()); err != nil { return err } } { var entries []named[recorder.Recorder] for _, c := range cfg.Recorders { entries = append(entries, named[recorder.Recorder]{c.Name, recorder_parser.ParseRecorder(c)}) } if err := registerGroup(entries, registry.RecorderRegistry()); err != nil { return err } } { var entries []named[traffic.TrafficLimiter] for _, c := range cfg.Limiters { entries = append(entries, named[traffic.TrafficLimiter]{c.Name, limiter_parser.ParseTrafficLimiter(c)}) } if err := registerGroup(entries, registry.TrafficLimiterRegistry()); err != nil { return err } } { var entries []named[*quota.Limiter] for _, c := range cfg.Quotas { entries = append(entries, named[*quota.Limiter]{c.Name, quota_parser.ParseQuotaLimiter(c)}) } if err := registerGroup(entries, registry.QuotaLimiterRegistry()); err != nil { return err } } { var entries []named[conn.ConnLimiter] for _, c := range cfg.CLimiters { entries = append(entries, named[conn.ConnLimiter]{c.Name, limiter_parser.ParseConnLimiter(c)}) } if err := registerGroup(entries, registry.ConnLimiterRegistry()); err != nil { return err } } { var entries []named[rate.RateLimiter] for _, c := range cfg.RLimiters { entries = append(entries, named[rate.RateLimiter]{c.Name, limiter_parser.ParseRateLimiter(c)}) } if err := registerGroup(entries, registry.RateLimiterRegistry()); err != nil { return err } } // --- hops (references bypasses, resolvers, hosts from registries) --- { var entries []named[hop.Hop] for _, c := range cfg.Hops { h, err := hop_parser.ParseHop(c, logger.Default()) if err != nil { return err } entries = append(entries, named[hop.Hop]{c.Name, h}) } if err := registerGroup(entries, registry.HopRegistry()); err != nil { return err } } // --- chains (references hops from registries) --- { var entries []named[chain.Chainer] for _, c := range cfg.Chains { ch, err := chain_parser.ParseChain(c, logger.Default()) if err != nil { return err } entries = append(entries, named[chain.Chainer]{c.Name, ch}) } if err := registerGroup(entries, registry.ChainRegistry()); err != nil { return err } } // --- services (references chains, resolvers, hosts, recorders, // limiters, observers, hops from registries) --- // // Services are the only component group whose construction binds a port: // service_parser.ParseService calls listener.Init, which binds. The generic // registerGroup "parse-all-then-swap" sequence used by the other groups // would therefore bind every new listener while the old services are still // listening, causing EADDRINUSE on SIGHUP reload (issue #754, regressed by // 82e7e50). Instead, unregister all old services first — unregisterAll // closes each one (a service implements io.Closer), freeing its port — // then parse, bind, and register the new ones. // // Trade-off: a parse error in the loop below leaves the registry partially // updated (the old services are already closed and only the services parsed // before the failure are registered). The construct/bind split would be // needed for atomic reload, but this restores the pre-82e7e50 behavior. { unregisterAll(registry.ServiceRegistry()) for _, c := range cfg.Services { svc, err := service_parser.ParseService(c) if err != nil { return err } if svc != nil { if err := registry.ServiceRegistry().Register(c.Name, svc); err != nil { return err } } } } return nil }