package httpserver import ( "context" "fmt" "html/template" "io" "log" "mime" "net/http" "time" "go.uber.org/zap" "go.c3c.cz/cv/app/server/internal/files" ) func init() { err := mime.AddExtensionType(".ico", "image/x-icon") if err != nil { log.Fatal(err) } err = mime.AddExtensionType(".json", "application/json; charset=utf-8") if err != nil { log.Fatal(err) } } type Options struct { Logger *zap.Logger BindAddr string FrontendConfig string RequireIndex bool } type server struct{ http.Server } func New(options *Options) (*server, error) { handler := &handler{ frontendConfig: options.FrontendConfig, logger: options.Logger, } httpSrv := &server{ http.Server{ Addr: options.BindAddr, Handler: handler, }, } index, err := files.Public.Open("data/public/index.html") if err != nil { if !options.RequireIndex { return httpSrv, nil } return nil, fmt.Errorf("could not open index.html: %w", err) } fi, err := index.Stat() if err != nil || fi.IsDir() { if !options.RequireIndex { return httpSrv, nil } return nil, fmt.Errorf("invalid index.html: %w", err) } tb, err := io.ReadAll(index) if err != nil { if !options.RequireIndex { return httpSrv, nil } return nil, fmt.Errorf("could not read index.html: %w", err) } handler.indexTemplate, err = template.New("index").Parse(string(tb)) if err != nil { if !options.RequireIndex { return httpSrv, nil } return nil, fmt.Errorf("could not parse index.html: index.html: %w", err) } return httpSrv, nil } func (s *server) ShutdownWithDelay(delay time.Duration, limit time.Duration) { h, ok := s.Handler.(*handler) var logger *zap.Logger if ok { logger = h.logger } else { logger = zap.L() } if delay > 0 { logger.Info(fmt.Sprintf("Waiting %s for incoming requests\n", delay)) time.Sleep(delay) } ctx, cancel := context.WithTimeout(context.Background(), limit) defer cancel() logger.Info("Gracefully shutting down http server") if err := s.Shutdown(ctx); err != nil { logger.Error("Failed to shutdown http server", zap.Error(err)) } else { logger.Info("Http server was stopped") } }