diff --git a/functions/http/http.go b/functions/http/http.go deleted file mode 100644 index d1efd22..0000000 --- a/functions/http/http.go +++ /dev/null @@ -1,495 +0,0 @@ -package http - -import ( - "fmt" - "sync" - "time" - - luajit "git.sharkk.net/Sky/LuaJIT-to-Go" - "github.com/valyala/fasthttp" -) - -// Handler is a fasthttp request handler with parameters -type Handler func(ctx *fasthttp.RequestCtx, params []string) - -type node struct { - segment string - handler Handler - children []*node - isDynamic bool - isWildcard bool - maxParams uint8 -} - -type Router struct { - get, post, put, patch, delete *node - paramsBuffer []string -} - -func newRouter() *Router { - return &Router{ - get: &node{}, - post: &node{}, - put: &node{}, - patch: &node{}, - delete: &node{}, - paramsBuffer: make([]string, 64), - } -} - -// HTTPServer with efficient serialized Lua handling -type HTTPServer struct { - server *fasthttp.Server - router *Router - addr string - running bool - mu sync.RWMutex - luaMu sync.Mutex // Serializes Lua calls -} - -var ( - serverRegistry = struct { - sync.RWMutex - servers map[int]*HTTPServer - nextID int - }{ - servers: make(map[int]*HTTPServer), - nextID: 1, - } -) - -func GetHTTPFunctions() map[string]luajit.GoFunction { - return map[string]luajit.GoFunction{ - "http_create_server": func(s *luajit.State) int { - server := &HTTPServer{ - server: &fasthttp.Server{ - ReadTimeout: 10 * time.Second, - WriteTimeout: 10 * time.Second, - IdleTimeout: 60 * time.Second, - }, - router: newRouter(), - } - - server.server.Handler = server.handleRequest - - serverRegistry.Lock() - id := serverRegistry.nextID - serverRegistry.nextID++ - serverRegistry.servers[id] = server - serverRegistry.Unlock() - - s.PushNumber(float64(id)) - return 1 - }, - - "http_server_listen": func(s *luajit.State) int { - if err := s.CheckExactArgs(2); err != nil { - return s.PushError("http_server_listen: %v", err) - } - - serverID, err := s.SafeToNumber(1) - if err != nil || serverID != float64(int(serverID)) { - return s.PushError("http_server_listen: server ID must be an integer") - } - - addr, err := s.SafeToString(2) - if err != nil { - return s.PushError("http_server_listen: address must be a string") - } - - serverRegistry.RLock() - server, exists := serverRegistry.servers[int(serverID)] - serverRegistry.RUnlock() - - if !exists { - return s.PushError("http_server_listen: server not found") - } - - server.mu.Lock() - if server.running { - server.mu.Unlock() - return s.PushError("http_server_listen: server already running") - } - - server.addr = addr - server.running = true - server.mu.Unlock() - - go func() { - err := server.server.ListenAndServe(addr) - if err != nil { - server.mu.Lock() - server.running = false - server.mu.Unlock() - } - }() - - s.PushBoolean(true) - return 1 - }, - - "http_server_stop": func(s *luajit.State) int { - if err := s.CheckMinArgs(1); err != nil { - return s.PushError("http_server_stop: %v", err) - } - - serverID, err := s.SafeToNumber(1) - if err != nil || serverID != float64(int(serverID)) { - return s.PushError("http_server_stop: server ID must be an integer") - } - - serverRegistry.RLock() - server, exists := serverRegistry.servers[int(serverID)] - serverRegistry.RUnlock() - - if !exists { - return s.PushError("http_server_stop: server not found") - } - - server.mu.Lock() - if !server.running { - server.mu.Unlock() - s.PushBoolean(false) - return 1 - } - - server.running = false - server.mu.Unlock() - - if err := server.server.Shutdown(); err != nil { - return s.PushError("http_server_stop: %v", err) - } - - s.PushBoolean(true) - return 1 - }, - - "http_server_get": createRouteHandler("GET"), - "http_server_post": createRouteHandler("POST"), - "http_server_put": createRouteHandler("PUT"), - "http_server_patch": createRouteHandler("PATCH"), - "http_server_delete": createRouteHandler("DELETE"), - - "http_server_is_running": func(s *luajit.State) int { - if err := s.CheckMinArgs(1); err != nil { - return s.PushError("http_server_is_running: %v", err) - } - - serverID, err := s.SafeToNumber(1) - if err != nil || serverID != float64(int(serverID)) { - return s.PushError("http_server_is_running: server ID must be an integer") - } - - serverRegistry.RLock() - server, exists := serverRegistry.servers[int(serverID)] - serverRegistry.RUnlock() - - if !exists { - s.PushBoolean(false) - return 1 - } - - server.mu.RLock() - running := server.running - server.mu.RUnlock() - - s.PushBoolean(running) - return 1 - }, - - "http_cleanup_servers": func(s *luajit.State) int { - serverRegistry.Lock() - for id, server := range serverRegistry.servers { - server.mu.Lock() - if server.running { - server.server.Shutdown() - server.running = false - } - server.mu.Unlock() - delete(serverRegistry.servers, id) - } - serverRegistry.Unlock() - - s.PushBoolean(true) - return 1 - }, - } -} - -func createRouteHandler(method string) luajit.GoFunction { - return func(s *luajit.State) int { - if err := s.CheckExactArgs(3); err != nil { - return s.PushError("http_server_%s: %v", method, err) - } - - serverID, err := s.SafeToNumber(1) - if err != nil || serverID != float64(int(serverID)) { - return s.PushError("http_server_%s: server ID must be an integer", method) - } - - path, err := s.SafeToString(2) - if err != nil { - return s.PushError("http_server_%s: path must be a string", method) - } - - if !s.IsFunction(3) { - return s.PushError("http_server_%s: handler must be a function", method) - } - - serverRegistry.RLock() - server, exists := serverRegistry.servers[int(serverID)] - serverRegistry.RUnlock() - - if !exists { - return s.PushError("http_server_%s: server not found", method) - } - - luaFunc, err := s.StoreLuaFunction(3) - if err != nil { - return s.PushError("http_server_%s: failed to store function: %v", method, err) - } - - handler := func(ctx *fasthttp.RequestCtx, params []string) { - server.callLuaHandler(ctx, params, luaFunc) - } - - if err := server.router.addRoute(method, path, handler); err != nil { - return s.PushError("http_server_%s: failed to add route: %v", method, err) - } - - s.PushBoolean(true) - return 1 - } -} - -// Router methods -func (r *Router) methodNode(method string) *node { - switch method { - case "GET": - return r.get - case "POST": - return r.post - case "PUT": - return r.put - case "PATCH": - return r.patch - case "DELETE": - return r.delete - default: - return nil - } -} - -func (r *Router) addRoute(method, path string, h Handler) error { - root := r.methodNode(method) - if root == nil { - return fmt.Errorf("unsupported method: %s", method) - } - - if path == "/" { - root.handler = h - return nil - } - - current := root - pos := 0 - lastWC := false - count := uint8(0) - - for { - seg, newPos, more := readSegment(path, pos) - if seg == "" { - break - } - - isDyn := len(seg) > 0 && seg[0] == ':' - isWC := len(seg) > 0 && seg[0] == '*' - - if isWC { - if lastWC || more { - return fmt.Errorf("wildcard must be the last segment in the path") - } - lastWC = true - } - - if isDyn || isWC { - count++ - } - - var child *node - for _, c := range current.children { - if c.segment == seg { - child = c - break - } - } - - if child == nil { - child = &node{segment: seg, isDynamic: isDyn, isWildcard: isWC} - current.children = append(current.children, child) - } - - if child.maxParams < count { - child.maxParams = count - } - - current = child - pos = newPos - } - - current.handler = h - return nil -} - -func (r *Router) lookup(method, path string) (Handler, []string, bool) { - root := r.methodNode(method) - if root == nil { - return nil, nil, false - } - - if path == "/" { - return root.handler, nil, root.handler != nil - } - - buffer := r.paramsBuffer - if cap(buffer) < int(root.maxParams) { - buffer = make([]string, root.maxParams) - r.paramsBuffer = buffer - } - buffer = buffer[:0] - - h, paramCount, found := match(root, path, 0, &buffer) - if !found { - return nil, nil, false - } - - return h, buffer[:paramCount], true -} - -// HTTPServer methods -func (hs *HTTPServer) handleRequest(ctx *fasthttp.RequestCtx) { - method := string(ctx.Method()) - path := string(ctx.Path()) - - handler, params, found := hs.router.lookup(method, path) - if !found { - ctx.SetStatusCode(fasthttp.StatusNotFound) - ctx.WriteString("Not Found") - return - } - - handler(ctx, params) -} - -func (hs *HTTPServer) callLuaHandler(ctx *fasthttp.RequestCtx, params []string, handler *luajit.LuaFunction) { - hs.luaMu.Lock() - defer hs.luaMu.Unlock() - - request := map[string]interface{}{ - "method": string(ctx.Method()), - "path": string(ctx.Path()), - "query": string(ctx.QueryArgs().QueryString()), - "headers": make(map[string]string), - "body": string(ctx.PostBody()), - "remote": ctx.RemoteAddr().String(), - "params": params, - } - - headers := request["headers"].(map[string]string) - ctx.Request.Header.VisitAll(func(key, value []byte) { - headers[string(key)] = string(value) - }) - - response := map[string]interface{}{ - "status": 200, - "headers": make(map[string]string), - "body": "", - } - - results, err := handler.Call(request, response) - if err != nil { - ctx.SetStatusCode(fasthttp.StatusInternalServerError) - ctx.WriteString(fmt.Sprintf("Handler error: %v", err)) - return - } - - if len(results) > 0 { - if respMap, ok := results[0].(map[string]interface{}); ok { - response = respMap - } - } - - if status, ok := response["status"].(int); ok { - ctx.SetStatusCode(status) - } else if status, ok := response["status"].(float64); ok { - ctx.SetStatusCode(int(status)) - } - - if headers, ok := response["headers"].(map[string]interface{}); ok { - for k, v := range headers { - if str, ok := v.(string); ok { - ctx.Response.Header.Set(k, str) - } - } - } - - if body, ok := response["body"].(string); ok { - ctx.WriteString(body) - } -} - -// Utility functions -func readSegment(path string, start int) (segment string, end int, hasMore bool) { - if start >= len(path) { - return "", start, false - } - if path[start] == '/' { - start++ - } - if start >= len(path) { - return "", start, false - } - end = start - for end < len(path) && path[end] != '/' { - end++ - } - return path[start:end], end, end < len(path) -} - -func match(current *node, path string, start int, params *[]string) (Handler, int, bool) { - paramCount := 0 - - for _, c := range current.children { - if c.isWildcard { - rem := path[start:] - if len(rem) > 0 && rem[0] == '/' { - rem = rem[1:] - } - *params = append(*params, rem) - return c.handler, 1, c.handler != nil - } - } - - seg, pos, more := readSegment(path, start) - if seg == "" { - return current.handler, 0, current.handler != nil - } - - for _, c := range current.children { - if c.segment == seg || c.isDynamic { - if c.isDynamic { - *params = append(*params, seg) - paramCount++ - } - if !more { - return c.handler, paramCount, c.handler != nil - } - h, nestedCount, ok := match(c, path, pos, params) - if ok { - return h, paramCount + nestedCount, true - } - } - } - - return nil, 0, false -} diff --git a/functions/json.go b/functions/json.go index c8aa163..130ab62 100644 --- a/functions/json.go +++ b/functions/json.go @@ -37,7 +37,7 @@ func GetJSONFunctions() map[string]luajit.GoFunction { return s.PushError("json_decode: input must be a string") } - var result interface{} + var result any if err := json.Unmarshal([]byte(jsonStr), &result); err != nil { // Return nil and error string instead of PushError for JSON parsing errors s.PushNil() diff --git a/functions/registry.go b/functions/registry.go index c81d725..2524852 100644 --- a/functions/registry.go +++ b/functions/registry.go @@ -4,8 +4,6 @@ import ( "maps" "sync" - "Moonshark/functions/http" - luajit "git.sharkk.net/Sky/LuaJIT-to-Go" ) @@ -21,7 +19,6 @@ func GetAll() Registry { maps.Copy(registry, GetMathFunctions()) maps.Copy(registry, GetFSFunctions()) maps.Copy(registry, GetCryptoFunctions()) - maps.Copy(registry, http.GetHTTPFunctions()) return registry } diff --git a/functions/string.go b/functions/string.go index 9fc220d..4b60d0a 100644 --- a/functions/string.go +++ b/functions/string.go @@ -84,7 +84,7 @@ func GetStringFunctions() map[string]luajit.GoFunction { switch v := arr.(type) { case []string: parts = v - case []interface{}: + case []any: parts = make([]string, len(v)) for i, val := range v { if val == nil { @@ -93,7 +93,7 @@ func GetStringFunctions() map[string]luajit.GoFunction { parts[i] = fmt.Sprintf("%v", val) } } - case map[string]interface{}: + case map[string]any: if len(v) == 0 { parts = []string{} } else { diff --git a/modules/http.lua b/modules/http.lua deleted file mode 100644 index 7feca09..0000000 --- a/modules/http.lua +++ /dev/null @@ -1,277 +0,0 @@ --- modules/http.lua - HTTP server with Go-based routing - -local http = {} - --- ====================================================================== --- HTTP SERVER --- ====================================================================== - -local HTTPServer = {} -HTTPServer.__index = HTTPServer - -function HTTPServer:listen(addr) - local success, err = moonshark.http_server_listen(self.id, addr) - if not success then - error("Failed to start server: " .. (err or "unknown error")) - end - self.addr = addr - return self -end - -function HTTPServer:stop() - local success, err = moonshark.http_server_stop(self.id) - if not success then - error("Failed to stop server: " .. (err or "unknown error")) - end - return self -end - -function HTTPServer:get(path, handler) - if type(handler) ~= "function" then - error("Handler must be a function") - end - - local success, err = moonshark.http_server_get(self.id, path, handler) - if not success then - error("Failed to add GET route: " .. (err or "unknown error")) - end - return self -end - -function HTTPServer:post(path, handler) - if type(handler) ~= "function" then - error("Handler must be a function") - end - - local success, err = moonshark.http_server_post(self.id, path, handler) - if not success then - error("Failed to add POST route: " .. (err or "unknown error")) - end - return self -end - -function HTTPServer:put(path, handler) - if type(handler) ~= "function" then - error("Handler must be a function") - end - - local success, err = moonshark.http_server_put(self.id, path, handler) - if not success then - error("Failed to add PUT route: " .. (err or "unknown error")) - end - return self -end - -function HTTPServer:patch(path, handler) - if type(handler) ~= "function" then - error("Handler must be a function") - end - - local success, err = moonshark.http_server_patch(self.id, path, handler) - if not success then - error("Failed to add PATCH route: " .. (err or "unknown error")) - end - return self -end - -function HTTPServer:delete(path, handler) - if type(handler) ~= "function" then - error("Handler must be a function") - end - - local success, err = moonshark.http_server_delete(self.id, path, handler) - if not success then - error("Failed to add DELETE route: " .. (err or "unknown error")) - end - return self -end - -function HTTPServer:isRunning() - return moonshark.http_server_is_running(self.id) -end - --- Handle cleanup when server is garbage collected -function HTTPServer:__gc() - if self:isRunning() then - pcall(function() self:stop() end) - end -end - --- ====================================================================== --- HTTP MODULE FUNCTIONS --- ====================================================================== - -function http.newServer(options) - options = options or {} - - local serverID = moonshark.http_create_server() - if not serverID then - error("Failed to create HTTP server") - end - - local server = setmetatable({ - id = serverID, - addr = nil - }, HTTPServer) - - return server -end - --- Convenience function to create and start server in one call -function http.listen(addr, handler) - local server = http.newServer() - - if handler then - server:get("/*", handler) - end - - return server:listen(addr) -end - --- Clean up all servers -function http.cleanup() - moonshark.http_cleanup_servers() -end - --- ====================================================================== --- REQUEST/RESPONSE HELPERS --- ====================================================================== - -http.status = { - OK = 200, - CREATED = 201, - NO_CONTENT = 204, - MOVED_PERMANENTLY = 301, - FOUND = 302, - NOT_MODIFIED = 304, - BAD_REQUEST = 400, - UNAUTHORIZED = 401, - FORBIDDEN = 403, - NOT_FOUND = 404, - METHOD_NOT_ALLOWED = 405, - CONFLICT = 409, - INTERNAL_SERVER_ERROR = 500, - NOT_IMPLEMENTED = 501, - BAD_GATEWAY = 502, - SERVICE_UNAVAILABLE = 503 -} - --- Create a response object -function http.response(status, body, headers) - return { - status = status or http.status.OK, - body = body or "", - headers = headers or {} - } -end - --- Create JSON response -function http.json(data, status) - local json_str = moonshark.json_encode(data) - return { - status = status or http.status.OK, - body = json_str, - headers = { - ["Content-Type"] = "application/json" - } - } -end - --- Create text response -function http.text(text, status) - return { - status = status or http.status.OK, - body = tostring(text), - headers = { - ["Content-Type"] = "text/plain" - } - } -end - --- Create HTML response -function http.html(html, status) - return { - status = status or http.status.OK, - body = tostring(html), - headers = { - ["Content-Type"] = "text/html" - } - } -end - --- Redirect response -function http.redirect(location, status) - return { - status = status or http.status.FOUND, - body = "", - headers = { - ["Location"] = location - } - } -end - --- Error response -function http.error(message, status) - return http.json({ - error = message or "Internal Server Error" - }, status or http.status.INTERNAL_SERVER_ERROR) -end - --- ====================================================================== --- UTILITY FUNCTIONS --- ====================================================================== - --- Parse query string -function http.parseQuery(queryString) - local params = {} - if not queryString or queryString == "" then - return params - end - - for pair in queryString:gmatch("[^&]+") do - local key, value = pair:match("([^=]+)=?(.*)") - if key then - key = http.urlDecode(key) - value = http.urlDecode(value or "") - params[key] = value - end - end - - return params -end - --- URL decode -function http.urlDecode(str) - if not str then return "" end - str = str:gsub("+", " ") - str = str:gsub("%%(%x%x)", function(hex) - return string.char(tonumber(hex, 16)) - end) - return str -end - --- URL encode -function http.urlEncode(str) - if not str then return "" end - str = str:gsub("([^%w%-%.%_%~])", function(c) - return string.format("%%%02X", string.byte(c)) - end) - return str -end - --- Parse cookies -function http.parseCookies(cookieHeader) - local cookies = {} - if not cookieHeader then return cookies end - - for pair in cookieHeader:gmatch("[^;]+") do - local key, value = pair:match("^%s*([^=]+)=?(.*)") - if key then - cookies[key:match("^%s*(.-)%s*$")] = value and value:match("^%s*(.-)%s*$") or "" - end - end - - return cookies -end - -return http \ No newline at end of file diff --git a/moonshark.go b/moonshark.go index 2ac0492..7608ae2 100644 --- a/moonshark.go +++ b/moonshark.go @@ -1,12 +1,11 @@ package main import ( + "Moonshark/functions" "Moonshark/modules" "fmt" "os" - "os/signal" "path/filepath" - "syscall" luajit "git.sharkk.net/Sky/LuaJIT-to-Go" ) @@ -74,43 +73,12 @@ func main() { os.Exit(1) } + // Store the bytecode so our Go module implementations can use it elsewhere + functions.SetStoredBytecode(bytecode) + // Execute the compiled bytecode if err := state.LoadAndRunBytecode(bytecode, scriptPath); err != nil { fmt.Fprintf(os.Stderr, "Error executing '%s': %v\n", scriptPath, err) os.Exit(1) } - - // Check if any servers are running to determine if we need to wait - hasRunningServers := false - if result, err := state.ExecuteWithResult(` - for i = 1, 100 do - if moonshark.http_server_is_running(i) then - return true - end - end - return false - `); err == nil { - if running, ok := result.(bool); ok && running { - hasRunningServers = true - } - } - - // Only set up signal handling if there are long-running services - if hasRunningServers { - sigChan := make(chan os.Signal, 1) - signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) - - fmt.Printf("Script executed successfully. Press Ctrl+C to stop...\n") - - // Wait for signal - <-sigChan - fmt.Printf("\nReceived shutdown signal. Cleaning up...\n") - - // Cleanup HTTP servers through Lua - if err := state.DoString("moonshark.http_cleanup_servers()"); err != nil { - fmt.Printf("Warning: failed to cleanup servers: %v\n", err) - } - - fmt.Printf("Shutdown complete.\n") - } }