134 lines
3.3 KiB
Go
134 lines
3.3 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 (
|
|
// VinegarServlet 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.
|
|
VinegarServlet struct {
|
|
Port string
|
|
Routes []*VinegarRoute
|
|
ErrorRoutes map[int]*TemplateRoute
|
|
}
|
|
|
|
// VinegarRoute 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.
|
|
VinegarRoute struct {
|
|
Pattern *regexp.Regexp
|
|
Handler VinegarHandlerFunction
|
|
Cache vinegarUtil.Cache
|
|
}
|
|
|
|
VinegarHandlerFunction func(w http.ResponseWriter, req *http.Request)
|
|
)
|
|
|
|
func NewServlet(port string) *VinegarServlet {
|
|
errors := make(map[int]*TemplateRoute)
|
|
srv := VinegarServlet{Port: port, ErrorRoutes: errors}
|
|
|
|
return &srv
|
|
}
|
|
|
|
func NewServletRoute(routePattern string, handleFunc VinegarHandlerFunction) *VinegarRoute {
|
|
|
|
pattern := regexp.MustCompile(routePattern)
|
|
|
|
route := VinegarRoute{Pattern: pattern, Handler: handleFunc, Cache: vinegarUtil.NewLRU(defaultLruSize)}
|
|
return &route
|
|
}
|
|
|
|
func (s *VinegarServlet) AddRoute(route *VinegarRoute) {
|
|
route.Announce()
|
|
s.Routes = append(s.Routes, route)
|
|
}
|
|
|
|
func (s *VinegarServlet) AddErrorRoute(code int, route *TemplateRoute) {
|
|
route.Announce()
|
|
s.ErrorRoutes[code] = route
|
|
}
|
|
|
|
func (s *VinegarServlet) 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())
|
|
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 *VinegarServlet) 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 *VinegarRoute) Announce() {
|
|
log.Printf("Added route for [%s]\n", r.Pattern.String())
|
|
}
|
|
|
|
func (s *VinegarServlet) 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
|
|
}
|
|
}
|