package http import ( "context" "errors" "fmt" "log/slog" "net" "net/http" "time" "go.sudomsg.com/kit/logging" "golang.org/x/sync/errgroup" ) // RunHTTPServers runs HTTP servers concurrently on all provided listeners. // // The provided handler is used for all servers. // Servers respond to context cancellation by performing a graceful shutdown with a timeout of 10 seconds. // // Logging is performed using the slog.Logger extracted from context. // Each server logs startup, shutdown, and errors. // // This function blocks until all servers have stopped or an error occurs. func RunHTTPServers(ctx context.Context, lns Listeners, handler http.Handler) error { g, ctx := errgroup.WithContext(ctx) for _, ln := range lns { g.Go(func() error { logger, ctx := logging.With(ctx, "address", ln.Addr()) srv := &http.Server{ Addr: ln.Addr().String(), Handler: handler, BaseContext: func(l net.Listener) context.Context { return ctx }, ErrorLog: slog.NewLogLogger(logger.Handler(), slog.LevelError), } logger.Log(ctx, slog.LevelInfo, "HTTP server serving") if err := httpServeContext(ctx, srv, ln); err != nil { return fmt.Errorf("HTTP server Serve Error: %w", err) } return nil }) } return g.Wait() } func httpServeContext(ctx context.Context, srv *http.Server, ln net.Listener) error { logger := logging.FromContext(ctx) shutdownErrCh := make(chan error, 1) go func() { <-ctx.Done() shutdownErrCh <- httpServeShutdown(srv, logger) }() serveErr := srv.Serve(ln) // Always wait for shutdown result shutdownErr := <-shutdownErrCh // Prioritize Serve error if serveErr != nil && !errors.Is(serveErr, http.ErrServerClosed) { return fmt.Errorf("http serve error: %w", serveErr) } if shutdownErr != nil { return fmt.Errorf("http shutdown error: %w", shutdownErr) } return nil } func httpServeShutdown(srv *http.Server, logger *slog.Logger) error { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() if err := srv.Shutdown(ctx); err != nil { logger.Log(ctx, slog.LevelWarn, "HTTP server Shutdown Error", slog.Any("error", err)) return err } logger.Log(ctx, slog.LevelInfo, "HTTP Server Shutdown Complete") return nil }