JSON API, fetch() API
This commit is contained in:
37
flumi/Scripts/Utils/Lua/JSON.gd
Normal file
37
flumi/Scripts/Utils/Lua/JSON.gd
Normal file
@@ -0,0 +1,37 @@
|
||||
class_name LuaJSONUtils
|
||||
extends RefCounted
|
||||
|
||||
static func setup_json_api(vm: LuauVM):
|
||||
vm.lua_newtable()
|
||||
|
||||
vm.lua_pushcallable(_lua_json_parse_handler, "JSON.parse")
|
||||
vm.lua_setfield(-2, "parse")
|
||||
|
||||
vm.lua_pushcallable(_lua_json_stringify_handler, "JSON.stringify")
|
||||
vm.lua_setfield(-2, "stringify")
|
||||
|
||||
vm.lua_setglobal("JSON")
|
||||
|
||||
static func _lua_json_parse_handler(vm: LuauVM) -> int:
|
||||
var json_string: String = vm.luaL_checkstring(1)
|
||||
|
||||
var json = JSON.new()
|
||||
var parse_result = json.parse(json_string)
|
||||
|
||||
if parse_result == OK:
|
||||
vm.lua_pushvariant(json.data)
|
||||
return 1
|
||||
else:
|
||||
# Return nil and error message
|
||||
vm.lua_pushnil()
|
||||
vm.lua_pushstring("JSON parse error: " + json.get_error_message())
|
||||
return 2
|
||||
|
||||
static func _lua_json_stringify_handler(vm: LuauVM) -> int:
|
||||
var value = vm.lua_tovariant(1)
|
||||
|
||||
var json = JSON.new()
|
||||
var json_string = json.stringify(value)
|
||||
|
||||
vm.lua_pushstring(json_string)
|
||||
return 1
|
||||
1
flumi/Scripts/Utils/Lua/JSON.gd.uid
Normal file
1
flumi/Scripts/Utils/Lua/JSON.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://caxwx127g48l3
|
||||
240
flumi/Scripts/Utils/Lua/Network.gd
Normal file
240
flumi/Scripts/Utils/Lua/Network.gd
Normal file
@@ -0,0 +1,240 @@
|
||||
class_name LuaNetworkUtils
|
||||
extends RefCounted
|
||||
|
||||
static func setup_network_api(vm: LuauVM):
|
||||
vm.lua_pushcallable(_lua_fetch_handler, "fetch")
|
||||
vm.lua_setglobal("fetch")
|
||||
|
||||
static func _lua_fetch_handler(vm: LuauVM) -> int:
|
||||
var url: String = vm.luaL_checkstring(1)
|
||||
var options: Dictionary = {}
|
||||
|
||||
if vm.lua_gettop() >= 2 and vm.lua_istable(2):
|
||||
options = vm.lua_todictionary(2)
|
||||
|
||||
# Default options
|
||||
var method = options.get("method", "GET").to_upper()
|
||||
var headers = options.get("headers", {})
|
||||
var body = options.get("body", "")
|
||||
|
||||
# Set request options
|
||||
var headers_array: PackedStringArray = []
|
||||
for header_name in headers:
|
||||
headers_array.append(str(header_name) + ": " + str(headers[header_name]))
|
||||
|
||||
var response_data = make_http_request(url, method, headers_array, body)
|
||||
|
||||
# Create response object with actual data
|
||||
vm.lua_newtable()
|
||||
|
||||
# Add response properties
|
||||
vm.lua_pushinteger(response_data.status)
|
||||
vm.lua_setfield(-2, "status")
|
||||
|
||||
vm.lua_pushstring(response_data.status_text)
|
||||
vm.lua_setfield(-2, "statusText")
|
||||
|
||||
# Convert response headers to table
|
||||
vm.lua_newtable()
|
||||
for header_name in response_data.headers:
|
||||
vm.lua_pushstring(response_data.headers[header_name])
|
||||
vm.lua_setfield(-2, header_name.to_lower())
|
||||
vm.lua_setfield(-2, "headers")
|
||||
|
||||
# Store response body
|
||||
vm.lua_pushstring(response_data.body)
|
||||
vm.lua_setfield(-2, "_response_body")
|
||||
|
||||
# Add response methods
|
||||
vm.lua_pushcallable(_response_text_handler, "response.text")
|
||||
vm.lua_setfield(-2, "text")
|
||||
|
||||
vm.lua_pushcallable(_response_json_handler, "response.json")
|
||||
vm.lua_setfield(-2, "json")
|
||||
|
||||
vm.lua_pushcallable(_response_ok_handler, "response.ok")
|
||||
vm.lua_setfield(-2, "ok")
|
||||
|
||||
return 1
|
||||
|
||||
static func _response_text_handler(vm: LuauVM) -> int:
|
||||
vm.luaL_checktype(1, vm.LUA_TTABLE)
|
||||
|
||||
vm.lua_getfield(1, "_response_body")
|
||||
var response_text = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
|
||||
vm.lua_pushstring(response_text)
|
||||
return 1
|
||||
|
||||
static func _response_json_handler(vm: LuauVM) -> int:
|
||||
vm.luaL_checktype(1, vm.LUA_TTABLE)
|
||||
|
||||
vm.lua_getfield(1, "_response_body")
|
||||
var response_text = vm.lua_tostring(-1)
|
||||
vm.lua_pop(1)
|
||||
|
||||
var json = JSON.new()
|
||||
var parse_result = json.parse(response_text)
|
||||
|
||||
if parse_result == OK:
|
||||
vm.lua_pushvariant(json.data)
|
||||
else:
|
||||
vm.luaL_error("Invalid JSON in response")
|
||||
|
||||
return 1
|
||||
|
||||
static func _response_ok_handler(vm: LuauVM) -> int:
|
||||
vm.luaL_checktype(1, vm.LUA_TTABLE)
|
||||
|
||||
vm.lua_getfield(1, "status")
|
||||
var status = vm.lua_tointeger(-1)
|
||||
vm.lua_pop(1)
|
||||
|
||||
vm.lua_pushboolean(status >= 200 and status < 300)
|
||||
return 1
|
||||
|
||||
static func make_http_request(url: String, method: String, headers: PackedStringArray, body: String) -> Dictionary:
|
||||
var http_client = HTTPClient.new()
|
||||
var response_data = {
|
||||
"status": 0,
|
||||
"status_text": "Network Error",
|
||||
"headers": {},
|
||||
"body": ""
|
||||
}
|
||||
|
||||
# Parse URL
|
||||
var url_parts = url.split("://")
|
||||
if url_parts.size() < 2:
|
||||
response_data.status = 400
|
||||
response_data.status_text = "Bad Request - Invalid URL"
|
||||
return response_data
|
||||
|
||||
var protocol = url_parts[0].to_lower()
|
||||
var use_ssl = (protocol == "https" or protocol == "gurt+ssl")
|
||||
var remaining_url = url_parts[1]
|
||||
|
||||
var host_path = remaining_url.split("/", false, 1)
|
||||
var host = host_path[0]
|
||||
var path = "/" + (host_path[1] if host_path.size() > 1 else "")
|
||||
|
||||
# Extract port if specified
|
||||
var port = 80
|
||||
if use_ssl:
|
||||
port = 443
|
||||
|
||||
var host_parts = host.split(":")
|
||||
if host_parts.size() > 1:
|
||||
host = host_parts[0]
|
||||
port = host_parts[1].to_int()
|
||||
|
||||
# Connect to host
|
||||
var error: Error
|
||||
if use_ssl:
|
||||
var tls_options = TLSOptions.client()
|
||||
error = http_client.connect_to_host(host, port, tls_options)
|
||||
else:
|
||||
error = http_client.connect_to_host(host, port)
|
||||
if error != OK:
|
||||
response_data.status = 0
|
||||
response_data.status_text = "Connection Failed"
|
||||
return response_data
|
||||
|
||||
# Wait for connection
|
||||
var timeout_count = 0
|
||||
while http_client.get_status() == HTTPClient.STATUS_CONNECTING or http_client.get_status() == HTTPClient.STATUS_RESOLVING:
|
||||
http_client.poll()
|
||||
OS.delay_msec(10)
|
||||
timeout_count += 1
|
||||
if timeout_count > 300: # 3 second timeout
|
||||
response_data.status = 0
|
||||
response_data.status_text = "Connection Timeout"
|
||||
return response_data
|
||||
|
||||
if http_client.get_status() != HTTPClient.STATUS_CONNECTED:
|
||||
response_data.status = 0
|
||||
response_data.status_text = "Connection Failed"
|
||||
return response_data
|
||||
|
||||
# Convert method string to HTTPClient.Method enum
|
||||
var http_method: HTTPClient.Method
|
||||
match method:
|
||||
"GET":
|
||||
http_method = HTTPClient.METHOD_GET
|
||||
"POST":
|
||||
http_method = HTTPClient.METHOD_POST
|
||||
"PUT":
|
||||
http_method = HTTPClient.METHOD_PUT
|
||||
"DELETE":
|
||||
http_method = HTTPClient.METHOD_DELETE
|
||||
"HEAD":
|
||||
http_method = HTTPClient.METHOD_HEAD
|
||||
"OPTIONS":
|
||||
http_method = HTTPClient.METHOD_OPTIONS
|
||||
"PATCH":
|
||||
http_method = HTTPClient.METHOD_PATCH
|
||||
_:
|
||||
http_method = HTTPClient.METHOD_GET
|
||||
|
||||
# Make request
|
||||
error = http_client.request(http_method, path, headers, body)
|
||||
if error != OK:
|
||||
response_data.status = 0
|
||||
response_data.status_text = "Request Failed"
|
||||
return response_data
|
||||
|
||||
# Wait for response
|
||||
timeout_count = 0
|
||||
while http_client.get_status() == HTTPClient.STATUS_REQUESTING:
|
||||
http_client.poll()
|
||||
OS.delay_msec(10)
|
||||
timeout_count += 1
|
||||
if timeout_count > 1000: # 10 second timeout
|
||||
response_data.status = 0
|
||||
response_data.status_text = "Request Timeout"
|
||||
return response_data
|
||||
|
||||
if http_client.get_status() != HTTPClient.STATUS_BODY and http_client.get_status() != HTTPClient.STATUS_CONNECTED:
|
||||
response_data.status = 0
|
||||
response_data.status_text = "Request Failed"
|
||||
return response_data
|
||||
|
||||
# Get response
|
||||
if http_client.has_response():
|
||||
response_data.status = http_client.get_response_code()
|
||||
|
||||
# Get status text
|
||||
match response_data.status:
|
||||
200: response_data.status_text = "OK"
|
||||
201: response_data.status_text = "Created"
|
||||
204: response_data.status_text = "No Content"
|
||||
400: response_data.status_text = "Bad Request"
|
||||
401: response_data.status_text = "Unauthorized"
|
||||
403: response_data.status_text = "Forbidden"
|
||||
404: response_data.status_text = "Not Found"
|
||||
500: response_data.status_text = "Internal Server Error"
|
||||
_: response_data.status_text = "Unknown"
|
||||
|
||||
# Get response headers
|
||||
var response_headers = http_client.get_response_headers_as_dictionary()
|
||||
response_data.headers = response_headers
|
||||
|
||||
# Get response body
|
||||
var body_bytes = PackedByteArray()
|
||||
timeout_count = 0
|
||||
while http_client.get_status() == HTTPClient.STATUS_BODY:
|
||||
http_client.poll()
|
||||
var chunk = http_client.read_response_body_chunk()
|
||||
if chunk.size() > 0:
|
||||
body_bytes.append_array(chunk)
|
||||
timeout_count = 0
|
||||
else:
|
||||
OS.delay_msec(10)
|
||||
timeout_count += 1
|
||||
if timeout_count > 1000: # 10 second timeout for body
|
||||
break
|
||||
|
||||
response_data.body = body_bytes.get_string_from_utf8()
|
||||
|
||||
http_client.close()
|
||||
return response_data
|
||||
1
flumi/Scripts/Utils/Lua/Network.gd.uid
Normal file
1
flumi/Scripts/Utils/Lua/Network.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://ffy8hyqtivhg
|
||||
@@ -325,6 +325,12 @@ func _setup_additional_lua_apis():
|
||||
|
||||
# Setup Clipboard API for threaded execution
|
||||
LuaClipboardUtils.setup_clipboard_api(lua_vm)
|
||||
|
||||
# Setup Network API for threaded execution
|
||||
LuaNetworkUtils.setup_network_api(lua_vm)
|
||||
|
||||
# Setup JSON API for threaded execution
|
||||
LuaJSONUtils.setup_json_api(lua_vm)
|
||||
|
||||
func _threaded_table_tostring_handler(vm: LuauVM) -> int:
|
||||
vm.luaL_checktype(1, vm.LUA_TTABLE)
|
||||
|
||||
Reference in New Issue
Block a user