├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── addons └── unirest-gdscript │ ├── plugin.cfg │ ├── plugin.gd │ └── src │ ├── configuration │ ├── configuration.gd │ ├── configuration.tres │ ├── http_log_format.gd │ ├── http_log_format.tres │ ├── http_proxy.gd │ └── http_proxy.tres │ ├── http │ ├── error.gd │ ├── request │ │ ├── base_request.gd │ │ ├── get_request.gd │ │ ├── http_request_with_body.gd │ │ └── multipart_request.gd │ └── response │ │ ├── base_response.gd │ │ ├── empty_response.gd │ │ ├── json_response.gd │ │ ├── object_response.gd │ │ └── string_response.gd │ ├── unirest.gd │ └── utils │ ├── json_node.gd │ └── operations.gd └── icon.svg /.gitattributes: -------------------------------------------------------------------------------- 1 | # Normalize EOL for all files that Git considers text files. 2 | * text=auto eol=lf 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Godot 4+ specific ignores 2 | .godot/ 3 | scn/ 4 | *.import 5 | project.godot -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | * Copyright for portions of unirest-java are held by Kong Inc (c) 2013-2019. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # unirest-gdscript (4.x) 2 | Unirest in GDScript: Simplified, lightweight HTTP client library. Godot Engine HTTPClient extension inspired by Kong Unirest. 3 | 4 | 👉 [3.x](https://github.com/fenix-hub/unirest-gdscript) 5 | 6 | ### sync example 7 | ```gdscript 8 | func _ready() -> void: 9 | var json_response: JsonResponse = await Unirest.Get("https://jsonplaceholder.typicode.com/posts/{id}") \ 10 | .header("Accept", "application/json") \ 11 | .route_param("id", "1") \ 12 | .as_json() 13 | 14 | # Execution will stop until Unirest receives a response 15 | 16 | var json_node: JsonNode = json_response.get_body() 17 | print(json_node.as_dictionary().get("title")) 18 | ``` 19 | 20 | ### async example (lambda function) 21 | ```gdscript 22 | func _ready() -> void: 23 | Unirest.Get("https://jsonplaceholder.typicode.com/posts/{id}") \ 24 | .header("Accept", "application/json") \ 25 | .route_param("id", "1") \ 26 | .as_json_async( 27 | func(json_response: JsonResponse) -> void: 28 | var title: String = json_response.get_body().as_dictionary().get("title") 29 | print("Title of 1st post is: %s" % title) 30 | ) 31 | 32 | # Execution won't stop, and the anonymous function will be executed automatically 33 | ``` 34 | 35 | ### async example (signals) 36 | ```gdscript 37 | func _ready() -> void: 38 | Unirest.Get("https://jsonplaceholder.typicode.com/posts/{id}") \ 39 | .header("Accept", "application/json") \ 40 | .route_param("id", "1") \ 41 | .as_json_async() \ 42 | .completed.connect(handle_response) 43 | 44 | # Execution won't stop here, and your function will be called upon signal emission 45 | 46 | 47 | func handle_response(json_response: JsonResponse) -> void: 48 | var title: String = json_response.get_body().as_dictionary().get("title") 49 | print("Title of 1st post is: %s" % title) 50 | ``` 51 | -------------------------------------------------------------------------------- /addons/unirest-gdscript/plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="unirest-gdscript" 4 | description="Unirest in GDScript: Simplified, lightweight HTTP client library. Godot Engine HTTPClient extension based on Kong Unirest. " 5 | author="Nicolò (fenix-hub) Santilio" 6 | version="2.0" 7 | script="plugin.gd" 8 | -------------------------------------------------------------------------------- /addons/unirest-gdscript/plugin.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends EditorPlugin 3 | 4 | func _enter_tree() -> void: 5 | add_autoload_singleton("Unirest", "res://addons/unirest-gdscript/src/unirest.gd") 6 | 7 | 8 | func _exit_tree() -> void: 9 | remove_autoload_singleton("Unirest") 10 | -------------------------------------------------------------------------------- /addons/unirest-gdscript/src/configuration/configuration.gd: -------------------------------------------------------------------------------- 1 | extends Resource 2 | class_name UnirestConfig 3 | 4 | @export var http_log_format: HttpLogFormat 5 | @export var http_proxy: HttpProxy 6 | @export var default_base_url: String 7 | 8 | func _init( 9 | http_log_format: HttpLogFormat = null, 10 | http_proxy: HttpProxy = null, 11 | default_base_url: String = "" 12 | ) -> void: 13 | self.http_log_format = http_log_format 14 | self.http_proxy = http_proxy 15 | self.default_base_url = default_base_url 16 | 17 | func client_certificate_store(cert_path: String) -> void: 18 | ProjectSettings.set_setting("network/ssl/certificates", cert_path) 19 | -------------------------------------------------------------------------------- /addons/unirest-gdscript/src/configuration/configuration.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Resource" load_steps=4 format=3 uid="uid://dm2iaxgvkjq1r"] 2 | 3 | [ext_resource type="Script" path="res://addons/unirest-gdscript/src/configuration/configuration.gd" id="1_6kwt3"] 4 | [ext_resource type="Resource" uid="uid://6c0f7fftersw" path="res://addons/unirest-gdscript/src/configuration/http_log_format.tres" id="1_rd3ry"] 5 | [ext_resource type="Resource" uid="uid://c15rd4a1w3jud" path="res://addons/unirest-gdscript/src/configuration/http_proxy.tres" id="2_6rbo7"] 6 | 7 | [resource] 8 | script = ExtResource("1_6kwt3") 9 | http_log_format = ExtResource("1_rd3ry") 10 | http_proxy = ExtResource("2_6rbo7") 11 | default_base_url = "" 12 | -------------------------------------------------------------------------------- /addons/unirest-gdscript/src/configuration/http_log_format.gd: -------------------------------------------------------------------------------- 1 | extends Resource 2 | class_name HttpLogFormat 3 | 4 | @export var request: String = '[{date}] - {host} >> "{method} {URI} {protocol}" {bytes} "{agent}"' 5 | @export var response: String = '[{date}] - {host} << "{protocol} {status} {message}" {headers} {body} ({ttr}ms)' 6 | 7 | func _init( 8 | request: String = "", 9 | response: String = "" 10 | ) -> void: 11 | self.request = request 12 | self.response = response 13 | -------------------------------------------------------------------------------- /addons/unirest-gdscript/src/configuration/http_log_format.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Resource" load_steps=2 format=3 uid="uid://6c0f7fftersw"] 2 | 3 | [ext_resource type="Script" path="res://addons/unirest-gdscript/src/configuration/http_log_format.gd" id="1_3l0rp"] 4 | 5 | [resource] 6 | resource_name = "HttpLogFormat" 7 | script = ExtResource("1_3l0rp") 8 | request = "[{date}] - {host} >> \"{method} {URI} {protocol}\" {bytes} \"{agent}\"" 9 | response = "[{date}] - {host} << \"{protocol} {status} {message}\" {headers} {body} ({ttr}ms)" 10 | -------------------------------------------------------------------------------- /addons/unirest-gdscript/src/configuration/http_proxy.gd: -------------------------------------------------------------------------------- 1 | extends Resource 2 | class_name HttpProxy 3 | 4 | @export var host: String = "" 5 | @export var port: int = -1 6 | @export var username: String = "" 7 | @export var password: String = "" 8 | 9 | func _init( 10 | host: String = "", 11 | port: int = -1, 12 | username: String = "", 13 | password: String = "" 14 | ) -> void: 15 | self.host = host 16 | self.port = port 17 | self.username = username 18 | self.password = password 19 | -------------------------------------------------------------------------------- /addons/unirest-gdscript/src/configuration/http_proxy.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Resource" load_steps=2 format=3 uid="uid://c15rd4a1w3jud"] 2 | 3 | [ext_resource type="Script" path="res://addons/unirest-gdscript/src/configuration/http_proxy.gd" id="1_ex4u1"] 4 | 5 | [resource] 6 | resource_name = "HttpProxy" 7 | script = ExtResource("1_ex4u1") 8 | host = "" 9 | port = -1 10 | username = "" 11 | password = "" 12 | -------------------------------------------------------------------------------- /addons/unirest-gdscript/src/http/error.gd: -------------------------------------------------------------------------------- 1 | extends RefCounted 2 | class_name UnirestError 3 | 4 | var cause: Dictionary 5 | var message: String 6 | var original_body: String 7 | 8 | func _init(cause: Dictionary, message: String = "", original_body: String = "") -> void: 9 | self.cause = cause 10 | self.message = message 11 | self.original_body = original_body 12 | 13 | func get_cause() -> Dictionary: 14 | return self.cause 15 | 16 | func get_message() -> String: 17 | return self.message 18 | 19 | func get_original_body() -> String: 20 | return self.original_body 21 | 22 | func _to_string() -> String: 23 | return str({ 24 | cause = cause, 25 | message = message, 26 | original_body = original_body 27 | }) 28 | -------------------------------------------------------------------------------- /addons/unirest-gdscript/src/http/request/base_request.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends HTTPRequest 3 | class_name BaseRequest 4 | 5 | signal completed(http_response) 6 | 7 | enum ResponseType { 8 | EMPTY, 9 | BINARY, 10 | STRING, 11 | UJSON, 12 | OBJECT, 13 | } 14 | 15 | var http_log_format: HttpLogFormat 16 | var http_proxy: HttpProxy 17 | 18 | var response_type: ResponseType 19 | var content_type: String 20 | 21 | var method: int = 0 22 | var uri: String = "" 23 | var _headers: Dictionary = {} 24 | var _body: PackedByteArray = [] 25 | var query_params: Dictionary = {} 26 | var route_params: Dictionary = {} 27 | var verify_ssl: bool = false 28 | 29 | func _init( 30 | uri: String, method: int, _headers: Dictionary = {}, 31 | query_params: Dictionary = {}, route_params: Dictionary = {}, _body: PackedByteArray = PackedByteArray([]) 32 | ) -> void: 33 | request_completed.connect(_on_http_request_completed) 34 | self.uri = uri 35 | self.method = method 36 | self._headers = _headers 37 | self.query_params = query_params 38 | self.route_params = route_params 39 | self._body = _body 40 | 41 | func _ready() -> void: 42 | http_proxy = get_parent().config.http_proxy 43 | http_log_format = get_parent().config.http_log_format 44 | 45 | # Check if proxy is enabled from configuration 46 | if (http_proxy.host != null and http_proxy.port != null): 47 | proxy(http_proxy.host, http_proxy.port) 48 | if !(http_proxy.username.is_empty() and http_proxy.password.is_empty()): 49 | self._headers["Proxy-Authorization"] = \ 50 | "Basic %s" % UniOperations.basic_auth_str(http_proxy.username, http_proxy.password) 51 | 52 | # Base Url 53 | if not get_parent().config.default_base_url.is_empty(): 54 | set_meta("base_url", get_parent().config.default_base_url) 55 | 56 | func _get_url() -> String: 57 | return (get_meta("base_url", "") + uri) 58 | 59 | func make_request(response_type: ResponseType) -> int: 60 | self.response_type = response_type 61 | 62 | # URL 63 | var URL: String = UniOperations.get_full_url( 64 | _get_url(), route_params, query_params 65 | ) 66 | 67 | # PROXY 68 | if get_meta("proxying", false): 69 | if URL.begins_with("https"): 70 | set_https_proxy(get_meta("proxy_host"), get_meta("proxy_port")) 71 | else: 72 | set_http_proxy(get_meta("proxy_host"), get_meta("proxy_port")) 73 | 74 | # _body 75 | self._body = _parse_body() 76 | 77 | # _headers 78 | self._headers["Content-Type"] = self.content_type 79 | self._headers["Content-Length"] = self._body.size() 80 | 81 | var headers: PackedStringArray = UniOperations.headers_from_dictionary(_headers) 82 | 83 | set_meta("t0_ttr", Time.get_ticks_msec()) 84 | return request_raw( 85 | URL, 86 | headers, 87 | method, 88 | self._body 89 | ) 90 | 91 | func _parse_body() -> PackedByteArray: 92 | return self._body 93 | 94 | func _as(type: ResponseType) -> BaseResponse: 95 | make_request(type) 96 | return await completed 97 | 98 | func _as_async(type: ResponseType, callback: Callable = Callable()) -> BaseRequest: 99 | if not callback.is_null(): 100 | self.completed.connect(callback, Object.ConnectFlags.CONNECT_ONE_SHOT) 101 | make_request(type) 102 | return self 103 | 104 | 105 | ## EMPTY 106 | func as_empty() -> EmptyResponse: 107 | return await _as(ResponseType.EMPTY) 108 | 109 | func as_empty_async(callback: Callable = Callable()) -> BaseRequest: 110 | return _as_async(ResponseType.EMPTY, callback) 111 | 112 | 113 | ## BINARY 114 | func as_binary() -> BaseResponse: 115 | return await _as(ResponseType.BINARY) 116 | 117 | func as_binary_async(callback: Callable = Callable()) -> BaseRequest: 118 | return _as_async(ResponseType.BINARY, callback) 119 | 120 | 121 | ## STRING 122 | func as_string() -> StringResponse: 123 | return await _as(ResponseType.STRING) 124 | 125 | func as_string_async(callback: Callable = Callable()) -> BaseRequest: 126 | return _as_async(ResponseType.STRING, callback) 127 | 128 | 129 | ## JSON 130 | func as_json() -> JsonResponse: 131 | return await _as(ResponseType.UJSON) 132 | 133 | func as_json_async(callback: Callable = Callable()) -> BaseRequest: 134 | return _as_async(ResponseType.UJSON, callback) 135 | 136 | 137 | ## OBJECT 138 | func as_object(object: Object) -> ObjectResponse: 139 | set_meta("object", object) 140 | return await _as(ResponseType.OBJECT) 141 | 142 | func as_object_async(object: Object, callback: Callable = Callable()) -> BaseRequest: 143 | set_meta("object", object) 144 | return _as_async(ResponseType.OBJECT, callback) 145 | 146 | 147 | 148 | func proxy(host: String, port: int) -> BaseRequest: 149 | set_meta("proxying", !(host.is_empty() and port == -1)) 150 | set_meta("proxy_host", host) 151 | set_meta("proxy_port", port) 152 | return self 153 | 154 | func with_verify_ssl(verify_ssl: bool = false) -> BaseRequest: 155 | self.verify_ssl = verify_ssl 156 | return self 157 | 158 | func using_threads(use_threads: bool = false) -> BaseRequest: 159 | self.use_threads = use_threads 160 | return self 161 | 162 | func _on_http_request_completed(result: int, response_code: int, _headers: PackedStringArray, _body: PackedByteArray) -> void: 163 | var ttr: int = Time.get_ticks_msec() - get_meta("t0_ttr") 164 | var response: EmptyResponse 165 | match response_type: 166 | ResponseType.EMPTY: 167 | response = EmptyResponse.new(_headers, response_code, result, _body) 168 | ResponseType.BINARY: 169 | response = BaseResponse.new(_body, _headers, response_code, result) 170 | ResponseType.STRING: 171 | response = StringResponse.new(_body, _headers, response_code, result) 172 | ResponseType.UJSON: 173 | response = JsonResponse.new(_body, _headers, response_code, result) 174 | ResponseType.OBJECT: 175 | response = ObjectResponse.new(_body, _headers, response_code, result, get_meta("object")) 176 | response.set_meta("ttr", ttr) 177 | response.set_meta("host", UniOperations.get_host(_get_url())) 178 | response.set_meta("log_format", http_log_format.response) 179 | emit_signal("completed", response) 180 | queue_free() 181 | 182 | func _to_string() -> String: 183 | return http_log_format.request \ 184 | .format({ 185 | host = IP.get_local_addresses()[0] if !get_meta("proxying") else get_meta("proxy_host"), 186 | date = Time.get_datetime_string_from_system(), 187 | method = UniOperations.http_method_int_to_string(method), 188 | URL = UniOperations.get_full_url(_get_url(), route_params, query_params), 189 | query = UniOperations.query_string_from_dict(query_params), 190 | protocol = "HTTP/1.1", 191 | bytes = _body.size(), 192 | agent = "Unirest/1.2 (Godot Engine %s)" % Engine.get_version_info().hex 193 | }) 194 | 195 | 196 | ##### SHARED METHODS #### 197 | func basic_auth(username: String, password: String) -> GetRequest: 198 | header("Authorization", "Basic " + UniOperations.basic_auth_str(username, password)) 199 | return self 200 | 201 | func bearer_auth(token: String) -> GetRequest: 202 | header("Authorization", "Bearer " + token) 203 | return self 204 | 205 | func header(name: String, value: String) -> GetRequest: 206 | _headers[name] = value 207 | return self 208 | 209 | func headers(headers: Dictionary) -> GetRequest: 210 | _headers = headers 211 | return self 212 | 213 | func query_string(name: String, value) -> GetRequest: 214 | self.query_params[name] = value 215 | return self 216 | 217 | func route_param(name: String, value: String) -> GetRequest: 218 | self.route_params[name] = value 219 | return self 220 | -------------------------------------------------------------------------------- /addons/unirest-gdscript/src/http/request/get_request.gd: -------------------------------------------------------------------------------- 1 | extends BaseRequest 2 | class_name GetRequest 3 | 4 | func _init(uri: String, method: int) -> void: 5 | super(uri, method) 6 | 7 | 8 | -------------------------------------------------------------------------------- /addons/unirest-gdscript/src/http/request/http_request_with_body.gd: -------------------------------------------------------------------------------- 1 | extends BaseRequest 2 | class_name HttpRequestWithBody 3 | 4 | func _init(uri: String, method: int) -> void: 5 | super(uri, method) 6 | 7 | func body(body: Object) -> HttpRequestWithBody: 8 | return dict_body(UniOperations.class_to_json(body)) 9 | 10 | func raw_body(body: PackedByteArray, content_type: String = "application/octet-stream") -> HttpRequestWithBody: 11 | self.content_type = content_type 12 | self._body = body 13 | return self 14 | 15 | func str_body(string_body: String, content_type: String = "text/plain") -> HttpRequestWithBody: 16 | return raw_body(string_body.to_utf8_buffer(), content_type) 17 | 18 | func dict_body(dictionary_body: Dictionary) -> HttpRequestWithBody: 19 | return str_body(str(dictionary_body), "application/json") 20 | 21 | func field(name: String, value: String, filename: String = "") -> MultipartRequest: 22 | return MultipartRequest.new(self as BaseRequest).field(name, value, filename) 23 | -------------------------------------------------------------------------------- /addons/unirest-gdscript/src/http/request/multipart_request.gd: -------------------------------------------------------------------------------- 1 | extends BaseRequest 2 | class_name MultipartRequest 3 | 4 | class MultipartField: 5 | var name: String = "" 6 | var filename: String = "" 7 | var value: String = "" 8 | 9 | func _init(name: String, value: String, filename: String = "") -> void: 10 | self.name = name 11 | self.value = value 12 | self.filename = filename 13 | 14 | const boundary: String = "gdunirest" 15 | 16 | var fields: Array = [] 17 | 18 | func _init(base_request: BaseRequest) -> void: 19 | super( 20 | base_request.uri, base_request.method, base_request._headers, 21 | base_request.query_params, base_request.route_params, base_request._body 22 | ) 23 | 24 | func field(name: String, value: String, filename: String = "") -> MultipartRequest: 25 | self.fields.append(MultipartField.new(name, value, filename)) 26 | return self 27 | 28 | func _parse_body() -> PackedByteArray: 29 | self.content_type = "multipart/form-data; boundary=" + boundary 30 | var body: String = "" 31 | for field in fields: 32 | body += "--%s\n" % boundary 33 | body += "Content-Disposition: form-data; name=" + field.name 34 | if !field.filename.empty(): 35 | body += "; filename=" + field.filename 36 | body += "\n\n%s\n" % field.value 37 | body += "--%s--" % boundary 38 | return body.to_utf8_buffer() 39 | -------------------------------------------------------------------------------- /addons/unirest-gdscript/src/http/response/base_response.gd: -------------------------------------------------------------------------------- 1 | extends EmptyResponse 2 | class_name BaseResponse 3 | 4 | var raw_body: PackedByteArray 5 | 6 | func _init( 7 | body: PackedByteArray, headers: PackedStringArray, status: int, 8 | code: int, props: Dictionary = {} 9 | ) -> void: 10 | super(headers, status, code, body, props) 11 | if code == 0: 12 | self.raw_body = body 13 | _parse_body(raw_body) 14 | 15 | # @override 16 | func _parse_body(body: PackedByteArray) -> void: 17 | pass 18 | 19 | func get_body(): 20 | return raw_body 21 | -------------------------------------------------------------------------------- /addons/unirest-gdscript/src/http/response/empty_response.gd: -------------------------------------------------------------------------------- 1 | extends RefCounted 2 | class_name EmptyResponse 3 | 4 | var props: Dictionary 5 | 6 | var headers: Dictionary 7 | var status: int 8 | var error: UnirestError = null 9 | 10 | func _init(headers: PackedStringArray, status: int, code: int, raw_body: PackedByteArray, props: Dictionary = {}) -> void: 11 | self.props = props 12 | if code > 0: 13 | error = UnirestError.new( 14 | {type = "HttpClient error", code = code}, 15 | "HttpClient error is %s" % code 16 | ) 17 | else: 18 | self.headers = UniOperations.dictionary_to_headers(headers) 19 | self.status = status 20 | if (status in [300, 500]): 21 | error = UnirestError.new( 22 | {status = status}, 23 | "HTTP Status Code is %s" % status, raw_body.get_string_from_utf8() 24 | ) 25 | 26 | func get_headers() -> Dictionary: 27 | return headers 28 | 29 | func get_status() -> int: 30 | return status 31 | 32 | func _to_string() -> String: 33 | return get_meta("log_format", "").format({ 34 | host = UniOperations.resolve_host(get_meta("host")), 35 | date = Time.get_datetime_string_from_system(), 36 | headers = self.headers, 37 | status = self.status, 38 | ttr = get_meta("ttr", "?") 39 | }) 40 | -------------------------------------------------------------------------------- /addons/unirest-gdscript/src/http/response/json_response.gd: -------------------------------------------------------------------------------- 1 | extends BaseResponse 2 | class_name JsonResponse 3 | 4 | var _body: JsonNode = null 5 | 6 | func _parse_body(raw_body: PackedByteArray) -> void: 7 | var json: JSON = JSON.new() 8 | var err: int = json.parse(raw_body.get_string_from_utf8()) 9 | if err != OK: 10 | var cause: Dictionary = { 11 | code = err, 12 | line = json.get_error_line(), 13 | string = json.get_error_message() 14 | } 15 | self.error = UnirestError.new( 16 | cause, 17 | "error while parsing the response", 18 | raw_body.get_string_from_utf8() 19 | ) 20 | else: 21 | _body = JsonNode.new(json.data) 22 | 23 | func _init(body: PackedByteArray, headers: PackedStringArray, status: int, code: int) -> void: 24 | super(body, headers, status, code) 25 | 26 | func get_body() -> JsonNode: 27 | return self._body 28 | 29 | func _to_string() -> String: 30 | return super._to_string().format({ 31 | body = self._body 32 | }) 33 | -------------------------------------------------------------------------------- /addons/unirest-gdscript/src/http/response/object_response.gd: -------------------------------------------------------------------------------- 1 | extends BaseResponse 2 | class_name ObjectResponse 3 | 4 | var _dict: Dictionary = {} 5 | var _body: Object = null 6 | 7 | func _parse_body(raw_body: PackedByteArray) -> void: 8 | var json: JSON = JSON.new() 9 | var err: int = json.parse(raw_body.get_string_from_utf8()) 10 | if err != OK: 11 | var cause: Dictionary = { 12 | code = err, 13 | line = json.get_error_line(), 14 | string = json.get_error_message() 15 | } 16 | self.error = UnirestError.new( 17 | cause, 18 | "error while parsing the response", 19 | raw_body.get_string_from_utf8() 20 | ) 21 | else: 22 | _dict = json.data 23 | _body = UniOperations.json_to_class(_dict, self.props.obj) 24 | 25 | func _init( 26 | body: PackedByteArray, headers: PackedStringArray, 27 | status: int, code: int, obj: Object 28 | ) -> void: 29 | super(body, headers, status, code, { obj = obj }) 30 | 31 | func get_dict() -> Dictionary: 32 | return self._dict 33 | 34 | func get_body() -> Object: 35 | return self._body 36 | 37 | func _to_string() -> String: 38 | return super._to_string().format({ 39 | body = self._body 40 | }) 41 | -------------------------------------------------------------------------------- /addons/unirest-gdscript/src/http/response/string_response.gd: -------------------------------------------------------------------------------- 1 | extends BaseResponse 2 | class_name StringResponse 3 | 4 | var _body: String 5 | 6 | func _parse_body(raw_body: PackedByteArray) -> void: 7 | _body = raw_body.get_string_from_utf8() 8 | 9 | func _init(body: PackedByteArray, headers: PackedStringArray, status: int, code: int) -> void: 10 | super(body, headers, status, code) 11 | 12 | func get_body() -> String: 13 | return _body 14 | 15 | func _to_string() -> String: 16 | return super._to_string().format({ 17 | body = self._body 18 | }) 19 | -------------------------------------------------------------------------------- /addons/unirest-gdscript/src/unirest.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends Node 3 | 4 | var config: UnirestConfig = load("res://addons/unirest-gdscript/src/configuration/configuration.tres") 5 | 6 | func _get_request(url: String, method: int) -> GetRequest: 7 | var http_request: GetRequest = GetRequest.new(url, method) 8 | add_child(http_request) 9 | return http_request 10 | 11 | func _http_request(url: String, method: int) -> HttpRequestWithBody: 12 | var http_request: HttpRequestWithBody = HttpRequestWithBody.new(url, method) 13 | add_child(http_request) 14 | return http_request 15 | 16 | func options(url: String) -> HttpRequestWithBody: 17 | return _http_request(url, HTTPClient.METHOD_OPTIONS) 18 | 19 | func head(url: String) -> GetRequest: 20 | return _get_request(url, HTTPClient.METHOD_HEAD) 21 | 22 | func Get(url: String) -> GetRequest: 23 | return _get_request(url, HTTPClient.METHOD_GET) 24 | 25 | func post(url: String) -> HttpRequestWithBody: 26 | return _http_request(url, HTTPClient.METHOD_POST) 27 | 28 | func put(url: String) -> HttpRequestWithBody: 29 | return _http_request(url, HTTPClient.METHOD_PUT) 30 | 31 | func patch(url: String) -> HttpRequestWithBody: 32 | return _http_request(url, HTTPClient.METHOD_PATCH) 33 | 34 | func delete(url: String) -> HttpRequestWithBody: 35 | return _http_request(url, HTTPClient.METHOD_DELETE) 36 | 37 | 38 | -------------------------------------------------------------------------------- /addons/unirest-gdscript/src/utils/json_node.gd: -------------------------------------------------------------------------------- 1 | extends RefCounted 2 | class_name JsonNode 3 | 4 | var result = null 5 | 6 | func _init(result) -> void: 7 | self.result = result 8 | 9 | func is_array() -> bool: 10 | return (typeof(result) == TYPE_ARRAY) 11 | 12 | func is_dict() -> bool: 13 | return (typeof(result) == TYPE_DICTIONARY) 14 | 15 | # Untyped return 16 | func get_result(): 17 | return result 18 | 19 | func as_array() -> Array: 20 | if not is_array(): 21 | printerr("Result is not an array!") 22 | return [] 23 | var _res: Array = result 24 | return _res 25 | 26 | func as_dictionary() -> Dictionary: 27 | if not is_dict(): 28 | printerr("Result is not a dictionary!") 29 | return {} 30 | var _res: Dictionary = result 31 | return _res 32 | 33 | func _to_string() -> String: 34 | return JSON.stringify(result) 35 | -------------------------------------------------------------------------------- /addons/unirest-gdscript/src/utils/operations.gd: -------------------------------------------------------------------------------- 1 | extends RefCounted 2 | class_name UniOperations 3 | 4 | static func http_method_int_to_string(method: int) -> String: 5 | var str_method: String = "" 6 | match method: 7 | HTTPClient.METHOD_HEAD: 8 | str_method = "HEAD" 9 | HTTPClient.METHOD_OPTIONS: 10 | str_method = "OPTIONS" 11 | HTTPClient.METHOD_DELETE: 12 | str_method = "DELETE" 13 | HTTPClient.METHOD_PATCH: 14 | str_method = "PATCH" 15 | HTTPClient.METHOD_PUT: 16 | str_method = "PUT" 17 | HTTPClient.METHOD_POST: 18 | str_method = "POST" 19 | HTTPClient.METHOD_TRACE: 20 | str_method = "TRACE" 21 | HTTPClient.METHOD_GET, _: 22 | str_method = "GET" 23 | return str_method 24 | 25 | static func basic_auth_str(username: String, password: String) -> String: 26 | return Marshalls.utf8_to_base64("%s:%s" % [username, password]) 27 | 28 | static func get_host(url: String) -> String: 29 | var host: String = url.split("/")[2] 30 | return host 31 | 32 | static func resolve_host(host: String) -> String: 33 | return IP.resolve_hostname(host) if !(":" in host) else host.right(5) 34 | 35 | static func get_full_url(base_url: String, route_params: Dictionary = {}, query_params: Dictionary = {}) -> String: 36 | var URL: String = base_url.format(route_params) 37 | var query_string: String = query_string_from_dict(query_params) 38 | if !query_string.is_empty(): 39 | URL += "?" + query_string 40 | return URL 41 | 42 | static func headers_from_dictionary(headers: Dictionary) -> PackedStringArray: 43 | var array: Array = [] 44 | for key in headers.keys(): 45 | array.append("%s: %s" % [key, headers.get(key)]) 46 | return PackedStringArray(array) 47 | 48 | static func dictionary_to_headers(headers: PackedStringArray) -> Dictionary: 49 | var dictionary: Dictionary = {} 50 | for header in headers: 51 | var kv: Array = header.split(":", true, 1) 52 | var name: String = kv[0] 53 | var value: Dictionary = {} 54 | var t_value: String = str(kv[1]).lstrip(" ") 55 | if (t_value.begins_with("{") and t_value.ends_with("}")) \ 56 | or (t_value.begins_with("[") and t_value.ends_with("]")): 57 | value = JSON.parse_string(t_value) 58 | dictionary[name] = value 59 | return dictionary 60 | 61 | static func query_array_from_dict(query: Dictionary) -> PackedStringArray: 62 | var array: Array = [] 63 | for key in query.keys(): 64 | match typeof(query[key]): 65 | TYPE_ARRAY: 66 | for value in query.get(key): 67 | array.append("%s=%s" % [key, value]) 68 | TYPE_DICTIONARY: 69 | for k_key in query.get(key).keys(): 70 | array.append("%s=%s" % [k_key, ]) 71 | _: 72 | array.append("%s=%s" % [key, query.get(key)]) 73 | return PackedStringArray(array) 74 | 75 | static func query_string_from_dict(query: Dictionary) -> String: 76 | return "".join(query_array_from_dict(query)) 77 | 78 | static func json_string_to_class(json_string: String, _class: Object) -> Object: 79 | var json: JSON = JSON.new() 80 | if json.parse(json_string) == OK: 81 | return json_to_class(json.data, _class) 82 | return _class 83 | 84 | static func json_to_class(json: Dictionary, _class: Object) -> Object: 85 | var properties: Array = _class.get_property_list() 86 | for key in json.keys(): 87 | for property in properties: 88 | if property.name == key and property.usage >= 4096: 89 | if String(property["class_name"]).is_empty(): 90 | _class.set(key, json[key]) 91 | elif property["class_name"] in ["RefCounted", "Object"]: 92 | _class.set(key, json_to_class(json[key], _class.get(key))) 93 | break 94 | return _class 95 | 96 | static func class_to_json_string(_class: Object) -> String: 97 | return JSON.stringify(class_to_json(_class)) 98 | 99 | static func class_to_json(_class: Object) -> Dictionary: 100 | var dictionary: Dictionary = {} 101 | var properties: Array = _class.get_property_list() 102 | for property in properties: 103 | if not property["name"].empty() and property.usage >= (1 << 13): 104 | if (property["class_name"] in ["Reference", "Object"] and property["type"] == 17): 105 | dictionary[property.name] = class_to_json(_class.get(property.name)) 106 | else: 107 | dictionary[property.name] = _class.get(property.name) 108 | if not property["hint_string"].empty() and property.usage >= (1 << 13): 109 | if (property["class_name"] in ["Reference", "Object"] and property["type"] == 17): 110 | dictionary[property.hint_string] = class_to_json(_class.get(property.name)) 111 | else: 112 | dictionary[property.hint_string] = _class.get(property.name) 113 | return dictionary 114 | -------------------------------------------------------------------------------- /icon.svg: -------------------------------------------------------------------------------- 1 | 2 | --------------------------------------------------------------------------------