error page upgrades
This commit is contained in:
parent
be57476a8e
commit
5ab4fd7456
|
@ -98,7 +98,28 @@ func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// Try Lua routes first
|
||||
params := &routers.Params{}
|
||||
if bytecode, scriptPath, found := s.luaRouter.GetBytecode(r.Method, r.URL.Path, params); found {
|
||||
bytecode, scriptPath, found := s.luaRouter.GetBytecode(r.Method, r.URL.Path, params)
|
||||
|
||||
// Check if we found a route but it has no valid bytecode (compile error)
|
||||
if found && (bytecode == nil || len(bytecode) == 0) {
|
||||
// Get the actual error from the router - this requires exposing the actual error
|
||||
// from the node in the GetBytecode method
|
||||
errorMsg := "Route exists but failed to compile. Check server logs for details."
|
||||
|
||||
// Get the actual node to access its error
|
||||
if node, _ := s.luaRouter.GetNodeWithError(r.Method, r.URL.Path, params); node != nil && node.Error != nil {
|
||||
errorMsg = node.Error.Error()
|
||||
}
|
||||
|
||||
s.logger.Error("%s %s - %s", r.Method, r.URL.Path, errorMsg)
|
||||
|
||||
// Show error page with the actual error message
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
errorHTML := utils.InternalErrorPage(s.errorConfig, r.URL.Path, errorMsg)
|
||||
w.Write([]byte(errorHTML))
|
||||
return
|
||||
} else if found {
|
||||
s.logger.Debug("Found Lua route match for %s %s with %d params", r.Method, r.URL.Path, params.Count)
|
||||
s.handleLuaRoute(w, r, bytecode, scriptPath, params)
|
||||
return
|
||||
|
|
26
core/routers/errors.go
Normal file
26
core/routers/errors.go
Normal file
|
@ -0,0 +1,26 @@
|
|||
package routers
|
||||
|
||||
import "errors"
|
||||
|
||||
// Common errors
|
||||
var (
|
||||
// ErrRoutesCompilationErrors indicates that some routes failed to compile
|
||||
// but the router is still operational
|
||||
ErrRoutesCompilationErrors = errors.New("some routes failed to compile")
|
||||
)
|
||||
|
||||
// RouteError represents an error with a specific route
|
||||
type RouteError struct {
|
||||
Path string // The URL path
|
||||
Method string // HTTP method
|
||||
ScriptPath string // Path to the Lua script
|
||||
Err error // The actual error
|
||||
}
|
||||
|
||||
// Error returns the error message
|
||||
func (re *RouteError) Error() string {
|
||||
if re.Err == nil {
|
||||
return "unknown route error"
|
||||
}
|
||||
return re.Err.Error()
|
||||
}
|
|
@ -17,6 +17,7 @@ const maxParams = 20
|
|||
type LuaRouter struct {
|
||||
routesDir string // Root directory containing route files
|
||||
routes map[string]*node // Method -> route tree
|
||||
failedRoutes map[string]*RouteError // Track failed routes
|
||||
mu sync.RWMutex // Lock for concurrent access to routes
|
||||
}
|
||||
|
||||
|
@ -27,6 +28,7 @@ type node struct {
|
|||
paramName string // Parameter name (if this is a parameter node)
|
||||
staticChild map[string]*node // Static children by segment name
|
||||
paramChild *node // Parameter/wildcard child
|
||||
err error // Compilation error if any
|
||||
}
|
||||
|
||||
// Params holds URL parameters with fixed-size arrays to avoid allocations
|
||||
|
@ -60,6 +62,7 @@ func NewLuaRouter(routesDir string) (*LuaRouter, error) {
|
|||
r := &LuaRouter{
|
||||
routesDir: routesDir,
|
||||
routes: make(map[string]*node),
|
||||
failedRoutes: make(map[string]*RouteError),
|
||||
}
|
||||
|
||||
// Initialize method trees
|
||||
|
@ -71,15 +74,22 @@ func NewLuaRouter(routesDir string) (*LuaRouter, error) {
|
|||
}
|
||||
|
||||
// Build routes
|
||||
if err := r.buildRoutes(); err != nil {
|
||||
return nil, err
|
||||
err = r.buildRoutes()
|
||||
|
||||
// If some routes failed to compile, return the router with a warning error
|
||||
// This allows the server to continue running with the routes that did compile
|
||||
if len(r.failedRoutes) > 0 {
|
||||
return r, ErrRoutesCompilationErrors
|
||||
}
|
||||
|
||||
return r, nil
|
||||
return r, err
|
||||
}
|
||||
|
||||
// buildRoutes scans the routes directory and builds the routing tree
|
||||
func (r *LuaRouter) buildRoutes() error {
|
||||
// Clear failed routes map
|
||||
r.failedRoutes = make(map[string]*RouteError)
|
||||
|
||||
return filepath.Walk(r.routesDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -116,8 +126,9 @@ func (r *LuaRouter) buildRoutes() error {
|
|||
urlPath = "/" + strings.ReplaceAll(relDir, "\\", "/")
|
||||
}
|
||||
|
||||
// Add route to tree
|
||||
return r.addRoute(root, urlPath, path)
|
||||
// Add route to tree - continue even if there are errors
|
||||
r.addRoute(root, urlPath, path)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -152,13 +163,24 @@ func (r *LuaRouter) addRoute(root *node, urlPath, handlerPath string) error {
|
|||
current.handler = handlerPath
|
||||
|
||||
// Compile Lua file to bytecode
|
||||
if err := r.compileHandler(current); err != nil {
|
||||
return err
|
||||
if err := r.compileHandler(current, urlPath); err != nil {
|
||||
// Track the failure but don't fail the entire process
|
||||
routeKey := getRouteKey(urlPath, handlerPath)
|
||||
r.failedRoutes[routeKey] = &RouteError{
|
||||
Path: urlPath,
|
||||
ScriptPath: handlerPath,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getRouteKey generates a unique key for a route
|
||||
func getRouteKey(path, handler string) string {
|
||||
return path + ":" + handler
|
||||
}
|
||||
|
||||
// Match finds a handler for the given method and path
|
||||
// Uses the pre-allocated params struct to avoid allocations
|
||||
func (r *LuaRouter) Match(method, path string, params *Params) (*node, bool) {
|
||||
|
@ -222,7 +244,7 @@ func (r *LuaRouter) matchPath(current *node, segments []string, params *Params,
|
|||
}
|
||||
|
||||
// compileHandler compiles a Lua file to bytecode
|
||||
func (r *LuaRouter) compileHandler(n *node) error {
|
||||
func (r *LuaRouter) compileHandler(n *node, urlPath string) error {
|
||||
if n.handler == "" {
|
||||
return nil
|
||||
}
|
||||
|
@ -230,32 +252,44 @@ func (r *LuaRouter) compileHandler(n *node) error {
|
|||
// Read the Lua file
|
||||
content, err := os.ReadFile(n.handler)
|
||||
if err != nil {
|
||||
n.err = err // Store the error in the node
|
||||
return err
|
||||
}
|
||||
|
||||
// Compile to bytecode
|
||||
state := luajit.New()
|
||||
if state == nil {
|
||||
return errors.New("failed to create Lua state")
|
||||
compileErr := errors.New("failed to create Lua state")
|
||||
n.err = compileErr // Store the error in the node
|
||||
return compileErr
|
||||
}
|
||||
defer state.Close()
|
||||
|
||||
bytecode, err := state.CompileBytecode(string(content), n.handler)
|
||||
if err != nil {
|
||||
n.err = err // Store the error in the node
|
||||
return err
|
||||
}
|
||||
|
||||
// Store bytecode in the node
|
||||
n.bytecode = bytecode
|
||||
n.err = nil // Clear any previous error
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetBytecode returns the compiled bytecode for a matched route
|
||||
// If a route exists but failed to compile, returns nil bytecode with found=true
|
||||
func (r *LuaRouter) GetBytecode(method, path string, params *Params) ([]byte, string, bool) {
|
||||
node, found := r.Match(method, path, params)
|
||||
if !found {
|
||||
return nil, "", false
|
||||
}
|
||||
|
||||
// If the route exists but has a compilation error
|
||||
if node.err != nil {
|
||||
return nil, node.handler, true
|
||||
}
|
||||
|
||||
return node.bytecode, node.handler, true
|
||||
}
|
||||
|
||||
|
@ -271,6 +305,47 @@ func (r *LuaRouter) Refresh() error {
|
|||
}
|
||||
}
|
||||
|
||||
// Clear failed routes
|
||||
r.failedRoutes = make(map[string]*RouteError)
|
||||
|
||||
// Rebuild routes
|
||||
return r.buildRoutes()
|
||||
err := r.buildRoutes()
|
||||
|
||||
// If some routes failed to compile, return a warning error
|
||||
if len(r.failedRoutes) > 0 {
|
||||
return ErrRoutesCompilationErrors
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// ReportFailedRoutes returns a list of routes that failed to compile
|
||||
func (r *LuaRouter) ReportFailedRoutes() []*RouteError {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
result := make([]*RouteError, 0, len(r.failedRoutes))
|
||||
for _, re := range r.failedRoutes {
|
||||
result = append(result, re)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
type NodeWithError struct {
|
||||
ScriptPath string
|
||||
Error error
|
||||
}
|
||||
|
||||
// GetNodeWithError returns the node with its error for a given path
|
||||
func (r *LuaRouter) GetNodeWithError(method, path string, params *Params) (*NodeWithError, bool) {
|
||||
node, found := r.Match(method, path, params)
|
||||
if !found {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return &NodeWithError{
|
||||
ScriptPath: node.handler,
|
||||
Error: node.err,
|
||||
}, true
|
||||
}
|
||||
|
|
|
@ -89,11 +89,7 @@ func generateInternalErrorHTML(debugMode bool, url string, errMsg string) string
|
|||
}
|
||||
|
||||
randomMessage := errorMessages[rand.Intn(len(errorMessages))]
|
||||
codeContent := url
|
||||
if errMsg != "" {
|
||||
codeContent = url + " → " + errMsg
|
||||
}
|
||||
return generateErrorHTML("500", randomMessage, "Internal Server Error", debugMode, codeContent)
|
||||
return generateErrorHTML("500", randomMessage, "Internal Server Error", debugMode, errMsg)
|
||||
}
|
||||
|
||||
// generateNotFoundHTML creates a 404 Not Found error page
|
||||
|
|
15
moonshark.go
15
moonshark.go
|
@ -2,6 +2,7 @@ package main
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
|
@ -37,8 +38,22 @@ func initRouters(routesDir, staticDir string, log *logger.Logger) (*routers.LuaR
|
|||
// Initialize Lua router for dynamic routes
|
||||
luaRouter, err := routers.NewLuaRouter(routesDir)
|
||||
if err != nil {
|
||||
// Check if this is a compilation warning or a more serious error
|
||||
if errors.Is(err, routers.ErrRoutesCompilationErrors) {
|
||||
// Some routes failed to compile, but router is still usable
|
||||
log.Warning("Some Lua routes failed to compile. Check logs for details.")
|
||||
|
||||
// Log details about each failed route
|
||||
if failedRoutes := luaRouter.ReportFailedRoutes(); len(failedRoutes) > 0 {
|
||||
for _, re := range failedRoutes {
|
||||
log.Error("Route %s %s failed to compile: %v", re.Method, re.Path, re.Err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// More serious error that prevents router initialization
|
||||
return nil, nil, fmt.Errorf("failed to initialize Lua router: %v", err)
|
||||
}
|
||||
}
|
||||
log.Info("Lua router initialized with routes from %s", routesDir)
|
||||
|
||||
// Initialize static file router
|
||||
|
|
Loading…
Reference in New Issue
Block a user