From abb090ef642805741f62d56f705ed95521a26e36 Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Fri, 6 Mar 2026 13:59:57 -0500 Subject: [PATCH] refactor(app): migrate server lifecycle to Echo v5 - Add http.Server field to App struct for explicit server management - Add StartServer() method to create and start HTTP server - Replace echo.Close() with http.Server.Shutdown() in Shutdown() - Update import from echo/v4 to echo/v5 Changes: - New() initializes server field as nil - StartServer() creates http.Server with Echo as handler - Shutdown() uses http.Server.Shutdown() with context timeout - Removed deprecated echo.Close() call (v5 API change) This provides better control over server lifecycle and graceful shutdown. --- internal/app/app.go | 37 +++++++++++++++++++++++++++---------- internal/app/app_test.go | 2 +- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/internal/app/app.go b/internal/app/app.go index 4aa417c..2145c10 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -3,18 +3,20 @@ package app import ( "context" "log" + "net/http" "os" "os/signal" "sync" "syscall" "time" - "github.com/labstack/echo/v4" + "github.com/labstack/echo/v5" ) // App manages application lifecycle and graceful shutdown type App struct { echo *echo.Echo + server *http.Server shutdownTimeout time.Duration shutdownMutex sync.Mutex shutdownDone chan struct{} @@ -24,11 +26,28 @@ type App struct { func New(echo *echo.Echo) *App { return &App{ echo: echo, + server: nil, shutdownTimeout: 30 * time.Second, shutdownDone: make(chan struct{}), } } +func (a *App) StartServer(addr string) error { + a.server = &http.Server{ + Addr: addr, + Handler: a.echo, + } + + // Start HTTP server in background + go func() { + if err := a.server.ListenAndServe(); err != nil && err != http.ErrServerClosed { + log.Fatalf("Server failed to start: %v", err) + } + }() + + return nil +} + // Start begins all background services and blocks until shutdown signal func (a *App) Start() error { log.Println("Starting application lifecycle management...") @@ -77,19 +96,17 @@ func (a *App) Shutdown() error { // Channel to track shutdown completion done := make(chan struct{}) - // Perform shutdown in goroutine - go func() { - defer close(done) + log.Println("Stopping HTTP Server...") + if a.server != nil { + ctx, cancel := context.WithTimeout(context.Background(), a.shutdownTimeout) + defer cancel() - // Stop accepting new connections and shutdown HTTP server - log.Println("Stopping HTTP server...") - if err := a.echo.Close(); err != nil { + if err := a.server.Shutdown(ctx); err != nil { log.Printf("Error stopping HTTP server: %v", err) } + } - log.Println("All services stopped") - }() - + log.Println("All services stopped") // Wait for shutdown or timeout select { case <-done: diff --git a/internal/app/app_test.go b/internal/app/app_test.go index 6bee51c..4ff7cac 100644 --- a/internal/app/app_test.go +++ b/internal/app/app_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "github.com/labstack/echo/v4" + "github.com/labstack/echo/v5" "github.com/stretchr/testify/assert" )