127 lines
2.6 KiB
Go
127 lines
2.6 KiB
Go
// 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")
|
|
}
|