package server import ( "fmt" "net/http" "os" "strings" "time" "git.staur.ca/stobbsm/clustvirt/cluster" "git.staur.ca/stobbsm/clustvirt/lib/log" "git.staur.ca/stobbsm/clustvirt/router" "git.staur.ca/stobbsm/clustvirt/router/htmx" "git.staur.ca/stobbsm/clustvirt/router/middleware" ) // Server represents an HTTP server that uses net/http ServeMux to route requests // Originally done with chi, but a migration to the new net/http patterns seems like // a good choice to reduce dependencies, but does mean middleware needs to be implemented // in the server type Server struct { bindAddr string ssl bool c *cluster.Cluster middleware []middleware.Middleware mux *http.ServeMux } // New creates a new HTTP Server instance. // Requires the IP and port number to bind to func New(listen string, port int, cluster *cluster.Cluster) *Server { s := &Server{bindAddr: fmt.Sprintf("%s:%d", listen, port), c: cluster} return s } // Start starts the server and initializes the router and common middleware func (s *Server) Start() { tstart := time.Now() defer func() { log.Info("router.Server.Start"). Dur("upTime", time.Since(tstart)). Msg("http server stopped") }() s.mux = http.NewServeMux() indev, _ := os.LookupEnv("CLUSTVIRT_DEV") indev = strings.ToLower(indev) switch indev { case "true": fallthrough case "1": fallthrough case "yes": fallthrough case "on": s.AddMiddleware(middleware.NoCache) s.mux.Handle("GET /_/debug", middleware.Profiler()) } s.AddMiddleware(middleware.Logger) // Add routes s.baseRoutes() if err := s.AddSubRouter("/htmx", htmx.Htmx); err != nil { log.Error("server.Start"). Str("subroute", "htmx"). Err(err).Send() } // Start the server if err := http.ListenAndServe(s.bindAddr, s); err != nil { log.Error("router.Server.Start"). Str("bindaddr", s.bindAddr). Err(err).Send() } } // AddMiddleware appends middleware to the chi router func (s *Server) AddMiddleware(m ...middleware.Middleware) { s.middleware = append(s.middleware, m...) } // AddSubRouter attachs a SubRouter using it's MountTo method. This method // needs the path prefix and the defined routes func (s *Server) AddSubRouter(pfx string, sr router.SubRouter) error { return sr.MountTo(pfx, s.c, s.mux) } // ServeHTTP implements the Handler interface func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { s.chain().ServeHTTP(w, r) } func (s *Server) chain() http.Handler { h := http.Handler(s.mux) for i := range s.middleware { h = s.middleware[len(s.middleware)-1-i](h) } return h }