diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp index 034f914db37..cfeca83ec9d 100644 --- a/core/io/http_client.cpp +++ b/core/io/http_client.cpp @@ -77,9 +77,21 @@ Error HTTPClient::connect_to_host(const String &p_host, int p_port, bool p_ssl, connection = tcp_connection; - if (conn_host.is_valid_ip_address()) { + if (ssl && https_proxy_port != -1) { + proxy_client.instance(); + server_host = https_proxy_host; + server_port = https_proxy_port; + } else if (!ssl && http_proxy_port != -1) { + server_host = http_proxy_host; + server_port = http_proxy_port; + } else { + server_host = conn_host; + server_port = conn_port; + } + + if (server_host.is_valid_ip_address()) { // Host contains valid IP - Error err = tcp_connection->connect_to_host(IP_Address(conn_host), p_port); + Error err = tcp_connection->connect_to_host(IP_Address(server_host), server_port); if (err) { status = STATUS_CANT_CONNECT; return err; @@ -88,7 +100,7 @@ Error HTTPClient::connect_to_host(const String &p_host, int p_port, bool p_ssl, status = STATUS_CONNECTING; } else { // Host contains hostname and needs to be resolved to IP - resolving = IP::get_singleton()->resolve_hostname_queue_item(conn_host); + resolving = IP::get_singleton()->resolve_hostname_queue_item(server_host); status = STATUS_RESOLVING; } @@ -141,7 +153,12 @@ Error HTTPClient::request_raw(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); - String request = String(_methods[p_method]) + " " + p_url + " HTTP/1.1\r\n"; + String uri = p_url; + if (!ssl && http_proxy_port != -1) { + uri = vformat("http://%s:%d%s", conn_host, conn_port, p_url); + } + + String request = String(_methods[p_method]) + " " + uri + " HTTP/1.1\r\n"; bool add_host = true; bool add_clen = p_body.size() > 0; bool add_uagent = true; @@ -214,7 +231,12 @@ Error HTTPClient::request(Method p_method, const String &p_url, const Vector 0) { - err = tcp_connection->connect_to_host(ip_candidates.pop_front(), conn_port); + err = tcp_connection->connect_to_host(ip_candidates.pop_front(), server_port); if (err == OK) { break; } @@ -366,7 +389,48 @@ Error HTTPClient::poll() { return OK; } break; case StreamPeerTCP::STATUS_CONNECTED: { - if (ssl) { + if (ssl && proxy_client.is_valid()) { + Error err = proxy_client->poll(); + if (err == ERR_UNCONFIGURED) { + proxy_client->set_connection(tcp_connection); + const Vector headers; + err = proxy_client->request(METHOD_CONNECT, vformat("%s:%d", conn_host, conn_port), headers); + if (err != OK) { + status = STATUS_CANT_CONNECT; + return err; + } + } else if (err != OK) { + status = STATUS_CANT_CONNECT; + return err; + } + switch (proxy_client->get_status()) { + case STATUS_REQUESTING: { + return OK; + } break; + case STATUS_BODY: { + proxy_client->read_response_body_chunk(); + return OK; + } break; + case STATUS_CONNECTED: { + if (proxy_client->get_response_code() != RESPONSE_OK) { + status = STATUS_CANT_CONNECT; + return ERR_CANT_CONNECT; + } + proxy_client.unref(); + return OK; + } + case STATUS_DISCONNECTED: + case STATUS_RESOLVING: + case STATUS_CONNECTING: { + status = STATUS_CANT_CONNECT; + ERR_FAIL_V(ERR_BUG); + } break; + default: { + status = STATUS_CANT_CONNECT; + return ERR_CANT_CONNECT; + } break; + } + } else if (ssl) { Ref ssl; if (!handshaking) { // Connect the StreamPeerSSL and start handshaking @@ -416,7 +480,7 @@ Error HTTPClient::poll() { Error err = ERR_CANT_CONNECT; while (ip_candidates.size() > 0) { tcp_connection->disconnect_from_host(); - err = tcp_connection->connect_to_host(ip_candidates.pop_front(), conn_port); + err = tcp_connection->connect_to_host(ip_candidates.pop_front(), server_port); if (err == OK) { return OK; } @@ -756,6 +820,9 @@ HTTPClient::HTTPClient() { status = STATUS_DISCONNECTED; head_request = false; conn_port = -1; + server_port = -1; + http_proxy_port = -1; + https_proxy_port = -1; body_size = -1; chunked = false; body_left = 0; @@ -775,6 +842,34 @@ HTTPClient::~HTTPClient() { #endif // #ifndef JAVASCRIPT_ENABLED +void HTTPClient::set_http_proxy(const String &p_host, int p_port) { +#ifdef JAVASCRIPT_ENABLED + WARN_PRINT("HTTP proxy feature is not available"); +#else + if (p_host.empty() || p_port == -1) { + http_proxy_host = ""; + http_proxy_port = -1; + } else { + http_proxy_host = p_host; + http_proxy_port = p_port; + } +#endif +} + +void HTTPClient::set_https_proxy(const String &p_host, int p_port) { +#ifdef JAVASCRIPT_ENABLED + WARN_PRINT("HTTPS proxy feature is not available"); +#else + if (p_host.empty() || p_port == -1) { + https_proxy_host = ""; + https_proxy_port = -1; + } else { + https_proxy_host = p_host; + https_proxy_port = p_port; + } +#endif +} + String HTTPClient::query_string_from_dict(const Dictionary &p_dict) { String query = ""; Array keys = p_dict.keys(); @@ -860,6 +955,9 @@ void HTTPClient::_bind_methods() { ClassDB::bind_method(D_METHOD("get_status"), &HTTPClient::get_status); ClassDB::bind_method(D_METHOD("poll"), &HTTPClient::poll); + ClassDB::bind_method(D_METHOD("set_http_proxy", "host", "port"), &HTTPClient::set_http_proxy); + ClassDB::bind_method(D_METHOD("set_https_proxy", "host", "port"), &HTTPClient::set_https_proxy); + ClassDB::bind_method(D_METHOD("query_string_from_dict", "fields"), &HTTPClient::query_string_from_dict); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "blocking_mode_enabled"), "set_blocking_mode", "is_blocking_mode_enabled"); diff --git a/core/io/http_client.h b/core/io/http_client.h index 47ad576f5d7..972c652fd2f 100644 --- a/core/io/http_client.h +++ b/core/io/http_client.h @@ -160,8 +160,14 @@ private: Status status; IP::ResolverID resolving; Array ip_candidates; - int conn_port; + int conn_port; // Server to make requests to. String conn_host; + int server_port; // Server to connect to (might be a proxy server). + String server_host; + int http_proxy_port; // Proxy server for http requests. + String http_proxy_host; + int https_proxy_port; // Proxy server for https requests. + String https_proxy_host; bool ssl; bool ssl_verify_host; bool blocking; @@ -180,6 +186,7 @@ private: Ref tcp_connection; Ref connection; + Ref proxy_client; int response_num; Vector response_headers; @@ -227,6 +234,10 @@ public: String query_string_from_dict(const Dictionary &p_dict); + // Use empty string or -1 to unset. + void set_http_proxy(const String &p_host, int p_port); + void set_https_proxy(const String &p_host, int p_port); + HTTPClient(); ~HTTPClient(); }; diff --git a/doc/classes/HTTPClient.xml b/doc/classes/HTTPClient.xml index dc18d69d337..2294986c272 100644 --- a/doc/classes/HTTPClient.xml +++ b/doc/classes/HTTPClient.xml @@ -149,6 +149,24 @@ Sends the body data raw, as a byte array and does not encode it in any way. + + + + + + Sets the proxy server for HTTP requests. + The proxy server is unset if [code]host[/code] is empty or [code]port[/code] is -1. + + + + + + + + Sets the proxy server for HTTPS requests. + The proxy server is unset if [code]host[/code] is empty or [code]port[/code] is -1. + + diff --git a/doc/classes/HTTPRequest.xml b/doc/classes/HTTPRequest.xml index 99698c4efd1..89ed769135a 100644 --- a/doc/classes/HTTPRequest.xml +++ b/doc/classes/HTTPRequest.xml @@ -121,6 +121,24 @@ Returns [constant OK] if request is successfully created. (Does not imply that the server has responded), [constant ERR_UNCONFIGURED] if not in the tree, [constant ERR_BUSY] if still processing previous request, [constant ERR_INVALID_PARAMETER] if given string is not a valid URL format, or [constant ERR_CANT_CONNECT] if not using thread and the [HTTPClient] cannot connect to host. + + + + + + Sets the proxy server for HTTP requests. + The proxy server is unset if [code]host[/code] is empty or [code]port[/code] is -1. + + + + + + + + Sets the proxy server for HTTPS requests. + The proxy server is unset if [code]host[/code] is empty or [code]port[/code] is -1. + + diff --git a/editor/export_template_manager.cpp b/editor/export_template_manager.cpp index 42dc2b3fbdc..7f1992fd794 100644 --- a/editor/export_template_manager.cpp +++ b/editor/export_template_manager.cpp @@ -150,6 +150,11 @@ void ExportTemplateManager::_download_template(const String &p_url, bool p_skip_ download_templates->set_download_file(EditorSettings::get_singleton()->get_cache_dir().plus_file("tmp_templates.tpz")); download_templates->set_use_threads(true); + const String proxy_host = EDITOR_DEF("network/http_proxy/host", ""); + const int proxy_port = EDITOR_DEF("network/http_proxy/port", -1); + download_templates->set_http_proxy(proxy_host, proxy_port); + download_templates->set_https_proxy(proxy_host, proxy_port); + Error err = download_templates->request(p_url); if (err != OK) { _set_current_progress_status(TTR("Error requesting URL:") + " " + p_url, true); diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index b41802dc8e6..8c882b78c87 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -37,6 +37,15 @@ #include "editor/editor_settings.h" #include "editor/project_settings_editor.h" +static inline void setup_http_request(HTTPRequest *request) { + request->set_use_threads(EDITOR_DEF("asset_library/use_threads", true)); + + const String proxy_host = EDITOR_DEF("network/http_proxy/host", ""); + const int proxy_port = EDITOR_DEF("network/http_proxy/port", -1); + request->set_http_proxy(proxy_host, proxy_port); + request->set_https_proxy(proxy_host, proxy_port); +} + void EditorAssetLibraryItem::configure(const String &p_title, int p_asset_id, const String &p_category, int p_category_id, const String &p_author, int p_author_id, const String &p_cost) { title->set_text(p_title); asset_id = p_asset_id; @@ -543,7 +552,7 @@ EditorAssetLibraryItemDownload::EditorAssetLibraryItemDownload() { download = memnew(HTTPRequest); add_child(download); download->connect("request_completed", this, "_http_download_completed"); - download->set_use_threads(EDITOR_DEF("asset_library/use_threads", true)); + setup_http_request(download); download_error = memnew(AcceptDialog); add_child(download_error); @@ -863,7 +872,7 @@ void EditorAssetLibrary::_request_image(ObjectID p_for, String p_image_url, Imag iq.image_index = p_image_index; iq.image_type = p_type; iq.request = memnew(HTTPRequest); - iq.request->set_use_threads(EDITOR_DEF("asset_library/use_threads", true)); + setup_http_request(iq.request); iq.target = p_for; iq.queue_id = ++last_queue_id; @@ -1482,7 +1491,7 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) { request = memnew(HTTPRequest); add_child(request); - request->set_use_threads(EDITOR_DEF("asset_library/use_threads", true)); + setup_http_request(request); request->connect("request_completed", this, "_http_request_completed"); last_queue_id = 0; diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp index e7d82c7c2ae..1826cb37193 100644 --- a/scene/main/http_request.cpp +++ b/scene/main/http_request.cpp @@ -463,6 +463,14 @@ int HTTPRequest::get_body_size() const { return body_len; } +void HTTPRequest::set_http_proxy(const String &p_host, int p_port) { + client->set_http_proxy(p_host, p_port); +} + +void HTTPRequest::set_https_proxy(const String &p_host, int p_port) { + client->set_https_proxy(p_host, p_port); +} + void HTTPRequest::set_timeout(int p_timeout) { ERR_FAIL_COND(p_timeout < 0); timeout = p_timeout; @@ -508,6 +516,9 @@ void HTTPRequest::_bind_methods() { ClassDB::bind_method(D_METHOD("set_download_chunk_size"), &HTTPRequest::set_download_chunk_size); ClassDB::bind_method(D_METHOD("get_download_chunk_size"), &HTTPRequest::get_download_chunk_size); + ClassDB::bind_method(D_METHOD("set_http_proxy", "host", "port"), &HTTPRequest::set_http_proxy); + ClassDB::bind_method(D_METHOD("set_https_proxy", "host", "port"), &HTTPRequest::set_https_proxy); + ClassDB::bind_method(D_METHOD("_timeout"), &HTTPRequest::_timeout); ADD_PROPERTY(PropertyInfo(Variant::STRING, "download_file", PROPERTY_HINT_FILE), "set_download_file", "get_download_file"); diff --git a/scene/main/http_request.h b/scene/main/http_request.h index d7edae3071b..82fda70cd62 100644 --- a/scene/main/http_request.h +++ b/scene/main/http_request.h @@ -146,6 +146,10 @@ public: int get_downloaded_bytes() const; int get_body_size() const; + // Use empty string or -1 to unset. + void set_http_proxy(const String &p_host, int p_port); + void set_https_proxy(const String &p_host, int p_port); + HTTPRequest(); ~HTTPRequest(); };