130 lines
4.5 KiB
GDScript
130 lines
4.5 KiB
GDScript
@tool
|
|
extends Node
|
|
|
|
signal new_sse_event(headers, event, data)
|
|
signal connected
|
|
signal connection_error(error)
|
|
|
|
const event_tag = "event:"
|
|
const data_tag = "data:"
|
|
const continue_internal = "continue_internal"
|
|
|
|
var httpclient = HTTPClient.new()
|
|
var is_connected = false
|
|
|
|
var domain
|
|
var url_after_domain
|
|
var port
|
|
var trusted_chain
|
|
var common_name_override
|
|
var told_to_connect = false
|
|
var connection_in_progress = false
|
|
var is_requested = false
|
|
var response_body = PackedByteArray()
|
|
|
|
func connect_to_host(domain : String, url_after_domain : String, port : int = -1, trusted_chain : X509Certificate = null, common_name_override : String = ""):
|
|
process_mode = Node.PROCESS_MODE_INHERIT
|
|
self.domain = domain
|
|
self.url_after_domain = url_after_domain
|
|
self.port = port
|
|
self.trusted_chain = trusted_chain
|
|
self.common_name_override = common_name_override
|
|
told_to_connect = true
|
|
|
|
func attempt_to_connect():
|
|
var tls_options = TLSOptions.client(trusted_chain, common_name_override)
|
|
var err = httpclient.connect_to_host(domain, port, tls_options)
|
|
if err == OK:
|
|
connected.emit()
|
|
is_connected = true
|
|
else:
|
|
connection_error.emit(str(err))
|
|
|
|
func attempt_to_request(httpclient_status):
|
|
if httpclient_status == HTTPClient.STATUS_CONNECTING or httpclient_status == HTTPClient.STATUS_RESOLVING:
|
|
return
|
|
|
|
if httpclient_status == HTTPClient.STATUS_CONNECTED:
|
|
var err = httpclient.request(HTTPClient.METHOD_POST, url_after_domain, ["Accept: text/event-stream"])
|
|
if err == OK:
|
|
is_requested = true
|
|
|
|
func _parse_response_body(headers):
|
|
var body = response_body.get_string_from_utf8()
|
|
if body:
|
|
var event_data = get_event_data(body)
|
|
if event_data.event != "keep-alive" and event_data.event != continue_internal:
|
|
var result = Utilities.get_json_data(event_data.data)
|
|
if result != null:
|
|
var parsed_text = result
|
|
if response_body.size() > 0: # stop here if the value doesn't parse
|
|
response_body.resize(0)
|
|
new_sse_event.emit(headers, event_data.event, result)
|
|
else:
|
|
if event_data.event != continue_internal:
|
|
response_body.resize(0)
|
|
|
|
func _process(delta):
|
|
if !told_to_connect:
|
|
return
|
|
|
|
if !is_connected:
|
|
if !connection_in_progress:
|
|
attempt_to_connect()
|
|
connection_in_progress = true
|
|
return
|
|
|
|
httpclient.poll()
|
|
var httpclient_status = httpclient.get_status()
|
|
if !is_requested:
|
|
attempt_to_request(httpclient_status)
|
|
return
|
|
|
|
if httpclient.has_response() or httpclient_status == HTTPClient.STATUS_BODY:
|
|
var headers = httpclient.get_response_headers_as_dictionary()
|
|
|
|
if httpclient_status == HTTPClient.STATUS_BODY:
|
|
httpclient.poll()
|
|
var chunk = httpclient.read_response_body_chunk()
|
|
if(chunk.size() == 0):
|
|
return
|
|
else:
|
|
response_body = response_body + chunk
|
|
|
|
_parse_response_body(headers)
|
|
|
|
elif Firebase.emulating and Firebase._config.workarounds.database_connection_closed_issue:
|
|
# Emulation does not send the close connection header currently, so we need to manually read the response body
|
|
# see issue https://github.com/firebase/firebase-tools/issues/3329 in firebase-tools
|
|
# also comment https://github.com/GodotNuts/GodotFirebase/issues/154#issuecomment-831377763 which explains the issue
|
|
while httpclient.connection.get_available_bytes():
|
|
var data = httpclient.connection.get_partial_data(1)
|
|
if data[0] == OK:
|
|
response_body.append_array(data[1])
|
|
if response_body.size() > 0:
|
|
_parse_response_body(headers)
|
|
|
|
func get_event_data(body : String):
|
|
var result = {}
|
|
var event_idx = body.find(event_tag)
|
|
if event_idx == -1:
|
|
result["event"] = continue_internal
|
|
return result
|
|
assert(event_idx != -1)
|
|
var data_idx = body.find(data_tag, event_idx + event_tag.length())
|
|
assert(data_idx != -1)
|
|
var event = body.substr(event_idx, data_idx)
|
|
var event_value = event.replace(event_tag, "").strip_edges()
|
|
assert(event_value)
|
|
assert(event_value.length() > 0)
|
|
result["event"] = event_value
|
|
var data = body.right(body.length() - (data_idx + data_tag.length())).strip_edges()
|
|
assert(data)
|
|
assert(data.length() > 0)
|
|
result["data"] = data
|
|
return result
|
|
|
|
func _exit_tree():
|
|
if httpclient:
|
|
httpclient.close()
|