middleware 1

This commit is contained in:
Sky Johnson 2025-05-24 18:29:39 -05:00
parent ac607213fc
commit e548849f88
4 changed files with 316 additions and 118 deletions

View File

@ -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

View File

@ -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 := ""

View File

@ -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
}

View File

@ -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++ {