package webserver import ( "context" "errors" "log" "net/http" "os" "os/signal" "syscall" "time" ) 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) { if srv == nil { log.Panicf("missing http.server params") return } if srv.ReadTimeout == 0 { srv.ReadTimeout = defaultTimeout } if srv.WriteTimeout == 0 { srv.WriteTimeout = defaultTimeout } log.Printf("launching webserver on %s", srv.Addr) go func() { if err := srv.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) { log.Panicf("HTTP server error: %v", err) return } log.Printf("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.Panicf("HTTP shutdown error: %v", err) return } if params != nil && params.Cleanup != nil { if err := params.Cleanup(); err != nil { log.Panicf("Impossible to cleanup correctly after shutdown. error : %v", err) return } } log.Print("Graceful shutdown complete.") }