summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHaoyu Qiu <timothyqiu32@gmail.com>2021-07-03 13:11:59 +0800
committerHaoyu Qiu <timothyqiu32@gmail.com>2021-11-24 14:36:42 +0800
commitc09ea8d45acdf02df30b658408137f2ca7be2533 (patch)
tree63d4352a3101286da56d57bd39ccd14e9e73eaeb
parentc5ab537617d9461964e67bb135bb0818efe21d58 (diff)
Adds proxy support for HTTPClient
Also fixed a potential infinite loop when connecting to server.
-rw-r--r--core/io/http_client.cpp11
-rw-r--r--core/io/http_client.h4
-rw-r--r--core/io/http_client_tcp.cpp93
-rw-r--r--core/io/http_client_tcp.h11
-rw-r--r--doc/classes/HTTPClient.xml18
5 files changed, 129 insertions, 8 deletions
diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp
index 800ac779e5..5c67bc4c48 100644
--- a/core/io/http_client.cpp
+++ b/core/io/http_client.cpp
@@ -49,6 +49,14 @@ HTTPClient *HTTPClient::create() {
return nullptr;
}
+void HTTPClient::set_http_proxy(const String &p_host, int p_port) {
+ WARN_PRINT("HTTP proxy feature is not available");
+}
+
+void HTTPClient::set_https_proxy(const String &p_host, int p_port) {
+ WARN_PRINT("HTTPS proxy feature is not available");
+}
+
Error HTTPClient::_request_raw(Method p_method, const String &p_url, const Vector<String> &p_headers, const Vector<uint8_t> &p_body) {
int size = p_body.size();
return request(p_method, p_url, p_headers, size > 0 ? p_body.ptr() : nullptr, size);
@@ -142,6 +150,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 718c3a905e..9bc0134a6a 100644
--- a/core/io/http_client.h
+++ b/core/io/http_client.h
@@ -192,6 +192,10 @@ public:
virtual Error poll() = 0;
+ // Use empty string or -1 to unset
+ virtual void set_http_proxy(const String &p_host, int p_port);
+ virtual void set_https_proxy(const String &p_host, int p_port);
+
HTTPClient() {}
virtual ~HTTPClient() {}
};
diff --git a/core/io/http_client_tcp.cpp b/core/io/http_client_tcp.cpp
index b3d35b3603..f2ddeddbb8 100644
--- a/core/io/http_client_tcp.cpp
+++ b/core/io/http_client_tcp.cpp
@@ -70,9 +70,21 @@ Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_ss
connection = tcp_connection;
- if (conn_host.is_valid_ip_address()) {
+ if (ssl && https_proxy_port != -1) {
+ proxy_client.instantiate(); // Needs proxy negotiation
+ 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(IPAddress(conn_host), p_port);
+ Error err = tcp_connection->connect_to_host(IPAddress(server_host), server_port);
if (err) {
status = STATUS_CANT_CONNECT;
return err;
@@ -81,7 +93,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
- resolving = IP::get_singleton()->resolve_hostname_queue_item(conn_host);
+ resolving = IP::get_singleton()->resolve_hostname_queue_item(server_host);
status = STATUS_RESOLVING;
}
@@ -134,7 +146,12 @@ 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);
- 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;
@@ -229,6 +246,7 @@ void HTTPClientTCP::close() {
}
connection.unref();
+ proxy_client.unref();
status = STATUS_DISCONNECTED;
head_request = false;
if (resolving != IP::RESOLVER_INVALID_ID) {
@@ -265,7 +283,7 @@ Error HTTPClientTCP::poll() {
Error err = ERR_BUG; // Should be at least one entry.
while (ip_candidates.size() > 0) {
- err = tcp_connection->connect_to_host(ip_candidates.front(), conn_port);
+ err = tcp_connection->connect_to_host(ip_candidates.pop_front(), server_port);
if (err == OK) {
break;
}
@@ -294,7 +312,48 @@ Error HTTPClientTCP::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, nullptr, 0);
+ 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
@@ -344,7 +403,7 @@ Error HTTPClientTCP::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;
}
@@ -678,6 +737,26 @@ int HTTPClientTCP::get_read_chunk_size() const {
return read_chunk_size;
}
+void HTTPClientTCP::set_http_proxy(const String &p_host, int p_port) {
+ if (p_host.is_empty() || p_port == -1) {
+ http_proxy_host = "";
+ http_proxy_port = -1;
+ } else {
+ http_proxy_host = p_host;
+ http_proxy_port = p_port;
+ }
+}
+
+void HTTPClientTCP::set_https_proxy(const String &p_host, int p_port) {
+ if (p_host.is_empty() || p_port == -1) {
+ https_proxy_host = "";
+ https_proxy_port = -1;
+ } else {
+ https_proxy_host = p_host;
+ https_proxy_port = p_port;
+ }
+}
+
HTTPClientTCP::HTTPClientTCP() {
tcp_connection.instantiate();
}
diff --git a/core/io/http_client_tcp.h b/core/io/http_client_tcp.h
index 170afb551c..40a962925e 100644
--- a/core/io/http_client_tcp.h
+++ b/core/io/http_client_tcp.h
@@ -38,8 +38,14 @@ private:
Status status = STATUS_DISCONNECTED;
IP::ResolverID resolving = IP::RESOLVER_INVALID_ID;
Array ip_candidates;
- int conn_port = -1;
+ 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)
+ String server_host;
+ int http_proxy_port = -1; // Proxy server for http requests
+ String http_proxy_host;
+ int https_proxy_port = -1; // Proxy server for https requests
+ String https_proxy_host;
bool ssl = false;
bool ssl_verify_host = false;
bool blocking = false;
@@ -58,6 +64,7 @@ private:
Ref<StreamPeerTCP> tcp_connection;
Ref<StreamPeer> connection;
+ Ref<HTTPClientTCP> proxy_client; // Negotiate with proxy server
int response_num = 0;
Vector<String> response_headers;
@@ -87,6 +94,8 @@ public:
void set_read_chunk_size(int p_size) override;
int get_read_chunk_size() const override;
Error poll() override;
+ void set_http_proxy(const String &p_host, int p_port) override;
+ void set_https_proxy(const String &p_host, int p_port) override;
HTTPClientTCP();
};
diff --git a/doc/classes/HTTPClient.xml b/doc/classes/HTTPClient.xml
index a50983853d..e15b065744 100644
--- a/doc/classes/HTTPClient.xml
+++ b/doc/classes/HTTPClient.xml
@@ -173,6 +173,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">