package webserver import ( "context" "errors" "fmt" "net/http" "os" "os/signal" "syscall" "time" "git.dev.m-and-m.ovh/mderasse/gocommon/commonctx" ) const defaultTimeout = 90 * time.Second const defaultShutdownTimeout = 10 * time.Second type cleanupFunc func() error // ExecuteServerParams are the parameters expected in the ExecuteServer function. type ExecuteServerParams struct { Cleanup cleanupFunc ShutdownTimeout *time.Duration } // ExecuteServer will launche a http.Server and handle the "exit" signals and gracefully stop the server // and call the cleanup function. func ExecuteServer(ctx context.Context, srv *http.Server, params *ExecuteServerParams) error { log := commonctx.GetLogger(ctx) if srv == nil { return fmt.Errorf("missing http.server params") } if srv.ReadTimeout == 0 { srv.ReadTimeout = defaultTimeout } if srv.WriteTimeout == 0 { srv.WriteTimeout = defaultTimeout } log.Infof("launching webserver on %s", srv.Addr) go func() { if err := srv.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) { log.WithError(err).Fatalf("HTTP server error: %v", err) } log.Info("Stopped serving new connections.") }() sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) <-sigChan shutdownCtx, shutdownRelease := context.WithTimeout(ctx, defaultShutdownTimeout) defer shutdownRelease() if err := srv.Shutdown(shutdownCtx); err != nil { log.WithError(err).Warnf("HTTP shutdown error: %v", err) } if params.Cleanup != nil { if err := params.Cleanup(); err != nil { log.WithError(err).Warnf("Impossible to cleanup correctly after shutdown. error : %v", err) return nil } } log.Info("Graceful shutdown complete.") return nil }