iot_server/internal/pkg/handlers/httpserver.go

114 lines
3.0 KiB
Go

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
}