more docs
This commit is contained in:
parent
1748b65cad
commit
42d2e61c5d
@ -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)
|
||||||
|
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user