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