From c2b22f615e81145d2fb4c3a21b6d299dd2a5ecf2 Mon Sep 17 00:00:00 2001 From: Captain ALM Date: Thu, 14 Jul 2022 17:18:08 +0100 Subject: [PATCH] Add config and initial functionality to main. --- cmd/wappcityuni/main.go | 182 ++++++++++++++++++++++++++++++++++++++++ conf/cache.go | 11 +++ conf/config.go | 6 ++ conf/listen.go | 28 +++++++ conf/serve.go | 7 ++ go.mod | 5 ++ go.sum | 6 ++ 7 files changed, 245 insertions(+) create mode 100644 cmd/wappcityuni/main.go create mode 100644 conf/cache.go create mode 100644 conf/config.go create mode 100644 conf/listen.go create mode 100644 conf/serve.go create mode 100644 go.sum diff --git a/cmd/wappcityuni/main.go b/cmd/wappcityuni/main.go new file mode 100644 index 0000000..0ade9eb --- /dev/null +++ b/cmd/wappcityuni/main.go @@ -0,0 +1,182 @@ +package main + +import ( + "fmt" + "github.com/joho/godotenv" + "golang.captainalm.com/cityuni-webserver/conf" + "gopkg.in/yaml.v3" + "log" + "net" + "net/http" + "os" + "os/signal" + "path" + "path/filepath" + "strings" + "sync" + "syscall" + "time" +) + +var ( + buildVersion = "develop" + buildDate = "" +) + +func main() { + log.Printf("[Main] Starting up GO Package Header Server #%s (%s)\n", buildVersion, buildDate) + y := time.Now() + + //Hold main thread till safe shutdown exit: + wg := &sync.WaitGroup{} + wg.Add(1) + + //Get working directory: + cwdDir, err := os.Getwd() + if err != nil { + log.Println(err) + } + + //Load environment file: + err = godotenv.Load() + if err != nil { + log.Fatalln("Error loading .env file") + } + + //Data directory processing: + dataDir := os.Getenv("DIR_DATA") + if dataDir == "" { + dataDir = path.Join(cwdDir, ".data") + } + + check(os.MkdirAll(dataDir, 0777)) + + //Config file processing: + configLocation := os.Getenv("CONFIG_FILE") + if configLocation == "" { + configLocation = path.Join(dataDir, "config.yml") + } else { + if !filepath.IsAbs(configLocation) { + configLocation = path.Join(dataDir, configLocation) + } + } + + //Config loading: + configFile, err := os.Open(configLocation) + if err != nil { + log.Fatalln("Failed to open config.yml") + } + + var configYml conf.ConfigYaml + groupsDecoder := yaml.NewDecoder(configFile) + err = groupsDecoder.Decode(&configYml) + if err != nil { + log.Fatalln("Failed to parse config.yml:", err) + } + + //Server definitions: + var webServer *http.Server + var fcgiListen net.Listener + switch strings.ToLower(configYml.Listen.WebMethod) { + case "http": + webServer, _ = web.New(configYml, getListener(configYml, cwdDir)) + case "fcgi": + fcgiListen = getListener(configYml, cwdDir) + if fcgiListen == nil { + log.Fatalln("Listener Nil") + } else { + //Serve FCGI + } + default: + log.Fatalln("Unknown Web Method.") + } + + //===================== + // Safe shutdown + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + + //Startup complete: + z := time.Now().Sub(y) + log.Printf("[Main] Took '%s' to fully initialize modules\n", z.String()) + + go func() { + <-sigs + fmt.Printf("\n") + + log.Printf("[Main] Attempting safe shutdown\n") + a := time.Now() + + if webServer != nil { + log.Printf("[Main] Shutting down HTTP server...\n") + err := webServer.Close() + if err != nil { + log.Println(err) + } + } + + if fcgiListen != nil { + log.Printf("[Main] Shutting down FCGI server...\n") + err := fcgiListen.Close() + if err != nil { + log.Println(err) + } + } + + log.Printf("[Main] Signalling program exit...\n") + b := time.Now().Sub(a) + log.Printf("[Main] Took '%s' to fully shutdown modules\n", b.String()) + wg.Done() + }() + // + //===================== + wg.Wait() + log.Println("[Main] Goodbye") + //os.Exit(0) +} + +func check(err error) { + if err != nil { + panic(err) + } +} + +func getListener(config conf.ConfigYaml, cwd string) net.Listener { + split := strings.Split(strings.ToLower(config.Listen.WebNetwork), ":") + if len(split) == 0 { + log.Fatalln("Invalid Web Network") + return nil + } else { + var theListener net.Listener + var theError error + log.Println("[Main] Socket Network Type:" + split[0]) + log.Printf("[Main] Starting up %s server on %s...\n", config.Listen.WebMethod, config.Listen.Web) + switch split[0] { + case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6", "ip", "ip4", "ip6": + theListener, theError = net.Listen(strings.ToLower(config.Listen.WebNetwork), config.Listen.Web) + case "unix", "unixgram", "unixpacket": + socketPath := config.Listen.Web + if !filepath.IsAbs(socketPath) { + if !filepath.IsAbs(cwd) { + log.Fatalln("Web Path Not Absolute And No Working Directory.") + return nil + } + socketPath = path.Join(cwd, socketPath) + } + log.Println("[Main] Removing old socket.") + if err := os.RemoveAll(socketPath); err != nil { + log.Fatalln("Could Not Remove Old Socket.") + return nil + } + theListener, theError = net.Listen(strings.ToLower(config.Listen.WebNetwork), config.Listen.Web) + default: + log.Fatalln("Unknown Web Network.") + return nil + } + if theError != nil { + log.Fatalln("Failed to listen due to:", theError) + return nil + } + return theListener + } +} diff --git a/conf/cache.go b/conf/cache.go new file mode 100644 index 0000000..8f8b254 --- /dev/null +++ b/conf/cache.go @@ -0,0 +1,11 @@ +package conf + +type CacheSettingsYaml struct { + EnableTemplateCaching bool `yaml:"enableTemplateCaching"` + EnableTemplateCachePurge bool `yaml:"enableTemplateCachePurge"` + EnableContentsCaching bool `yaml:"enableContentsCaching"` + EnableContentsCachePurge bool `yaml:"enableContentsCachePurge"` + MaxAge uint `yaml:"maxAge"` + NotModifiedResponseUsingLastModified bool `yaml:"notModifiedUsingLastModified"` + NotModifiedResponseUsingETags bool `yaml:"notModifiedUsingETags"` +} diff --git a/conf/config.go b/conf/config.go new file mode 100644 index 0000000..8ac8c65 --- /dev/null +++ b/conf/config.go @@ -0,0 +1,6 @@ +package conf + +type ConfigYaml struct { + Listen ListenYaml `yaml:"listen"` + Serve ServeYaml `yaml:"serve"` +} diff --git a/conf/listen.go b/conf/listen.go new file mode 100644 index 0000000..9ef1795 --- /dev/null +++ b/conf/listen.go @@ -0,0 +1,28 @@ +package conf + +import "time" + +type ListenYaml struct { + Web string `yaml:"web"` + WebMethod string `yaml:"webMethod"` + WebNetwork string `yaml:"webNetwork"` + ReadTimeout time.Duration `yaml:"readTimeout"` + WriteTimeout time.Duration `yaml:"writeTimeout"` + Identify bool `yaml:"identify"` +} + +func (ly ListenYaml) GetReadTimeout() time.Duration { + if ly.ReadTimeout.Seconds() < 1 { + return 1 * time.Second + } else { + return ly.ReadTimeout + } +} + +func (ly ListenYaml) GetWriteTimeout() time.Duration { + if ly.WriteTimeout.Seconds() < 1 { + return 1 * time.Second + } else { + return ly.WriteTimeout + } +} diff --git a/conf/serve.go b/conf/serve.go new file mode 100644 index 0000000..2cab102 --- /dev/null +++ b/conf/serve.go @@ -0,0 +1,7 @@ +package conf + +type ServeYaml struct { + Domains []string `yaml:"domains"` + RangeSupported bool `yaml:"rangeSupported"` + CacheSettings CacheSettingsYaml `yaml:"cacheSettings"` +} diff --git a/go.mod b/go.mod index b317df0..da3b6a5 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,8 @@ module golang.captainalm.com/cityuni-webserver go 1.18 + +require ( + github.com/joho/godotenv v1.4.0 + gopkg.in/yaml.v3 v3.0.1 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..b6897df --- /dev/null +++ b/go.sum @@ -0,0 +1,6 @@ +github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= +github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +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=