package api import ( "context" "crypto/tls" "net" "net/http" "time" "git.giftfish.de/ston1th/haproxy-lb/pkg/api/types" serverv1 "git.giftfish.de/ston1th/haproxy-lb/pkg/api/v1/server" "git.giftfish.de/ston1th/haproxy-lb/pkg/config" "git.giftfish.de/ston1th/haproxy-lb/pkg/db" "git.giftfish.de/ston1th/netalloc" "github.com/go-logr/logr" "github.com/gorilla/mux" ) // Server is the webapp and api server type Server struct { srv *http.Server mux *mux.Router log logr.Logger Data *types.ContextData cidr string init chan struct{} stop chan struct{} stopKeyReset chan struct{} tlsConfig *tls.Config listen net.Listener } type notFoundHandler struct { s *Server } func (nf *notFoundHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { types.NewContext(w, r, nf.s.Data, nf.s.log).NotFound() } // NewHTTPServer returns a new HTTPServer func NewServer(c *config.Config, log logr.Logger) (*Server, error) { s := &Server{ mux: mux.NewRouter(), log: log, cidr: c.VIP.Prefix, Data: &types.ContextData{}, init: make(chan struct{}), stop: make(chan struct{}), stopKeyReset: make(chan struct{}), } s.Data.Auth = types.NewAuth(c.Server.BasicAuth) if c.Server.TLS != nil { cert, err := tls.LoadX509KeyPair(c.Server.TLS.Cert, c.Server.TLS.Key) if err != nil { return nil, err } s.tlsConfig = &tls.Config{ MinVersion: tls.VersionTLS12, CipherSuites: []uint16{ tls.TLS_AES_256_GCM_SHA384, tls.TLS_CHACHA20_POLY1305_SHA256, tls.TLS_AES_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, }, CurvePreferences: []tls.CurveID{ tls.X25519, tls.CurveP384, tls.CurveP521, }, PreferServerCipherSuites: true, Certificates: []tls.Certificate{cert}, } l, err := tls.Listen("tcp", c.Server.Listen, s.tlsConfig) if err != nil { return nil, err } s.listen = l } else { l, err := net.Listen("tcp", c.Server.Listen) if err != nil { return nil, err } s.listen = l } s.mux.NotFoundHandler = ¬FoundHandler{s} for _, v := range serverv1.Routes { s.mux.HandleFunc(v.Path, s.contextWrapper(v.Handler)).Methods(v.Methods...) } s.start() return s, nil } func (s *Server) UpdateDB(db *db.DB) error { s.Data.DB = db if s.Data.Net == nil { na, err := netalloc.NewGenericAlloc(db, s.cidr) if err != nil { return err } s.Data.Net = na close(s.init) } else { s.Data.Net.SetStore(db) } return nil } func (s *Server) start() { s.srv = &http.Server{ Handler: s.mux, TLSConfig: s.tlsConfig, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, } go func() { <-s.init err := s.srv.Serve(s.listen) if err != nil { s.log.Error(err, "") } }() return } func (s *Server) contextWrapper(h types.CtxHandler) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { h(types.NewContext(w, r, s.Data, s.log)) } } // Stop stops listening for incoming connections and closes currently open connections func (s *Server) Stop() { s.log.Info("stopping") ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() err := s.srv.Shutdown(ctx) if err != nil { s.log.Error(err, "") } s.log.Info("stopped") }