Files
leonwww/flumi/Scripts/Utils/Lua/Network.gd
2025-08-09 14:03:46 +03:00

241 lines
6.7 KiB
GDScript

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