Initial commit for testing/fixing a deprecated godot-colyseus addon
This commit is contained in:
229
addons/godot_colyseus/lib/http.gd
Normal file
229
addons/godot_colyseus/lib/http.gd
Normal file
@@ -0,0 +1,229 @@
|
||||
extends RefCounted
|
||||
|
||||
const promises = preload("res://addons/godot_colyseus/lib/promises.gd")
|
||||
const Promise = promises.Promise
|
||||
const RunPromise = promises.RunPromise
|
||||
|
||||
var _connected = false
|
||||
var _client_promise: promises.Promise
|
||||
|
||||
class RequestInfo:
|
||||
var method: String = "GET"
|
||||
var path: String = "/"
|
||||
var headers: PackedStringArray = []
|
||||
var body
|
||||
|
||||
func _init(method: String = "GET",path: String = "/"):
|
||||
self.method = method
|
||||
self.path = path
|
||||
|
||||
func add_header(key: String, value):
|
||||
headers.append(str(key, ": ", value))
|
||||
return self
|
||||
|
||||
func http_method():
|
||||
match method.to_upper():
|
||||
"GET":
|
||||
return HTTPClient.METHOD_GET
|
||||
"POST":
|
||||
return HTTPClient.METHOD_POST
|
||||
"PUT":
|
||||
return HTTPClient.METHOD_PUT
|
||||
"DELETE":
|
||||
return HTTPClient.METHOD_DELETE
|
||||
"HEAD":
|
||||
return HTTPClient.METHOD_HEAD
|
||||
"OPTIONS":
|
||||
return HTTPClient.METHOD_OPTIONS
|
||||
|
||||
func get_body():
|
||||
if body == null:
|
||||
body = ""
|
||||
if body is Dictionary or body is Array:
|
||||
body = JSON.stringify(body)
|
||||
return body
|
||||
|
||||
class Response:
|
||||
var _response_chunks: Array = []
|
||||
var _body
|
||||
var _has_response = false
|
||||
|
||||
var _status_code: int = 0
|
||||
var _content_length: int = 0
|
||||
var _headers
|
||||
|
||||
func body() -> PackedByteArray:
|
||||
if _body == null:
|
||||
_body = PackedByteArray()
|
||||
for chunk in _response_chunks:
|
||||
_body.append_array(chunk)
|
||||
return _body
|
||||
|
||||
func text() -> String:
|
||||
return body().get_string_from_utf8()
|
||||
|
||||
func json():
|
||||
var test_json_conv = JSON.new()
|
||||
var err = test_json_conv.parse(text())
|
||||
if err == OK:
|
||||
return test_json_conv.get_data()
|
||||
print(str(test_json_conv.get_error_message(), ":", test_json_conv.get_error_line()))
|
||||
return null
|
||||
|
||||
func headers() -> Dictionary:
|
||||
return _headers
|
||||
|
||||
func status_code() -> int:
|
||||
return _status_code
|
||||
|
||||
func content_length() -> int:
|
||||
return _content_length
|
||||
|
||||
func _to_string():
|
||||
var lines = PackedStringArray()
|
||||
lines.append(str("StatusCode: ", status_code()))
|
||||
lines.append(str("ContentLength: ", content_length()))
|
||||
lines.append(str("Headers: "))
|
||||
var header = headers()
|
||||
for key in header.keys():
|
||||
lines.append(str(" ", key, ": ", header[key]))
|
||||
lines.append(str("Body: [", body().size(), "]"))
|
||||
return "\n".join(lines)
|
||||
|
||||
var _old_status
|
||||
|
||||
func _init(server: String):
|
||||
var url = parseUrl(server)
|
||||
_client_promise = promises.RunPromise.new(Callable(self, "_setup"), [url.host, url.port, url.ssl]);
|
||||
|
||||
static func parseUrl(url) -> Dictionary:
|
||||
var regex = RegEx.new()
|
||||
regex.compile("(\\w+):\\/\\/([^\\/:]+)(:(\\d+))?")
|
||||
var result = regex.search(url)
|
||||
var scheme = result.get_string(1)
|
||||
var ssl = scheme == "https"
|
||||
var host = result.get_string(2)
|
||||
var portstr = result.get_string(4)
|
||||
var port = -1
|
||||
if portstr != "":
|
||||
port = int(portstr)
|
||||
return {
|
||||
"host": host,
|
||||
"ssl": ssl,
|
||||
"port": port,
|
||||
}
|
||||
|
||||
var host: String
|
||||
func _setup(promise: promises.Promise, host, port, ssl):
|
||||
var client = HTTPClient.new()
|
||||
self.host = host
|
||||
var error = client.connect_to_host(host, port, TLSOptions.client() if ssl else null)
|
||||
if error != OK:
|
||||
promise.reject(str("ErrorCode: ", error))
|
||||
var root = Engine.get_main_loop()
|
||||
while true:
|
||||
await root.process_frame
|
||||
client.poll()
|
||||
var status = client.get_status()
|
||||
|
||||
match status:
|
||||
HTTPClient.STATUS_CONNECTED:
|
||||
promise.resolve(client)
|
||||
break
|
||||
HTTPClient.STATUS_DISCONNECTED:
|
||||
promise.reject("Disconnected from Host")
|
||||
break
|
||||
HTTPClient.STATUS_CANT_CONNECT:
|
||||
promise.reject("Can't Connect to Host")
|
||||
break
|
||||
|
||||
func _request(promise: Promise, request: RequestInfo):
|
||||
if _client_promise.get_state() == promises.Promise.State.Waiting:
|
||||
await _client_promise.completed
|
||||
if _client_promise.get_state() == Promise.State.Failed:
|
||||
promise.reject(_client_promise.get_error())
|
||||
return
|
||||
var client: HTTPClient = _client_promise.get_data()
|
||||
var body = request.get_body()
|
||||
var error
|
||||
if body is String:
|
||||
error = client.request(request.http_method(), request.path, request.headers, body)
|
||||
elif body is PackedByteArray:
|
||||
error = client.request_raw(request.http_method(), request.path, request.headers, body)
|
||||
else:
|
||||
promise.reject("Unsupport body type")
|
||||
return
|
||||
if error != OK:
|
||||
promise.reject(str("Error code ", error))
|
||||
return
|
||||
var root = Engine.get_main_loop()
|
||||
var response = Response.new()
|
||||
while true:
|
||||
await root.process_frame
|
||||
error = client.poll()
|
||||
var status = client.get_status()
|
||||
match status:
|
||||
HTTPClient.STATUS_DISCONNECTED:
|
||||
if response._has_response:
|
||||
promise.resolve(response)
|
||||
else:
|
||||
promise.reject("Disconnected from Host")
|
||||
return
|
||||
HTTPClient.STATUS_CANT_CONNECT:
|
||||
promise.reject("Can't Connect to Host")
|
||||
return
|
||||
HTTPClient.STATUS_CONNECTION_ERROR:
|
||||
promise.reject("Connection Error")
|
||||
return
|
||||
HTTPClient.STATUS_BODY:
|
||||
if not response._has_response:
|
||||
response._has_response = true
|
||||
response._status_code = client.get_response_code()
|
||||
response._content_length = client.get_response_body_length()
|
||||
response._headers = client.get_response_headers_as_dictionary()
|
||||
var chunk = client.read_response_body_chunk()
|
||||
if chunk.is_empty():
|
||||
continue
|
||||
response._response_chunks.append(chunk)
|
||||
HTTPClient.STATUS_CONNECTED:
|
||||
promise.resolve(response)
|
||||
return
|
||||
pass
|
||||
|
||||
func _resolve(promise: Promise, request: RequestInfo, response: Response):
|
||||
if response.status_code() == 301:
|
||||
var new_request = RequestInfo.new(request.method, request.path)
|
||||
new_request.headers = request.headers
|
||||
new_request.body = request.body
|
||||
|
||||
var path: String = response.headers()["Location"]
|
||||
if path.begins_with("http://") or path.begins_with("https://"):
|
||||
var idx = path.find('/', 8)
|
||||
var host
|
||||
if idx >= 0:
|
||||
host = path.substr(0, idx)
|
||||
path = path.substr(idx)
|
||||
else:
|
||||
host = path
|
||||
path = '/'
|
||||
var url = parseUrl(host)
|
||||
if url.host != self.host:
|
||||
printerr("Cannot connect to new host ", host, self.host)
|
||||
promise.resolve(response)
|
||||
return
|
||||
else:
|
||||
new_request.path = path
|
||||
|
||||
promise.resolve(fetch(new_request))
|
||||
pass
|
||||
else:
|
||||
promise.resolve(response)
|
||||
|
||||
func fetch(request = null) -> Promise:
|
||||
if request == null:
|
||||
request = RequestInfo.new()
|
||||
elif request is String:
|
||||
var path = request
|
||||
request = RequestInfo.new()
|
||||
request.path = path
|
||||
return RunPromise.new(_request, [request])
|
||||
Reference in New Issue
Block a user