close to the lua func goal.
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
tm = require("tm")
|
||||
|
||||
function func_to_executed(param)
|
||||
local module = {}
|
||||
|
||||
function module.func_to_be_executed(param)
|
||||
-- return tm.gd(param)
|
||||
return param
|
||||
end
|
||||
|
||||
return module
|
||||
@@ -76,7 +76,7 @@ class LuaFuncExecEngine:
|
||||
lua_env = tm.gd("lua_env", {})
|
||||
|
||||
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.
|
||||
|
||||
@@ -33,20 +33,34 @@ local function _get_func_by_path(file_path, func_name)
|
||||
return target_func
|
||||
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
|
||||
-- 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")
|
||||
pfile = utils.join_paths(td, file)
|
||||
end
|
||||
|
||||
-- 2. retrieve the function "fname"
|
||||
local func, err = _get_func_by_path(pfile, fname)
|
||||
|
||||
-- 3. Execute the function
|
||||
local res = nil
|
||||
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
|
||||
|
||||
-- 4. Returns result
|
||||
|
||||
@@ -4,12 +4,14 @@ local utils = require("utils")
|
||||
local JSONRPC = {}
|
||||
JSONRPC.__index = JSONRPC
|
||||
|
||||
function JSONRPC.new(send_fn)
|
||||
function JSONRPC.new(sock)
|
||||
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.pending = {} -- Requests sent to client waiting for response
|
||||
self.next_id = 1
|
||||
|
||||
self.sock:settimeout(0.2)
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -25,8 +27,9 @@ function JSONRPC:handle_message(raw_data)
|
||||
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')
|
||||
if msg.result ~= nil or msg.error ~= nil then
|
||||
return self:_handle_response(msg)
|
||||
if (msg.id ~= nil) and (msg.result ~= nil or msg.error ~= nil) then
|
||||
self.pending[msg.id] = msg
|
||||
return
|
||||
end
|
||||
|
||||
-- 2. Check if it's a Request
|
||||
@@ -38,67 +41,72 @@ end
|
||||
--- INTERNAL: Handle requests from the client
|
||||
function JSONRPC:_handle_request(req)
|
||||
local method = self.methods[req.method]
|
||||
local ok, ret
|
||||
local res, err
|
||||
if not method then
|
||||
if req.id then self:_send_error(req.id, -32601, "Method not found") end
|
||||
return
|
||||
end
|
||||
|
||||
local ok, result = pcall(method, req.params)
|
||||
utils.log("calling '%s'", method)
|
||||
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)
|
||||
if req.id 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_error(req.id, -32603, "Internal error: " .. tostring(result))
|
||||
self:_send({ jsonrpc = "2.0", result = {returned = res}, id = req.id })
|
||||
end
|
||||
else
|
||||
self:_send_error(req.id, -32603, "Internal error: " .. tostring(ret))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- INTERNAL: Handle responses to requests WE sent
|
||||
function JSONRPC:_handle_response(res)
|
||||
local callback = self.pending[res.id]
|
||||
if callback then
|
||||
callback(res.error, res.result)
|
||||
self.pending[res.id] = nil
|
||||
end
|
||||
end
|
||||
-- function JSONRPC:_handle_response(res)
|
||||
-- local callback = self.pending[res.id]
|
||||
-- if callback then
|
||||
-- callback(res.error, res.result)
|
||||
-- self.pending[res.id] = nil
|
||||
-- end
|
||||
-- end
|
||||
|
||||
--- Call a method on the client
|
||||
function JSONRPC:_call(method, params, callback)
|
||||
function JSONRPC:call(method, params)
|
||||
local id = self.next_id
|
||||
self.next_id = self.next_id + 1
|
||||
|
||||
if callback then
|
||||
self.pending[id] = callback
|
||||
end
|
||||
|
||||
self:_send({
|
||||
jsonrpc = "2.0",
|
||||
method = method,
|
||||
params = params,
|
||||
id = id
|
||||
})
|
||||
end
|
||||
|
||||
function JSONRPC:call_sync(method, params)
|
||||
local callco = coroutine.create(function(m, p)
|
||||
local co = coroutine.running()
|
||||
-- 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)
|
||||
-- ---- Wait for response (re-entrant loop)
|
||||
while true do
|
||||
self:poll()
|
||||
|
||||
-- Pause execution here until 'resume' is called
|
||||
return coroutine.yield()
|
||||
end)
|
||||
return coroutine.resume(callco, method, params)
|
||||
if self.pending[id] then
|
||||
local resp = self.pending[id]
|
||||
self.pending[id] = nil
|
||||
|
||||
if resp.error then
|
||||
error(resp.error.message)
|
||||
end
|
||||
return resp.result
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function JSONRPC:_send(data)
|
||||
local j = json.encode(data)
|
||||
utils.log("sending: '%s'", j)
|
||||
self.send_raw(j)
|
||||
return self.sock:send(j .. "\n")
|
||||
end
|
||||
|
||||
function JSONRPC:_send_error(id, code, message)
|
||||
@@ -109,4 +117,24 @@ function JSONRPC:_send_error(id, code, message)
|
||||
})
|
||||
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
|
||||
@@ -59,6 +59,7 @@ local socket = require("socket")
|
||||
local JSONRPC = require("json-rpc") -- The module from the previous response
|
||||
local utils = require("utils")
|
||||
local tm = require("tm")
|
||||
local handle = require("handle")
|
||||
|
||||
utils.verbose = config.verbose
|
||||
|
||||
@@ -75,35 +76,18 @@ if err then
|
||||
os.exit(0)
|
||||
end
|
||||
|
||||
client_sock:settimeout(10) -- Prevents hanging on dead connections
|
||||
|
||||
utils.log("Client connected")
|
||||
|
||||
-- Initialize the RPC instance for this specific connection
|
||||
local rpc = JSONRPC.new(function(data)
|
||||
client_sock:send(data .. "\n") -- Standard JSON-RPC uses newline delimiters over TCP
|
||||
end)
|
||||
local rpc = JSONRPC.new(client_sock)
|
||||
|
||||
tm._init_api(rpc)
|
||||
utils.setup_remote_print(rpc)
|
||||
|
||||
-- Example: Send a request TO the client immediately upon connection
|
||||
print("Welcome to the lua server")
|
||||
|
||||
local tdir = tm.gd("test_directory")
|
||||
rpc:register("func_call", handle.func_call)
|
||||
|
||||
-- Communication Loop for this client
|
||||
while true do
|
||||
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
|
||||
rpc:loop()
|
||||
|
||||
client_sock:close()
|
||||
utils.log("Server stopped")
|
||||
|
||||
@@ -14,7 +14,7 @@ function tm._init_api(rpc)
|
||||
|
||||
local function _api_request(fname, ...)
|
||||
local args = {...}
|
||||
return tm._rpc:call_sync(fname, args)
|
||||
return tm._rpc:call(fname, args)
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
-- 1. Check for POSIX absolute path (starts with /)
|
||||
if path:sub(1, 1) == "/" then
|
||||
if string.sub(path, 1, 1) == "/" then
|
||||
return true
|
||||
end
|
||||
|
||||
-- 2. Check for Windows drive letter (e.g., C:\ or D:/)
|
||||
-- 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
|
||||
end
|
||||
|
||||
-- 3. Check for Windows UNC/Network paths (starts with \\ or //)
|
||||
if path:match("^[/\\][/\\]") then
|
||||
if string.match(path, "^[/\\][/\\]") then
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -61,7 +61,7 @@ function utils.setup_remote_print(rpc)
|
||||
message = message .. "\n"
|
||||
end
|
||||
pcall(function()
|
||||
rpc:call_sync("print", message )
|
||||
rpc:call("print", message )
|
||||
end)
|
||||
-- Optional: Still print to the server's local console
|
||||
-- utils.log("[Remote Log Sent]: " .. message)
|
||||
|
||||
Reference in New Issue
Block a user