diff --git a/go.sum b/go.sum index 9fb8621..f584782 100644 --- a/go.sum +++ b/go.sum @@ -7,3 +7,6 @@ github.com/hashicorp/golang-lru v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uG github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ= github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/config/config.go b/internal/config/config.go index 8722658..c6366ea 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -1,5 +1,14 @@ package config +import ( + "log" + "os" + + "gopkg.in/yaml.v3" + + "github.com/chubin/wttr.in/internal/util" +) + // Config of the program. type Config struct { Cache @@ -12,13 +21,13 @@ type Config struct { type Logging struct { // AccessLog path. - AccessLog string + AccessLog string `yaml:"accessLog,omitempty"` // ErrorsLog path. - ErrorsLog string + ErrorsLog string `yaml:"errorsLog,omitempty"` // Interval between access log flushes, in seconds. - Interval int + Interval int `yaml:"interval,omitempty"` } // Server configuration. @@ -26,36 +35,36 @@ type Server struct { // PortHTTP is port where HTTP server must listen. // If 0, HTTP is disabled. - PortHTTP int + PortHTTP int `yaml:"portHTTP,omitempty"` // PortHTTP is port where the HTTPS server must listen. // If 0, HTTPS is disabled. - PortHTTPS int + PortHTTPS int `yaml:"portHTTPS,omitempty"` // TLSCertFile contains path to cert file for TLS Server. - TLSCertFile string + TLSCertFile string `yaml:"tlsCertFile,omitempty"` // TLSCertFile contains path to key file for TLS Server. - TLSKeyFile string + TLSKeyFile string `yaml:"tlsKeyFile,omitempty"` } // Uplink configuration. type Uplink struct { // Address contains address of the uplink server in form IP:PORT. - Address string + Address string `yaml:"address,omitempty"` // Timeout for upstream queries. - Timeout int + Timeout int `yaml:"timeout,omitempty"` // PrefetchInterval contains time (in milliseconds) indicating, // how long the prefetch procedure should take. - PrefetchInterval int + PrefetchInterval int `yaml:"prefetchInterval,omitempty"` } // Cache configuration. type Cache struct { // Size of the main cache. - Size int + Size int `yaml:"size,omitempty"` } // Default contains the default configuration. @@ -83,5 +92,32 @@ func Default() *Config { } } -// Conf contains the current configuration -var Conf = Default() +// Load config from file. +func Load(filename string) (*Config, error) { + var ( + config Config + data []byte + err error + ) + + data, err = os.ReadFile(filename) + if err != nil { + return nil, err + } + + err = util.YamlUnmarshalStrict(data, &config) + if err != nil { + return nil, err + } + + return &config, nil +} + +func (c *Config) Dump() []byte { + data, err := yaml.Marshal(c) + if err != nil { + // should never happen. + log.Fatalln("config.Dump():", err) + } + return data +} diff --git a/srv.go b/srv.go index 50409f1..c39b80e 100644 --- a/srv.go +++ b/srv.go @@ -44,7 +44,7 @@ func serveHTTP(mux *http.ServeMux, port int, logFile io.Writer, errs chan<- erro errs <- srv.ListenAndServe() } -func serveHTTPS(mux *http.ServeMux, port int, logFile io.Writer, errs chan<- error) { +func serveHTTPS(mux *http.ServeMux, port int, certFile, keyFile string, logFile io.Writer, errs chan<- error) { tlsConfig := &tls.Config{ // CipherSuites: []uint16{ @@ -63,18 +63,16 @@ func serveHTTPS(mux *http.ServeMux, port int, logFile io.Writer, errs chan<- err TLSConfig: tlsConfig, Handler: mux, } - errs <- srv.ListenAndServeTLS(config.Conf.Server.TLSCertFile, config.Conf.Server.TLSKeyFile) + errs <- srv.ListenAndServeTLS(certFile, keyFile) } -func main() { +func serve(conf *config.Config) error { var ( // mux is main HTTP/HTTP requests multiplexer. mux *http.ServeMux = http.NewServeMux() // logger is optimized requests logger. - logger *logging.RequestLogger = logging.NewRequestLogger( - config.Conf.Logging.AccessLog, - time.Duration(config.Conf.Logging.Interval)*time.Second) + logger *logging.RequestLogger rp *processor.RequestProcessor @@ -84,19 +82,26 @@ func main() { // numberOfServers started. If 0, exit. numberOfServers int - errorsLog *logging.LogSuppressor = logging.NewLogSuppressor( - config.Conf.Logging.ErrorsLog, - []string{ - "error reading preface from client", - "TLS handshake error from", - }, - logLineStart, - ) + errorsLog *logging.LogSuppressor err error ) - rp, err = processor.NewRequestProcessor(config.Conf) + // logger is optimized requests logger. + logger = logging.NewRequestLogger( + conf.Logging.AccessLog, + time.Duration(conf.Logging.Interval)*time.Second) + + errorsLog = logging.NewLogSuppressor( + conf.Logging.ErrorsLog, + []string{ + "error reading preface from client", + "TLS handshake error from", + }, + logLineStart, + ) + + rp, err = processor.NewRequestProcessor(conf) if err != nil { log.Fatalln("log processor initialization:", err) } @@ -129,17 +134,45 @@ func main() { w.Write(response.Body) }) - if config.Conf.Server.PortHTTP != 0 { - go serveHTTP(mux, config.Conf.Server.PortHTTP, errorsLog, errs) + if conf.Server.PortHTTP != 0 { + go serveHTTP(mux, conf.Server.PortHTTP, errorsLog, errs) numberOfServers++ } - if config.Conf.Server.PortHTTPS != 0 { - go serveHTTPS(mux, config.Conf.Server.PortHTTPS, errorsLog, errs) + if conf.Server.PortHTTPS != 0 { + go serveHTTPS(mux, conf.Server.PortHTTPS, conf.Server.TLSCertFile, conf.Server.TLSKeyFile, errorsLog, errs) numberOfServers++ } if numberOfServers == 0 { - log.Println("no servers configured; exiting") + return errors.New("no servers configured") + } + return <-errs // block until one of the servers writes an error +} + +func main() { + var ( + conf *config.Config + err error + ) + + ctx := kong.Parse(&cli) + + if cli.ConfigFile != "" { + conf, err = config.Load(cli.ConfigFile) + if err != nil { + log.Fatalf("reading config from %s: %s\n", cli.ConfigFile, err) + } + } else { + conf = config.Default() + } + + if cli.ConfigDump { + fmt.Print(string(conf.Dump())) + } + + if cli.ConfigCheck || cli.ConfigDump { return } - log.Fatal(<-errs) // block until one of the servers writes an error + + err = serve(conf) + ctx.FatalIfErrorf(err) }