interface{} to any
This commit is contained in:
parent
03d1b93f35
commit
4ff04e141d
@ -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
|
||||
}
|
@ -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()
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
277
modules/http.lua
277
modules/http.lua
@ -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
|
40
moonshark.go
40
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")
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user