1
0
Fork 0
mirror of https://git.sr.ht/~ashkeel/strimertul synced 2024-09-18 01:50:50 +00:00

fix: server config doesn't get half-changed in case of error

This commit is contained in:
Ash Keel 2022-12-04 00:36:13 +01:00
parent db31c75662
commit a99f944f33
No known key found for this signature in database
GPG key ID: BAD8D93E7314ED3E

View file

@ -8,6 +8,8 @@ import (
"net/http" "net/http"
"net/http/pprof" "net/http/pprof"
"github.com/strimertul/strimertul/utils"
"git.sr.ht/~hamcha/containers" "git.sr.ht/~hamcha/containers"
jsoniter "github.com/json-iterator/go" jsoniter "github.com/json-iterator/go"
"github.com/strimertul/strimertul/database" "github.com/strimertul/strimertul/database"
@ -20,14 +22,14 @@ import (
var json = jsoniter.ConfigFastest var json = jsoniter.ConfigFastest
type Server struct { type Server struct {
Config ServerConfig Config *containers.RWSync[ServerConfig]
db *database.LocalDBClient db *database.LocalDBClient
logger *zap.Logger logger *zap.Logger
server *http.Server server *http.Server
frontend fs.FS frontend fs.FS
hub *kv.Hub hub *kv.Hub
mux *http.ServeMux mux *http.ServeMux
requestedRoutes map[string]http.Handler requestedRoutes *containers.SyncMap[string, http.Handler]
cancelConfigSub database.CancelFunc cancelConfigSub database.CancelFunc
} }
@ -36,17 +38,18 @@ func NewServer(db *database.LocalDBClient, logger *zap.Logger) (*Server, error)
logger: logger, logger: logger,
db: db, db: db,
server: &http.Server{}, server: &http.Server{},
requestedRoutes: make(map[string]http.Handler), requestedRoutes: containers.NewSyncMap[string, http.Handler](),
Config: containers.NewRWSync(ServerConfig{}),
} }
err := db.GetJSON(ServerConfigKey, &server.Config) err := utils.LoadJSONToWrapped[ServerConfig](ServerConfigKey, server.Config)
if err != nil { if err != nil {
// Initialize with default config // Initialize with default config
server.Config = ServerConfig{ server.Config.Set(ServerConfig{
Bind: "localhost:4337", Bind: "localhost:4337",
EnableStaticServer: false, EnableStaticServer: false,
KVPassword: "", KVPassword: "",
} })
// Save // Save
err = db.PutJSON(ServerConfigKey, server.Config) err = db.PutJSON(ServerConfigKey, server.Config)
if err != nil { if err != nil {
@ -59,7 +62,7 @@ func NewServer(db *database.LocalDBClient, logger *zap.Logger) (*Server, error)
// Set password // Set password
server.hub.SetOptions(kv.HubOptions{ server.hub.SetOptions(kv.HubOptions{
Password: server.Config.KVPassword, Password: server.Config.Get().KVPassword,
}) })
return server, nil return server, nil
@ -100,10 +103,11 @@ func (s *Server) makeMux() *http.ServeMux {
kv.ServeWs(s.hub, w, r) kv.ServeWs(s.hub, w, r)
}) })
} }
if s.Config.EnableStaticServer { config := s.Config.Get()
mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(s.Config.Path)))) if config.EnableStaticServer {
mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(config.Path))))
} }
for route, handler := range s.requestedRoutes { for route, handler := range s.requestedRoutes.Copy() {
mux.Handle(route, handler) mux.Handle(route, handler)
} }
@ -111,12 +115,12 @@ func (s *Server) makeMux() *http.ServeMux {
} }
func (s *Server) RegisterRoute(route string, handler http.Handler) { func (s *Server) RegisterRoute(route string, handler http.Handler) {
s.requestedRoutes[route] = handler s.requestedRoutes.SetKey(route, handler)
s.mux = s.makeMux() s.mux = s.makeMux()
} }
func (s *Server) UnregisterRoute(route string) { func (s *Server) UnregisterRoute(route string) {
delete(s.requestedRoutes, route) s.requestedRoutes.DeleteKey(route)
s.mux = s.makeMux() s.mux = s.makeMux()
} }
@ -126,22 +130,25 @@ func (s *Server) Listen() error {
exit := make(chan error) exit := make(chan error)
var err error var err error
err, s.cancelConfigSub = s.db.SubscribeKey(ServerConfigKey, func(value string) { err, s.cancelConfigSub = s.db.SubscribeKey(ServerConfigKey, func(value string) {
oldBind := s.Config.Bind oldConfig := s.Config.Get()
oldPassword := s.Config.KVPassword
err := json.Unmarshal([]byte(value), &s.Config) var config ServerConfig
err := json.Unmarshal([]byte(value), &config)
if err != nil { if err != nil {
s.logger.Error("Failed to unmarshal config", zap.Error(err)) s.logger.Error("Failed to unmarshal config", zap.Error(err))
return return
} }
s.Config.Set(config)
s.mux = s.makeMux() s.mux = s.makeMux()
// Restart hub if password changed // Restart hub if password changed
if oldPassword != s.Config.KVPassword { if oldConfig.KVPassword != config.KVPassword {
s.hub.SetOptions(kv.HubOptions{ s.hub.SetOptions(kv.HubOptions{
Password: s.Config.KVPassword, Password: config.KVPassword,
}) })
} }
// Restart server if bind changed // Restart server if bind changed
if oldBind != s.Config.Bind { if oldConfig.Bind != config.Bind {
restart.Set(true) restart.Set(true)
err = s.server.Shutdown(context.Background()) err = s.server.Shutdown(context.Background())
if err != nil { if err != nil {
@ -155,13 +162,14 @@ func (s *Server) Listen() error {
} }
go func() { go func() {
for { for {
s.logger.Info("Starting HTTP server", zap.String("bind", s.Config.Bind)) config := s.Config.Get()
s.logger.Info("Starting HTTP server", zap.String("bind", config.Bind))
s.mux = s.makeMux() s.mux = s.makeMux()
s.server = &http.Server{ s.server = &http.Server{
Handler: s, Handler: s,
Addr: s.Config.Bind, Addr: config.Bind,
} }
s.logger.Info("HTTP server started", zap.String("bind", s.Config.Bind)) s.logger.Info("HTTP server started", zap.String("bind", config.Bind))
err := s.server.ListenAndServe() err := s.server.ListenAndServe()
s.logger.Debug("HTTP server died", zap.Error(err)) s.logger.Debug("HTTP server died", zap.Error(err))
if err != nil && !errors.Is(err, http.ErrServerClosed) { if err != nil && !errors.Is(err, http.ErrServerClosed) {