// package server // import ( // "context" // "fmt" // "log/slog" // "net" // "net/http" // "sync/atomic" // "time" // "git.oblat.lv/alex/triggerssmith/internal/config" // "git.oblat.lv/alex/triggerssmith/internal/safe" // ) // type LiveServer struct { // current atomic.Value // *Server // } // type Server struct { // generalLogger *slog.Logger // cfg *config.ServerConfig // srv *http.Server // ln net.Listener // } // func (s *Server) GetConfig() *config.ServerConfig { // return s.cfg // } // func (ls *LiveServer) Start(cfg *config.ServerConfig, handler http.Handler) error { // slog.Debug("Starting new server", slog.Any("config", *cfg)) // addr := fmt.Sprintf("%s:%d", cfg.Addr, cfg.Port) // ln, err := net.Listen("tcp", addr) // if err != nil { // return err // } // srv := &http.Server{ // Handler: handler, // } // hs := &Server{ // cfg: cfg, // ln: ln, // srv: srv, // } // started := make(chan error, 1) // go func() { // err := srv.Serve(ln) // started <- err // }() // select { // case err := <-started: // return fmt.Errorf("cannot start server: %w", err) // case <-time.After(1 * time.Millisecond): // } // old := ls.current.Load() // ls.current.Store(hs) // if old != nil { // errorChan := make(chan error, 1) // safe.SafeGO(func() { // ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) // defer cancel() // old.(*Server).srv.Shutdown(ctx) // }, errorChan) // select { // case err := <-errorChan: // return err // case <-time.After(4 * time.Second): // return fmt.Errorf("timeout while shutting down old server") // } // } // return nil // } package server import ( "context" "fmt" "log/slog" "net" "net/http" "sync" "sync/atomic" ) type LiveServer struct { name string data any // config, etc handler http.Handler active atomic.Value // *instance mu sync.Mutex statusMu sync.Mutex status Status initDone bool } type instance struct { srv *http.Server ln net.Listener addr string } func Create(name string) (*LiveServer, error) { if name == "" { return nil, fmt.Errorf("server name is empty") } ls := &LiveServer{name: name} ls.setStatus(Status{ID: StatusStopped}) return ls, nil } func (ls *LiveServer) setStatus(st Status) { ls.statusMu.Lock() defer ls.statusMu.Unlock() ls.status = st } func (ls *LiveServer) Status() Status { ls.statusMu.Lock() defer ls.statusMu.Unlock() return ls.status } func (ls *LiveServer) SetHandler(h http.Handler) { ls.mu.Lock() defer ls.mu.Unlock() ls.handler = h } func (ls *LiveServer) listen(addr string) (net.Listener, error) { slog.Debug("listening", slog.String("addr", addr)) ln, err := net.Listen("tcp", addr) if err != nil { slog.Debug("listening failed", slog.String("err", err.Error())) return nil, err } return ln, nil } func (ls *LiveServer) serve(inst *instance) (chan error, error) { slog.Debug("serving", slog.Any("instance", *inst)) errChan := make(chan error, 1) if inst == nil { err := fmt.Errorf("instance is nil") slog.Debug("serving failed", slog.String("err", err.Error())) return nil, err } srv := inst.srv ln := inst.ln go func() { err := srv.Serve(ln) errChan <- err }() return errChan, nil } func (ls *LiveServer) Init() error { slog.Debug("initializating live server", slog.Any("liveserver", *ls)) ls.mu.Lock() defer ls.mu.Unlock() ls.setStatus(Status{ID: StatusInitializing}) if ls.handler == nil { err := fmt.Errorf("handler not set") slog.Debug("initializating failed", slog.String("err", err.Error())) return err } ls.initDone = true return nil } func (ls *LiveServer) Start(addr string) error { slog.Debug("starting server", slog.String("addr", addr)) if !ls.initDone { err := fmt.Errorf("server is not initialized") slog.Debug("starting failed", slog.String("err", err.Error())) return err } ls.setStatus(Status{ID: StatusStarting}) ln, err := ls.listen(addr) if err != nil { ls.setStatus(Status{ID: StatusError, Err: err}) return err } srv := &http.Server{Handler: ls.handler} ls.active.Store(&instance{ srv: srv, ln: ln, addr: addr, }) _, err = ls.serve(ls.active.Load().(*instance)) if err != nil { ls.setStatus(Status{ID: StatusError, Err: err}) return err } ls.setStatus(Status{ID: StatusOK}) // go func() { // err := <-errChan // if err != nil && err != http.ErrServerClosed { // ls.setStatus(Status{ID: StatusError, Err: err}) // slog.Error("Server stopped with error", slog.String("name", ls.name), slog.String("error", err.Error())) // return // } // }() slog.Debug("Server started", slog.String("name", ls.name), slog.String("address", addr)) return nil } func (ls *LiveServer) stop(inst *instance) error { slog.Debug("stopping server") inst.ln.Close() err := inst.srv.Shutdown(context.Background()) if err != nil { slog.Debug("shutdown", slog.String("err", err.Error())) } ls.setStatus(Status{ID: StatusStopped}) if err != http.ErrServerClosed { return err } return nil } func (ls *LiveServer) Stop() error { inst := ls.active.Load().(*instance) if inst == nil { return nil } return ls.stop(inst) } func (ls *LiveServer) Reload(newAddr string) error { ls.mu.Lock() oldInstAny := ls.active.Load() var oldAddr string if oldInstAny != nil { oldAddr = oldInstAny.(*instance).addr } ls.mu.Unlock() if oldAddr == newAddr { return nil } slog.Debug("Reloading server", slog.String("name", ls.name), slog.String("new_address", newAddr)) ls.setStatus(Status{ID: StatusStarting}) slog.Debug("starting new server") err := ls.Start(newAddr) if err != nil { ls.active.Store(oldInstAny) return fmt.Errorf("cannot start new server: %w", err) } if err := ls.stop(oldInstAny.(*instance)); err != nil { slog.Debug("stopping failed", slog.String("err", err.Error())) return err } return nil } // package server // import ( // "context" // "errors" // "fmt" // "log/slog" // "net" // "net/http" // "sync" // "sync/atomic" // "time" // "git.oblat.lv/alex/triggerssmith/internal/config" // ) // type LiveServer struct { // name string // handler http.Handler // cfg *config.Config // active atomic.Value // *instance // mu sync.Mutex // защищает операции Start/Stop/Reload // statusMu sync.Mutex // status Status // } // type instance struct { // srv *http.Server // ln net.Listener // cfg *config.ServerConfig // } // func Create(name string) (*LiveServer, error) { // if name == "" { // return nil, errors.New("server name is empty") // } // ls := &LiveServer{name: name} // ls.setStatus(Status{ID: StatusStopped}) // return ls, nil // } // func (ls *LiveServer) setStatus(st Status) { // ls.statusMu.Lock() // ls.status = st // ls.statusMu.Unlock() // } // func (ls *LiveServer) Status() Status { // ls.statusMu.Lock() // s := ls.status // ls.statusMu.Unlock() // return s // } // func (ls *LiveServer) LoadConfiguration(cfg *config.Config) { // ls.mu.Lock() // defer ls.mu.Unlock() // ls.cfg = cfg // } // func (ls *LiveServer) SetHandler(h http.Handler) { // ls.mu.Lock() // defer ls.mu.Unlock() // ls.handler = h // } // func (ls *LiveServer) Start() error { // ls.mu.Lock() // defer ls.mu.Unlock() // if ls.cfg == nil { // return errors.New("configuration not loaded") // } // if ls.handler == nil { // return errors.New("handler not set") // } // ln, err := net.Listen("tcp", fmt.Sprintf("%s:%d", ls.cfg.Server.Addr, ls.cfg.Server.Port)) // if err != nil { // ls.setStatus(Status{ID: StatusError, Err: err}) // return err // } // srv := &http.Server{Handler: ls.handler} // inst := &instance{ // srv: srv, // ln: ln, // cfg: &ls.cfg.Server, // } // ls.setStatus(Status{ID: StatusStarting}) // ls.active.Store(inst) // go func() { // err := srv.Serve(ln) // if err != nil && err != http.ErrServerClosed { // ls.setStatus(Status{ID: StatusError, Err: err}) // return // } // }() // // даём серверу время забиндиться // time.Sleep(5 * time.Millisecond) // ls.setStatus(Status{ID: StatusOK}) // slog.Info("Server started", slog.String("name", ls.name), slog.String("address", fmt.Sprintf("%s:%d", ls.cfg.Server.Addr, ls.cfg.Server.Port))) // return nil // } // func (ls *LiveServer) Stop() error { // ls.mu.Lock() // defer ls.mu.Unlock() // instAny := ls.active.Load() // if instAny == nil { // return nil // } // inst := instAny.(*instance) // ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) // defer cancel() // err := inst.srv.Shutdown(ctx) // inst.ln.Close() // ls.setStatus(Status{ID: StatusStopped}) // return err // } // func (ls *LiveServer) Reload(newCfg *config.Config) error { // ls.mu.Lock() // defer ls.mu.Unlock() // oldInstAny := ls.active.Load() // var oldCfg *config.ServerConfig // if oldInstAny != nil { // oldCfg = oldInstAny.(*instance).cfg // } // if oldCfg != nil && // oldCfg.Addr == newCfg.Server.Addr && // oldCfg.Port == newCfg.Server.Port { // ls.cfg = newCfg // return nil // } // // ln, err := net.Listen("tcp", fmt.Sprintf("%s:%d", newCfg.Server.Addr, newCfg.Server.Port)) // // if err != nil { // // return fmt.Errorf("cannot bind new address: %w", err) // // } // // srv := &http.Server{Handler: ls.handler} // // newInst := &instance{ // // srv: srv, // // ln: ln, // // cfg: &newCfg.Server, // // } // ls.setStatus(Status{ID: StatusStarting}) // err := ls.Start() // if err != nil { // ls.active.Store(oldInstAny) // return fmt.Errorf("cannot start new server: %w", err) // } // if err := ls.Stop(); err != nil { // return err // } // return nil // }