148 lines
4.7 KiB
Go
148 lines
4.7 KiB
Go
package config
|
|
|
|
import (
|
|
"strings"
|
|
"time"
|
|
|
|
"git.yuntuo.hk/k2safe/go-omni/httpx"
|
|
"git.yuntuo.hk/k2safe/go-omni/logx"
|
|
)
|
|
|
|
const defaultRequestIDHeader = "X-Request-ID"
|
|
|
|
const (
|
|
TransportNetHTTP = "nethttp"
|
|
TransportNative = "native"
|
|
TransportHTTP2 = "http2"
|
|
)
|
|
|
|
// TLSConfig enables HTTPS when both certificate paths are provided.
|
|
type TLSConfig struct {
|
|
CertFile string `json:"certFile"`
|
|
KeyFile string `json:"keyFile"`
|
|
}
|
|
|
|
// Config controls the default runtime behavior of an App.
|
|
type Config struct {
|
|
Address string `json:"address"`
|
|
Transport string `json:"transport"`
|
|
TLS TLSConfig `json:"tls"`
|
|
RequestIDHeader string `json:"requestIdHeader"`
|
|
EnableLogger bool `json:"enableLogger"`
|
|
TrustedProxies []string `json:"trustedProxies"`
|
|
ShutdownTimeout time.Duration `json:"shutdownTimeout"`
|
|
Log logx.Config `json:"log"`
|
|
}
|
|
|
|
func Default() Config {
|
|
return Config{
|
|
Address: ":8080",
|
|
Transport: TransportNetHTTP,
|
|
RequestIDHeader: defaultRequestIDHeader,
|
|
ShutdownTimeout: 10 * time.Second,
|
|
Log: logx.Default(),
|
|
}
|
|
}
|
|
|
|
// Validate checks configuration values before an application starts.
|
|
func (c Config) Validate() error {
|
|
if strings.TrimSpace(c.Address) == "" {
|
|
return &ConfigError{Field: "address", Message: "is required"}
|
|
}
|
|
if strings.TrimSpace(c.RequestIDHeader) == "" {
|
|
return &ConfigError{Field: "request_id_header", Message: "is required"}
|
|
}
|
|
if c.ShutdownTimeout <= 0 {
|
|
return &ConfigError{Field: "shutdown_timeout", Message: "must be positive"}
|
|
}
|
|
if _, err := httpx.NewClientIPResolver(c.TrustedProxies...); err != nil {
|
|
return &ConfigError{Field: "trusted_proxies", Message: err.Error()}
|
|
}
|
|
return c.ValidateTransport()
|
|
}
|
|
|
|
// ValidateTransport checks built-in transport selection without opening files
|
|
// or sockets. Empty Transport preserves the standard net/http default.
|
|
func (c Config) ValidateTransport() error {
|
|
switch c.Transport {
|
|
case "", TransportNetHTTP, TransportNative, TransportHTTP2:
|
|
default:
|
|
return &ConfigError{Field: "transport", Message: "must be nethttp, native or http2"}
|
|
}
|
|
cert, key := strings.TrimSpace(c.TLS.CertFile), strings.TrimSpace(c.TLS.KeyFile)
|
|
if (cert == "") != (key == "") {
|
|
return &ConfigError{Field: "tls", Message: "requires both certFile and keyFile"}
|
|
}
|
|
if c.Transport == TransportHTTP2 && cert == "" {
|
|
return &ConfigError{Field: "tls", Message: "is required for the experimental http2 transport"}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ConfigError describes an invalid application configuration field.
|
|
type ConfigError struct {
|
|
Field string
|
|
Message string
|
|
}
|
|
|
|
func (e *ConfigError) Error() string { return "config " + e.Field + " " + e.Message }
|
|
|
|
// Option changes an App's configuration.
|
|
type Option func(*Config)
|
|
|
|
// WithTransport selects a built-in server. HTTP/2 is experimental and requires TLS.
|
|
func WithTransport(name string) Option {
|
|
return func(cfg *Config) { cfg.Transport = name }
|
|
}
|
|
|
|
// WithTLS enables HTTPS. Certificates are loaded before Run opens its listener.
|
|
func WithTLS(certFile, keyFile string) Option {
|
|
return func(cfg *Config) { cfg.TLS = TLSConfig{CertFile: certFile, KeyFile: keyFile} }
|
|
}
|
|
|
|
// WithRequestIDHeader changes the header used to read and return request IDs.
|
|
func WithRequestIDHeader(header string) Option {
|
|
return func(cfg *Config) {
|
|
if value := strings.TrimSpace(header); value != "" {
|
|
cfg.RequestIDHeader = value
|
|
}
|
|
}
|
|
}
|
|
|
|
// WithLogger enables GoOmni's structured access logger.
|
|
func WithLogger(enabled bool) Option {
|
|
return func(cfg *Config) { cfg.EnableLogger = enabled }
|
|
}
|
|
|
|
// WithTrustedProxies allows only these IPs/CIDRs to supply X-Forwarded-For.
|
|
// An empty list disables forwarding-header trust. Invalid entries are rejected
|
|
// by Config.Validate and panic when passed directly to App.New.
|
|
func WithTrustedProxies(proxies ...string) Option {
|
|
proxies = append([]string(nil), proxies...)
|
|
return func(cfg *Config) { cfg.TrustedProxies = append([]string(nil), proxies...) }
|
|
}
|
|
|
|
// WithConfig applies a complete configuration loaded from a file or
|
|
// environment. Later options can still override individual fields.
|
|
func WithConfig(settings Config) Option {
|
|
settings.TrustedProxies = append([]string(nil), settings.TrustedProxies...)
|
|
return func(cfg *Config) {
|
|
*cfg = settings
|
|
cfg.TrustedProxies = append([]string(nil), settings.TrustedProxies...)
|
|
}
|
|
}
|
|
|
|
// WithLogConfig configures the logger used by the application runtime.
|
|
func WithLogConfig(settings logx.Config) Option {
|
|
return func(cfg *Config) { cfg.Log = settings }
|
|
}
|
|
|
|
// WithShutdownTimeout controls how long RunContext waits for active requests.
|
|
func WithShutdownTimeout(timeout time.Duration) Option {
|
|
return func(cfg *Config) {
|
|
if timeout > 0 {
|
|
cfg.ShutdownTimeout = timeout
|
|
}
|
|
}
|
|
}
|