close to the lua func goal.
This commit is contained in:
@@ -1,6 +1,10 @@
|
|||||||
tm = require("tm")
|
tm = require("tm")
|
||||||
|
|
||||||
function func_to_executed(param)
|
local module = {}
|
||||||
|
|
||||||
|
function module.func_to_be_executed(param)
|
||||||
-- return tm.gd(param)
|
-- return tm.gd(param)
|
||||||
return param
|
return param
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return module
|
||||||
@@ -76,7 +76,7 @@ class LuaFuncExecEngine:
|
|||||||
lua_env = tm.gd("lua_env", {})
|
lua_env = tm.gd("lua_env", {})
|
||||||
|
|
||||||
self._process = subprocess.Popen(
|
self._process = subprocess.Popen(
|
||||||
[self._lpath, "main.lua", "--host", "localhost", "--port", f"{self._port}"], env=lua_env, cwd=func_proc_path
|
[self._lpath, "main.lua", "--host", "127.0.0.1", "--port", f"{self._port}"], env=lua_env, cwd=func_proc_path
|
||||||
)
|
)
|
||||||
|
|
||||||
# Port was reserved until the sub-process is started. Now released.
|
# Port was reserved until the sub-process is started. Now released.
|
||||||
|
|||||||
@@ -33,20 +33,34 @@ local function _get_func_by_path(file_path, func_name)
|
|||||||
return target_func
|
return target_func
|
||||||
end
|
end
|
||||||
|
|
||||||
function handle.func_call(file, fname, params)
|
function handle.func_call(params)
|
||||||
|
local file = params.file
|
||||||
|
local fname = params.fname
|
||||||
|
local prms = params.params
|
||||||
|
local res = nil
|
||||||
|
local succ, ret
|
||||||
|
|
||||||
local pfile = file
|
local pfile = file
|
||||||
-- 1. modify the file path if it is relative
|
-- 1. modify the file path if it is relative
|
||||||
if utils.is_relative_path(file) then
|
if utils.is_relative_path(pfile) then
|
||||||
local td = tm.gd("test_directory")
|
local td = tm.gd("test_directory")
|
||||||
pfile = utils.join_paths(td, file)
|
pfile = utils.join_paths(td, file)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 2. retrieve the function "fname"
|
-- 2. retrieve the function "fname"
|
||||||
local func, err = _get_func_by_path(pfile, fname)
|
local func, err = _get_func_by_path(pfile, fname)
|
||||||
|
|
||||||
-- 3. Execute the function
|
-- 3. Execute the function
|
||||||
local res = nil
|
|
||||||
if err == nil then
|
if err == nil then
|
||||||
succ, res = pcall(func, table.unpack(params))
|
utils.log("func_call function found '%s', '%s'", file, fname)
|
||||||
|
succ, ret = pcall(func, table.unpack(prms))
|
||||||
|
utils.log("func_call returned '%s'", tostring(ret))
|
||||||
|
|
||||||
|
if succ then
|
||||||
|
res = ret
|
||||||
|
else
|
||||||
|
err = ret
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 4. Returns result
|
-- 4. Returns result
|
||||||
|
|||||||
@@ -4,12 +4,14 @@ local utils = require("utils")
|
|||||||
local JSONRPC = {}
|
local JSONRPC = {}
|
||||||
JSONRPC.__index = JSONRPC
|
JSONRPC.__index = JSONRPC
|
||||||
|
|
||||||
function JSONRPC.new(send_fn)
|
function JSONRPC.new(sock)
|
||||||
local self = setmetatable({}, JSONRPC)
|
local self = setmetatable({}, JSONRPC)
|
||||||
self.send_raw = send_fn -- Function to transmit string data to transport (TCP/Websocket)
|
self.sock = sock -- Function to transmit string data to transport (TCP/Websocket)
|
||||||
self.methods = {} -- Methods the server provides to the client
|
self.methods = {} -- Methods the server provides to the client
|
||||||
self.pending = {} -- Requests sent to client waiting for response
|
self.pending = {} -- Requests sent to client waiting for response
|
||||||
self.next_id = 1
|
self.next_id = 1
|
||||||
|
|
||||||
|
self.sock:settimeout(0.2)
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -25,8 +27,9 @@ function JSONRPC:handle_message(raw_data)
|
|||||||
if not ok then return self:_send_error(nil, -32700, "Parse error") end
|
if not ok then return self:_send_error(nil, -32700, "Parse error") end
|
||||||
|
|
||||||
-- 1. Check if it's a Response (has 'result' or 'error' and 'id')
|
-- 1. Check if it's a Response (has 'result' or 'error' and 'id')
|
||||||
if msg.result ~= nil or msg.error ~= nil then
|
if (msg.id ~= nil) and (msg.result ~= nil or msg.error ~= nil) then
|
||||||
return self:_handle_response(msg)
|
self.pending[msg.id] = msg
|
||||||
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 2. Check if it's a Request
|
-- 2. Check if it's a Request
|
||||||
@@ -38,67 +41,72 @@ end
|
|||||||
--- INTERNAL: Handle requests from the client
|
--- INTERNAL: Handle requests from the client
|
||||||
function JSONRPC:_handle_request(req)
|
function JSONRPC:_handle_request(req)
|
||||||
local method = self.methods[req.method]
|
local method = self.methods[req.method]
|
||||||
|
local ok, ret
|
||||||
|
local res, err
|
||||||
if not method then
|
if not method then
|
||||||
if req.id then self:_send_error(req.id, -32601, "Method not found") end
|
if req.id then self:_send_error(req.id, -32601, "Method not found") end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
utils.log("calling '%s'", method)
|
||||||
local ok, result = pcall(method, req.params)
|
ok, ret = pcall(method, req.params)
|
||||||
|
utils.log("returned '%s', '%s'", tostring(ok), tostring(res))
|
||||||
|
|
||||||
-- Only send response if it's not a Notification (notifications have no ID)
|
-- Only send response if it's not a Notification (notifications have no ID)
|
||||||
if req.id then
|
if req.id then
|
||||||
if ok then
|
if ok then
|
||||||
self:_send({ jsonrpc = "2.0", result = result, id = req.id })
|
res, err = ret
|
||||||
|
if res == nil then
|
||||||
|
self:_send_error(req.id, -32603, "Internal error: " .. tostring(err))
|
||||||
|
else
|
||||||
|
self:_send({ jsonrpc = "2.0", result = {returned = res}, id = req.id })
|
||||||
|
end
|
||||||
else
|
else
|
||||||
self:_send_error(req.id, -32603, "Internal error: " .. tostring(result))
|
self:_send_error(req.id, -32603, "Internal error: " .. tostring(ret))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- INTERNAL: Handle responses to requests WE sent
|
--- INTERNAL: Handle responses to requests WE sent
|
||||||
function JSONRPC:_handle_response(res)
|
-- function JSONRPC:_handle_response(res)
|
||||||
local callback = self.pending[res.id]
|
-- local callback = self.pending[res.id]
|
||||||
if callback then
|
-- if callback then
|
||||||
callback(res.error, res.result)
|
-- callback(res.error, res.result)
|
||||||
self.pending[res.id] = nil
|
-- self.pending[res.id] = nil
|
||||||
end
|
-- end
|
||||||
end
|
-- end
|
||||||
|
|
||||||
--- Call a method on the client
|
--- Call a method on the client
|
||||||
function JSONRPC:_call(method, params, callback)
|
function JSONRPC:call(method, params)
|
||||||
local id = self.next_id
|
local id = self.next_id
|
||||||
self.next_id = self.next_id + 1
|
self.next_id = self.next_id + 1
|
||||||
|
|
||||||
if callback then
|
|
||||||
self.pending[id] = callback
|
|
||||||
end
|
|
||||||
|
|
||||||
self:_send({
|
self:_send({
|
||||||
jsonrpc = "2.0",
|
jsonrpc = "2.0",
|
||||||
method = method,
|
method = method,
|
||||||
params = params,
|
params = params,
|
||||||
id = id
|
id = id
|
||||||
})
|
})
|
||||||
end
|
|
||||||
|
|
||||||
function JSONRPC:call_sync(method, params)
|
-- ---- Wait for response (re-entrant loop)
|
||||||
local callco = coroutine.create(function(m, p)
|
while true do
|
||||||
local co = coroutine.running()
|
self:poll()
|
||||||
-- Call the async version, but use the callback to resume this coroutine
|
|
||||||
self:_call(m, p, function(err, res)
|
|
||||||
coroutine.resume(co, err, res)
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- Pause execution here until 'resume' is called
|
if self.pending[id] then
|
||||||
return coroutine.yield()
|
local resp = self.pending[id]
|
||||||
end)
|
self.pending[id] = nil
|
||||||
return coroutine.resume(callco, method, params)
|
|
||||||
|
if resp.error then
|
||||||
|
error(resp.error.message)
|
||||||
|
end
|
||||||
|
return resp.result
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function JSONRPC:_send(data)
|
function JSONRPC:_send(data)
|
||||||
local j = json.encode(data)
|
local j = json.encode(data)
|
||||||
utils.log("sending: '%s'", j)
|
utils.log("sending: '%s'", j)
|
||||||
self.send_raw(j)
|
return self.sock:send(j .. "\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
function JSONRPC:_send_error(id, code, message)
|
function JSONRPC:_send_error(id, code, message)
|
||||||
@@ -109,4 +117,24 @@ function JSONRPC:_send_error(id, code, message)
|
|||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function JSONRPC:poll()
|
||||||
|
local line, err = self.sock:receive("*l")
|
||||||
|
|
||||||
|
if line then
|
||||||
|
self:handle_message(line)
|
||||||
|
elseif err ~= "timeout" and err ~= nil then
|
||||||
|
utils.log("Connection ended: %s", err)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function JSONRPC:loop()
|
||||||
|
while true do
|
||||||
|
if not self:poll() then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return JSONRPC
|
return JSONRPC
|
||||||
@@ -59,6 +59,7 @@ local socket = require("socket")
|
|||||||
local JSONRPC = require("json-rpc") -- The module from the previous response
|
local JSONRPC = require("json-rpc") -- The module from the previous response
|
||||||
local utils = require("utils")
|
local utils = require("utils")
|
||||||
local tm = require("tm")
|
local tm = require("tm")
|
||||||
|
local handle = require("handle")
|
||||||
|
|
||||||
utils.verbose = config.verbose
|
utils.verbose = config.verbose
|
||||||
|
|
||||||
@@ -75,35 +76,18 @@ if err then
|
|||||||
os.exit(0)
|
os.exit(0)
|
||||||
end
|
end
|
||||||
|
|
||||||
client_sock:settimeout(10) -- Prevents hanging on dead connections
|
|
||||||
|
|
||||||
utils.log("Client connected")
|
utils.log("Client connected")
|
||||||
|
|
||||||
-- Initialize the RPC instance for this specific connection
|
-- Initialize the RPC instance for this specific connection
|
||||||
local rpc = JSONRPC.new(function(data)
|
local rpc = JSONRPC.new(client_sock)
|
||||||
client_sock:send(data .. "\n") -- Standard JSON-RPC uses newline delimiters over TCP
|
|
||||||
end)
|
|
||||||
|
|
||||||
tm._init_api(rpc)
|
tm._init_api(rpc)
|
||||||
utils.setup_remote_print(rpc)
|
utils.setup_remote_print(rpc)
|
||||||
|
|
||||||
-- Example: Send a request TO the client immediately upon connection
|
rpc:register("func_call", handle.func_call)
|
||||||
print("Welcome to the lua server")
|
|
||||||
|
|
||||||
local tdir = tm.gd("test_directory")
|
|
||||||
|
|
||||||
-- Communication Loop for this client
|
-- Communication Loop for this client
|
||||||
while true do
|
rpc:loop()
|
||||||
local line, err = client_sock:receive() -- Read until newline
|
|
||||||
if err == "closed" then
|
|
||||||
utils.log("Connection ended: %s", err)
|
|
||||||
break
|
|
||||||
elseif err then
|
|
||||||
socket.sleep(0.01)
|
|
||||||
else
|
|
||||||
rpc:handle_message(line)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
client_sock:close()
|
client_sock:close()
|
||||||
utils.log("Server stopped")
|
utils.log("Server stopped")
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ function tm._init_api(rpc)
|
|||||||
|
|
||||||
local function _api_request(fname, ...)
|
local function _api_request(fname, ...)
|
||||||
local args = {...}
|
local args = {...}
|
||||||
return tm._rpc:call_sync(fname, args)
|
return tm._rpc:call(fname, args)
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, fname in ipairs(SUPPORTED_API) do
|
for _, fname in ipairs(SUPPORTED_API) do
|
||||||
|
|||||||
@@ -19,18 +19,18 @@ function utils.is_absolute_path(path)
|
|||||||
if not path or path == "" then return false end
|
if not path or path == "" then return false end
|
||||||
|
|
||||||
-- 1. Check for POSIX absolute path (starts with /)
|
-- 1. Check for POSIX absolute path (starts with /)
|
||||||
if path:sub(1, 1) == "/" then
|
if string.sub(path, 1, 1) == "/" then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 2. Check for Windows drive letter (e.g., C:\ or D:/)
|
-- 2. Check for Windows drive letter (e.g., C:\ or D:/)
|
||||||
-- Pattern: %a (letter) followed by : (colon)
|
-- Pattern: %a (letter) followed by : (colon)
|
||||||
if path:match("^%a:[/\\]") or path:match("^%a:$") then
|
if string.match(path, "^%a:[/\\]") or string.match(path, "^%a:$") then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 3. Check for Windows UNC/Network paths (starts with \\ or //)
|
-- 3. Check for Windows UNC/Network paths (starts with \\ or //)
|
||||||
if path:match("^[/\\][/\\]") then
|
if string.match(path, "^[/\\][/\\]") then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ function utils.setup_remote_print(rpc)
|
|||||||
message = message .. "\n"
|
message = message .. "\n"
|
||||||
end
|
end
|
||||||
pcall(function()
|
pcall(function()
|
||||||
rpc:call_sync("print", message )
|
rpc:call("print", message )
|
||||||
end)
|
end)
|
||||||
-- Optional: Still print to the server's local console
|
-- Optional: Still print to the server's local console
|
||||||
-- utils.log("[Remote Log Sent]: " .. message)
|
-- utils.log("[Remote Log Sent]: " .. message)
|
||||||
|
|||||||
Reference in New Issue
Block a user