package handlers import ( "context" "github.com/winc-link/hummingbird/internal/pkg/container" "github.com/winc-link/hummingbird/internal/pkg/di" "github.com/winc-link/hummingbird/internal/pkg/startup" "net/http" "strconv" "sync" "time" "github.com/gin-gonic/gin" ) // HttpServer contains references to dependencies required by the http server implementation. type HttpServer struct { router *gin.Engine isRunning bool doListenAndServe bool } // NewHttpServer is a factory method that returns an initialized HttpServer receiver struct. func NewHttpServer(router *gin.Engine, doListenAndServe bool) *HttpServer { return &HttpServer{ router: router, isRunning: false, doListenAndServe: doListenAndServe, } } // IsRunning returns whether or not the http server is running. It is provided to support delayed shutdown of // any resources required to successfully process http requests until after all outstanding requests have been // processed (e.g. a database connection). func (b *HttpServer) IsRunning() bool { return b.isRunning } // BootstrapHandler fulfills the BootstrapHandler contract. It creates two go routines -- one that executes ListenAndServe() // and another that waits on closure of a context's done channel before calling Shutdown() to cleanly shut down the // http server. func (b *HttpServer) BootstrapHandler( ctx context.Context, wg *sync.WaitGroup, _ startup.Timer, dic *di.Container) bool { lc := container.LoggingClientFrom(dic.Get) if !b.doListenAndServe { lc.Info("Web server intentionally NOT started.") wg.Add(1) go func() { defer wg.Done() b.isRunning = true <-ctx.Done() b.isRunning = false }() return true } bootstrapConfig := container.ConfigurationFrom(dic.Get).GetBootstrap() // this allows env override to explicitly set the value used // for ListenAndServe as needed for different deployments port := strconv.Itoa(bootstrapConfig.Service.Port) addr := bootstrapConfig.Service.ServerBindAddr + ":" + port // for backwards compatibility, the Host value is the default value if // the ServerBindAddr value is not specified if bootstrapConfig.Service.ServerBindAddr == "" { addr = bootstrapConfig.Service.Host + ":" + port } timeout := time.Millisecond * time.Duration(bootstrapConfig.Service.Timeout) server := &http.Server{ Addr: addr, Handler: b.router, WriteTimeout: timeout, ReadTimeout: timeout, } wg.Add(1) go func() { defer wg.Done() <-ctx.Done() _ = server.Shutdown(context.Background()) lc.Info("Web server shut down") }() lc.Info("Web server starting (" + addr + ")") wg.Add(1) go func() { defer func() { wg.Done() b.isRunning = false }() b.isRunning = true err := server.ListenAndServe() if err != nil { lc.Errorf("Web server failed: %v", err) cancel := container.CancelFuncFrom(dic.Get) cancel() // this will caused the service to stop } else { lc.Info("Web server stopped") } }() return true }