removed chi router

- switched to go 1.22 net/http ServeMux for routing
- seems to be working really well
This commit is contained in:
Matthew Stobbs 2024-03-23 23:59:56 -06:00
parent 8e62f7e271
commit 4c132c4abf
10 changed files with 152 additions and 106 deletions

View File

@ -962,10 +962,6 @@ span>a:visited {
flex-wrap: nowrap;
}
.items-end {
align-items: flex-end;
}
.items-center {
align-items: center;
}

View File

@ -9,46 +9,52 @@ import (
"git.staur.ca/stobbsm/clustvirt/cluster"
"git.staur.ca/stobbsm/clustvirt/lib/log"
"git.staur.ca/stobbsm/clustvirt/router"
"github.com/go-chi/chi/v5"
)
type htmx struct {
router chi.Router
routes []router.Route
}
type htmx []router.Route
func (h *htmx) MountTo(c *cluster.Cluster, rr chi.Router) error {
func prefix(b, p string) string { return fmt.Sprintf("%s%s", b, p)}
func trace(p string) string { return fmt.Sprintf("TRACE %s", p) }
func options(p string) string { return fmt.Sprintf("OPTIONS %s", p) }
func connect(p string) string { return fmt.Sprintf("CONNECT %s", p) }
func head(p string) string { return fmt.Sprintf("HEAD %s", p) }
func get(p string) string { return fmt.Sprintf("GET %s", p) }
func post(p string) string { return fmt.Sprintf("POST %s", p) }
func put(p string) string { return fmt.Sprintf("PUT %s", p) }
func patch(p string) string { return fmt.Sprintf("PATCH %s", p) }
func delete(p string) string { return fmt.Sprintf("DELETE %s", p) }
func (h htmx) Prefix() string { return "/htmx" }
func (h htmx) MountTo(c *cluster.Cluster, mux *http.ServeMux) error {
var errs []error
for _, r := range h.routes {
for _, r := range h {
switch r.Method {
case http.MethodTrace:
h.router.Trace(r.Path, r.Handler(c))
mux.Handle(trace(prefix(h.Prefix(), r.Path)), r.Handler(c))
case http.MethodOptions:
h.router.Options(r.Path, r.Handler(c))
mux.Handle(options(prefix(h.Prefix(), r.Path)), r.Handler(c))
case http.MethodConnect:
h.router.Connect(r.Path, r.Handler(c))
mux.Handle(connect(prefix(h.Prefix(), r.Path)), r.Handler(c))
case http.MethodHead:
h.router.Head(r.Path, r.Handler(c))
mux.Handle(head(prefix(h.Prefix(), r.Path)), r.Handler(c))
case http.MethodGet:
h.router.Get(r.Path, r.Handler(c))
mux.Handle(get(prefix(h.Prefix(), r.Path)), r.Handler(c))
case http.MethodPost:
h.router.Post(r.Path, r.Handler(c))
mux.Handle(post(prefix(h.Prefix(), r.Path)), r.Handler(c))
case http.MethodPut:
h.router.Put(r.Path, r.Handler(c))
mux.Handle(put(prefix(h.Prefix(), r.Path)), r.Handler(c))
case http.MethodPatch:
h.router.Patch(r.Path, r.Handler(c))
mux.Handle(patch(prefix(h.Prefix(), r.Path)), r.Handler(c))
case http.MethodDelete:
h.router.Delete(r.Path, r.Handler(c))
mux.Handle(delete(prefix(h.Prefix(), r.Path)), r.Handler(c))
default:
err := fmt.Errorf("method: %s: %w", r.Method, router.ErrMethodNotExist)
errs = append(errs, err)
continue
mux.Handle(prefix(h.Prefix(), r.Path), r.Handler(c))
}
log.Info("htmx.MoutnTo").
Str("Route.Path", r.Path).
Str("Route.Method", r.Method).
Msg("route registered")
}
rr.Mount("/htmx", h.router)
return errors.Join(errs...)
}

View File

@ -6,14 +6,10 @@ import (
"git.staur.ca/stobbsm/clustvirt/cluster"
"git.staur.ca/stobbsm/clustvirt/lib/log"
"git.staur.ca/stobbsm/clustvirt/router"
"git.staur.ca/stobbsm/clustvirt/view"
"github.com/go-chi/chi/v5"
)
var Htmx = &htmx{
router: chi.NewRouter(),
routes: []router.Route{
var Htmx = htmx{
{
Method: http.MethodGet,
Path: "/cluster",
@ -34,7 +30,7 @@ var Htmx = &htmx{
Path: "/host/{hostname}",
Handler: func(c *cluster.Cluster) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
host, err := c.GetHost(chi.URLParam(r, "hostname"))
host, err := c.GetHost(r.PathValue("hostname"))
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
log.Error("htmx.Handler").
@ -59,7 +55,7 @@ var Htmx = &htmx{
Path: "/host/{hostname}/stats",
Handler: func(c *cluster.Cluster) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
host, err := c.GetHost(chi.URLParam(r, "hostname"))
host, err := c.GetHost(r.PathValue("hostname"))
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
log.Error("htmx.Handler").
@ -79,5 +75,4 @@ var Htmx = &htmx{
}
},
},
},
}

View File

@ -9,7 +9,7 @@ var (
BasicAuth = middleware.BasicAuth
Compress = middleware.Compress
Heartbeat = middleware.Heartbeat
NoCache = middleware.NoCache
//NoCache = middleware.NoCache
Profiler = middleware.Profiler
RealIP = middleware.RealIP
Recoverer = middleware.Recoverer

View File

@ -9,6 +9,7 @@ import (
// Logger uses the in package log module to handle route logging
func Logger(next http.Handler) http.Handler {
log.Info("logger.Logger").Str("middleware", "Logger").Msg("middleware loaded")
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
defer func() {

View File

@ -0,0 +1,7 @@
package middleware
import "net/http"
// Middleware is a function that is meant to be chained with other functions
// through an http request lifecycle
type Middleware func(http.Handler) http.Handler

View File

@ -0,0 +1,22 @@
package middleware
import (
"net/http"
"time"
"git.staur.ca/stobbsm/clustvirt/lib/log"
)
// NoCache adds headers indicating the browser shouldn't cache
// any of the responses. Useful for debugging, should not be used
// on production
func NoCache(next http.Handler) http.Handler {
log.Info("router.middleware").Str("middleware", "NoCache").Msg("middleware loaded")
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Cache-Control", "no-cache, no-store, no-transform, must-revalidate, private, max-age=0")
w.Header().Add("Expires", time.Unix(0, 0).UTC().Format(http.TimeFormat))
w.Header().Add("Pragma", "no-cache")
next.ServeHTTP(w, r)
})
}

View File

@ -4,7 +4,6 @@ import (
"net/http"
"git.staur.ca/stobbsm/clustvirt/cluster"
"github.com/go-chi/chi/v5"
)
// Types that are shared among routers
@ -12,7 +11,10 @@ import (
// SubRouter defines an interface to be able to add a subrouter to a
// chi router
type SubRouter interface {
MountTo(*cluster.Cluster, chi.Router) error
// MountTo needs the cluster handle and the mux to add to
MountTo(*cluster.Cluster, *http.ServeMux) error
// Prefix returns the path prefix
Prefix() string
}
// Route defines a route that should be added to a chi router or

View File

@ -13,9 +13,9 @@ import (
func (s *Server) baseRoutes() {
// fileserver for static content
fs := http.StripPrefix("/static", http.FileServer(http.Dir("public")))
s.r.Get("/static/*", fs.ServeHTTP)
s.mux.Handle("GET /static/*", fs)
// Root defaults to the cluster view
s.r.Get("/", templ.Handler(layouts.Manager("ClustVirt", "libvirt made simple")).ServeHTTP)
s.r.Get("/about", templ.Handler(static.About()).ServeHTTP)
s.mux.Handle("GET /", templ.Handler(layouts.Manager("ClustVirt", "libvirt made simple")))
s.mux.Handle("GET /about", templ.Handler(static.About()))
}

View File

@ -12,15 +12,18 @@ import (
"git.staur.ca/stobbsm/clustvirt/router"
"git.staur.ca/stobbsm/clustvirt/router/htmx"
"git.staur.ca/stobbsm/clustvirt/router/middleware"
"github.com/go-chi/chi/v5"
)
// Server represents an HTTP server that uses chi for routes
// 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
r chi.Router
middleware []middleware.Middleware
mux *http.ServeMux
}
// New creates a new HTTP Server instance.
@ -39,7 +42,7 @@ func (s *Server) Start() {
Dur("upTime", time.Since(tstart)).
Msg("http server stopped")
}()
s.r = chi.NewRouter()
s.mux = http.NewServeMux()
indev, _ := os.LookupEnv("CLUSTVIRT_DEV")
indev = strings.ToLower(indev)
@ -52,8 +55,9 @@ func (s *Server) Start() {
fallthrough
case "on":
s.AddMiddleware(middleware.NoCache)
s.r.Get("/_/debug", middleware.Profiler().ServeHTTP)
s.mux.Handle("GET /_/debug", middleware.Profiler())
}
s.AddMiddleware(middleware.Logger)
// Add routes
s.baseRoutes()
if err := s.AddSubRouter(htmx.Htmx); err != nil {
@ -62,7 +66,7 @@ func (s *Server) Start() {
Err(err).Send()
}
// Start the server
if err := http.ListenAndServe(s.bindAddr, s.r); err != nil {
if err := http.ListenAndServe(s.bindAddr, s); err != nil {
log.Error("router.Server.Start").
Str("bindaddr", s.bindAddr).
Err(err).Send()
@ -70,11 +74,24 @@ func (s *Server) Start() {
}
// AddMiddleware appends middleware to the chi router
func (s *Server) AddMiddleware(m ...func(h http.Handler) http.Handler) {
s.r.Use(m...)
func (s *Server) AddMiddleware(m ...middleware.Middleware) {
s.middleware = append(s.middleware, m...)
}
// AddSubRouter attachs a SubRouter using it's MountTo method
func (s *Server) AddSubRouter(sr router.SubRouter) error {
return sr.MountTo(s.c, s.r)
return sr.MountTo(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
}