From 42d2e61c5db92cb4f07e89ea312377662028280d Mon Sep 17 00:00:00 2001 From: dtookey Date: Tue, 1 Aug 2023 16:06:43 -0400 Subject: [PATCH] more docs --- servlet/Router.go | 75 ++++++++++++++++++++++ servlet/server.go | 135 +++++++++++++++++++++++++++++++++++++++ servlet/templateRoute.go | 39 +++++++++++ 3 files changed, 249 insertions(+) diff --git a/servlet/Router.go b/servlet/Router.go index 1be9ec7..29392a3 100644 --- a/servlet/Router.go +++ b/servlet/Router.go @@ -10,6 +10,30 @@ import ( ) 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 { 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) { route.Announce() 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 { path := req.URL.Path 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 + "]") } +// 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) { w.WriteHeader(http.StatusNotFound) diff --git a/servlet/server.go b/servlet/server.go index a38b4d4..da618c6 100644 --- a/servlet/server.go +++ b/servlet/server.go @@ -94,6 +94,20 @@ type ( 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 { errs := make(map[int]*TemplateRoute) srv := VinegarWebServlet{Port: port, ErrorRoutes: errs, interrupts: make(chan struct{}, 500)} @@ -101,6 +115,21 @@ func NewServlet(port string) *VinegarWebServlet { 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 { pattern := regexp.MustCompile(routePattern) @@ -108,11 +137,47 @@ func NewServletRoute(routePattern string, handleFunc VinegarHandlerFunction) *Vi 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) { route.Announce() 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) { err := s.Router.RouteRequest(w, req) 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 { if len(s.Router.Routes) < 1 { log.Fatal("No routes found for server. Nothing to listen and serve.") @@ -156,14 +236,69 @@ func (s *VinegarWebServlet) Start() error { 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() { 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() { 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) { log.Printf("[%d][%s]. Rendering template for code %d with message: %s\n", code, req.URL.Path, code, msg) out, _ := json.Marshal(aErr) diff --git a/servlet/templateRoute.go b/servlet/templateRoute.go index 3af19ad..a1ad3db 100644 --- a/servlet/templateRoute.go +++ b/servlet/templateRoute.go @@ -6,6 +6,18 @@ import ( ) 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 { *VinegarWebRoute srv *VinegarWebServlet @@ -16,6 +28,33 @@ type ( 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 { defaultPrune := strings.Replace(urlPattern, ".*", "", -1) tm := NewTemplateManager(templatePath, componentPath)