vinegar/servlet/server.go
2023-07-31 15:49:04 -04:00

133 lines
3.4 KiB
Go

package servlet
import (
"encoding/json"
"errors"
"fmt"
"geniuscartel.xyz/vinegar/vinegarUtil"
"log"
"net/http"
"os"
"regexp"
"strconv"
)
const (
defaultLruSize = int64(1024 * 1024 * 50)
ContentTypeHeaderKey = "Content-Type"
ContentEncodingHeaderKey = "Content-Encoding"
AcceptEncodingHeaderKey = "Accept-Encoding"
)
type ErrorResponse struct {
Code int
Message string
}
type (
// VinegarHttpServlet is the main server struct that handles HTTP requests and routing.
// It contains the TCP port to listen on, the routes to match requests against,
// and a map of status code to error handling routes.
VinegarHttpServlet struct {
Port string
Routes []*VinegarWebRoute
ErrorRoutes map[int]*TemplateRoute
}
// VinegarWebRoute defines a single route in the router.
// It contains a regex Pattern to match against the URL path,
// a Handler function to call when the route matches,
// and an optional Cache to enable caching for the route.
VinegarWebRoute struct {
Pattern *regexp.Regexp
Handler VinegarHandlerFunction
Cache vinegarUtil.Cache
}
VinegarHandlerFunction func(w http.ResponseWriter, req *http.Request)
)
func NewServlet(port string) *VinegarHttpServlet {
errs := make(map[int]*TemplateRoute)
srv := VinegarHttpServlet{Port: port, ErrorRoutes: errs}
return &srv
}
func NewServletRoute(routePattern string, handleFunc VinegarHandlerFunction) *VinegarWebRoute {
pattern := regexp.MustCompile(routePattern)
route := VinegarWebRoute{Pattern: pattern, Handler: handleFunc, Cache: vinegarUtil.NewLRU(defaultLruSize)}
return &route
}
func (s *VinegarHttpServlet) AddRoute(route *VinegarWebRoute) {
route.Announce()
s.Routes = append(s.Routes, route)
}
func (s *VinegarHttpServlet) AddErrorRoute(code int, route *TemplateRoute) {
route.Announce()
s.ErrorRoutes[code] = route
}
func (s *VinegarHttpServlet) ServeHTTP(w http.ResponseWriter, req *http.Request) {
path := req.URL.Path
for _, route := range s.Routes {
if route.Pattern.MatchString(path) {
//fmt.Printf("SERVING: [%s]=>{%s}\n", path, route.Pattern.String())
go route.Handler(w, req)
return
}
}
s.SendError(w, req, 404, "Couldn't find your content.", errors.New("failed to match route for ["+path+"]"))
}
func (s *VinegarHttpServlet) Start() {
if len(s.Routes) < 1 {
log.Fatal("No routes found for server. Nothing to listen and serve.")
os.Exit(1)
}
log.Printf("Listening on [%s]\n", s.Port)
err := http.ListenAndServe(s.Port, s)
if err != nil {
panic(err)
}
}
func (r *VinegarWebRoute) Announce() {
log.Printf("Added route for [%s]\n", r.Pattern.String())
}
func (s *VinegarHttpServlet) SendError(w http.ResponseWriter, req *http.Request, code int, msg string, aErr error) {
fmt.Printf("[%d][%s]. Rendering template for code %d with message: %s\n", code, req.URL.Path, code, msg)
fmt.Println(aErr)
tmpl, exists := s.ErrorRoutes[code]
if exists {
tmpl.TemplateManager.AddMixin("code", strconv.Itoa(code))
tmpl.TemplateManager.AddMixin("msg", msg)
w.WriteHeader(code)
_, err := w.Write([]byte(tmpl.TemplateManager.RenderTemplate(fmt.Sprintf("%d.html", code))))
if err != nil {
panic(err)
}
return
} else {
w.WriteHeader(code)
errorPayload := ErrorResponse{Code: code, Message: msg}
genericError, sErr := json.Marshal(errorPayload)
if sErr != nil {
panic(sErr)
}
_, err := w.Write(genericError)
if err != nil {
panic(err)
}
return
}
}