package api import ( "context" "errors" "net" "net/http" "os" "os/signal" "strconv" "syscall" "time" ) func (app *application) serve() error { maintenanceContext, stopMaintenance := context.WithCancel(context.Background()) defer stopMaintenance() app.background(func() { if err := app.runLifecycleMaintenance(time.Now()); err != nil { app.logger.Error("lifecycle maintenance failed", "error", err) } ticker := time.NewTicker(6 * time.Hour) defer ticker.Stop() for { select { case <-maintenanceContext.Done(): return case now := <-ticker.C: if err := app.runLifecycleMaintenance(now); err != nil { app.logger.Error("lifecycle maintenance failed", "error", err) } } } }) srv := &http.Server{ Addr: net.JoinHostPort(app.config.Host, strconv.Itoa(app.config.Port)), Handler: app.routes(), IdleTimeout: time.Minute, ReadHeaderTimeout: 5 * time.Second, ReadTimeout: 2 * time.Minute, WriteTimeout: 2 * time.Minute, } shutdownError := make(chan error) go func() { quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) s := <-quit app.logger.Info("stopping server", "addr", srv.Addr, "signal", s.String()) shutdownError <- srv.Shutdown(context.Background()) }() app.logger.Info("starting server", "addr", srv.Addr, "env", app.config.Env) err := srv.ListenAndServe() if !errors.Is(err, http.ErrServerClosed) { return err } err = <-shutdownError if err != nil { return err } stopMaintenance() app.logger.Info("waiting for background tasks") app.wg.Wait() app.logger.Info("shutdown complete") return nil }