more docs

This commit is contained in:
dtookey 2023-08-01 16:06:43 -04:00
parent 1748b65cad
commit 42d2e61c5d
3 changed files with 249 additions and 0 deletions

View File

@ -10,6 +10,30 @@ import (
) )
type ( type (
// VinegarWebRouter is the router struct that handles routing HTTP requests.
//
// It contains a single field:
//
// Routes - A slice of pointers to VinegarWebRoute structs. Each route in
// this slice is checked to see if it matches the incoming request path.
//
// When a request comes in, the router iterates through the Routes and executes
// the handler of the first route that matches the path.
//
// Custom routes can be added using the AddRoute method. The routes will be
// checked in the order they are added.
//
// Example Usage:
//
// router := VinegarWebRouter{}
// route := VinegarWebRoute{
// //...initialize route
// }
//
// router.AddRoute(&route) // Adds route to router
//
// router.RouteRequest(w, r) // Handle request by matching against routes
VinegarWebRouter struct { VinegarWebRouter struct {
Routes []*VinegarWebRoute Routes []*VinegarWebRoute
} }
@ -45,11 +69,43 @@ type (
} }
) )
// AddRoute adds a new route to the router.
//
// It accepts a pointer to a VinegarWebRoute struct to add as a parameter.
//
// It calls the Announce() method on the route to print a message that the
// route was added.
//
// It appends the route to the Routes slice field of the VinegarWebRouter.
//
// This allows custom routes to be registered with the router. The routes
// will be checked in order when handling incoming requests.
//
// Parameters:
// route: Pointer to a VinegarWebRoute instance to add as a route.
func (s *VinegarWebRouter) AddRoute(route *VinegarWebRoute) { func (s *VinegarWebRouter) AddRoute(route *VinegarWebRoute) {
route.Announce() route.Announce()
s.Routes = append(s.Routes, route) s.Routes = append(s.Routes, route)
} }
// RouteRequest routes an incoming HTTP request to the matching handler
// function for that request path.
//
// It takes an http.ResponseWriter and *http.Request as parameters.
//
// It iterates through the router's routes and tries to match the request
// path against the route patterns. If a match is found, it executes the
// route's handler function, prints the match, and returns nil.
//
// If no match is found, it calls the notFoundHandler helper and returns
// an error.
//
// Parameters:
// w: The http.ResponseWriter used to send the response back to the client
// req: The *http.Request containing details about the incoming request
//
// Returns:
// error: Any error encountered while routing the request, otherwise nil
func (r *VinegarWebRouter) RouteRequest(w http.ResponseWriter, req *http.Request) error { func (r *VinegarWebRouter) RouteRequest(w http.ResponseWriter, req *http.Request) error {
path := req.URL.Path path := req.URL.Path
for _, route := range r.Routes { for _, route := range r.Routes {
@ -63,6 +119,25 @@ func (r *VinegarWebRouter) RouteRequest(w http.ResponseWriter, req *http.Request
return errors.New("failed to match route for [" + path + "]") return errors.New("failed to match route for [" + path + "]")
} }
// notFoundHandler handles requests for unknown routes by returning a 404 Not Found response.
//
// It takes an http.ResponseWriter and *http.Request as parameters.
//
// It sets the response status code to 404.
//
// It creates an ErrorResponse struct with a 404 status code and error message.
//
// It marshals the ErrorResponse to JSON.
//
// It sets the Content-Type header to application/json.
//
// It writes the JSON error response to the http.ResponseWriter.
//
// This provides a consistent JSON error response when no matching route is found.
//
// Parameters:
// w: The http.ResponseWriter used to send the response back to the client.
// r: The *http.Request that could not be routed.
func notFoundHandler(w http.ResponseWriter, r *http.Request) { func notFoundHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound) w.WriteHeader(http.StatusNotFound)

View File

@ -94,6 +94,20 @@ type (
VinegarHandlerFunction func(w http.ResponseWriter, req *http.Request) VinegarHandlerFunction func(w http.ResponseWriter, req *http.Request)
) )
// NewServlet creates and initializes a new VinegarWebServlet instance.
//
// It accepts the TCP port to listen on as a string parameter.
//
// It initializes an empty ErrorRoutes map to store custom error routes.
//
// It creates an interrupts channel to handle shutdown signals.
//
// It returns a pointer to the initialized VinegarWebServlet struct.
//
// Example usage:
//
// port := ":8080"
// srv := NewServlet(port)
func NewServlet(port string) *VinegarWebServlet { func NewServlet(port string) *VinegarWebServlet {
errs := make(map[int]*TemplateRoute) errs := make(map[int]*TemplateRoute)
srv := VinegarWebServlet{Port: port, ErrorRoutes: errs, interrupts: make(chan struct{}, 500)} srv := VinegarWebServlet{Port: port, ErrorRoutes: errs, interrupts: make(chan struct{}, 500)}
@ -101,6 +115,21 @@ func NewServlet(port string) *VinegarWebServlet {
return &srv return &srv
} }
// NewServletRoute creates a new VinegarWebRoute instance.
//
// It accepts a routePattern string and handleFunc function as parameters:
//
// - routePattern is a regex string that will be compiled into the route Pattern.
//
// - handleFunc is the handler function to call when the route matches.
//
// It compiles the routePattern into a regexp.Pattern.
//
// It creates a VinegarWebRoute struct initialized with the Pattern,
// the handler function, and a new LRU cache.
//
// The returned VinegarWebRoute pointer can be added to a router to handle requests
// that match the routePattern.
func NewServletRoute(routePattern string, handleFunc VinegarHandlerFunction) *VinegarWebRoute { func NewServletRoute(routePattern string, handleFunc VinegarHandlerFunction) *VinegarWebRoute {
pattern := regexp.MustCompile(routePattern) pattern := regexp.MustCompile(routePattern)
@ -108,11 +137,47 @@ func NewServletRoute(routePattern string, handleFunc VinegarHandlerFunction) *Vi
return &route return &route
} }
// AddErrorRoute adds a custom error route for the given status code.
//
// It accepts two parameters:
//
// - code: The HTTP status code this error route should handle.
//
// - route: A pointer to the TemplateRoute to use for that status code.
//
// It adds the route to the ErrorRoutes map, mapped to the status code.
//
// This allows custom error handling routes to be defined for different
// HTTP status codes. The ErrorRoutes map can then be checked to see if
// a custom error template has been defined for a given code.
//
// For example:
//
// err404 := NewTemplateRoute(...)
// srv.AddErrorRoute(404, err404)
//
// // Later...
// src.SendError(404)
func (s *VinegarWebServlet) AddErrorRoute(code int, route *TemplateRoute) { func (s *VinegarWebServlet) AddErrorRoute(code int, route *TemplateRoute) {
route.Announce() route.Announce()
s.ErrorRoutes[code] = route s.ErrorRoutes[code] = route
} }
// ServeHTTP handles all incoming HTTP requests.
//
// It is called by the net/http server whenever a new request comes in.
//
// It takes an http.ResponseWriter and *http.Request as parameters.
//
// It calls the router's RouteRequest method to try to find a matching
// route and execute the handler.
//
// If routing fails, it returns a 404 Not Found response.
//
// This is the main request handler that handles routing and responding
// to all incoming requests. The VinegarWebServlet implements the
// http.Handler interface so it can be used by the net/http server.
//
func (s *VinegarWebServlet) ServeHTTP(w http.ResponseWriter, req *http.Request) { func (s *VinegarWebServlet) ServeHTTP(w http.ResponseWriter, req *http.Request) {
err := s.Router.RouteRequest(w, req) err := s.Router.RouteRequest(w, req)
if err != nil { if err != nil {
@ -120,6 +185,21 @@ func (s *VinegarWebServlet) ServeHTTP(w http.ResponseWriter, req *http.Request)
} }
} }
// Start starts the VinegarWebServlet HTTP server listening on the configured port.
//
// It first checks that there is at least one route configured, otherwise it
// logs a fatal error and exits.
//
// It then calls net.Listen() to listen on the TCP port. Any errors are returned.
//
// It spawns a new goroutine to handle serving HTTP requests via http.Serve().
// Any errors from http.Serve() are sent on the s.errors channel.
//
// The interrupts channel in VinegarWebServlet is used to gracefully shutdown the HTTP server when needed
// If VinegarWebServlet.interrupts receives a signal via VinegarWebServlet.Shutdown(), the listener is closed and the server exits.
//
// Any errors from starting the listener or serving requests are returned.
//
func (s *VinegarWebServlet) Start() error { func (s *VinegarWebServlet) Start() error {
if len(s.Router.Routes) < 1 { if len(s.Router.Routes) < 1 {
log.Fatal("No routes found for server. Nothing to listen and serve.") log.Fatal("No routes found for server. Nothing to listen and serve.")
@ -156,14 +236,69 @@ func (s *VinegarWebServlet) Start() error {
return nil return nil
} }
// Shutdown gracefully shuts down the VinegarWebServlet HTTP server.
//
// It sends a signal on the s.interrupts channel, which signals the
// server's main loop to exit and stop listening/serving.
//
// This allows cleanly shutting down the HTTP server on demand without
// having to force kill any processes.
//
//Currently, this does not wait for outstanding responses to finish.
//
// Typical usage:
//
// srv := NewServlet(":8080")
// // ...start server
//
// // Later, to shutdown
// srv.Shutdown()
//
func (s *VinegarWebServlet) Shutdown() { func (s *VinegarWebServlet) Shutdown() {
s.interrupts <- struct{}{} s.interrupts <- struct{}{}
} }
// Announce logs a message when a VinegarWebRoute is added to the router.
//
// It does not accept any parameters.
//
// It prints a log message containing the route's URL pattern string.
// This indicates a new route was added.
//
// For example:
//
// route := NewServletRoute("/users", handler)
// router.AddRoute(route)
//
// // Prints:
// // Added route for [/users]
//
func (r *VinegarWebRoute) Announce() { func (r *VinegarWebRoute) Announce() {
log.Printf("Added route for [%s]\n", r.Pattern.String()) log.Printf("Added route for [%s]\n", r.Pattern.String())
} }
// SendError sends an error response to the client with a custom status code
// and error message.
//
// It takes the following parameters:
//
// w - http.ResponseWriter to write the response to
// req - *http.Request that is causing the error
// code - int HTTP status code for the error response
// msg - string message describing the error
// aErr - error instance with more details on the error
//
// It first logs the error details.
//
// It then checks if there is a custom ErrorRoute defined for the status code.
// If so, it renders that template and writes it to the response.
//
// If not, it calls http.Error to send a generic error response.
//
// In both cases it sets the appropriate HTTP status code.
//
// This allows customizing error pages for different status codes,
// while falling back to default Go error handling.
func (s *VinegarWebServlet) SendError(w http.ResponseWriter, req *http.Request, code int, msg string, aErr error) { func (s *VinegarWebServlet) SendError(w http.ResponseWriter, req *http.Request, code int, msg string, aErr error) {
log.Printf("[%d][%s]. Rendering template for code %d with message: %s\n", code, req.URL.Path, code, msg) log.Printf("[%d][%s]. Rendering template for code %d with message: %s\n", code, req.URL.Path, code, msg)
out, _ := json.Marshal(aErr) out, _ := json.Marshal(aErr)

View File

@ -6,6 +6,18 @@ import (
) )
type ( type (
// TemplateRoute defines a template route in the router.
// It contains:
//
// VinegarWebRoute: The base VinegarWebRoute containing the URL pattern and handler.
//
// srv: The VinegarHttpServlet instance that this route is attached to.
//
// fileRoot: The base file path to serve files from for this template route.
//
// TemplateManager: The TemplateManager instance that manages templates for this route.
//
// UseCache: Whether to use caching for this template route.
TemplateRoute struct { TemplateRoute struct {
*VinegarWebRoute *VinegarWebRoute
srv *VinegarWebServlet srv *VinegarWebServlet
@ -16,6 +28,33 @@ type (
TemplateRouteHandlerFunc func(w http.ResponseWriter, r *http.Request, tm *TemplateManager) TemplateRouteHandlerFunc func(w http.ResponseWriter, r *http.Request, tm *TemplateManager)
) )
// NewTemplateRoute creates and configures a new TemplateRoute.
//
// It accepts the following parameters:
//
// servlet - The VinegarWebServlet instance to attach the route to
//
// urlPattern - The URL regex pattern that the route will match
//
// templatePath - Filepath to the template files
//
// componentPath - Filepath to template component files
//
// handler - The handler function to call for this route
//
// It does the following:
//
// - Creates a TemplateManager instance using templatePath and componentPath
//
// - Generates a default route pattern by removing wildcards from urlPattern
//
// - Creates a VinegarWebRoute using the default pattern and a handler
// that calls the provided handler function
//
// - Creates a new TemplateRoute instance configured with the VinegarWebRoute,
// servlet, TemplateManager, and other settings
//
// - Returns a pointer to the TemplateRoute
func NewTemplateRoute(servlet *VinegarWebServlet, urlPattern string, templatePath string, componentPath string, handler TemplateRouteHandlerFunc) *TemplateRoute { func NewTemplateRoute(servlet *VinegarWebServlet, urlPattern string, templatePath string, componentPath string, handler TemplateRouteHandlerFunc) *TemplateRoute {
defaultPrune := strings.Replace(urlPattern, ".*", "", -1) defaultPrune := strings.Replace(urlPattern, ".*", "", -1)
tm := NewTemplateManager(templatePath, componentPath) tm := NewTemplateManager(templatePath, componentPath)