1 /*************************************************************************/
2 /* http_client.cpp */
3 /*************************************************************************/
4 /* This file is part of: */
5 /* GODOT ENGINE */
6 /* https://godotengine.org */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
9 /* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
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_client.h"
32
33 #include "core/io/stream_peer_ssl.h"
34 #include "core/version.h"
35
36 const char *HTTPClient::_methods[METHOD_MAX] = {
37 "GET",
38 "HEAD",
39 "POST",
40 "PUT",
41 "DELETE",
42 "OPTIONS",
43 "TRACE",
44 "CONNECT",
45 "PATCH"
46 };
47
48 #ifndef JAVASCRIPT_ENABLED
connect_to_host(const String & p_host,int p_port,bool p_ssl,bool p_verify_host)49 Error HTTPClient::connect_to_host(const String &p_host, int p_port, bool p_ssl, bool p_verify_host) {
50
51 close();
52
53 conn_port = p_port;
54 conn_host = p_host;
55
56 ssl = p_ssl;
57 ssl_verify_host = p_verify_host;
58
59 String host_lower = conn_host.to_lower();
60 if (host_lower.begins_with("http://")) {
61
62 conn_host = conn_host.substr(7, conn_host.length() - 7);
63 } else if (host_lower.begins_with("https://")) {
64
65 ssl = true;
66 conn_host = conn_host.substr(8, conn_host.length() - 8);
67 }
68
69 ERR_FAIL_COND_V(conn_host.length() < HOST_MIN_LEN, ERR_INVALID_PARAMETER);
70
71 if (conn_port < 0) {
72 if (ssl) {
73 conn_port = PORT_HTTPS;
74 } else {
75 conn_port = PORT_HTTP;
76 }
77 }
78
79 connection = tcp_connection;
80
81 if (conn_host.is_valid_ip_address()) {
82 // Host contains valid IP
83 Error err = tcp_connection->connect_to_host(IP_Address(conn_host), p_port);
84 if (err) {
85 status = STATUS_CANT_CONNECT;
86 return err;
87 }
88
89 status = STATUS_CONNECTING;
90 } else {
91 // Host contains hostname and needs to be resolved to IP
92 resolving = IP::get_singleton()->resolve_hostname_queue_item(conn_host);
93 status = STATUS_RESOLVING;
94 }
95
96 return OK;
97 }
98
set_connection(const Ref<StreamPeer> & p_connection)99 void HTTPClient::set_connection(const Ref<StreamPeer> &p_connection) {
100
101 ERR_FAIL_COND_MSG(p_connection.is_null(), "Connection is not a reference to a valid StreamPeer object.");
102
103 close();
104 connection = p_connection;
105 status = STATUS_CONNECTED;
106 }
107
get_connection() const108 Ref<StreamPeer> HTTPClient::get_connection() const {
109
110 return connection;
111 }
112
request_raw(Method p_method,const String & p_url,const Vector<String> & p_headers,const PoolVector<uint8_t> & p_body)113 Error HTTPClient::request_raw(Method p_method, const String &p_url, const Vector<String> &p_headers, const PoolVector<uint8_t> &p_body) {
114
115 ERR_FAIL_INDEX_V(p_method, METHOD_MAX, ERR_INVALID_PARAMETER);
116 ERR_FAIL_COND_V(!p_url.begins_with("/"), ERR_INVALID_PARAMETER);
117 ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_INVALID_PARAMETER);
118 ERR_FAIL_COND_V(connection.is_null(), ERR_INVALID_DATA);
119
120 String request = String(_methods[p_method]) + " " + p_url + " HTTP/1.1\r\n";
121 if ((ssl && conn_port == PORT_HTTPS) || (!ssl && conn_port == PORT_HTTP)) {
122 // Don't append the standard ports
123 request += "Host: " + conn_host + "\r\n";
124 } else {
125 request += "Host: " + conn_host + ":" + itos(conn_port) + "\r\n";
126 }
127 bool add_clen = p_body.size() > 0;
128 bool add_uagent = true;
129 bool add_accept = true;
130 for (int i = 0; i < p_headers.size(); i++) {
131 request += p_headers[i] + "\r\n";
132 if (add_clen && p_headers[i].findn("Content-Length:") == 0) {
133 add_clen = false;
134 }
135 if (add_uagent && p_headers[i].findn("User-Agent:") == 0) {
136 add_uagent = false;
137 }
138 if (add_accept && p_headers[i].findn("Accept:") == 0) {
139 add_accept = false;
140 }
141 }
142 if (add_clen) {
143 request += "Content-Length: " + itos(p_body.size()) + "\r\n";
144 // Should it add utf8 encoding?
145 }
146 if (add_uagent) {
147 request += "User-Agent: GodotEngine/" + String(VERSION_FULL_BUILD) + " (" + OS::get_singleton()->get_name() + ")\r\n";
148 }
149 if (add_accept) {
150 request += "Accept: */*\r\n";
151 }
152 request += "\r\n";
153 CharString cs = request.utf8();
154
155 PoolVector<uint8_t> data;
156 data.resize(cs.length());
157 {
158 PoolVector<uint8_t>::Write data_write = data.write();
159 for (int i = 0; i < cs.length(); i++) {
160 data_write[i] = cs[i];
161 }
162 }
163
164 data.append_array(p_body);
165
166 PoolVector<uint8_t>::Read r = data.read();
167 Error err = connection->put_data(&r[0], data.size());
168
169 if (err) {
170 close();
171 status = STATUS_CONNECTION_ERROR;
172 return err;
173 }
174
175 status = STATUS_REQUESTING;
176 head_request = p_method == METHOD_HEAD;
177
178 return OK;
179 }
180
request(Method p_method,const String & p_url,const Vector<String> & p_headers,const String & p_body)181 Error HTTPClient::request(Method p_method, const String &p_url, const Vector<String> &p_headers, const String &p_body) {
182
183 ERR_FAIL_INDEX_V(p_method, METHOD_MAX, ERR_INVALID_PARAMETER);
184 ERR_FAIL_COND_V(!p_url.begins_with("/"), ERR_INVALID_PARAMETER);
185 ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_INVALID_PARAMETER);
186 ERR_FAIL_COND_V(connection.is_null(), ERR_INVALID_DATA);
187
188 String request = String(_methods[p_method]) + " " + p_url + " HTTP/1.1\r\n";
189 if ((ssl && conn_port == PORT_HTTPS) || (!ssl && conn_port == PORT_HTTP)) {
190 // Don't append the standard ports
191 request += "Host: " + conn_host + "\r\n";
192 } else {
193 request += "Host: " + conn_host + ":" + itos(conn_port) + "\r\n";
194 }
195 bool add_uagent = true;
196 bool add_accept = true;
197 bool add_clen = p_body.length() > 0;
198 for (int i = 0; i < p_headers.size(); i++) {
199 request += p_headers[i] + "\r\n";
200 if (add_clen && p_headers[i].findn("Content-Length:") == 0) {
201 add_clen = false;
202 }
203 if (add_uagent && p_headers[i].findn("User-Agent:") == 0) {
204 add_uagent = false;
205 }
206 if (add_accept && p_headers[i].findn("Accept:") == 0) {
207 add_accept = false;
208 }
209 }
210 if (add_clen) {
211 request += "Content-Length: " + itos(p_body.utf8().length()) + "\r\n";
212 // Should it add utf8 encoding?
213 }
214 if (add_uagent) {
215 request += "User-Agent: GodotEngine/" + String(VERSION_FULL_BUILD) + " (" + OS::get_singleton()->get_name() + ")\r\n";
216 }
217 if (add_accept) {
218 request += "Accept: */*\r\n";
219 }
220 request += "\r\n";
221 request += p_body;
222
223 CharString cs = request.utf8();
224 Error err = connection->put_data((const uint8_t *)cs.ptr(), cs.length());
225 if (err) {
226 close();
227 status = STATUS_CONNECTION_ERROR;
228 return err;
229 }
230
231 status = STATUS_REQUESTING;
232 head_request = p_method == METHOD_HEAD;
233
234 return OK;
235 }
236
has_response() const237 bool HTTPClient::has_response() const {
238
239 return response_headers.size() != 0;
240 }
241
is_response_chunked() const242 bool HTTPClient::is_response_chunked() const {
243
244 return chunked;
245 }
246
get_response_code() const247 int HTTPClient::get_response_code() const {
248
249 return response_num;
250 }
251
get_response_headers(List<String> * r_response)252 Error HTTPClient::get_response_headers(List<String> *r_response) {
253
254 if (!response_headers.size())
255 return ERR_INVALID_PARAMETER;
256
257 for (int i = 0; i < response_headers.size(); i++) {
258
259 r_response->push_back(response_headers[i]);
260 }
261
262 response_headers.clear();
263
264 return OK;
265 }
266
close()267 void HTTPClient::close() {
268
269 if (tcp_connection->get_status() != StreamPeerTCP::STATUS_NONE)
270 tcp_connection->disconnect_from_host();
271
272 connection.unref();
273 status = STATUS_DISCONNECTED;
274 head_request = false;
275 if (resolving != IP::RESOLVER_INVALID_ID) {
276
277 IP::get_singleton()->erase_resolve_item(resolving);
278 resolving = IP::RESOLVER_INVALID_ID;
279 }
280
281 response_headers.clear();
282 response_str.clear();
283 body_size = -1;
284 body_left = 0;
285 chunk_left = 0;
286 chunk_trailer_part = 0;
287 read_until_eof = false;
288 response_num = 0;
289 handshaking = false;
290 }
291
poll()292 Error HTTPClient::poll() {
293
294 switch (status) {
295
296 case STATUS_RESOLVING: {
297 ERR_FAIL_COND_V(resolving == IP::RESOLVER_INVALID_ID, ERR_BUG);
298
299 IP::ResolverStatus rstatus = IP::get_singleton()->get_resolve_item_status(resolving);
300 switch (rstatus) {
301 case IP::RESOLVER_STATUS_WAITING:
302 return OK; // Still resolving
303
304 case IP::RESOLVER_STATUS_DONE: {
305
306 IP_Address host = IP::get_singleton()->get_resolve_item_address(resolving);
307 Error err = tcp_connection->connect_to_host(host, conn_port);
308 IP::get_singleton()->erase_resolve_item(resolving);
309 resolving = IP::RESOLVER_INVALID_ID;
310 if (err) {
311 status = STATUS_CANT_CONNECT;
312 return err;
313 }
314
315 status = STATUS_CONNECTING;
316 } break;
317 case IP::RESOLVER_STATUS_NONE:
318 case IP::RESOLVER_STATUS_ERROR: {
319
320 IP::get_singleton()->erase_resolve_item(resolving);
321 resolving = IP::RESOLVER_INVALID_ID;
322 close();
323 status = STATUS_CANT_RESOLVE;
324 return ERR_CANT_RESOLVE;
325 } break;
326 }
327 } break;
328 case STATUS_CONNECTING: {
329
330 StreamPeerTCP::Status s = tcp_connection->get_status();
331 switch (s) {
332
333 case StreamPeerTCP::STATUS_CONNECTING: {
334 return OK;
335 } break;
336 case StreamPeerTCP::STATUS_CONNECTED: {
337 if (ssl) {
338 Ref<StreamPeerSSL> ssl;
339 if (!handshaking) {
340 // Connect the StreamPeerSSL and start handshaking
341 ssl = Ref<StreamPeerSSL>(StreamPeerSSL::create());
342 ssl->set_blocking_handshake_enabled(false);
343 Error err = ssl->connect_to_stream(tcp_connection, ssl_verify_host, conn_host);
344 if (err != OK) {
345 close();
346 status = STATUS_SSL_HANDSHAKE_ERROR;
347 return ERR_CANT_CONNECT;
348 }
349 connection = ssl;
350 handshaking = true;
351 } else {
352 // We are already handshaking, which means we can use your already active SSL connection
353 ssl = static_cast<Ref<StreamPeerSSL> >(connection);
354 if (ssl.is_null()) {
355 close();
356 status = STATUS_SSL_HANDSHAKE_ERROR;
357 return ERR_CANT_CONNECT;
358 }
359
360 ssl->poll(); // Try to finish the handshake
361 }
362
363 if (ssl->get_status() == StreamPeerSSL::STATUS_CONNECTED) {
364 // Handshake has been successful
365 handshaking = false;
366 status = STATUS_CONNECTED;
367 return OK;
368 } else if (ssl->get_status() != StreamPeerSSL::STATUS_HANDSHAKING) {
369 // Handshake has failed
370 close();
371 status = STATUS_SSL_HANDSHAKE_ERROR;
372 return ERR_CANT_CONNECT;
373 }
374 // ... we will need to poll more for handshake to finish
375 } else {
376 status = STATUS_CONNECTED;
377 }
378 return OK;
379 } break;
380 case StreamPeerTCP::STATUS_ERROR:
381 case StreamPeerTCP::STATUS_NONE: {
382
383 close();
384 status = STATUS_CANT_CONNECT;
385 return ERR_CANT_CONNECT;
386 } break;
387 }
388 } break;
389 case STATUS_BODY:
390 case STATUS_CONNECTED: {
391 // Check if we are still connected
392 if (ssl) {
393 Ref<StreamPeerSSL> tmp = connection;
394 tmp->poll();
395 if (tmp->get_status() != StreamPeerSSL::STATUS_CONNECTED) {
396 status = STATUS_CONNECTION_ERROR;
397 return ERR_CONNECTION_ERROR;
398 }
399 } else if (tcp_connection->get_status() != StreamPeerTCP::STATUS_CONNECTED) {
400 status = STATUS_CONNECTION_ERROR;
401 return ERR_CONNECTION_ERROR;
402 }
403 // Connection established, requests can now be made
404 return OK;
405 } break;
406 case STATUS_REQUESTING: {
407
408 while (true) {
409 uint8_t byte;
410 int rec = 0;
411 Error err = _get_http_data(&byte, 1, rec);
412 if (err != OK) {
413 close();
414 status = STATUS_CONNECTION_ERROR;
415 return ERR_CONNECTION_ERROR;
416 }
417
418 if (rec == 0)
419 return OK; // Still requesting, keep trying!
420
421 response_str.push_back(byte);
422 int rs = response_str.size();
423 if (
424 (rs >= 2 && response_str[rs - 2] == '\n' && response_str[rs - 1] == '\n') ||
425 (rs >= 4 && response_str[rs - 4] == '\r' && response_str[rs - 3] == '\n' && response_str[rs - 2] == '\r' && response_str[rs - 1] == '\n')) {
426
427 // End of response, parse.
428 response_str.push_back(0);
429 String response;
430 response.parse_utf8((const char *)response_str.ptr());
431 Vector<String> responses = response.split("\n");
432 body_size = -1;
433 chunked = false;
434 body_left = 0;
435 chunk_left = 0;
436 chunk_trailer_part = false;
437 read_until_eof = false;
438 response_str.clear();
439 response_headers.clear();
440 response_num = RESPONSE_OK;
441
442 // Per the HTTP 1.1 spec, keep-alive is the default.
443 // Not following that specification breaks standard implementations.
444 // Broken web servers should be fixed.
445 bool keep_alive = true;
446
447 for (int i = 0; i < responses.size(); i++) {
448
449 String header = responses[i].strip_edges();
450 String s = header.to_lower();
451 if (s.length() == 0)
452 continue;
453 if (s.begins_with("content-length:")) {
454 body_size = s.substr(s.find(":") + 1, s.length()).strip_edges().to_int();
455 body_left = body_size;
456
457 } else if (s.begins_with("transfer-encoding:")) {
458 String encoding = header.substr(header.find(":") + 1, header.length()).strip_edges();
459 if (encoding == "chunked") {
460 chunked = true;
461 }
462 } else if (s.begins_with("connection: close")) {
463 keep_alive = false;
464 }
465
466 if (i == 0 && responses[i].begins_with("HTTP")) {
467
468 String num = responses[i].get_slicec(' ', 1);
469 response_num = num.to_int();
470 } else {
471
472 response_headers.push_back(header);
473 }
474 }
475
476 // This is a HEAD request, we wont receive anything.
477 if (head_request) {
478 body_size = 0;
479 body_left = 0;
480 }
481
482 if (body_size != -1 || chunked) {
483
484 status = STATUS_BODY;
485 } else if (!keep_alive) {
486
487 read_until_eof = true;
488 status = STATUS_BODY;
489 } else {
490
491 status = STATUS_CONNECTED;
492 }
493 return OK;
494 }
495 }
496 } break;
497 case STATUS_DISCONNECTED: {
498 return ERR_UNCONFIGURED;
499 } break;
500 case STATUS_CONNECTION_ERROR:
501 case STATUS_SSL_HANDSHAKE_ERROR: {
502 return ERR_CONNECTION_ERROR;
503 } break;
504 case STATUS_CANT_CONNECT: {
505 return ERR_CANT_CONNECT;
506 } break;
507 case STATUS_CANT_RESOLVE: {
508 return ERR_CANT_RESOLVE;
509 } break;
510 }
511
512 return OK;
513 }
514
get_response_body_length() const515 int HTTPClient::get_response_body_length() const {
516
517 return body_size;
518 }
519
read_response_body_chunk()520 PoolByteArray HTTPClient::read_response_body_chunk() {
521
522 ERR_FAIL_COND_V(status != STATUS_BODY, PoolByteArray());
523
524 PoolByteArray ret;
525 Error err = OK;
526
527 if (chunked) {
528
529 while (true) {
530
531 if (chunk_trailer_part) {
532 // We need to consume the trailer part too or keep-alive will break
533 uint8_t b;
534 int rec = 0;
535 err = _get_http_data(&b, 1, rec);
536
537 if (rec == 0)
538 break;
539
540 chunk.push_back(b);
541 int cs = chunk.size();
542 if ((cs >= 2 && chunk[cs - 2] == '\r' && chunk[cs - 1] == '\n')) {
543 if (cs == 2) {
544 // Finally over
545 chunk_trailer_part = false;
546 status = STATUS_CONNECTED;
547 chunk.clear();
548 break;
549 } else {
550 // We do not process nor return the trailer data
551 chunk.clear();
552 }
553 }
554 } else if (chunk_left == 0) {
555 // Reading length
556 uint8_t b;
557 int rec = 0;
558 err = _get_http_data(&b, 1, rec);
559
560 if (rec == 0)
561 break;
562
563 chunk.push_back(b);
564
565 if (chunk.size() > 32) {
566 ERR_PRINT("HTTP Invalid chunk hex len");
567 status = STATUS_CONNECTION_ERROR;
568 break;
569 }
570
571 if (chunk.size() > 2 && chunk[chunk.size() - 2] == '\r' && chunk[chunk.size() - 1] == '\n') {
572
573 int len = 0;
574 for (int i = 0; i < chunk.size() - 2; i++) {
575 char c = chunk[i];
576 int v = 0;
577 if (c >= '0' && c <= '9')
578 v = c - '0';
579 else if (c >= 'a' && c <= 'f')
580 v = c - 'a' + 10;
581 else if (c >= 'A' && c <= 'F')
582 v = c - 'A' + 10;
583 else {
584 ERR_PRINT("HTTP Chunk len not in hex!!");
585 status = STATUS_CONNECTION_ERROR;
586 break;
587 }
588 len <<= 4;
589 len |= v;
590 if (len > (1 << 24)) {
591 ERR_PRINT("HTTP Chunk too big!! >16mb");
592 status = STATUS_CONNECTION_ERROR;
593 break;
594 }
595 }
596
597 if (len == 0) {
598 // End reached!
599 chunk_trailer_part = true;
600 chunk.clear();
601 break;
602 }
603
604 chunk_left = len + 2;
605 chunk.resize(chunk_left);
606 }
607 } else {
608
609 int rec = 0;
610 err = _get_http_data(&chunk.write[chunk.size() - chunk_left], chunk_left, rec);
611 if (rec == 0) {
612 break;
613 }
614 chunk_left -= rec;
615
616 if (chunk_left == 0) {
617
618 if (chunk[chunk.size() - 2] != '\r' || chunk[chunk.size() - 1] != '\n') {
619 ERR_PRINT("HTTP Invalid chunk terminator (not \\r\\n)");
620 status = STATUS_CONNECTION_ERROR;
621 break;
622 }
623
624 ret.resize(chunk.size() - 2);
625 PoolByteArray::Write w = ret.write();
626 copymem(w.ptr(), chunk.ptr(), chunk.size() - 2);
627 chunk.clear();
628 }
629
630 break;
631 }
632 }
633
634 } else {
635
636 int to_read = !read_until_eof ? MIN(body_left, read_chunk_size) : read_chunk_size;
637 ret.resize(to_read);
638 int _offset = 0;
639 while (to_read > 0) {
640 int rec = 0;
641 {
642 PoolByteArray::Write w = ret.write();
643 err = _get_http_data(w.ptr() + _offset, to_read, rec);
644 }
645 if (rec <= 0) { // Ended up reading less
646 ret.resize(_offset);
647 break;
648 } else {
649 _offset += rec;
650 to_read -= rec;
651 if (!read_until_eof) {
652 body_left -= rec;
653 }
654 }
655 if (err != OK)
656 break;
657 }
658 }
659
660 if (err != OK) {
661
662 close();
663
664 if (err == ERR_FILE_EOF) {
665
666 status = STATUS_DISCONNECTED; // Server disconnected
667 } else {
668
669 status = STATUS_CONNECTION_ERROR;
670 }
671 } else if (body_left == 0 && !chunked && !read_until_eof) {
672
673 status = STATUS_CONNECTED;
674 }
675
676 return ret;
677 }
678
get_status() const679 HTTPClient::Status HTTPClient::get_status() const {
680
681 return status;
682 }
683
set_blocking_mode(bool p_enable)684 void HTTPClient::set_blocking_mode(bool p_enable) {
685
686 blocking = p_enable;
687 }
688
is_blocking_mode_enabled() const689 bool HTTPClient::is_blocking_mode_enabled() const {
690
691 return blocking;
692 }
693
_get_http_data(uint8_t * p_buffer,int p_bytes,int & r_received)694 Error HTTPClient::_get_http_data(uint8_t *p_buffer, int p_bytes, int &r_received) {
695
696 if (blocking) {
697
698 // We can't use StreamPeer.get_data, since when reaching EOF we will get an
699 // error without knowing how many bytes we received.
700 Error err = ERR_FILE_EOF;
701 int read = 0;
702 int left = p_bytes;
703 r_received = 0;
704 while (left > 0) {
705 err = connection->get_partial_data(p_buffer + r_received, left, read);
706 if (err == OK) {
707 r_received += read;
708 } else if (err == ERR_FILE_EOF) {
709 r_received += read;
710 return err;
711 } else {
712 return err;
713 }
714 left -= read;
715 }
716 return err;
717 } else {
718 return connection->get_partial_data(p_buffer, p_bytes, r_received);
719 }
720 }
721
set_read_chunk_size(int p_size)722 void HTTPClient::set_read_chunk_size(int p_size) {
723 ERR_FAIL_COND(p_size < 256 || p_size > (1 << 24));
724 read_chunk_size = p_size;
725 }
726
get_read_chunk_size() const727 int HTTPClient::get_read_chunk_size() const {
728 return read_chunk_size;
729 }
730
HTTPClient()731 HTTPClient::HTTPClient() {
732
733 tcp_connection.instance();
734 resolving = IP::RESOLVER_INVALID_ID;
735 status = STATUS_DISCONNECTED;
736 head_request = false;
737 conn_port = -1;
738 body_size = -1;
739 chunked = false;
740 body_left = 0;
741 read_until_eof = false;
742 chunk_left = 0;
743 chunk_trailer_part = false;
744 response_num = 0;
745 ssl = false;
746 blocking = false;
747 handshaking = false;
748 read_chunk_size = 4096;
749 }
750
~HTTPClient()751 HTTPClient::~HTTPClient() {
752 }
753
754 #endif // #ifndef JAVASCRIPT_ENABLED
755
query_string_from_dict(const Dictionary & p_dict)756 String HTTPClient::query_string_from_dict(const Dictionary &p_dict) {
757 String query = "";
758 Array keys = p_dict.keys();
759 for (int i = 0; i < keys.size(); ++i) {
760 String encoded_key = String(keys[i]).http_escape();
761 Variant value = p_dict[keys[i]];
762 switch (value.get_type()) {
763 case Variant::ARRAY: {
764 // Repeat the key with every values
765 Array values = value;
766 for (int j = 0; j < values.size(); ++j) {
767 query += "&" + encoded_key + "=" + String(values[j]).http_escape();
768 }
769 break;
770 }
771 case Variant::NIL: {
772 // Add the key with no value
773 query += "&" + encoded_key;
774 break;
775 }
776 default: {
777 // Add the key-value pair
778 query += "&" + encoded_key + "=" + String(value).http_escape();
779 }
780 }
781 }
782 query.erase(0, 1);
783 return query;
784 }
785
_get_response_headers_as_dictionary()786 Dictionary HTTPClient::_get_response_headers_as_dictionary() {
787
788 List<String> rh;
789 get_response_headers(&rh);
790 Dictionary ret;
791 for (const List<String>::Element *E = rh.front(); E; E = E->next()) {
792 const String &s = E->get();
793 int sp = s.find(":");
794 if (sp == -1)
795 continue;
796 String key = s.substr(0, sp).strip_edges();
797 String value = s.substr(sp + 1, s.length()).strip_edges();
798 ret[key] = value;
799 }
800
801 return ret;
802 }
803
_get_response_headers()804 PoolStringArray HTTPClient::_get_response_headers() {
805
806 List<String> rh;
807 get_response_headers(&rh);
808 PoolStringArray ret;
809 ret.resize(rh.size());
810 int idx = 0;
811 for (const List<String>::Element *E = rh.front(); E; E = E->next()) {
812 ret.set(idx++, E->get());
813 }
814
815 return ret;
816 }
817
_bind_methods()818 void HTTPClient::_bind_methods() {
819
820 ClassDB::bind_method(D_METHOD("connect_to_host", "host", "port", "use_ssl", "verify_host"), &HTTPClient::connect_to_host, DEFVAL(-1), DEFVAL(false), DEFVAL(true));
821 ClassDB::bind_method(D_METHOD("set_connection", "connection"), &HTTPClient::set_connection);
822 ClassDB::bind_method(D_METHOD("get_connection"), &HTTPClient::get_connection);
823 ClassDB::bind_method(D_METHOD("request_raw", "method", "url", "headers", "body"), &HTTPClient::request_raw);
824 ClassDB::bind_method(D_METHOD("request", "method", "url", "headers", "body"), &HTTPClient::request, DEFVAL(String()));
825 ClassDB::bind_method(D_METHOD("close"), &HTTPClient::close);
826
827 ClassDB::bind_method(D_METHOD("has_response"), &HTTPClient::has_response);
828 ClassDB::bind_method(D_METHOD("is_response_chunked"), &HTTPClient::is_response_chunked);
829 ClassDB::bind_method(D_METHOD("get_response_code"), &HTTPClient::get_response_code);
830 ClassDB::bind_method(D_METHOD("get_response_headers"), &HTTPClient::_get_response_headers);
831 ClassDB::bind_method(D_METHOD("get_response_headers_as_dictionary"), &HTTPClient::_get_response_headers_as_dictionary);
832 ClassDB::bind_method(D_METHOD("get_response_body_length"), &HTTPClient::get_response_body_length);
833 ClassDB::bind_method(D_METHOD("read_response_body_chunk"), &HTTPClient::read_response_body_chunk);
834 ClassDB::bind_method(D_METHOD("set_read_chunk_size", "bytes"), &HTTPClient::set_read_chunk_size);
835 ClassDB::bind_method(D_METHOD("get_read_chunk_size"), &HTTPClient::get_read_chunk_size);
836
837 ClassDB::bind_method(D_METHOD("set_blocking_mode", "enabled"), &HTTPClient::set_blocking_mode);
838 ClassDB::bind_method(D_METHOD("is_blocking_mode_enabled"), &HTTPClient::is_blocking_mode_enabled);
839
840 ClassDB::bind_method(D_METHOD("get_status"), &HTTPClient::get_status);
841 ClassDB::bind_method(D_METHOD("poll"), &HTTPClient::poll);
842
843 ClassDB::bind_method(D_METHOD("query_string_from_dict", "fields"), &HTTPClient::query_string_from_dict);
844
845 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "blocking_mode_enabled"), "set_blocking_mode", "is_blocking_mode_enabled");
846 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "connection", PROPERTY_HINT_RESOURCE_TYPE, "StreamPeer", 0), "set_connection", "get_connection");
847 ADD_PROPERTY(PropertyInfo(Variant::INT, "read_chunk_size", PROPERTY_HINT_RANGE, "256,16777216"), "set_read_chunk_size", "get_read_chunk_size");
848
849 BIND_ENUM_CONSTANT(METHOD_GET);
850 BIND_ENUM_CONSTANT(METHOD_HEAD);
851 BIND_ENUM_CONSTANT(METHOD_POST);
852 BIND_ENUM_CONSTANT(METHOD_PUT);
853 BIND_ENUM_CONSTANT(METHOD_DELETE);
854 BIND_ENUM_CONSTANT(METHOD_OPTIONS);
855 BIND_ENUM_CONSTANT(METHOD_TRACE);
856 BIND_ENUM_CONSTANT(METHOD_CONNECT);
857 BIND_ENUM_CONSTANT(METHOD_PATCH);
858 BIND_ENUM_CONSTANT(METHOD_MAX);
859
860 BIND_ENUM_CONSTANT(STATUS_DISCONNECTED);
861 BIND_ENUM_CONSTANT(STATUS_RESOLVING); // Resolving hostname (if hostname was passed in)
862 BIND_ENUM_CONSTANT(STATUS_CANT_RESOLVE);
863 BIND_ENUM_CONSTANT(STATUS_CONNECTING); // Connecting to IP
864 BIND_ENUM_CONSTANT(STATUS_CANT_CONNECT);
865 BIND_ENUM_CONSTANT(STATUS_CONNECTED); // Connected, now accepting requests
866 BIND_ENUM_CONSTANT(STATUS_REQUESTING); // Request in progress
867 BIND_ENUM_CONSTANT(STATUS_BODY); // Request resulted in body which must be read
868 BIND_ENUM_CONSTANT(STATUS_CONNECTION_ERROR);
869 BIND_ENUM_CONSTANT(STATUS_SSL_HANDSHAKE_ERROR);
870
871 BIND_ENUM_CONSTANT(RESPONSE_CONTINUE);
872 BIND_ENUM_CONSTANT(RESPONSE_SWITCHING_PROTOCOLS);
873 BIND_ENUM_CONSTANT(RESPONSE_PROCESSING);
874
875 // 2xx successful
876 BIND_ENUM_CONSTANT(RESPONSE_OK);
877 BIND_ENUM_CONSTANT(RESPONSE_CREATED);
878 BIND_ENUM_CONSTANT(RESPONSE_ACCEPTED);
879 BIND_ENUM_CONSTANT(RESPONSE_NON_AUTHORITATIVE_INFORMATION);
880 BIND_ENUM_CONSTANT(RESPONSE_NO_CONTENT);
881 BIND_ENUM_CONSTANT(RESPONSE_RESET_CONTENT);
882 BIND_ENUM_CONSTANT(RESPONSE_PARTIAL_CONTENT);
883 BIND_ENUM_CONSTANT(RESPONSE_MULTI_STATUS);
884 BIND_ENUM_CONSTANT(RESPONSE_ALREADY_REPORTED);
885 BIND_ENUM_CONSTANT(RESPONSE_IM_USED);
886
887 // 3xx redirection
888 BIND_ENUM_CONSTANT(RESPONSE_MULTIPLE_CHOICES);
889 BIND_ENUM_CONSTANT(RESPONSE_MOVED_PERMANENTLY);
890 BIND_ENUM_CONSTANT(RESPONSE_FOUND);
891 BIND_ENUM_CONSTANT(RESPONSE_SEE_OTHER);
892 BIND_ENUM_CONSTANT(RESPONSE_NOT_MODIFIED);
893 BIND_ENUM_CONSTANT(RESPONSE_USE_PROXY);
894 BIND_ENUM_CONSTANT(RESPONSE_SWITCH_PROXY);
895 BIND_ENUM_CONSTANT(RESPONSE_TEMPORARY_REDIRECT);
896 BIND_ENUM_CONSTANT(RESPONSE_PERMANENT_REDIRECT);
897
898 // 4xx client error
899 BIND_ENUM_CONSTANT(RESPONSE_BAD_REQUEST);
900 BIND_ENUM_CONSTANT(RESPONSE_UNAUTHORIZED);
901 BIND_ENUM_CONSTANT(RESPONSE_PAYMENT_REQUIRED);
902 BIND_ENUM_CONSTANT(RESPONSE_FORBIDDEN);
903 BIND_ENUM_CONSTANT(RESPONSE_NOT_FOUND);
904 BIND_ENUM_CONSTANT(RESPONSE_METHOD_NOT_ALLOWED);
905 BIND_ENUM_CONSTANT(RESPONSE_NOT_ACCEPTABLE);
906 BIND_ENUM_CONSTANT(RESPONSE_PROXY_AUTHENTICATION_REQUIRED);
907 BIND_ENUM_CONSTANT(RESPONSE_REQUEST_TIMEOUT);
908 BIND_ENUM_CONSTANT(RESPONSE_CONFLICT);
909 BIND_ENUM_CONSTANT(RESPONSE_GONE);
910 BIND_ENUM_CONSTANT(RESPONSE_LENGTH_REQUIRED);
911 BIND_ENUM_CONSTANT(RESPONSE_PRECONDITION_FAILED);
912 BIND_ENUM_CONSTANT(RESPONSE_REQUEST_ENTITY_TOO_LARGE);
913 BIND_ENUM_CONSTANT(RESPONSE_REQUEST_URI_TOO_LONG);
914 BIND_ENUM_CONSTANT(RESPONSE_UNSUPPORTED_MEDIA_TYPE);
915 BIND_ENUM_CONSTANT(RESPONSE_REQUESTED_RANGE_NOT_SATISFIABLE);
916 BIND_ENUM_CONSTANT(RESPONSE_EXPECTATION_FAILED);
917 BIND_ENUM_CONSTANT(RESPONSE_IM_A_TEAPOT);
918 BIND_ENUM_CONSTANT(RESPONSE_MISDIRECTED_REQUEST);
919 BIND_ENUM_CONSTANT(RESPONSE_UNPROCESSABLE_ENTITY);
920 BIND_ENUM_CONSTANT(RESPONSE_LOCKED);
921 BIND_ENUM_CONSTANT(RESPONSE_FAILED_DEPENDENCY);
922 BIND_ENUM_CONSTANT(RESPONSE_UPGRADE_REQUIRED);
923 BIND_ENUM_CONSTANT(RESPONSE_PRECONDITION_REQUIRED);
924 BIND_ENUM_CONSTANT(RESPONSE_TOO_MANY_REQUESTS);
925 BIND_ENUM_CONSTANT(RESPONSE_REQUEST_HEADER_FIELDS_TOO_LARGE);
926 BIND_ENUM_CONSTANT(RESPONSE_UNAVAILABLE_FOR_LEGAL_REASONS);
927
928 // 5xx server error
929 BIND_ENUM_CONSTANT(RESPONSE_INTERNAL_SERVER_ERROR);
930 BIND_ENUM_CONSTANT(RESPONSE_NOT_IMPLEMENTED);
931 BIND_ENUM_CONSTANT(RESPONSE_BAD_GATEWAY);
932 BIND_ENUM_CONSTANT(RESPONSE_SERVICE_UNAVAILABLE);
933 BIND_ENUM_CONSTANT(RESPONSE_GATEWAY_TIMEOUT);
934 BIND_ENUM_CONSTANT(RESPONSE_HTTP_VERSION_NOT_SUPPORTED);
935 BIND_ENUM_CONSTANT(RESPONSE_VARIANT_ALSO_NEGOTIATES);
936 BIND_ENUM_CONSTANT(RESPONSE_INSUFFICIENT_STORAGE);
937 BIND_ENUM_CONSTANT(RESPONSE_LOOP_DETECTED);
938 BIND_ENUM_CONSTANT(RESPONSE_NOT_EXTENDED);
939 BIND_ENUM_CONSTANT(RESPONSE_NETWORK_AUTH_REQUIRED);
940 }
941