Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3.x] Add proxy support for HTTPClient and the editor #55988

Merged
merged 1 commit into from
Dec 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 106 additions & 8 deletions core/io/http_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -214,7 +231,12 @@ Error HTTPClient::request(Method p_method, const String &p_url, const Vector<Str
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_uagent = true;
bool add_accept = true;
Expand Down Expand Up @@ -301,6 +323,7 @@ void HTTPClient::close() {
}

connection.unref();
proxy_client.unref();
status = STATUS_DISCONNECTED;
head_request = false;
if (resolving != IP::RESOLVER_INVALID_ID) {
Expand Down Expand Up @@ -337,7 +360,7 @@ Error HTTPClient::poll() {

Error err = ERR_BUG; // Should be at least one entry.
while (ip_candidates.size() > 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;
}
Expand Down Expand Up @@ -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<String> 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<StreamPeerSSL> ssl;
if (!handshaking) {
// Connect the StreamPeerSSL and start handshaking
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
Expand All @@ -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();
Expand Down Expand Up @@ -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");
Expand Down
13 changes: 12 additions & 1 deletion core/io/http_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,14 @@ class HTTPClient : public Reference {
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;
Expand All @@ -180,6 +186,7 @@ class HTTPClient : public Reference {

Ref<StreamPeerTCP> tcp_connection;
Ref<StreamPeer> connection;
Ref<HTTPClient> proxy_client;

int response_num;
Vector<String> response_headers;
Expand Down Expand Up @@ -227,6 +234,10 @@ class HTTPClient : public Reference {

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();
};
Expand Down
18 changes: 18 additions & 0 deletions doc/classes/HTTPClient.xml
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,24 @@
Sends the body data raw, as a byte array and does not encode it in any way.
</description>
</method>
<method name="set_http_proxy">
<return type="void" />
<argument index="0" name="host" type="String" />
<argument index="1" name="port" type="int" />
<description>
Sets the proxy server for HTTP requests.
The proxy server is unset if [code]host[/code] is empty or [code]port[/code] is -1.
</description>
</method>
<method name="set_https_proxy">
<return type="void" />
<argument index="0" name="host" type="String" />
<argument index="1" name="port" type="int" />
<description>
Sets the proxy server for HTTPS requests.
The proxy server is unset if [code]host[/code] is empty or [code]port[/code] is -1.
</description>
</method>
</methods>
<members>
<member name="blocking_mode_enabled" type="bool" setter="set_blocking_mode" getter="is_blocking_mode_enabled" default="false">
Expand Down
18 changes: 18 additions & 0 deletions doc/classes/HTTPRequest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
</description>
</method>
<method name="set_http_proxy">
<return type="void" />
<argument index="0" name="host" type="String" />
<argument index="1" name="port" type="int" />
<description>
Sets the proxy server for HTTP requests.
The proxy server is unset if [code]host[/code] is empty or [code]port[/code] is -1.
</description>
</method>
<method name="set_https_proxy">
<return type="void" />
<argument index="0" name="host" type="String" />
<argument index="1" name="port" type="int" />
<description>
Sets the proxy server for HTTPS requests.
The proxy server is unset if [code]host[/code] is empty or [code]port[/code] is -1.
</description>
</method>
</methods>
<members>
<member name="body_size_limit" type="int" setter="set_body_size_limit" getter="get_body_size_limit" default="-1">
Expand Down
5 changes: 5 additions & 0 deletions editor/export_template_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
15 changes: 12 additions & 3 deletions editor/plugins/asset_library_editor_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
11 changes: 11 additions & 0 deletions scene/main/http_request.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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");
Expand Down
4 changes: 4 additions & 0 deletions scene/main/http_request.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ class HTTPRequest : public Node {
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();
};
Expand Down