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 } }