haproxy-lb/pkg/api/server.go
2021-03-07 13:48:30 +01:00

156 lines
3.5 KiB
Go

package api
import (
"context"
"crypto/tls"
"net"
"net/http"
"time"
"git.giftfish.de/ston1th/haproxy-lb/pkg/alloc"
"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"
"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
cidrs []string
gw 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,
cidrs: c.VIP.VirtualIPs,
gw: c.VIP.Gateway,
Data: &types.ContextData{
Auth: c.Server.BasicAuth,
},
init: make(chan struct{}),
stop: make(chan struct{}),
stopKeyReset: make(chan struct{}),
}
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 = &notFoundHandler{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.Alloc == nil {
alloc, err := alloc.NewAlloc(db, s.cidrs, s.gw)
if err != nil {
return err
}
s.Data.Alloc = alloc
close(s.init)
} else {
s.Data.Alloc.UpdateDB(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")
}