Files
gronod 7bed99cc4c fix: keep robot listeners if health port is busy (v0.1.2)
ServeHealth bind failures (common on HAOS when OTBR owns 8080) are
logged and ignored. Lookup, firmware, and XMPP stay up. Log each
successful listen address so add-on logs show the sockets.
2026-09-24 16:17:30 +00:00

58 lines
1.3 KiB
Go

package httpx
import (
"context"
"fmt"
"log/slog"
"net"
"net/http"
"time"
"git.i3omb.com/gronod/ha-n95-local-control/internal/config"
)
// ServeHealth runs the GET /healthz listener until ctx is cancelled.
func ServeHealth(ctx context.Context, cfg config.Config) error {
mux := http.NewServeMux()
mux.HandleFunc("/healthz", healthHandler)
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
})
addr := net.JoinHostPort(cfg.BindAddress, fmt.Sprintf("%d", cfg.HealthPort))
ln, err := net.Listen("tcp", addr)
if err != nil {
return fmt.Errorf("%s: %w", addr, err)
}
slog.Info("health listening", "addr", addr)
srv := &http.Server{
Handler: mux,
ReadHeaderTimeout: 5 * time.Second,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
}
srv.SetKeepAlivesEnabled(false)
errCh := make(chan error, 1)
go func() {
errCh <- srv.Serve(ln)
}()
select {
case <-ctx.Done():
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
_ = srv.Shutdown(shutdownCtx)
return nil
case err := <-errCh:
return err
}
}
func healthHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("ok\n"))
}