// Copyright (C) 2023 Marius Schellenberger package api import ( "context" "net" "net/http" "os" "time" "git.giftfish.de/ston1th/godrop/v2" "git.giftfish.de/ston1th/keyctl/pkg/api/types" serverv1 "git.giftfish.de/ston1th/keyctl/pkg/api/v1/server" "git.giftfish.de/ston1th/keyctl/pkg/db" "github.com/go-logr/logr" ) // Server is the api server type Server struct { srv *http.Server log logr.Logger data *types.ContextData listen net.Listener socket net.Listener } type notFoundHandler struct { log logr.Logger } func (h *notFoundHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { types.NewContext(w, r, nil, h.log).Err(types.ErrInvalidAPIRoute) } type okHandler struct{} func (okHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { types.NewContext(w, r, nil, logr.Discard()).OK() } func NewServer(log logr.Logger, listen, socket string, db *db.DB) (*Server, error) { m := http.NewServeMux() s := &Server{ log: log, data: &types.ContextData{ Approver: types.NewApprover(), DB: db, }, srv: &http.Server{ Handler: m, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, }, } list, err := net.Listen("tcp", listen) if err != nil { return nil, err } s.listen = list sock, err := unixListener(socket) if err != nil { return nil, err } s.socket = sock m.Handle("/", ¬FoundHandler{log}) ok := okHandler{} m.Handle("GET /livez", ok) m.Handle("GET /readyz", ok) for _, v := range serverv1.Routes { for _, method := range v.Methods { m.HandleFunc(method+" "+v.Path, s.contextWrapper(v.Handler)) } } go func() { err := s.srv.Serve(s.listen) if err != nil && err != http.ErrServerClosed { s.log.Error(err, "") } }() go func() { err := s.srv.Serve(s.socket) if err != nil && err != http.ErrServerClosed { s.log.Error(err, "") } }() return s, nil } func unixListener(socket string) (sock net.Listener, err error) { err = godrop.Unveil(socket, "rwc") if err != nil { return } err = godrop.UnveilBlock() if err != nil { return } sock, err = net.Listen("unix", socket) if err != nil { return } err = os.Chmod(socket, 0o660) 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) Shutdown(ctx context.Context) { s.log.Info("stopping") err := s.srv.Shutdown(ctx) if err != nil { s.log.Error(err, "") } s.log.Info("stopped") }