From 12ba756b958bd5b3fc4f3fd9eca8b51b1693972e Mon Sep 17 00:00:00 2001 From: Sky Johnson Date: Thu, 17 Jul 2025 19:11:01 -0500 Subject: [PATCH] use fasthttp static file serving --- modules/http/http.go | 73 ++++++++++++++++++++++++++++++++++++++++--- modules/http/http.lua | 52 +++++++----------------------- 2 files changed, 79 insertions(+), 46 deletions(-) diff --git a/modules/http/http.go b/modules/http/http.go index 7647a30..78cc5b7 100644 --- a/modules/http/http.go +++ b/modules/http/http.go @@ -4,7 +4,9 @@ import ( "context" "fmt" "net" + "path/filepath" "runtime" + "strings" "sync" "time" @@ -18,6 +20,8 @@ var ( globalStateCreator StateCreator globalMu sync.RWMutex serverRunning bool + staticHandlers = make(map[string]*fasthttp.FS) + staticMu sync.RWMutex ) func SetStateCreator(creator StateCreator) { @@ -26,11 +30,12 @@ func SetStateCreator(creator StateCreator) { func GetFunctionList() map[string]luajit.GoFunction { return map[string]luajit.GoFunction{ - "http_create_server": http_create_server, - "http_spawn_workers": http_spawn_workers, - "http_listen": http_listen, - "http_close_server": http_close_server, - "http_has_servers": http_has_servers, + "http_create_server": http_create_server, + "http_spawn_workers": http_spawn_workers, + "http_listen": http_listen, + "http_close_server": http_close_server, + "http_has_servers": http_has_servers, + "http_register_static": http_register_static, } } @@ -162,6 +167,32 @@ func http_has_servers(s *luajit.State) int { return 1 } +func http_register_static(s *luajit.State) int { + if err := s.CheckMinArgs(2); err != nil { + return s.PushError("http_register_static: %v", err) + } + + urlPrefix := s.ToString(1) + rootPath := s.ToString(2) + + // Ensure prefix starts with / + if !strings.HasPrefix(urlPrefix, "/") { + urlPrefix = "/" + urlPrefix + } + + // Convert to absolute path + absPath, err := filepath.Abs(rootPath) + if err != nil { + s.PushBoolean(false) + s.PushString(fmt.Sprintf("invalid path: %v", err)) + return 2 + } + + RegisterStaticHandler(urlPrefix, absPath) + s.PushBoolean(true) + return 1 +} + func HasActiveServers() bool { globalMu.RLock() defer globalMu.RUnlock() @@ -175,6 +206,22 @@ func WaitForServers() { } func handleRequest(ctx *fasthttp.RequestCtx) { + path := string(ctx.Path()) + + // Check static handlers first + staticMu.RLock() + for prefix, fs := range staticHandlers { + if strings.HasPrefix(path, prefix) { + staticMu.RUnlock() + // Remove prefix and serve + ctx.Request.URI().SetPath(strings.TrimPrefix(path, prefix)) + fs.NewRequestHandler()(ctx) + return + } + } + staticMu.RUnlock() + + // Fall back to Lua handling globalMu.RLock() pool := globalWorkerPool globalMu.RUnlock() @@ -230,3 +277,19 @@ func handleRequest(ctx *fasthttp.RequestCtx) { ctx.SetBodyString(resp.Body) } } + +// RegisterStaticHandler adds a static file handler +func RegisterStaticHandler(urlPrefix, rootPath string) { + staticMu.Lock() + defer staticMu.Unlock() + + fs := &fasthttp.FS{ + Root: rootPath, + IndexNames: []string{"index.html"}, + GenerateIndexPages: false, + Compress: true, + AcceptByteRange: true, + } + + staticHandlers[urlPrefix] = fs +} diff --git a/modules/http/http.lua b/modules/http/http.lua index 214a2c9..39271c8 100644 --- a/modules/http/http.lua +++ b/modules/http/http.lua @@ -523,48 +523,18 @@ function http.cors(options) end end -function http.static(root_path) +function http.static(root_path, url_prefix) + url_prefix = url_prefix or "/" + + if not _G.__IS_WORKER then + local success, err = moonshark.http_register_static(url_prefix, root_path) + if not success then + error("Failed to register static handler: " .. (err or "unknown error")) + end + end + + -- Return no-op middleware return function(req, res, next) - if req.method ~= "GET" and req.method ~= "HEAD" then - next() - return - end - - local file_path = moonshark.path_join(root_path, req.path) - file_path = moonshark.path_clean(file_path) - - local abs_root = moonshark.path_abs(root_path) - local abs_file = moonshark.path_abs(file_path) - - if not abs_file or not abs_file:find("^" .. abs_root:gsub("([%(%)%.%+%-%*%?%[%]%^%$%%])", "%%%1")) then - next() - return - end - - if moonshark.file_exists(file_path) and not moonshark.file_is_dir(file_path) then - local content = moonshark.file_read(file_path) - if content then - local ext = moonshark.path_ext(file_path):lower() - local content_types = { - [".html"] = "text/html", - [".css"] = "text/css", - [".js"] = "application/javascript", - [".json"] = "application/json", - [".png"] = "image/png", - [".jpg"] = "image/jpeg", - [".jpeg"] = "image/jpeg", - [".gif"] = "image/gif", - [".svg"] = "image/svg+xml", - [".webp"] = "image/webp", - [".txt"] = "text/plain", - } - - local content_type = content_types[ext] or "application/octet-stream" - res:type(content_type):send(content) - return - end - end - next() end end