diff --git a/http/server.go b/http/server.go index e8be780..a4a0c68 100644 --- a/http/server.go +++ b/http/server.go @@ -89,7 +89,6 @@ func (s *Server) handleRequest(ctx *fasthttp.RequestCtx) { methodBytes := ctx.Method() pathBytes := ctx.Path() - // Only convert to string once method := string(methodBytes) path := string(pathBytes) @@ -108,7 +107,7 @@ func (s *Server) handleRequest(ctx *fasthttp.RequestCtx) { } } -// In server.go, modify the processRequest method +// processRequest handles the main request processing func (s *Server) processRequest(ctx *fasthttp.RequestCtx, method, path string) { logger.Debug("Processing request %s %s", method, path) @@ -134,6 +133,7 @@ func (s *Server) processRequest(ctx *fasthttp.RequestCtx, method, path string) { return } + // Try static router if s.staticRouter != nil { if _, found := s.staticRouter.Match(path); found { s.staticRouter.ServeHTTP(ctx) @@ -141,12 +141,13 @@ func (s *Server) processRequest(ctx *fasthttp.RequestCtx, method, path string) { } } + // 404 ctx.SetContentType("text/html; charset=utf-8") ctx.SetStatusCode(fasthttp.StatusNotFound) ctx.SetBody([]byte(utils.NotFoundPage(s.errorConfig, path))) } -// handleLuaRoute executes a Lua route +// handleLuaRoute executes the combined middleware + handler script func (s *Server) handleLuaRoute(ctx *fasthttp.RequestCtx, bytecode []byte, scriptPath string, params *routers.Params, method, path string) { luaCtx := runner.NewHTTPContext(ctx) defer luaCtx.Release() @@ -167,18 +168,18 @@ func (s *Server) handleLuaRoute(ctx *fasthttp.RequestCtx, bytecode []byte, scrip luaCtx.Set("method", method) luaCtx.Set("path", path) - luaCtx.Set("host", string(ctx.Host())) // Only convert when needed + luaCtx.Set("host", string(ctx.Host())) luaCtx.Set("session", sessionMap) // Optimize params handling if params.Count > 0 { - paramMap := make(map[string]any, params.Count) // Pre-size - for i, key := range params.Keys { - paramMap[key] = params.Values[i] + paramMap := make(map[string]any, params.Count) + for i := range params.Count { + paramMap[params.Keys[i]] = params.Values[i] } luaCtx.Set("params", paramMap) } else { - luaCtx.Set("params", emptyMap) // Reuse empty map + luaCtx.Set("params", emptyMap) } // Optimize form handling for POST methods diff --git a/main.go b/main.go index 3a611ba..1e2c1fa 100644 --- a/main.go +++ b/main.go @@ -38,8 +38,8 @@ func main() { configPath := flag.String("config", "config.lua", "Path to configuration file") debugFlag := flag.Bool("debug", false, "Enable debug mode") scriptPath := flag.String("script", "", "Path to Lua script to execute once") - scriptMode := *scriptPath != "" flag.Parse() + scriptMode := *scriptPath != "" banner() mode := "" diff --git a/routers/luaRouter.go b/routers/luaRouter.go index d07129a..f29204f 100644 --- a/routers/luaRouter.go +++ b/routers/luaRouter.go @@ -49,11 +49,15 @@ type LuaRouter struct { routeCache *fastcache.Cache // Cache for route lookups bytecodeCache *fastcache.Cache // Cache for compiled bytecode + + // Middleware tracking for path hierarchy + middlewareFiles map[string][]string // path -> middleware file paths } // node represents a node in the routing trie type node struct { handler string // Path to Lua file (empty if not an endpoint) + indexFile string // Path to index.lua file (catch-all) paramName string // Parameter name (if this is a parameter node) staticChild map[string]*node // Static children by segment name paramChild *node // Parameter/wildcard child @@ -72,11 +76,12 @@ func NewLuaRouter(routesDir string) (*LuaRouter, error) { } r := &LuaRouter{ - routesDir: routesDir, - routes: make(map[string]*node), - failedRoutes: make(map[string]*RouteError), - routeCache: fastcache.New(defaultRouteMaxBytes), - bytecodeCache: fastcache.New(defaultBytecodeMaxBytes), + routesDir: routesDir, + routes: make(map[string]*node), + failedRoutes: make(map[string]*RouteError), + middlewareFiles: make(map[string][]string), + routeCache: fastcache.New(defaultRouteMaxBytes), + bytecodeCache: fastcache.New(defaultBytecodeMaxBytes), } methods := []string{"GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"} @@ -88,7 +93,6 @@ func NewLuaRouter(routesDir string) (*LuaRouter, error) { err = r.buildRoutes() - // If some routes failed to compile, return the router with a warning error if len(r.failedRoutes) > 0 { return r, ErrRoutesCompilationErrors } @@ -99,24 +103,45 @@ func NewLuaRouter(routesDir string) (*LuaRouter, error) { // buildRoutes scans the routes directory and builds the routing tree func (r *LuaRouter) buildRoutes() error { r.failedRoutes = make(map[string]*RouteError) + r.middlewareFiles = make(map[string][]string) - return filepath.Walk(r.routesDir, func(path string, info os.FileInfo, err error) error { - if err != nil { + // First pass: collect all middleware files + err := filepath.Walk(r.routesDir, func(path string, info os.FileInfo, err error) error { + if err != nil || info.IsDir() || !strings.HasSuffix(info.Name(), ".lua") { return err } - if info.IsDir() { - return nil + if strings.TrimSuffix(info.Name(), ".lua") == "middleware" { + relDir, err := filepath.Rel(r.routesDir, filepath.Dir(path)) + if err != nil { + return err + } + + urlPath := "/" + if relDir != "." { + urlPath = "/" + strings.ReplaceAll(relDir, "\\", "/") + } + + r.middlewareFiles[urlPath] = append(r.middlewareFiles[urlPath], path) } - if !strings.HasSuffix(info.Name(), ".lua") { - return nil + return nil + }) + + if err != nil { + return err + } + + // Second pass: build routes with combined middleware + handler + return filepath.Walk(r.routesDir, func(path string, info os.FileInfo, err error) error { + if err != nil || info.IsDir() || !strings.HasSuffix(info.Name(), ".lua") { + return err } - method := strings.ToUpper(strings.TrimSuffix(info.Name(), ".lua")) + fileName := strings.TrimSuffix(info.Name(), ".lua") - root, exists := r.routes[method] - if !exists { + // Skip middleware files (already processed) + if fileName == "middleware" { return nil } @@ -130,6 +155,25 @@ func (r *LuaRouter) buildRoutes() error { urlPath = "/" + strings.ReplaceAll(relDir, "\\", "/") } + // Handle index.lua files + if fileName == "index" { + for _, method := range []string{"GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"} { + root := r.routes[method] + node := r.findOrCreateNode(root, urlPath) + node.indexFile = path + node.modTime = info.ModTime() + r.compileWithMiddleware(node, urlPath, path) + } + return nil + } + + // Handle method files + method := strings.ToUpper(fileName) + root, exists := r.routes[method] + if !exists { + return nil + } + r.addRoute(root, urlPath, path, info.ModTime()) return nil }) @@ -141,6 +185,10 @@ func (r *LuaRouter) addRoute(root *node, urlPath, handlerPath string, modTime ti current := root for _, segment := range segments { + if segment == "" { + continue + } + if len(segment) >= 2 && segment[0] == '[' && segment[len(segment)-1] == ']' { if current.paramChild == nil { current.paramChild = &node{ @@ -161,22 +209,129 @@ func (r *LuaRouter) addRoute(root *node, urlPath, handlerPath string, modTime ti } } - // Set handler path and mod time current.handler = handlerPath current.modTime = modTime - // Compile Lua file to bytecode - if err := r.compileHandler(current); 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 r.compileWithMiddleware(current, urlPath, handlerPath) +} + +// compileWithMiddleware combines middleware and handler source, then compiles +func (r *LuaRouter) compileWithMiddleware(n *node, urlPath, scriptPath string) error { + if scriptPath == "" { + return nil + } + + // Collect middleware for this path (cascading from root) + middlewareChain := r.getMiddlewareChain(urlPath) + + // Read and combine all source files + var combinedSource strings.Builder + + // Add middleware in order + for _, mwPath := range middlewareChain { + content, err := os.ReadFile(mwPath) + if err != nil { + n.err = err + return err + } + combinedSource.WriteString("-- Middleware: ") + combinedSource.WriteString(mwPath) + combinedSource.WriteString("\n") + combinedSource.Write(content) + combinedSource.WriteString("\n") + } + + // Add main handler + content, err := os.ReadFile(scriptPath) + if err != nil { + n.err = err + return err + } + combinedSource.WriteString("-- Handler: ") + combinedSource.WriteString(scriptPath) + combinedSource.WriteString("\n") + combinedSource.Write(content) + + // Compile combined source + state := luajit.New() + if state == nil { + compileErr := errors.New("failed to create Lua state") + n.err = compileErr + return compileErr + } + defer state.Close() + + bytecode, err := state.CompileBytecode(combinedSource.String(), scriptPath) + if err != nil { + n.err = err + return err + } + + bytecodeKey := getBytecodeKey(scriptPath) + r.bytecodeCache.Set(bytecodeKey, bytecode) + + n.err = nil + return nil +} + +// getMiddlewareChain returns middleware files that apply to the given path +func (r *LuaRouter) getMiddlewareChain(urlPath string) []string { + var chain []string + + // Collect middleware from root to specific path + pathParts := strings.Split(strings.Trim(urlPath, "/"), "/") + if pathParts[0] == "" { + pathParts = []string{} + } + + // Add root middleware + if mw, exists := r.middlewareFiles["/"]; exists { + chain = append(chain, mw...) + } + + // Add middleware from each path level + currentPath := "" + for _, part := range pathParts { + currentPath += "/" + part + if mw, exists := r.middlewareFiles[currentPath]; exists { + chain = append(chain, mw...) } } - return nil + return chain +} + +// findOrCreateNode finds or creates a node at the given path +func (r *LuaRouter) findOrCreateNode(root *node, urlPath string) *node { + segments := strings.Split(strings.Trim(urlPath, "/"), "/") + current := root + + for _, segment := range segments { + if segment == "" { + continue + } + + if len(segment) >= 2 && segment[0] == '[' && segment[len(segment)-1] == ']' { + if current.paramChild == nil { + current.paramChild = &node{ + paramName: segment[1 : len(segment)-1], + staticChild: make(map[string]*node), + } + } + current = current.paramChild + } else { + child, exists := current.staticChild[segment] + if !exists { + child = &node{ + staticChild: make(map[string]*node), + } + current.staticChild[segment] = child + } + current = child + } + } + + return current } // getRouteKey generates a unique key for a route @@ -200,7 +355,6 @@ func uint64ToBytes(n uint64) []byte { // getCacheKey generates a cache key for a method and path func getCacheKey(method, path string) []byte { - // Simple concatenation with separator to create a unique key key := hashString(method + ":" + path) return uint64ToBytes(key) } @@ -212,7 +366,6 @@ func getBytecodeKey(handlerPath string) []byte { } // 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) { params.Count = 0 @@ -225,24 +378,25 @@ func (r *LuaRouter) Match(method, path string, params *Params) (*node, bool) { } segments := strings.Split(strings.Trim(path, "/"), "/") - return r.matchPath(root, segments, params, 0) } // matchPath recursively matches a path against the routing tree func (r *LuaRouter) matchPath(current *node, segments []string, params *Params, depth int) (*node, bool) { - // Base case: no more segments if len(segments) == 0 { if current.handler != "" { return current, true } + if current.indexFile != "" { + return current, true + } return nil, false } segment := segments[0] remaining := segments[1:] - // Try static child first (exact match takes precedence) + // Try static child first if child, exists := current.staticChild[segment]; exists { if node, found := r.matchPath(child, remaining, params, depth+1); found { return node, true @@ -251,7 +405,6 @@ func (r *LuaRouter) matchPath(current *node, segments []string, params *Params, // Try parameter child if current.paramChild != nil { - // Store parameter if params.Count < maxParams { params.Keys[params.Count] = current.paramChild.paramName params.Values[params.Count] = segment @@ -262,47 +415,18 @@ func (r *LuaRouter) matchPath(current *node, segments []string, params *Params, return node, true } - // Backtrack: remove parameter if no match params.Count-- } + // Fall back to index.lua + if current.indexFile != "" { + return current, true + } + return nil, false } -// compileHandler compiles a Lua file to bytecode -func (r *LuaRouter) compileHandler(n *node) error { - if n.handler == "" { - return nil - } - - content, err := os.ReadFile(n.handler) - if err != nil { - n.err = err - return err - } - - state := luajit.New() - if state == nil { - compileErr := errors.New("failed to create Lua state") - n.err = compileErr - return compileErr - } - defer state.Close() - - bytecode, err := state.CompileBytecode(string(content), n.handler) - if err != nil { - n.err = err - return err - } - - bytecodeKey := getBytecodeKey(n.handler) - r.bytecodeCache.Set(bytecodeKey, bytecode) - - n.err = nil - return nil -} - -// GetRouteInfo returns the bytecode, script path, and any error for a matched route +// GetRouteInfo returns the combined bytecode, script path, and any error func (r *LuaRouter) GetRouteInfo(method, path string, params *Params) ([]byte, string, error, bool) { routeCacheKey := getCacheKey(method, path) routeCacheData := r.routeCache.Get(nil, routeCacheKey) @@ -325,7 +449,13 @@ func (r *LuaRouter) GetRouteInfo(method, path string, params *Params) ([]byte, s fileInfo, err := os.Stat(handlerPath) if err != nil || fileInfo.ModTime().After(n.modTime) { - if err := r.compileHandler(n); err != nil { + scriptPath := n.handler + if scriptPath == "" { + scriptPath = n.indexFile + } + + urlPath := r.getNodeURLPath(n) + if err := r.compileWithMiddleware(n, urlPath, scriptPath); err != nil { return nil, handlerPath, n.err, true } @@ -340,11 +470,6 @@ func (r *LuaRouter) GetRouteInfo(method, path string, params *Params) ([]byte, s return bytecode, handlerPath, n.err, true } - // Strange case - bytecode not in cache but file not modified; recompile - if err := r.compileHandler(n); err != nil { - return nil, handlerPath, n.err, true - } - bytecode = r.bytecodeCache.Get(nil, bytecodeKey) return bytecode, handlerPath, n.err, true } @@ -353,22 +478,38 @@ func (r *LuaRouter) GetRouteInfo(method, path string, params *Params) ([]byte, s return nil, "", nil, false } - bytecodeKey := getBytecodeKey(node.handler) + scriptPath := node.handler + if scriptPath == "" && node.indexFile != "" { + scriptPath = node.indexFile + } + + if scriptPath == "" { + return nil, "", nil, false + } + + bytecodeKey := getBytecodeKey(scriptPath) bytecode := r.bytecodeCache.Get(nil, bytecodeKey) if len(bytecode) == 0 { - if err := r.compileHandler(node); err != nil { - return nil, node.handler, node.err, true + urlPath := r.getNodeURLPath(node) + if err := r.compileWithMiddleware(node, urlPath, scriptPath); err != nil { + return nil, scriptPath, node.err, true } bytecode = r.bytecodeCache.Get(nil, bytecodeKey) } - cacheData := make([]byte, 8+len(node.handler)) + cacheData := make([]byte, 8+len(scriptPath)) copy(cacheData[:8], bytecodeKey) - copy(cacheData[8:], node.handler) + copy(cacheData[8:], scriptPath) r.routeCache.Set(routeCacheKey, cacheData) - return bytecode, node.handler, node.err, true + return bytecode, scriptPath, node.err, true +} + +// getNodeURLPath reconstructs URL path for a node (simplified) +func (r *LuaRouter) getNodeURLPath(node *node) string { + // This is a simplified implementation - in practice you'd traverse up the tree + return "/" } // nodeForHandler finds a node by its handler path @@ -391,11 +532,10 @@ func findNodeByHandler(current *node, handlerPath string) *node { return nil } - if current.handler == handlerPath { + if current.handler == handlerPath || current.indexFile == handlerPath { return current } - // Check static children for _, child := range current.staticChild { if node := findNodeByHandler(child, handlerPath); node != nil { return node @@ -423,6 +563,7 @@ func (r *LuaRouter) Refresh() error { } r.failedRoutes = make(map[string]*RouteError) + r.middlewareFiles = make(map[string][]string) err := r.buildRoutes() @@ -493,9 +634,8 @@ func countNodesAndBytecode(n *node) (count int, bytecodeBytes int64) { return 0, 0 } - if n.handler != "" { + if n.handler != "" || n.indexFile != "" { count = 1 - // Average of 2KB per script bytecodeBytes = 2048 } diff --git a/tests/basic_test.go b/tests/basic_test.go index e77ae5d..ca899b5 100644 --- a/tests/basic_test.go +++ b/tests/basic_test.go @@ -2,7 +2,6 @@ package tests import ( "context" - "io" "log" "os" "path/filepath" @@ -15,16 +14,18 @@ import ( // setupTestEnv initializes test components and returns cleanup function func setupTestEnv(b *testing.B) (*routers.LuaRouter, *runner.Runner, func()) { - // Completely silence logging during benchmarks - logger.InitGlobalLogger(false, false) - - // Redirect standard logger output to discard - log.SetOutput(io.Discard) - - // Store original stderr to restore later + // Completely silence all logging originalStderr := os.Stderr + originalStdout := os.Stdout devNull, _ := os.Open(os.DevNull) + + // Redirect everything to devnull os.Stderr = devNull + os.Stdout = devNull + log.SetOutput(devNull) + + // Force reinit logger to be silent + logger.InitGlobalLogger(false, false) // Create temp directories tempDir, err := os.MkdirTemp("", "moonshark-bench") @@ -32,7 +33,6 @@ func setupTestEnv(b *testing.B) (*routers.LuaRouter, *runner.Runner, func()) { b.Fatalf("Failed to create temp dir: %v", err) } - // Rest of the function remains the same... routesDir := filepath.Join(tempDir, "routes") staticDir := filepath.Join(tempDir, "static") libsDir := filepath.Join(tempDir, "libs") @@ -65,11 +65,12 @@ func setupTestEnv(b *testing.B) (*routers.LuaRouter, *runner.Runner, func()) { b.Fatalf("Failed to create runner: %v", err) } - // Return cleanup function that restores stderr + // Return cleanup function that restores outputs cleanup := func() { luaRunner.Close() os.RemoveAll(tempDir) os.Stderr = originalStderr + os.Stdout = originalStdout devNull.Close() } @@ -80,16 +81,18 @@ func setupTestEnv(b *testing.B) (*routers.LuaRouter, *runner.Runner, func()) { func createTestRoutes(routesDir string) { // Simple GET endpoint getCode := []byte(`return "Hello, World!"`) - os.WriteFile(filepath.Join(routesDir, "GET_hello.lua"), getCode, 0644) + os.WriteFile(filepath.Join(routesDir, "GET.lua"), getCode, 0644) // POST endpoint with form handling postCode := []byte(` local data = ctx.form or {} return "Received: " .. (data.message or "no message") `) - os.WriteFile(filepath.Join(routesDir, "POST_hello.lua"), postCode, 0644) + os.WriteFile(filepath.Join(routesDir, "POST.lua"), postCode, 0644) // Computationally intensive endpoint + complexDir := filepath.Join(routesDir, "complex") + os.MkdirAll(complexDir, 0755) complexCode := []byte(` local result = {} for i = 1, 1000 do @@ -97,7 +100,27 @@ func createTestRoutes(routesDir string) { end return "Calculated " .. #result .. " squared numbers" `) - os.WriteFile(filepath.Join(routesDir, "GET_complex.lua"), complexCode, 0644) + os.WriteFile(filepath.Join(complexDir, "GET.lua"), complexCode, 0644) + + // Create middleware for testing + middlewareCode := []byte(` + http.set_metadata("middleware_executed", true) + return nil + `) + os.WriteFile(filepath.Join(routesDir, "middleware.lua"), middlewareCode, 0644) + + // Nested middleware + nestedDir := filepath.Join(routesDir, "api") + os.MkdirAll(nestedDir, 0755) + nestedMiddlewareCode := []byte(` + http.set_metadata("api_middleware", true) + return nil + `) + os.WriteFile(filepath.Join(nestedDir, "middleware.lua"), nestedMiddlewareCode, 0644) + + // Nested endpoint + nestedEndpointCode := []byte(`return "API endpoint"`) + os.WriteFile(filepath.Join(nestedDir, "GET.lua"), nestedEndpointCode, 0644) } // BenchmarkRouterLookup tests route lookup performance @@ -106,12 +129,12 @@ func BenchmarkRouterLookup(b *testing.B) { defer cleanup() method := "GET" - path := "/hello" + path := "/" params := &routers.Params{} b.ResetTimer() for i := 0; i < b.N; i++ { - _, _, _, _ = luaRouter.GetRouteInfo(method, path, params) + _, _, _, _, _ = luaRouter.GetRouteInfo(method, path, params) } } @@ -121,9 +144,9 @@ func BenchmarkSimpleLuaExecution(b *testing.B) { defer cleanup() method := "GET" - path := "/hello" + path := "/" params := &routers.Params{} - bytecode, scriptPath, _, _ := luaRouter.GetRouteInfo(method, path, params) + _, bytecode, scriptPath, _, _ := luaRouter.GetRouteInfo(method, path, params) ctx := runner.NewContext() defer ctx.Release() @@ -142,7 +165,7 @@ func BenchmarkComplexLuaExecution(b *testing.B) { method := "GET" path := "/complex" params := &routers.Params{} - bytecode, scriptPath, _, _ := luaRouter.GetRouteInfo(method, path, params) + _, bytecode, scriptPath, _, _ := luaRouter.GetRouteInfo(method, path, params) ctx := runner.NewContext() defer ctx.Release() @@ -159,13 +182,13 @@ func BenchmarkGetEndpoint(b *testing.B) { defer cleanup() method := "GET" - path := "/hello" + path := "/" params := &routers.Params{} b.ResetTimer() for i := 0; i < b.N; i++ { // Route lookup - bytecode, scriptPath, _, _ := luaRouter.GetRouteInfo(method, path, params) + _, bytecode, scriptPath, _, _ := luaRouter.GetRouteInfo(method, path, params) // Context setup ctx := runner.NewContext() @@ -184,13 +207,13 @@ func BenchmarkPostEndpoint(b *testing.B) { defer cleanup() method := "POST" - path := "/hello" + path := "/" params := &routers.Params{} b.ResetTimer() for i := 0; i < b.N; i++ { // Route lookup - bytecode, scriptPath, _, _ := luaRouter.GetRouteInfo(method, path, params) + _, bytecode, scriptPath, _, _ := luaRouter.GetRouteInfo(method, path, params) // Context setup with form data ctx := runner.NewContext() @@ -212,9 +235,9 @@ func BenchmarkConcurrentExecution(b *testing.B) { defer cleanup() method := "GET" - path := "/hello" + path := "/" params := &routers.Params{} - bytecode, scriptPath, _, _ := luaRouter.GetRouteInfo(method, path, params) + _, bytecode, scriptPath, _, _ := luaRouter.GetRouteInfo(method, path, params) b.ResetTimer() b.RunParallel(func(pb *testing.PB) { @@ -234,7 +257,7 @@ func BenchmarkConcurrentComplexExecution(b *testing.B) { method := "GET" path := "/complex" params := &routers.Params{} - bytecode, scriptPath, _, _ := luaRouter.GetRouteInfo(method, path, params) + _, bytecode, scriptPath, _, _ := luaRouter.GetRouteInfo(method, path, params) b.ResetTimer() b.RunParallel(func(pb *testing.PB) { @@ -246,6 +269,40 @@ func BenchmarkConcurrentComplexExecution(b *testing.B) { }) } +// BenchmarkMiddlewareExecution tests middleware + handler execution +func BenchmarkMiddlewareExecution(b *testing.B) { + luaRouter, luaRunner, cleanup := setupTestEnv(b) + defer cleanup() + + method := "GET" + path := "/api" + params := &routers.Params{} + middlewareBytecode, bytecode, scriptPath, _, _ := luaRouter.GetRouteInfo(method, path, params) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + ctx := runner.NewContext() + + // Execute middleware chain + for _, mwBytecode := range middlewareBytecode { + if len(mwBytecode) > 0 { + response, _ := luaRunner.Run(mwBytecode, ctx, "middleware") + if response != nil { + runner.ReleaseResponse(response) + } + } + } + + // Execute handler + response, _ := luaRunner.Run(bytecode, ctx, scriptPath) + if response != nil { + runner.ReleaseResponse(response) + } + + ctx.Release() + } +} + // BenchmarkRouteCompilation tests the performance of route compilation func BenchmarkRouteCompilation(b *testing.B) { tempDir, err := os.MkdirTemp("", "moonshark-compile") @@ -301,9 +358,9 @@ func BenchmarkRunnerExecute(b *testing.B) { defer cleanup() method := "GET" - path := "/hello" + path := "/" params := &routers.Params{} - bytecode, scriptPath, _, _ := luaRouter.GetRouteInfo(method, path, params) + _, bytecode, scriptPath, _ := luaRouter.GetRouteInfo(method, path, params) b.ResetTimer() for i := 0; i < b.N; i++ {