Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/scene/main/http_request.cpp
9903 views
1
/**************************************************************************/
2
/* http_request.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "http_request.h"
32
33
#include "scene/main/timer.h"
34
35
Error HTTPRequest::_request() {
36
return client->connect_to_host(url, port, use_tls ? tls_options : nullptr);
37
}
38
39
Error HTTPRequest::_parse_url(const String &p_url) {
40
use_tls = false;
41
request_string = "";
42
port = 80;
43
request_sent = false;
44
got_response = false;
45
body_len = -1;
46
body.clear();
47
downloaded.set(0);
48
final_body_size.set(0);
49
redirections = 0;
50
51
String scheme;
52
String fragment;
53
Error err = p_url.parse_url(scheme, url, port, request_string, fragment);
54
ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Error parsing URL: '%s'.", p_url));
55
56
if (scheme == "https://") {
57
use_tls = true;
58
} else if (scheme != "http://") {
59
ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, vformat("Invalid URL scheme: '%s'.", scheme));
60
}
61
62
if (port == 0) {
63
port = use_tls ? 443 : 80;
64
}
65
if (request_string.is_empty()) {
66
request_string = "/";
67
}
68
return OK;
69
}
70
71
bool HTTPRequest::has_header(const PackedStringArray &p_headers, const String &p_header_name) {
72
bool exists = false;
73
74
String lower_case_header_name = p_header_name.to_lower();
75
for (int i = 0; i < p_headers.size() && !exists; i++) {
76
String sanitized = p_headers[i].strip_edges().to_lower();
77
if (sanitized.begins_with(lower_case_header_name)) {
78
exists = true;
79
}
80
}
81
82
return exists;
83
}
84
85
String HTTPRequest::get_header_value(const PackedStringArray &p_headers, const String &p_header_name) {
86
String value = "";
87
88
String lowwer_case_header_name = p_header_name.to_lower();
89
for (int i = 0; i < p_headers.size(); i++) {
90
if (p_headers[i].find_char(':') > 0) {
91
Vector<String> parts = p_headers[i].split(":", false, 1);
92
if (parts.size() > 1 && parts[0].strip_edges().to_lower() == lowwer_case_header_name) {
93
value = parts[1].strip_edges();
94
break;
95
}
96
}
97
}
98
99
return value;
100
}
101
102
Error HTTPRequest::request(const String &p_url, const Vector<String> &p_custom_headers, HTTPClient::Method p_method, const String &p_request_data) {
103
// Copy the string into a raw buffer.
104
Vector<uint8_t> raw_data;
105
106
CharString charstr = p_request_data.utf8();
107
size_t len = charstr.length();
108
if (len > 0) {
109
raw_data.resize(len);
110
uint8_t *w = raw_data.ptrw();
111
memcpy(w, charstr.ptr(), len);
112
}
113
114
return request_raw(p_url, p_custom_headers, p_method, raw_data);
115
}
116
117
Error HTTPRequest::request_raw(const String &p_url, const Vector<String> &p_custom_headers, HTTPClient::Method p_method, const Vector<uint8_t> &p_request_data_raw) {
118
ERR_FAIL_COND_V(!is_inside_tree(), ERR_UNCONFIGURED);
119
ERR_FAIL_COND_V_MSG(requesting, ERR_BUSY, "HTTPRequest is processing a request. Wait for completion or cancel it before attempting a new one.");
120
121
if (timeout > 0) {
122
timer->stop();
123
timer->start(timeout);
124
}
125
126
method = p_method;
127
128
Error err = _parse_url(p_url);
129
if (err) {
130
return err;
131
}
132
133
headers = p_custom_headers;
134
135
if (accept_gzip) {
136
// If the user has specified an Accept-Encoding header, don't overwrite it.
137
if (!has_header(headers, "Accept-Encoding")) {
138
headers.push_back("Accept-Encoding: gzip, deflate");
139
}
140
}
141
142
request_data = p_request_data_raw;
143
144
requesting = true;
145
146
if (use_threads.is_set()) {
147
thread_done.clear();
148
thread_request_quit.clear();
149
client->set_blocking_mode(true);
150
thread.start(_thread_func, this);
151
} else {
152
client->set_blocking_mode(false);
153
err = _request();
154
if (err != OK) {
155
_defer_done(RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray());
156
return ERR_CANT_CONNECT;
157
}
158
159
set_process_internal(true);
160
}
161
162
return OK;
163
}
164
165
void HTTPRequest::_thread_func(void *p_userdata) {
166
HTTPRequest *hr = static_cast<HTTPRequest *>(p_userdata);
167
168
Error err = hr->_request();
169
170
if (err != OK) {
171
hr->_defer_done(RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray());
172
} else {
173
while (!hr->thread_request_quit.is_set()) {
174
bool exit = hr->_update_connection();
175
if (exit) {
176
break;
177
}
178
OS::get_singleton()->delay_usec(1);
179
}
180
}
181
182
hr->thread_done.set();
183
}
184
185
void HTTPRequest::cancel_request() {
186
timer->stop();
187
188
if (!requesting) {
189
return;
190
}
191
192
if (!use_threads.is_set()) {
193
set_process_internal(false);
194
} else {
195
thread_request_quit.set();
196
if (thread.is_started()) {
197
thread.wait_to_finish();
198
}
199
}
200
201
file.unref();
202
decompressor.unref();
203
client->close();
204
body.clear();
205
got_response = false;
206
response_code = -1;
207
request_sent = false;
208
requesting = false;
209
}
210
211
bool HTTPRequest::_handle_response(bool *ret_value) {
212
if (!client->has_response()) {
213
_defer_done(RESULT_NO_RESPONSE, 0, PackedStringArray(), PackedByteArray());
214
*ret_value = true;
215
return true;
216
}
217
218
got_response = true;
219
response_code = client->get_response_code();
220
List<String> rheaders;
221
client->get_response_headers(&rheaders);
222
response_headers.clear();
223
downloaded.set(0);
224
final_body_size.set(0);
225
decompressor.unref();
226
227
for (const String &E : rheaders) {
228
response_headers.push_back(E);
229
}
230
231
if (response_code == 301 || response_code == 302) {
232
// Handle redirect.
233
234
if (max_redirects >= 0 && redirections >= max_redirects) {
235
_defer_done(RESULT_REDIRECT_LIMIT_REACHED, response_code, response_headers, PackedByteArray());
236
*ret_value = true;
237
return true;
238
}
239
240
String new_request;
241
242
for (const String &E : rheaders) {
243
if (E.to_lower().begins_with("location: ")) {
244
new_request = E.substr(9).strip_edges();
245
}
246
}
247
248
if (!new_request.is_empty()) {
249
// Process redirect.
250
client->close();
251
int new_redirs = redirections + 1; // Because _request() will clear it.
252
Error err;
253
if (new_request.begins_with("http")) {
254
// New url, new request.
255
_parse_url(new_request);
256
} else {
257
request_string = new_request;
258
}
259
260
err = _request();
261
if (err == OK) {
262
request_sent = false;
263
got_response = false;
264
body_len = -1;
265
body.clear();
266
downloaded.set(0);
267
final_body_size.set(0);
268
redirections = new_redirs;
269
*ret_value = false;
270
return true;
271
}
272
}
273
}
274
275
// Check if we need to start streaming decompression.
276
String content_encoding;
277
if (accept_gzip) {
278
content_encoding = get_header_value(response_headers, "Content-Encoding").to_lower();
279
}
280
if (content_encoding == "gzip") {
281
decompressor.instantiate();
282
decompressor->start_decompression(false, get_download_chunk_size());
283
} else if (content_encoding == "deflate") {
284
decompressor.instantiate();
285
decompressor->start_decompression(true, get_download_chunk_size());
286
}
287
288
return false;
289
}
290
291
bool HTTPRequest::_update_connection() {
292
switch (client->get_status()) {
293
case HTTPClient::STATUS_DISCONNECTED: {
294
_defer_done(RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray());
295
return true; // End it, since it's disconnected.
296
} break;
297
case HTTPClient::STATUS_RESOLVING: {
298
client->poll();
299
// Must wait.
300
return false;
301
} break;
302
case HTTPClient::STATUS_CANT_RESOLVE: {
303
_defer_done(RESULT_CANT_RESOLVE, 0, PackedStringArray(), PackedByteArray());
304
return true;
305
306
} break;
307
case HTTPClient::STATUS_CONNECTING: {
308
client->poll();
309
// Must wait.
310
return false;
311
} break; // Connecting to IP.
312
case HTTPClient::STATUS_CANT_CONNECT: {
313
_defer_done(RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray());
314
return true;
315
316
} break;
317
case HTTPClient::STATUS_CONNECTED: {
318
if (request_sent) {
319
if (!got_response) {
320
// No body.
321
322
bool ret_value;
323
324
if (_handle_response(&ret_value)) {
325
return ret_value;
326
}
327
328
_defer_done(RESULT_SUCCESS, response_code, response_headers, PackedByteArray());
329
return true;
330
}
331
if (body_len < 0) {
332
// Chunked transfer is done.
333
_defer_done(RESULT_SUCCESS, response_code, response_headers, body);
334
return true;
335
}
336
337
_defer_done(RESULT_CHUNKED_BODY_SIZE_MISMATCH, response_code, response_headers, PackedByteArray());
338
return true;
339
// Request might have been done.
340
} else {
341
// Did not request yet, do request.
342
343
int size = request_data.size();
344
Error err = client->request(method, request_string, headers, size > 0 ? request_data.ptr() : nullptr, size);
345
if (err != OK) {
346
_defer_done(RESULT_CONNECTION_ERROR, 0, PackedStringArray(), PackedByteArray());
347
return true;
348
}
349
350
request_sent = true;
351
return false;
352
}
353
} break; // Connected: break requests only accepted here.
354
case HTTPClient::STATUS_REQUESTING: {
355
// Must wait, still requesting.
356
client->poll();
357
return false;
358
359
} break; // Request in progress.
360
case HTTPClient::STATUS_BODY: {
361
if (!got_response) {
362
bool ret_value;
363
364
if (_handle_response(&ret_value)) {
365
return ret_value;
366
}
367
368
if (!client->is_response_chunked() && client->get_response_body_length() == 0) {
369
_defer_done(RESULT_SUCCESS, response_code, response_headers, PackedByteArray());
370
return true;
371
}
372
373
// No body len (-1) if chunked or no content-length header was provided.
374
// Change your webserver configuration if you want body len.
375
body_len = client->get_response_body_length();
376
377
if (body_size_limit >= 0 && body_len > body_size_limit) {
378
_defer_done(RESULT_BODY_SIZE_LIMIT_EXCEEDED, response_code, response_headers, PackedByteArray());
379
return true;
380
}
381
382
if (!download_to_file.is_empty()) {
383
file = FileAccess::open(download_to_file, FileAccess::WRITE);
384
if (file.is_null()) {
385
_defer_done(RESULT_DOWNLOAD_FILE_CANT_OPEN, response_code, response_headers, PackedByteArray());
386
return true;
387
}
388
}
389
}
390
391
client->poll();
392
if (client->get_status() != HTTPClient::STATUS_BODY) {
393
return false;
394
}
395
396
PackedByteArray chunk;
397
if (decompressor.is_null()) {
398
// Chunk can be read directly.
399
chunk = client->read_response_body_chunk();
400
downloaded.add(chunk.size());
401
} else {
402
// Chunk is the result of decompression.
403
PackedByteArray compressed = client->read_response_body_chunk();
404
downloaded.add(compressed.size());
405
406
int pos = 0;
407
int left = compressed.size();
408
while (left) {
409
int w = 0;
410
Error err = decompressor->put_partial_data(compressed.ptr() + pos, left, w);
411
if (err == OK) {
412
PackedByteArray dc;
413
dc.resize(decompressor->get_available_bytes());
414
err = decompressor->get_data(dc.ptrw(), dc.size());
415
chunk.append_array(dc);
416
}
417
if (err != OK) {
418
_defer_done(RESULT_BODY_DECOMPRESS_FAILED, response_code, response_headers, PackedByteArray());
419
return true;
420
}
421
// We need this check here because a "zip bomb" could result in a chunk of few kilos decompressing into gigabytes of data.
422
if (body_size_limit >= 0 && final_body_size.get() + chunk.size() > body_size_limit) {
423
_defer_done(RESULT_BODY_SIZE_LIMIT_EXCEEDED, response_code, response_headers, PackedByteArray());
424
return true;
425
}
426
pos += w;
427
left -= w;
428
}
429
}
430
final_body_size.add(chunk.size());
431
432
if (body_size_limit >= 0 && final_body_size.get() > body_size_limit) {
433
_defer_done(RESULT_BODY_SIZE_LIMIT_EXCEEDED, response_code, response_headers, PackedByteArray());
434
return true;
435
}
436
437
if (chunk.size()) {
438
if (file.is_valid()) {
439
const uint8_t *r = chunk.ptr();
440
file->store_buffer(r, chunk.size());
441
if (file->get_error() != OK) {
442
_defer_done(RESULT_DOWNLOAD_FILE_WRITE_ERROR, response_code, response_headers, PackedByteArray());
443
return true;
444
}
445
} else {
446
body.append_array(chunk);
447
}
448
}
449
450
if (body_len >= 0) {
451
if (downloaded.get() == body_len) {
452
_defer_done(RESULT_SUCCESS, response_code, response_headers, body);
453
return true;
454
}
455
} else if (client->get_status() == HTTPClient::STATUS_DISCONNECTED) {
456
// We read till EOF, with no errors. Request is done.
457
_defer_done(RESULT_SUCCESS, response_code, response_headers, body);
458
return true;
459
}
460
461
return false;
462
463
} break; // Request resulted in body: break which must be read.
464
case HTTPClient::STATUS_CONNECTION_ERROR: {
465
_defer_done(RESULT_CONNECTION_ERROR, 0, PackedStringArray(), PackedByteArray());
466
return true;
467
} break;
468
case HTTPClient::STATUS_TLS_HANDSHAKE_ERROR: {
469
_defer_done(RESULT_TLS_HANDSHAKE_ERROR, 0, PackedStringArray(), PackedByteArray());
470
return true;
471
} break;
472
}
473
474
ERR_FAIL_V(false);
475
}
476
477
void HTTPRequest::_defer_done(int p_status, int p_code, const PackedStringArray &p_headers, const PackedByteArray &p_data) {
478
callable_mp(this, &HTTPRequest::_request_done).call_deferred(p_status, p_code, p_headers, p_data);
479
}
480
481
void HTTPRequest::_request_done(int p_status, int p_code, const PackedStringArray &p_headers, const PackedByteArray &p_data) {
482
cancel_request();
483
484
emit_signal(SNAME("request_completed"), p_status, p_code, p_headers, p_data);
485
}
486
487
void HTTPRequest::_notification(int p_what) {
488
switch (p_what) {
489
case NOTIFICATION_INTERNAL_PROCESS: {
490
if (use_threads.is_set()) {
491
return;
492
}
493
bool done = _update_connection();
494
if (done) {
495
set_process_internal(false);
496
}
497
} break;
498
499
case NOTIFICATION_EXIT_TREE: {
500
if (requesting) {
501
cancel_request();
502
}
503
} break;
504
}
505
}
506
507
void HTTPRequest::set_use_threads(bool p_use) {
508
ERR_FAIL_COND(get_http_client_status() != HTTPClient::STATUS_DISCONNECTED);
509
#ifdef THREADS_ENABLED
510
use_threads.set_to(p_use);
511
#endif
512
}
513
514
bool HTTPRequest::is_using_threads() const {
515
return use_threads.is_set();
516
}
517
518
void HTTPRequest::set_accept_gzip(bool p_gzip) {
519
accept_gzip = p_gzip;
520
}
521
522
bool HTTPRequest::is_accepting_gzip() const {
523
return accept_gzip;
524
}
525
526
void HTTPRequest::set_body_size_limit(int p_bytes) {
527
ERR_FAIL_COND(get_http_client_status() != HTTPClient::STATUS_DISCONNECTED);
528
529
body_size_limit = p_bytes;
530
}
531
532
int HTTPRequest::get_body_size_limit() const {
533
return body_size_limit;
534
}
535
536
void HTTPRequest::set_download_file(const String &p_file) {
537
ERR_FAIL_COND(get_http_client_status() != HTTPClient::STATUS_DISCONNECTED);
538
539
download_to_file = p_file;
540
}
541
542
String HTTPRequest::get_download_file() const {
543
return download_to_file;
544
}
545
546
void HTTPRequest::set_download_chunk_size(int p_chunk_size) {
547
ERR_FAIL_COND(get_http_client_status() != HTTPClient::STATUS_DISCONNECTED);
548
549
client->set_read_chunk_size(p_chunk_size);
550
}
551
552
int HTTPRequest::get_download_chunk_size() const {
553
return client->get_read_chunk_size();
554
}
555
556
HTTPClient::Status HTTPRequest::get_http_client_status() const {
557
return client->get_status();
558
}
559
560
void HTTPRequest::set_max_redirects(int p_max) {
561
max_redirects = p_max;
562
}
563
564
int HTTPRequest::get_max_redirects() const {
565
return max_redirects;
566
}
567
568
int HTTPRequest::get_downloaded_bytes() const {
569
return downloaded.get();
570
}
571
572
int HTTPRequest::get_body_size() const {
573
return body_len;
574
}
575
576
void HTTPRequest::set_http_proxy(const String &p_host, int p_port) {
577
client->set_http_proxy(p_host, p_port);
578
}
579
580
void HTTPRequest::set_https_proxy(const String &p_host, int p_port) {
581
client->set_https_proxy(p_host, p_port);
582
}
583
584
void HTTPRequest::set_timeout(double p_timeout) {
585
ERR_FAIL_COND(p_timeout < 0);
586
timeout = p_timeout;
587
}
588
589
double HTTPRequest::get_timeout() {
590
return timeout;
591
}
592
593
void HTTPRequest::_timeout() {
594
cancel_request();
595
_defer_done(RESULT_TIMEOUT, 0, PackedStringArray(), PackedByteArray());
596
}
597
598
void HTTPRequest::set_tls_options(const Ref<TLSOptions> &p_options) {
599
ERR_FAIL_COND(p_options.is_null() || p_options->is_server());
600
tls_options = p_options;
601
}
602
603
void HTTPRequest::_bind_methods() {
604
ClassDB::bind_method(D_METHOD("request", "url", "custom_headers", "method", "request_data"), &HTTPRequest::request, DEFVAL(PackedStringArray()), DEFVAL(HTTPClient::METHOD_GET), DEFVAL(String()));
605
ClassDB::bind_method(D_METHOD("request_raw", "url", "custom_headers", "method", "request_data_raw"), &HTTPRequest::request_raw, DEFVAL(PackedStringArray()), DEFVAL(HTTPClient::METHOD_GET), DEFVAL(PackedByteArray()));
606
ClassDB::bind_method(D_METHOD("cancel_request"), &HTTPRequest::cancel_request);
607
ClassDB::bind_method(D_METHOD("set_tls_options", "client_options"), &HTTPRequest::set_tls_options);
608
609
ClassDB::bind_method(D_METHOD("get_http_client_status"), &HTTPRequest::get_http_client_status);
610
611
ClassDB::bind_method(D_METHOD("set_use_threads", "enable"), &HTTPRequest::set_use_threads);
612
ClassDB::bind_method(D_METHOD("is_using_threads"), &HTTPRequest::is_using_threads);
613
614
ClassDB::bind_method(D_METHOD("set_accept_gzip", "enable"), &HTTPRequest::set_accept_gzip);
615
ClassDB::bind_method(D_METHOD("is_accepting_gzip"), &HTTPRequest::is_accepting_gzip);
616
617
ClassDB::bind_method(D_METHOD("set_body_size_limit", "bytes"), &HTTPRequest::set_body_size_limit);
618
ClassDB::bind_method(D_METHOD("get_body_size_limit"), &HTTPRequest::get_body_size_limit);
619
620
ClassDB::bind_method(D_METHOD("set_max_redirects", "amount"), &HTTPRequest::set_max_redirects);
621
ClassDB::bind_method(D_METHOD("get_max_redirects"), &HTTPRequest::get_max_redirects);
622
623
ClassDB::bind_method(D_METHOD("set_download_file", "path"), &HTTPRequest::set_download_file);
624
ClassDB::bind_method(D_METHOD("get_download_file"), &HTTPRequest::get_download_file);
625
626
ClassDB::bind_method(D_METHOD("get_downloaded_bytes"), &HTTPRequest::get_downloaded_bytes);
627
ClassDB::bind_method(D_METHOD("get_body_size"), &HTTPRequest::get_body_size);
628
629
ClassDB::bind_method(D_METHOD("set_timeout", "timeout"), &HTTPRequest::set_timeout);
630
ClassDB::bind_method(D_METHOD("get_timeout"), &HTTPRequest::get_timeout);
631
632
ClassDB::bind_method(D_METHOD("set_download_chunk_size", "chunk_size"), &HTTPRequest::set_download_chunk_size);
633
ClassDB::bind_method(D_METHOD("get_download_chunk_size"), &HTTPRequest::get_download_chunk_size);
634
635
ClassDB::bind_method(D_METHOD("set_http_proxy", "host", "port"), &HTTPRequest::set_http_proxy);
636
ClassDB::bind_method(D_METHOD("set_https_proxy", "host", "port"), &HTTPRequest::set_https_proxy);
637
638
ADD_PROPERTY(PropertyInfo(Variant::STRING, "download_file", PROPERTY_HINT_FILE_PATH), "set_download_file", "get_download_file");
639
ADD_PROPERTY(PropertyInfo(Variant::INT, "download_chunk_size", PROPERTY_HINT_RANGE, "256,16777216,suffix:B"), "set_download_chunk_size", "get_download_chunk_size");
640
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_threads"), "set_use_threads", "is_using_threads");
641
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "accept_gzip"), "set_accept_gzip", "is_accepting_gzip");
642
ADD_PROPERTY(PropertyInfo(Variant::INT, "body_size_limit", PROPERTY_HINT_RANGE, "-1,2000000000,suffix:B"), "set_body_size_limit", "get_body_size_limit");
643
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_redirects", PROPERTY_HINT_RANGE, "-1,64"), "set_max_redirects", "get_max_redirects");
644
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "timeout", PROPERTY_HINT_RANGE, "0,3600,0.1,or_greater,suffix:s"), "set_timeout", "get_timeout");
645
646
ADD_SIGNAL(MethodInfo("request_completed", PropertyInfo(Variant::INT, "result"), PropertyInfo(Variant::INT, "response_code"), PropertyInfo(Variant::PACKED_STRING_ARRAY, "headers"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "body")));
647
648
BIND_ENUM_CONSTANT(RESULT_SUCCESS);
649
BIND_ENUM_CONSTANT(RESULT_CHUNKED_BODY_SIZE_MISMATCH);
650
BIND_ENUM_CONSTANT(RESULT_CANT_CONNECT);
651
BIND_ENUM_CONSTANT(RESULT_CANT_RESOLVE);
652
BIND_ENUM_CONSTANT(RESULT_CONNECTION_ERROR);
653
BIND_ENUM_CONSTANT(RESULT_TLS_HANDSHAKE_ERROR);
654
BIND_ENUM_CONSTANT(RESULT_NO_RESPONSE);
655
BIND_ENUM_CONSTANT(RESULT_BODY_SIZE_LIMIT_EXCEEDED);
656
BIND_ENUM_CONSTANT(RESULT_BODY_DECOMPRESS_FAILED);
657
BIND_ENUM_CONSTANT(RESULT_REQUEST_FAILED);
658
BIND_ENUM_CONSTANT(RESULT_DOWNLOAD_FILE_CANT_OPEN);
659
BIND_ENUM_CONSTANT(RESULT_DOWNLOAD_FILE_WRITE_ERROR);
660
BIND_ENUM_CONSTANT(RESULT_REDIRECT_LIMIT_REACHED);
661
BIND_ENUM_CONSTANT(RESULT_TIMEOUT);
662
}
663
664
HTTPRequest::HTTPRequest() {
665
client = Ref<HTTPClient>(HTTPClient::create());
666
tls_options = TLSOptions::client();
667
timer = memnew(Timer);
668
timer->set_one_shot(true);
669
timer->connect("timeout", callable_mp(this, &HTTPRequest::_timeout));
670
add_child(timer);
671
}
672
673