From e192b1e95637401982f61c31591e2098eecac359 Mon Sep 17 00:00:00 2001 From: ItisCaleb <40360126+ItisCaleb@users.noreply.github.com> Date: Fri, 10 Jan 2025 01:10:09 +0800 Subject: [PATCH] Add HTTP proxy Basic authentication * Implement Basic(user/pass) authentication for http/https proxy Co-authored-by: A Thousand Ships <96648715+AThousandShips@users.noreply.github.com> --- core/io/http_client.compat.inc | 46 +++++++++++++++++++ core/io/http_client.cpp | 9 ++-- core/io/http_client.h | 9 +++- core/io/http_client_tcp.cpp | 18 ++++++-- core/io/http_client_tcp.h | 8 +++- doc/classes/HTTPClient.xml | 4 ++ doc/classes/HTTPRequest.xml | 4 ++ .../4.3-stable.expected | 10 ++++ scene/main/http_request.compat.inc | 46 +++++++++++++++++++ scene/main/http_request.cpp | 13 +++--- scene/main/http_request.h | 9 +++- 11 files changed, 157 insertions(+), 19 deletions(-) create mode 100644 core/io/http_client.compat.inc create mode 100644 scene/main/http_request.compat.inc diff --git a/core/io/http_client.compat.inc b/core/io/http_client.compat.inc new file mode 100644 index 00000000000..de0490343b6 --- /dev/null +++ b/core/io/http_client.compat.inc @@ -0,0 +1,46 @@ +/**************************************************************************/ +/* http_client.compat.inc */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef DISABLE_DEPRECATED + +void HTTPClient::_set_http_proxy_bind_compat_101358(const String &p_host, int p_port) { + set_http_proxy(p_host, p_port, "", ""); +} + +void HTTPClient::_set_https_proxy_bind_compat_101358(const String &p_host, int p_port) { + set_https_proxy(p_host, p_port, "", ""); +} + +void HTTPClient::_bind_compatibility_methods() { + ClassDB::bind_compatibility_method(D_METHOD("set_http_proxy", "host", "port"), &HTTPClient::_set_http_proxy_bind_compat_101358); + ClassDB::bind_compatibility_method(D_METHOD("set_https_proxy", "host", "port"), &HTTPClient::_set_https_proxy_bind_compat_101358); +} + +#endif // DISABLE_DEPRECATED diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp index b7a324e710e..0a947a756c0 100644 --- a/core/io/http_client.cpp +++ b/core/io/http_client.cpp @@ -29,6 +29,7 @@ /**************************************************************************/ #include "http_client.h" +#include "http_client.compat.inc" const char *HTTPClient::_methods[METHOD_MAX] = { "GET", @@ -49,11 +50,11 @@ HTTPClient *HTTPClient::create(bool p_notify_postinitialize) { return nullptr; } -void HTTPClient::set_http_proxy(const String &p_host, int p_port) { +void HTTPClient::set_http_proxy(const String &p_host, int p_port, const String &p_user, const String &p_pass) { WARN_PRINT("HTTP proxy feature is not available"); } -void HTTPClient::set_https_proxy(const String &p_host, int p_port) { +void HTTPClient::set_https_proxy(const String &p_host, int p_port, const String &p_user, const String &p_pass) { WARN_PRINT("HTTPS proxy feature is not available"); } @@ -162,8 +163,8 @@ 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("set_http_proxy", "host", "port", "username", "password"), &HTTPClient::set_http_proxy, DEFVAL(String()), DEFVAL(String())); + ClassDB::bind_method(D_METHOD("set_https_proxy", "host", "port", "username", "password"), &HTTPClient::set_https_proxy, DEFVAL(String()), DEFVAL(String())); ClassDB::bind_method(D_METHOD("query_string_from_dict", "fields"), &HTTPClient::query_string_from_dict); diff --git a/core/io/http_client.h b/core/io/http_client.h index 59452911221..3002937985c 100644 --- a/core/io/http_client.h +++ b/core/io/http_client.h @@ -161,6 +161,11 @@ protected: static HTTPClient *(*_create)(bool p_notify_postinitialize); static void _bind_methods(); +#ifndef DISABLE_DEPRECATED + void _set_http_proxy_bind_compat_101358(const String &p_host, int p_port); + void _set_https_proxy_bind_compat_101358(const String &p_host, int p_port); + static void _bind_compatibility_methods(); +#endif public: static HTTPClient *create(bool p_notify_postinitialize = true); @@ -195,8 +200,8 @@ 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); + virtual void set_http_proxy(const String &p_host, int p_port, const String &p_user = "", const String &p_pass = ""); + virtual void set_https_proxy(const String &p_host, int p_port, const String &p_user = "", const String &p_pass = ""); HTTPClient() {} virtual ~HTTPClient() {} diff --git a/core/io/http_client_tcp.cpp b/core/io/http_client_tcp.cpp index 3147b59c601..405c8daaa30 100644 --- a/core/io/http_client_tcp.cpp +++ b/core/io/http_client_tcp.cpp @@ -32,6 +32,7 @@ #include "http_client_tcp.h" +#include "core/crypto/crypto_core.h" #include "core/io/stream_peer_tls.h" #include "core/version.h" @@ -201,6 +202,11 @@ Error HTTPClientTCP::request(Method p_method, const String &p_url, const Vector< if (add_accept) { request += "Accept: */*\r\n"; } + if (tls_options.is_null() && http_proxy_port != -1) { + CharString auth = vformat("%s:%s", http_proxy_user, http_proxy_pass).utf8(); + String encoded_auth = CryptoCore::b64_encode_str((unsigned char *)auth.get_data(), auth.length()); + request += vformat("Proxy-Authorization: Basic %s\r\n", encoded_auth); + } request += "\r\n"; CharString cs = request.utf8(); @@ -323,7 +329,9 @@ Error HTTPClientTCP::poll() { Error err = proxy_client->poll(); if (err == ERR_UNCONFIGURED) { proxy_client->set_connection(tcp_connection); - const Vector headers; + CharString auth = vformat("%s:%s", https_proxy_user, https_proxy_pass).utf8(); + String encoded_auth = CryptoCore::b64_encode_str((unsigned char *)auth.get_data(), auth.length()); + const Vector headers({ vformat("Proxy-Authorization: Basic %s", encoded_auth) }); err = proxy_client->request(METHOD_CONNECT, vformat("%s:%d", conn_host, conn_port), headers, nullptr, 0); if (err != OK) { status = STATUS_CANT_CONNECT; @@ -768,7 +776,7 @@ int HTTPClientTCP::get_read_chunk_size() const { return read_chunk_size; } -void HTTPClientTCP::set_http_proxy(const String &p_host, int p_port) { +void HTTPClientTCP::set_http_proxy(const String &p_host, int p_port, const String &p_user, const String &p_pass) { if (p_host.is_empty() || p_port == -1) { http_proxy_host = ""; http_proxy_port = -1; @@ -776,9 +784,11 @@ void HTTPClientTCP::set_http_proxy(const String &p_host, int p_port) { http_proxy_host = p_host; http_proxy_port = p_port; } + http_proxy_user = p_user; + http_proxy_pass = p_pass; } -void HTTPClientTCP::set_https_proxy(const String &p_host, int p_port) { +void HTTPClientTCP::set_https_proxy(const String &p_host, int p_port, const String &p_user, const String &p_pass) { if (p_host.is_empty() || p_port == -1) { https_proxy_host = ""; https_proxy_port = -1; @@ -786,6 +796,8 @@ void HTTPClientTCP::set_https_proxy(const String &p_host, int p_port) { https_proxy_host = p_host; https_proxy_port = p_port; } + https_proxy_user = p_user; + https_proxy_pass = p_pass; } HTTPClientTCP::HTTPClientTCP() { diff --git a/core/io/http_client_tcp.h b/core/io/http_client_tcp.h index dd6cc6b84f0..c3fb0b3b3fb 100644 --- a/core/io/http_client_tcp.h +++ b/core/io/http_client_tcp.h @@ -46,8 +46,12 @@ private: String server_host; int http_proxy_port = -1; // Proxy server for http requests. String http_proxy_host; + String http_proxy_user; + String http_proxy_pass; int https_proxy_port = -1; // Proxy server for https requests. String https_proxy_host; + String https_proxy_user; + String https_proxy_pass; bool blocking = false; bool handshaking = false; bool head_request = false; @@ -96,8 +100,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; + void set_http_proxy(const String &p_host, int p_port, const String &p_user = "", const String &p_pass = "") override; + void set_https_proxy(const String &p_host, int p_port, const String &p_user = "", const String &p_pass = "") override; HTTPClientTCP(); }; diff --git a/doc/classes/HTTPClient.xml b/doc/classes/HTTPClient.xml index 82fbbba146e..ab0b38d39cd 100644 --- a/doc/classes/HTTPClient.xml +++ b/doc/classes/HTTPClient.xml @@ -180,6 +180,8 @@ + + Sets the proxy server for HTTP requests. The proxy server is unset if [param host] is empty or [param port] is -1. @@ -189,6 +191,8 @@ + + Sets the proxy server for HTTPS requests. The proxy server is unset if [param host] is empty or [param port] is -1. diff --git a/doc/classes/HTTPRequest.xml b/doc/classes/HTTPRequest.xml index d294bfa3a4e..f92d7efcea8 100644 --- a/doc/classes/HTTPRequest.xml +++ b/doc/classes/HTTPRequest.xml @@ -210,6 +210,8 @@ + + Sets the proxy server for HTTP requests. The proxy server is unset if [param host] is empty or [param port] is -1. @@ -219,6 +221,8 @@ + + Sets the proxy server for HTTPS requests. The proxy server is unset if [param host] is empty or [param port] is -1. diff --git a/misc/extension_api_validation/4.3-stable.expected b/misc/extension_api_validation/4.3-stable.expected index 527aa0fa293..8801890969d 100644 --- a/misc/extension_api_validation/4.3-stable.expected +++ b/misc/extension_api_validation/4.3-stable.expected @@ -334,3 +334,13 @@ Validate extension JSON: Error: Field 'classes/EditorTranslationParserPlugin/met Validate extension JSON: JSON file: Field was added in a way that breaks compatibility 'classes/EditorTranslationParserPlugin/methods/_parse_file': return_value Returning by argument reference is not safe in extensions, changed to returning as an Array and merged with `get_comments`. Compatibility method registered. + + +GH-101358 +-------- +Validate extension JSON: Error: Field 'classes/HTTPClient/methods/set_http_proxy/arguments': size changed value in new API, from 2 to 4. +Validate extension JSON: Error: Field 'classes/HTTPClient/methods/set_https_proxy/arguments': size changed value in new API, from 2 to 4. +Validate extension JSON: Error: Field 'classes/HTTPRequest/methods/set_http_proxy/arguments': size changed value in new API, from 2 to 4. +Validate extension JSON: Error: Field 'classes/HTTPRequest/methods/set_https_proxy/arguments': size changed value in new API, from 2 to 4. + +Added optional "user" and "pass" argument allowing Basic authentication in both http and https proxy. diff --git a/scene/main/http_request.compat.inc b/scene/main/http_request.compat.inc new file mode 100644 index 00000000000..870551f7bc8 --- /dev/null +++ b/scene/main/http_request.compat.inc @@ -0,0 +1,46 @@ +/**************************************************************************/ +/* http_request.compat.inc */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef DISABLE_DEPRECATED + +void HTTPRequest::_set_http_proxy_bind_compat_101358(const String &p_host, int p_port) { + set_http_proxy(p_host, p_port, "", ""); +} + +void HTTPRequest::_set_https_proxy_bind_compat_101358(const String &p_host, int p_port) { + set_https_proxy(p_host, p_port, "", ""); +} + +void HTTPRequest::_bind_compatibility_methods() { + ClassDB::bind_compatibility_method(D_METHOD("set_http_proxy", "host", "port"), &HTTPRequest::_set_http_proxy_bind_compat_101358); + ClassDB::bind_compatibility_method(D_METHOD("set_https_proxy", "host", "port"), &HTTPRequest::_set_https_proxy_bind_compat_101358); +} + +#endif // DISABLE_DEPRECATED diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp index 9de2d62e40f..2db1dceea52 100644 --- a/scene/main/http_request.cpp +++ b/scene/main/http_request.cpp @@ -29,6 +29,7 @@ /**************************************************************************/ #include "http_request.h" +#include "http_request.compat.inc" #include "scene/main/timer.h" @@ -573,12 +574,12 @@ 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_http_proxy(const String &p_host, int p_port, const String &p_user, const String &p_pass) { + client->set_http_proxy(p_host, p_port, p_user, p_pass); } -void HTTPRequest::set_https_proxy(const String &p_host, int p_port) { - client->set_https_proxy(p_host, p_port); +void HTTPRequest::set_https_proxy(const String &p_host, int p_port, const String &p_user, const String &p_pass) { + client->set_https_proxy(p_host, p_port, p_user, p_pass); } void HTTPRequest::set_timeout(double p_timeout) { @@ -632,8 +633,8 @@ void HTTPRequest::_bind_methods() { ClassDB::bind_method(D_METHOD("set_download_chunk_size", "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("set_http_proxy", "host", "port", "username", "password"), &HTTPRequest::set_http_proxy, DEFVAL(String()), DEFVAL(String())); + ClassDB::bind_method(D_METHOD("set_https_proxy", "host", "port", "username", "password"), &HTTPRequest::set_https_proxy, DEFVAL(String()), DEFVAL(String())); ADD_PROPERTY(PropertyInfo(Variant::STRING, "download_file", PROPERTY_HINT_FILE), "set_download_file", "get_download_file"); ADD_PROPERTY(PropertyInfo(Variant::INT, "download_chunk_size", PROPERTY_HINT_RANGE, "256,16777216,suffix:B"), "set_download_chunk_size", "get_download_chunk_size"); diff --git a/scene/main/http_request.h b/scene/main/http_request.h index 9a91171eafd..22bbfbcf5fb 100644 --- a/scene/main/http_request.h +++ b/scene/main/http_request.h @@ -123,6 +123,11 @@ private: protected: void _notification(int p_what); static void _bind_methods(); +#ifndef DISABLE_DEPRECATED + void _set_http_proxy_bind_compat_101358(const String &p_host, int p_port); + void _set_https_proxy_bind_compat_101358(const String &p_host, int p_port); + static void _bind_compatibility_methods(); +#endif public: Error request(const String &p_url, const Vector &p_custom_headers = Vector(), HTTPClient::Method p_method = HTTPClient::METHOD_GET, const String &p_request_data = ""); //connects to a full url and perform request @@ -158,8 +163,8 @@ public: int get_downloaded_bytes() const; int get_body_size() const; - void set_http_proxy(const String &p_host, int p_port); - void set_https_proxy(const String &p_host, int p_port); + void set_http_proxy(const String &p_host, int p_port, const String &p_user = "", const String &p_pass = ""); + void set_https_proxy(const String &p_host, int p_port, const String &p_user = "", const String &p_pass = ""); void set_tls_options(const Ref &p_options);