Files
homelab-frontend/internal/server/server.go
T

84 lines
2.2 KiB
Go
Raw Normal View History

2026-08-19 20:52:13 -07:00
package server
import (
"context"
"net"
"net/http"
"sync"
"time"
)
// Server wraps an HTTP server with graceful shutdown support.
type Server struct {
httpServer *http.Server
shutdownTimeout time.Duration
listener net.Listener
listenerMu sync.RWMutex
healthChecker *HealthChecker
}
// New creates a new Server with the given configuration.
func New(listenAddr string, shutdownTimeout time.Duration, handler http.Handler) *Server {
return &Server{
httpServer: &http.Server{
Addr: listenAddr,
Handler: handler,
ReadTimeout: 15 * time.Second,
WriteTimeout: 15 * time.Second,
IdleTimeout: 60 * time.Second,
},
shutdownTimeout: shutdownTimeout,
healthChecker: NewHealthChecker(false, false),
}
}
// ListenAndServe starts the HTTP server and blocks until it exits.
// It returns the error from the server (if any), which will be
// http.ErrServerClosed if Shutdown was called.
func (s *Server) ListenAndServe() error {
listener, err := net.Listen("tcp", s.httpServer.Addr)
if err != nil {
return err
}
s.listenerMu.Lock()
s.listener = listener
s.listenerMu.Unlock()
return s.httpServer.Serve(listener)
}
// Shutdown gracefully shuts down the server. It stops accepting new
// connections and waits for in-flight requests to complete, with a
// bounded deadline. If the deadline is exceeded, it returns an error.
func (s *Server) Shutdown(ctx context.Context) error {
// Create a new context with the shutdown timeout
shutdownCtx, cancel := context.WithTimeout(ctx, s.shutdownTimeout)
defer cancel()
return s.httpServer.Shutdown(shutdownCtx)
}
// Addr returns the network address the server is listening on.
func (s *Server) Addr() string {
s.listenerMu.RLock()
defer s.listenerMu.RUnlock()
if s.listener != nil {
return s.listener.Addr().String()
}
return s.httpServer.Addr
}
// HealthChecker returns the server's health checker.
func (s *Server) HealthChecker() *HealthChecker {
return s.healthChecker
}
// SetHealthChecker sets the server's health checker.
func (s *Server) SetHealthChecker(hc *HealthChecker) {
s.healthChecker = hc
}
// SetHandler sets the server's HTTP handler.
func (s *Server) SetHandler(handler http.Handler) {
s.httpServer.Handler = handler
}