From 27fc5c1fd6481991055a8fbbcfd24effd23b7abe Mon Sep 17 00:00:00 2001 From: moecinnamo Date: Sat, 22 Nov 2025 17:09:25 +0800 Subject: [PATCH] Improve web service related aspects --- config.example.json | 14 ++++++++- internal/handler/index.go | 1 + internal/handler/stop.go | 20 +++++++++++++ internal/service/config.go | 55 ++++++++++++++++++++++++++++++++++ internal/service/route.go | 1 + internal/service/struct.go | 12 ++++++++ internal/service/web.go | 61 ++++++++++++++++++++++++++++++++++---- 7 files changed, 158 insertions(+), 6 deletions(-) create mode 100644 internal/handler/index.go create mode 100644 internal/handler/stop.go create mode 100644 internal/service/route.go diff --git a/config.example.json b/config.example.json index 4e735db..deb5a8e 100644 --- a/config.example.json +++ b/config.example.json @@ -1,9 +1,21 @@ { "server": { "host": "0.0.0.0", - "port": 2233 + "port": 2233, + "tls": { + "enabled": false, + "cert_file": "", + "key_file": "" + }, + "advanced": { + "read_timeout": 30, + "write_timeout": 30, + "idle_timeout": 300, + "max_header_bytes": 1 + } }, "database": { + "driver": "mysql", "host": "localhost", "port": 3306, "username": "root", diff --git a/internal/handler/index.go b/internal/handler/index.go new file mode 100644 index 0000000..abeebd1 --- /dev/null +++ b/internal/handler/index.go @@ -0,0 +1 @@ +package handler diff --git a/internal/handler/stop.go b/internal/handler/stop.go new file mode 100644 index 0000000..81037c6 --- /dev/null +++ b/internal/handler/stop.go @@ -0,0 +1,20 @@ +package handler + +import ( + "log" + "os" + "os/signal" + "syscall" +) + +// HandleStop handles the stop signal and exits the program. +func HandleStop() { + sigCh := make(chan os.Signal, 1) + signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) + + go func() { + sig := <-sigCh + log.Printf("[Info] Received signal: %v,stopping...\r\n", sig) + os.Exit(0) + }() +} diff --git a/internal/service/config.go b/internal/service/config.go index e69de29..f31dc91 100644 --- a/internal/service/config.go +++ b/internal/service/config.go @@ -0,0 +1,55 @@ +package service + +import ( + "encoding/json" + "os" + "path/filepath" + "sync" +) + +var ( + config Config + configOnce sync.Once + configErr error + configPath string +) + +// loadConfig initializes the config from file +func loadConfig() { + exePath, err := os.Executable() + configPath := os.Getenv("CONFIG_PATH") + if err != nil { + configErr = err + return + } + if configPath == "" { + exeDir := filepath.Dir(exePath) + configPath = filepath.Join(exeDir, "config.json") + } + + data, err := os.ReadFile(configPath) + if err != nil { + configErr = err + return + } + + err = json.Unmarshal(data, &config) + if err != nil { + configErr = err + return + } +} + +// GetConfig returns the singleton instance of the config. +// It's safe for concurrent use and loads the config only once per process lifetime. +func GetConfig() (*Config, error) { + configOnce.Do(loadConfig) + return &config, configErr +} + +// ConfigPath returns the path to the loaded config file (useful for logging or reload later if needed) +func ConfigPath() string { + // Make sure config is at least attempted to be loaded + configOnce.Do(loadConfig) + return configPath +} diff --git a/internal/service/route.go b/internal/service/route.go new file mode 100644 index 0000000..6d43c33 --- /dev/null +++ b/internal/service/route.go @@ -0,0 +1 @@ +package service diff --git a/internal/service/struct.go b/internal/service/struct.go index 7d27c12..f3bd5c0 100644 --- a/internal/service/struct.go +++ b/internal/service/struct.go @@ -5,8 +5,20 @@ type Config struct { Server struct { Host string `json:"host"` Port int `json:"port"` + Tls struct { + Enabled bool `json:"enabled"` + Cert string `json:"cert_file"` + Key string `json:"key_file"` + } `json:"tls"` + Advanced struct { + Readtimeout int `json:"read_timeout"` + Writetimeout int `json:"write_timeout"` + Idletimeout int `json:"idle_timeout"` + Maxheaderbytes int `json:"max_header_bytes"` + } `json:"advanced"` } `json:"server"` Database struct { + Driver string `json:"driver"` Host string `json:"host"` Port int `json:"port"` Username string `json:"username"` diff --git a/internal/service/web.go b/internal/service/web.go index 6ea1449..8d80615 100644 --- a/internal/service/web.go +++ b/internal/service/web.go @@ -1,15 +1,66 @@ package service import ( + "crypto/tls" "log" "net/http" + "strconv" + "time" ) -func WebService() { - log.Printf("[Info] Starting web service") - log.Printf("[Info] Web service started on 0.0.0.0:2233") - err := http.ListenAndServe("0.0.0.0:2233", nil) +func getServerAddress(host string, port int) string { + return host + ":" + strconv.Itoa(port) +} + +func CreateWebService() *http.Server { + log.Printf("[Info] Create web service") + config, err := GetConfig() if err != nil { - log.Fatalf("[Error] Failed to start server: %v", err) + log.Fatalf("[Error] Failed to load config: %v", err) + } + + addr := getServerAddress(config.Server.Host, config.Server.Port) + + server := &http.Server{ + Addr: addr, + ReadTimeout: time.Duration(config.Server.Advanced.Readtimeout) * time.Second, + WriteTimeout: time.Duration(config.Server.Advanced.Writetimeout) * time.Second, + IdleTimeout: time.Duration(config.Server.Advanced.Idletimeout) * time.Second, + MaxHeaderBytes: config.Server.Advanced.Maxheaderbytes << 16, + } + if config.Server.Tls.Enabled { + log.Printf("[Info] TLS enabled") + server.TLSConfig = &tls.Config{ + PreferServerCipherSuites: true, + SessionTicketsDisabled: false, + } + } + return server +} + +func ListenWebService(server *http.Server) { + config, err := GetConfig() + if err != nil { + log.Fatalf("[Error] Failed to load config: %v", err) + } + addr := getServerAddress(config.Server.Host, config.Server.Port) + if config.Server.Tls.Enabled { + log.Printf("[Info] Starting HTTPS server on %s", addr) + err := server.ListenAndServeTLS( + config.Server.Tls.Cert, + config.Server.Tls.Key, + ) + if err != nil { + log.Fatalf("[Error] HTTPS server failed: %v", err) + } + } else { + log.Printf("[Info] Starting HTTP server on %s", addr) + err := server.ListenAndServe() + if err != nil { + log.Fatalf("[Error] HTTP server failed: %v", err) + } + } + if err != nil && err != http.ErrServerClosed { + log.Fatalf("[Error] Web server terminated unexpectedly: %v", err) } }