3
0
corteza/pkg/api/server/server.go
Denis Arh d74239c735 Improve HTTP server startup, add wrapping handler
HTTP server now initializes much earlier and mounts "waiting"
router with debugging, version and health check routes.

When server is fully activated it switches to "active"
router.

Shutdown stage is also handled with catch-all route.
2022-02-13 18:52:59 +01:00

107 lines
2.4 KiB
Go

package server
import (
"context"
"net"
"net/http"
"github.com/cortezaproject/corteza-server/pkg/options"
"github.com/go-chi/chi/v5"
"go.uber.org/zap"
)
type (
server struct {
log *zap.Logger
httpOpt options.HttpServerOpt
waitForOpt options.WaitForOpt
environmentOpt options.EnvironmentOpt
endpoints []func(r chi.Router)
demux *demux
}
)
const (
waiting uint32 = iota
active
shutdown
)
// New initializes new HTTP server with special powers
// Server is started as early as possible and with a special request handler
// that demultiplexes request to one of the configured routers according to the server state.
//
// Waiting state
// This is initial state that with some simple route handlers:
// - /version
// - /healthcheck
// - /healthcheck
func New(log *zap.Logger, envOpt options.EnvironmentOpt, httpOpt options.HttpServerOpt, waitForOpt options.WaitForOpt) *server {
s := &server{
endpoints: make([]func(r chi.Router), 0),
log: log.Named("http"),
environmentOpt: envOpt,
httpOpt: httpOpt,
waitForOpt: waitForOpt,
}
s.demux = Demux(waiting, waitingRoutes(s.log, s.httpOpt))
s.demux.Router(shutdown, shutdownRoutes())
return s
}
// Activate reconfigures server to use active routes
func (s *server) Activate(mm ...func(chi.Router)) {
s.demux.Router(active, activeRoutes(s.log, mm, s.environmentOpt, s.httpOpt))
s.log.Debug("entering active state")
s.demux.State(active)
}
// Shutdown reconfigures server to use shutdown routes
func (s *server) Shutdown() {
s.log.Debug("entering shutdown state")
s.demux.State(shutdown)
}
func (s server) Serve(ctx context.Context) {
s.log.Info(
"starting HTTP server",
zap.String("path-prefix", s.httpOpt.BaseUrl),
zap.String("address", s.httpOpt.Addr),
)
listener, err := net.Listen("tcp", s.httpOpt.Addr)
if err != nil {
s.log.Error("cannot start server", zap.Error(err))
return
}
go func() {
srv := http.Server{
Handler: s.demux,
// use root context as server's base context and as a basis for
// context for all requests
// this enables us to send cancellation down to every request
BaseContext: func(listener net.Listener) context.Context { return ctx },
}
err = srv.Serve(listener)
}()
<-ctx.Done()
if err == nil {
err = ctx.Err()
if err == context.Canceled {
err = nil
}
}
s.log.Info("HTTP server stopped", zap.Error(err))
}