diff options
| -rw-r--r-- | core/io/http_client.cpp | 11 | ||||
| -rw-r--r-- | core/io/http_client.h | 1 | ||||
| -rw-r--r-- | core/io/http_client_tcp.cpp | 50 | ||||
| -rw-r--r-- | core/io/http_client_tcp.h | 10 | ||||
| -rw-r--r-- | doc/classes/Area2D.xml | 6 | ||||
| -rw-r--r-- | doc/classes/Area3D.xml | 6 | ||||
| -rw-r--r-- | doc/classes/Object.xml | 2 | ||||
| -rw-r--r-- | platform/javascript/http_client_javascript.cpp | 5 | ||||
| -rw-r--r-- | scene/main/http_request.cpp | 48 |
9 files changed, 79 insertions, 60 deletions
diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp index 4d0747c591..52b1120b2a 100644 --- a/core/io/http_client.cpp +++ b/core/io/http_client.cpp @@ -96,6 +96,17 @@ String HTTPClient::query_string_from_dict(const Dictionary &p_dict) { return query.substr(1); } +Error HTTPClient::verify_headers(const Vector<String> &p_headers) { + for (int i = 0; i < p_headers.size(); i++) { + String sanitized = p_headers[i].strip_edges(); + ERR_FAIL_COND_V_MSG(sanitized.is_empty(), ERR_INVALID_PARAMETER, "Invalid HTTP header at index " + itos(i) + ": empty."); + ERR_FAIL_COND_V_MSG(sanitized.find(":") < 1, ERR_INVALID_PARAMETER, + "Invalid HTTP header at index " + itos(i) + ": String must contain header-value pair, delimited by ':', but was: " + p_headers[i]); + } + + return OK; +} + Dictionary HTTPClient::_get_response_headers_as_dictionary() { List<String> rh; get_response_headers(&rh); diff --git a/core/io/http_client.h b/core/io/http_client.h index 90c859d685..de6045f647 100644 --- a/core/io/http_client.h +++ b/core/io/http_client.h @@ -165,6 +165,7 @@ public: static HTTPClient *create(); String query_string_from_dict(const Dictionary &p_dict); + Error verify_headers(const Vector<String> &p_headers); virtual Error request(Method p_method, const String &p_url, const Vector<String> &p_headers, const uint8_t *p_body, int p_body_size) = 0; virtual Error connect_to_host(const String &p_host, int p_port = -1, bool p_ssl = false, bool p_verify_host = true) = 0; diff --git a/core/io/http_client_tcp.cpp b/core/io/http_client_tcp.cpp index 6e4417e1ff..24ec35fa3d 100644 --- a/core/io/http_client_tcp.cpp +++ b/core/io/http_client_tcp.cpp @@ -71,7 +71,7 @@ Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_ss connection = tcp_connection; if (ssl && https_proxy_port != -1) { - proxy_client.instantiate(); // Needs proxy negotiation + proxy_client.instantiate(); // Needs proxy negotiation. server_host = https_proxy_host; server_port = https_proxy_port; } else if (!ssl && http_proxy_port != -1) { @@ -83,7 +83,7 @@ Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_ss } if (server_host.is_valid_ip_address()) { - // Host contains valid IP + // Host contains valid IP. Error err = tcp_connection->connect_to_host(IPAddress(server_host), server_port); if (err) { status = STATUS_CANT_CONNECT; @@ -92,7 +92,7 @@ Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_ss status = STATUS_CONNECTING; } else { - // Host contains hostname and needs to be resolved to IP + // Host contains hostname and needs to be resolved to IP. resolving = IP::get_singleton()->resolve_hostname_queue_item(server_host); status = STATUS_RESOLVING; } @@ -124,7 +124,7 @@ Ref<StreamPeer> HTTPClientTCP::get_connection() const { static bool _check_request_url(HTTPClientTCP::Method p_method, const String &p_url) { switch (p_method) { case HTTPClientTCP::METHOD_CONNECT: { - // Authority in host:port format, as in RFC7231 + // Authority in host:port format, as in RFC7231. int pos = p_url.find_char(':'); return 0 < pos && pos < p_url.length() - 1; } @@ -135,7 +135,7 @@ static bool _check_request_url(HTTPClientTCP::Method p_method, const String &p_u [[fallthrough]]; } default: - // Absolute path or absolute URL + // Absolute path or absolute URL. return p_url.begins_with("/") || p_url.begins_with("http://") || p_url.begins_with("https://"); } } @@ -146,6 +146,11 @@ Error HTTPClientTCP::request(Method p_method, const String &p_url, const Vector< ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(connection.is_null(), ERR_INVALID_DATA); + Error err = verify_headers(p_headers); + if (err) { + return err; + } + String uri = p_url; if (!ssl && http_proxy_port != -1) { uri = vformat("http://%s:%d%s", conn_host, conn_port, p_url); @@ -173,7 +178,7 @@ Error HTTPClientTCP::request(Method p_method, const String &p_url, const Vector< } if (add_host) { if ((ssl && conn_port == PORT_HTTPS) || (!ssl && conn_port == PORT_HTTP)) { - // Don't append the standard ports + // Don't append the standard ports. request += "Host: " + conn_host + "\r\n"; } else { request += "Host: " + conn_host + ":" + itos(conn_port) + "\r\n"; @@ -199,8 +204,7 @@ Error HTTPClientTCP::request(Method p_method, const String &p_url, const Vector< memcpy(data.ptrw() + cs.length(), p_body, p_body_size); } - // TODO Implement non-blocking requests. - Error err = connection->put_data(data.ptr(), data.size()); + err = connection->put_data(data.ptr(), data.size()); if (err) { close(); @@ -274,7 +278,7 @@ Error HTTPClientTCP::poll() { IP::ResolverStatus rstatus = IP::get_singleton()->get_resolve_item_status(resolving); switch (rstatus) { case IP::RESOLVER_STATUS_WAITING: - return OK; // Still resolving + return OK; // Still resolving. case IP::RESOLVER_STATUS_DONE: { ip_candidates = IP::get_singleton()->get_resolve_item_addresses(resolving); @@ -356,7 +360,7 @@ Error HTTPClientTCP::poll() { } else if (ssl) { Ref<StreamPeerSSL> ssl; if (!handshaking) { - // Connect the StreamPeerSSL and start handshaking + // Connect the StreamPeerSSL and start handshaking. ssl = Ref<StreamPeerSSL>(StreamPeerSSL::create()); ssl->set_blocking_handshake_enabled(false); Error err = ssl->connect_to_stream(tcp_connection, ssl_verify_host, conn_host); @@ -368,7 +372,7 @@ Error HTTPClientTCP::poll() { connection = ssl; handshaking = true; } else { - // We are already handshaking, which means we can use your already active SSL connection + // We are already handshaking, which means we can use your already active SSL connection. ssl = static_cast<Ref<StreamPeerSSL>>(connection); if (ssl.is_null()) { close(); @@ -376,22 +380,22 @@ Error HTTPClientTCP::poll() { return ERR_CANT_CONNECT; } - ssl->poll(); // Try to finish the handshake + ssl->poll(); // Try to finish the handshake. } if (ssl->get_status() == StreamPeerSSL::STATUS_CONNECTED) { - // Handshake has been successful + // Handshake has been successful. handshaking = false; ip_candidates.clear(); status = STATUS_CONNECTED; return OK; } else if (ssl->get_status() != StreamPeerSSL::STATUS_HANDSHAKING) { - // Handshake has failed + // Handshake has failed. close(); status = STATUS_SSL_HANDSHAKE_ERROR; return ERR_CANT_CONNECT; } - // ... we will need to poll more for handshake to finish + // ... we will need to poll more for handshake to finish. } else { ip_candidates.clear(); status = STATUS_CONNECTED; @@ -416,7 +420,7 @@ Error HTTPClientTCP::poll() { } break; case STATUS_BODY: case STATUS_CONNECTED: { - // Check if we are still connected + // Check if we are still connected. if (ssl) { Ref<StreamPeerSSL> tmp = connection; tmp->poll(); @@ -428,7 +432,7 @@ Error HTTPClientTCP::poll() { status = STATUS_CONNECTION_ERROR; return ERR_CONNECTION_ERROR; } - // Connection established, requests can now be made + // Connection established, requests can now be made. return OK; } break; case STATUS_REQUESTING: { @@ -547,7 +551,7 @@ PackedByteArray HTTPClientTCP::read_response_body_chunk() { if (chunked) { while (true) { if (chunk_trailer_part) { - // We need to consume the trailer part too or keep-alive will break + // We need to consume the trailer part too or keep-alive will break. uint8_t b; int rec = 0; err = _get_http_data(&b, 1, rec); @@ -560,18 +564,18 @@ PackedByteArray HTTPClientTCP::read_response_body_chunk() { int cs = chunk.size(); if ((cs >= 2 && chunk[cs - 2] == '\r' && chunk[cs - 1] == '\n')) { if (cs == 2) { - // Finally over + // Finally over. chunk_trailer_part = false; status = STATUS_CONNECTED; chunk.clear(); break; } else { - // We do not process nor return the trailer data + // We do not process nor return the trailer data. chunk.clear(); } } } else if (chunk_left == 0) { - // Reading length + // Reading length. uint8_t b; int rec = 0; err = _get_http_data(&b, 1, rec); @@ -658,7 +662,7 @@ PackedByteArray HTTPClientTCP::read_response_body_chunk() { uint8_t *w = ret.ptrw(); err = _get_http_data(w + _offset, to_read, rec); } - if (rec <= 0) { // Ended up reading less + if (rec <= 0) { // Ended up reading less. ret.resize(_offset); break; } else { @@ -679,7 +683,7 @@ PackedByteArray HTTPClientTCP::read_response_body_chunk() { close(); if (err == ERR_FILE_EOF) { - status = STATUS_DISCONNECTED; // Server disconnected + status = STATUS_DISCONNECTED; // Server disconnected. } else { status = STATUS_CONNECTION_ERROR; } diff --git a/core/io/http_client_tcp.h b/core/io/http_client_tcp.h index 3fe8e2c0df..f5f4450f73 100644 --- a/core/io/http_client_tcp.h +++ b/core/io/http_client_tcp.h @@ -38,13 +38,13 @@ private: Status status = STATUS_DISCONNECTED; IP::ResolverID resolving = IP::RESOLVER_INVALID_ID; Array ip_candidates; - int conn_port = -1; // Server to make requests to + int conn_port = -1; // Server to make requests to. String conn_host; - int server_port = -1; // Server to connect to (might be a proxy server) + int server_port = -1; // Server to connect to (might be a proxy server). String server_host; - int http_proxy_port = -1; // Proxy server for http requests + int http_proxy_port = -1; // Proxy server for http requests. String http_proxy_host; - int https_proxy_port = -1; // Proxy server for https requests + int https_proxy_port = -1; // Proxy server for https requests. String https_proxy_host; bool ssl = false; bool ssl_verify_host = false; @@ -64,7 +64,7 @@ private: Ref<StreamPeerTCP> tcp_connection; Ref<StreamPeer> connection; - Ref<HTTPClientTCP> proxy_client; // Negotiate with proxy server + Ref<HTTPClientTCP> proxy_client; // Negotiate with proxy server. int response_num = 0; Vector<String> response_headers; diff --git a/doc/classes/Area2D.xml b/doc/classes/Area2D.xml index 0f7e6799be..5280b7445d 100644 --- a/doc/classes/Area2D.xml +++ b/doc/classes/Area2D.xml @@ -31,7 +31,7 @@ <return type="bool" /> <argument index="0" name="area" type="Node" /> <description> - If [code]true[/code], the given area overlaps the Area2D. + Returns [code]true[/code] if the given [Area2D] intersects or overlaps this [Area2D], [code]false[/code] otherwise. [b]Note:[/b] The result of this test is not immediate after moving objects. For performance, the list of overlaps is updated once per frame and before the physics step. Consider using signals instead. </description> </method> @@ -39,9 +39,9 @@ <return type="bool" /> <argument index="0" name="body" type="Node" /> <description> - If [code]true[/code], the given physics body overlaps the Area2D. + Returns [code]true[/code] if the given physics body intersects or overlaps this [Area2D], [code]false[/code] otherwise. [b]Note:[/b] The result of this test is not immediate after moving objects. For performance, list of overlaps is updated once per frame and before the physics step. Consider using signals instead. - The [code]body[/code] argument can either be a [PhysicsBody2D] or a [TileMap] instance (while TileMaps are not physics bodies themselves, they register their tiles with collision shapes as a virtual physics body). + The [code]body[/code] argument can either be a [PhysicsBody2D] or a [TileMap] instance. While TileMaps are not physics bodies themselves, they register their tiles with collision shapes as a virtual physics body. </description> </method> </methods> diff --git a/doc/classes/Area3D.xml b/doc/classes/Area3D.xml index 450ed44307..4f456cb56d 100644 --- a/doc/classes/Area3D.xml +++ b/doc/classes/Area3D.xml @@ -29,7 +29,7 @@ <return type="bool" /> <argument index="0" name="area" type="Node" /> <description> - If [code]true[/code], the given area overlaps the Area3D. + Returns [code]true[/code] if the given [Area3D] intersects or overlaps this [Area3D], [code]false[/code] otherwise. [b]Note:[/b] The result of this test is not immediate after moving objects. For performance, list of overlaps is updated once per frame and before the physics step. Consider using signals instead. </description> </method> @@ -37,9 +37,9 @@ <return type="bool" /> <argument index="0" name="body" type="Node" /> <description> - If [code]true[/code], the given physics body overlaps the Area3D. + Returns [code]true[/code] if the given physics body intersects or overlaps this [Area3D], [code]false[/code] otherwise. [b]Note:[/b] The result of this test is not immediate after moving objects. For performance, list of overlaps is updated once per frame and before the physics step. Consider using signals instead. - The [code]body[/code] argument can either be a [PhysicsBody3D] or a [GridMap] instance (while GridMaps are not physics body themselves, they register their tiles with collision shapes as a virtual physics body). + The [code]body[/code] argument can either be a [PhysicsBody3D] or a [GridMap] instance. While GridMaps are not physics body themselves, they register their tiles with collision shapes as a virtual physics body. </description> </method> </methods> diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml index f5f6ba8b6d..bdc3a09322 100644 --- a/doc/classes/Object.xml +++ b/doc/classes/Object.xml @@ -490,7 +490,7 @@ <argument index="0" name="property" type="String" /> <argument index="1" name="value" type="Variant" /> <description> - Assigns a new value to the given property. If the [code]property[/code] does not exist, nothing will happen. + Assigns a new value to the given property. If the [code]property[/code] does not exist or the given value's type doesn't match, nothing will happen. [b]Note:[/b] In C#, the property name must be specified as snake_case if it is defined by a built-in Godot node. This doesn't apply to user-defined properties where you should use the same convention as in the C# source (typically PascalCase). </description> </method> diff --git a/platform/javascript/http_client_javascript.cpp b/platform/javascript/http_client_javascript.cpp index 45aa68ce7c..c946302862 100644 --- a/platform/javascript/http_client_javascript.cpp +++ b/platform/javascript/http_client_javascript.cpp @@ -87,6 +87,11 @@ Error HTTPClientJavaScript::request(Method p_method, const String &p_url, const ERR_FAIL_COND_V(port < 0, ERR_UNCONFIGURED); ERR_FAIL_COND_V(!p_url.begins_with("/"), ERR_INVALID_PARAMETER); + Error err = verify_headers(p_headers); + if (err) { + return err; + } + String url = (use_tls ? "https://" : "http://") + host + ":" + itos(port) + p_url; Vector<CharString> keeper; Vector<const char *> c_strings; diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp index dd66f566af..4e91548d14 100644 --- a/scene/main/http_request.cpp +++ b/scene/main/http_request.cpp @@ -86,9 +86,9 @@ String HTTPRequest::get_header_value(const PackedStringArray &p_headers, const S String lowwer_case_header_name = p_header_name.to_lower(); for (int i = 0; i < p_headers.size(); i++) { - if (p_headers[i].find(":", 0) >= 0) { + if (p_headers[i].find(":") > 0) { Vector<String> parts = p_headers[i].split(":", false, 1); - if (parts[0].strip_edges().to_lower() == lowwer_case_header_name) { + if (parts.size() > 1 && parts[0].strip_edges().to_lower() == lowwer_case_header_name) { value = parts[1].strip_edges(); break; } @@ -99,7 +99,7 @@ String HTTPRequest::get_header_value(const PackedStringArray &p_headers, const S } Error HTTPRequest::request(const String &p_url, const Vector<String> &p_custom_headers, bool p_ssl_validate_domain, HTTPClient::Method p_method, const String &p_request_data) { - // Copy the string into a raw buffer + // Copy the string into a raw buffer. Vector<uint8_t> raw_data; CharString charstr = p_request_data.utf8(); @@ -134,7 +134,7 @@ Error HTTPRequest::request_raw(const String &p_url, const Vector<String> &p_cust headers = p_custom_headers; if (accept_gzip) { - // If the user has specified a different Accept-Encoding, don't overwrite it + // If the user has specified an Accept-Encoding header, don't overwrite it. if (!has_header(headers, "Accept-Encoding")) { headers.push_back("Accept-Encoding: gzip, deflate"); } @@ -227,7 +227,7 @@ bool HTTPRequest::_handle_response(bool *ret_value) { } if (response_code == 301 || response_code == 302) { - // Handle redirect + // Handle redirect. if (max_redirects >= 0 && redirections >= max_redirects) { call_deferred(SNAME("_request_done"), RESULT_REDIRECT_LIMIT_REACHED, response_code, response_headers, PackedByteArray()); @@ -244,12 +244,12 @@ bool HTTPRequest::_handle_response(bool *ret_value) { } if (!new_request.is_empty()) { - // Process redirect + // Process redirect. client->close(); - int new_redirs = redirections + 1; // Because _request() will clear it + int new_redirs = redirections + 1; // Because _request() will clear it. Error err; if (new_request.begins_with("http")) { - // New url, request all again + // New url, new request. _parse_url(new_request); } else { request_string = new_request; @@ -276,11 +276,11 @@ bool HTTPRequest::_update_connection() { switch (client->get_status()) { case HTTPClient::STATUS_DISCONNECTED: { call_deferred(SNAME("_request_done"), RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray()); - return true; // End it, since it's doing something + return true; // End it, since it's disconnected. } break; case HTTPClient::STATUS_RESOLVING: { client->poll(); - // Must wait + // Must wait. return false; } break; case HTTPClient::STATUS_CANT_RESOLVE: { @@ -290,9 +290,9 @@ bool HTTPRequest::_update_connection() { } break; case HTTPClient::STATUS_CONNECTING: { client->poll(); - // Must wait + // Must wait. return false; - } break; // Connecting to IP + } break; // Connecting to IP. case HTTPClient::STATUS_CANT_CONNECT: { call_deferred(SNAME("_request_done"), RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray()); return true; @@ -301,7 +301,7 @@ bool HTTPRequest::_update_connection() { case HTTPClient::STATUS_CONNECTED: { if (request_sent) { if (!got_response) { - // No body + // No body. bool ret_value; @@ -313,16 +313,16 @@ bool HTTPRequest::_update_connection() { return true; } if (body_len < 0) { - // Chunked transfer is done + // Chunked transfer is done. call_deferred(SNAME("_request_done"), RESULT_SUCCESS, response_code, response_headers, body); return true; } call_deferred(SNAME("_request_done"), RESULT_CHUNKED_BODY_SIZE_MISMATCH, response_code, response_headers, PackedByteArray()); return true; - // Request might have been done + // Request might have been done. } else { - // Did not request yet, do request + // Did not request yet, do request. int size = request_data.size(); Error err = client->request(method, request_string, headers, size > 0 ? request_data.ptr() : nullptr, size); @@ -334,13 +334,13 @@ bool HTTPRequest::_update_connection() { request_sent = true; return false; } - } break; // Connected: break requests only accepted here + } break; // Connected: break requests only accepted here. case HTTPClient::STATUS_REQUESTING: { - // Must wait, still requesting + // Must wait, still requesting. client->poll(); return false; - } break; // Request in progress + } break; // Request in progress. case HTTPClient::STATUS_BODY: { if (!got_response) { bool ret_value; @@ -411,7 +411,7 @@ bool HTTPRequest::_update_connection() { return false; - } break; // Request resulted in body: break which must be read + } break; // Request resulted in body: break which must be read. case HTTPClient::STATUS_CONNECTION_ERROR: { call_deferred(SNAME("_request_done"), RESULT_CONNECTION_ERROR, 0, PackedStringArray(), PackedByteArray()); return true; @@ -428,7 +428,7 @@ bool HTTPRequest::_update_connection() { void HTTPRequest::_request_done(int p_status, int p_code, const PackedStringArray &p_headers, const PackedByteArray &p_data) { cancel_request(); - // Determine if the request body is compressed + // Determine if the request body is compressed. bool is_compressed; String content_encoding = get_header_value(p_headers, "Content-Encoding").to_lower(); Compression::Mode mode; @@ -452,11 +452,11 @@ void HTTPRequest::_request_done(int p_status, int p_code, const PackedStringArra } else if (result == -5) { WARN_PRINT("Decompressed size of HTTP response body exceeded body_size_limit"); p_status = RESULT_BODY_SIZE_LIMIT_EXCEEDED; - // Just return the raw data if we failed to decompress it + // Just return the raw data if we failed to decompress it. } else { WARN_PRINT("Failed to decompress HTTP response body"); p_status = RESULT_BODY_DECOMPRESS_FAILED; - // Just return the raw data if we failed to decompress it + // Just return the raw data if we failed to decompress it. } } @@ -471,7 +471,6 @@ void HTTPRequest::_notification(int p_what) { bool done = _update_connection(); if (done) { set_process_internal(false); - // cancel_request(); called from _request done now } } @@ -619,7 +618,6 @@ void HTTPRequest::_bind_methods() { 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"))); BIND_ENUM_CONSTANT(RESULT_SUCCESS); - //BIND_ENUM_CONSTANT( RESULT_NO_BODY ); BIND_ENUM_CONSTANT(RESULT_CHUNKED_BODY_SIZE_MISMATCH); BIND_ENUM_CONSTANT(RESULT_CANT_CONNECT); BIND_ENUM_CONSTANT(RESULT_CANT_RESOLVE); |