484 lines
10 KiB
Go
484 lines
10 KiB
Go
// 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")
|
|
}
|
|
|
|
if isExists(name) {
|
|
return nil, fmt.Errorf("server with this name is already exists")
|
|
}
|
|
|
|
ls := &LiveServer{name: name}
|
|
pushLs(ls)
|
|
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 StopAll() (errors []error) {
|
|
for key, ls := range reg.lss {
|
|
slog.Debug("stopping LiveServer", slog.String("name", key))
|
|
err := ls.Stop()
|
|
if err != nil {
|
|
errors = append(errors, err)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
// Close deletes [LiveServer] object from registry, and sets ls to nil
|
|
func (ls *LiveServer) Close() {
|
|
deleteLs(ls.name)
|
|
ls = 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
|
|
// }
|