summaryrefslogtreecommitdiffstats
path: root/http/http.go
diff options
context:
space:
mode:
Diffstat (limited to 'http/http.go')
-rw-r--r--http/http.go85
1 files changed, 85 insertions, 0 deletions
diff --git a/http/http.go b/http/http.go
new file mode 100644
index 0000000..063c1e8
--- /dev/null
+++ b/http/http.go
@@ -0,0 +1,85 @@
+package http
+
+import (
+ "context"
+ "fmt"
+ "go.sudomsg.com/kit/logging"
+ "golang.org/x/sync/errgroup"
+ "log/slog"
+ "net"
+ "net/http"
+)
+
+// 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
+}