2020-11-28 23:34:20 +05:00
|
|
|
package httpserver
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"net"
|
|
|
|
"net/http"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
2020-11-29 06:09:35 +05:00
|
|
|
"go.dev.pztrn.name/metricator/internal/common"
|
2020-11-28 23:34:20 +05:00
|
|
|
"go.dev.pztrn.name/metricator/internal/configuration"
|
2020-12-23 16:28:57 +05:00
|
|
|
"go.dev.pztrn.name/metricator/internal/logger"
|
2020-11-28 23:34:20 +05:00
|
|
|
)
|
|
|
|
|
|
|
|
// HTTPServer is a controlling structure for HTTP server.
|
|
|
|
type HTTPServer struct {
|
|
|
|
config *configuration.Config
|
|
|
|
ctx context.Context
|
|
|
|
doneChan chan struct{}
|
2020-12-23 16:28:57 +05:00
|
|
|
logger *logger.Logger
|
2020-11-28 23:34:20 +05:00
|
|
|
handler *handler
|
|
|
|
server *http.Server
|
|
|
|
}
|
|
|
|
|
2020-12-23 16:28:57 +05:00
|
|
|
func NewHTTPServer(ctx context.Context, cfg *configuration.Config, logger *logger.Logger) (*HTTPServer, chan struct{}) {
|
2020-11-28 23:34:20 +05:00
|
|
|
h := &HTTPServer{
|
|
|
|
config: cfg,
|
|
|
|
ctx: ctx,
|
|
|
|
doneChan: make(chan struct{}),
|
2020-12-23 16:28:57 +05:00
|
|
|
logger: logger,
|
2020-11-28 23:34:20 +05:00
|
|
|
}
|
|
|
|
h.initialize()
|
|
|
|
|
|
|
|
return h, h.doneChan
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns request's context based on main context of application.
|
|
|
|
// Basically it returns main context and does nothing more.
|
|
|
|
func (h *HTTPServer) getRequestContext(_ net.Listener) context.Context {
|
|
|
|
return h.ctx
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initializes handler and HTTP server structure.
|
|
|
|
func (h *HTTPServer) initialize() {
|
2020-12-23 16:28:57 +05:00
|
|
|
h.logger.Debugln("Initializing HTTP server...")
|
|
|
|
|
2020-11-29 06:09:35 +05:00
|
|
|
h.handler = &handler{
|
|
|
|
handlers: make(map[string]common.HTTPHandlerFunc),
|
|
|
|
}
|
2020-11-29 03:22:39 +05:00
|
|
|
// We do not need to specify all possible parameters for HTTP server, so:
|
2020-11-29 05:52:52 +05:00
|
|
|
// nolint:exhaustivestruct
|
2020-11-28 23:34:20 +05:00
|
|
|
h.server = &http.Server{
|
|
|
|
// ToDo: make it all configurable.
|
|
|
|
Addr: ":34421",
|
|
|
|
BaseContext: h.getRequestContext,
|
|
|
|
Handler: h.handler,
|
|
|
|
ReadTimeout: time.Second * 10,
|
|
|
|
WriteTimeout: time.Second * 10,
|
|
|
|
MaxHeaderBytes: 1 << 20,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-29 06:09:35 +05:00
|
|
|
// RegisterHandlerForApplication registers HTTP handler for application.
|
|
|
|
func (h *HTTPServer) RegisterHandlerForApplication(name string, handler common.HTTPHandlerFunc) {
|
2020-12-23 16:28:57 +05:00
|
|
|
h.logger.Debugln("Registering handler for application", name)
|
2020-11-29 06:09:35 +05:00
|
|
|
h.handler.register(name, handler)
|
|
|
|
}
|
|
|
|
|
2020-11-28 23:34:20 +05:00
|
|
|
// Start starts HTTP server in another goroutine and one more goroutine which
|
|
|
|
// is listening to main context's Cancel() call to stop HTTP server.
|
|
|
|
func (h *HTTPServer) Start() {
|
|
|
|
go func() {
|
|
|
|
err := h.server.ListenAndServe()
|
|
|
|
if err != nil {
|
|
|
|
if !strings.Contains(err.Error(), "Server closed") {
|
2020-12-23 16:28:57 +05:00
|
|
|
h.logger.Infoln("HTTP server failed to listen:", err.Error())
|
2020-11-28 23:34:20 +05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
<-h.ctx.Done()
|
2020-12-23 16:28:57 +05:00
|
|
|
h.logger.Infoln("Shutting down HTTP server")
|
2020-11-28 23:34:20 +05:00
|
|
|
|
|
|
|
err := h.server.Shutdown(h.ctx)
|
|
|
|
if err != nil && !strings.Contains(err.Error(), "context canceled") {
|
2020-12-23 16:28:57 +05:00
|
|
|
h.logger.Infoln("Failed to stop HTTP server:", err.Error())
|
2020-11-28 23:34:20 +05:00
|
|
|
}
|
|
|
|
|
2020-12-23 16:28:57 +05:00
|
|
|
h.logger.Infoln("HTTP server stopped")
|
2020-11-28 23:34:20 +05:00
|
|
|
|
|
|
|
h.doneChan <- struct{}{}
|
|
|
|
}()
|
|
|
|
}
|