Procházet zdrojové kódy

Merge pull request #92237 from timothyqiu/url-fragment

String: Parse fragment from URL
Rémi Verschelde před 11 měsíci
rodič
revize
cf1d910e10

+ 21 - 4
core/string/ustring.cpp

@@ -221,18 +221,35 @@ void CharString::copy_from(const char *p_cstr) {
 /*  String                                                               */
 /*************************************************************************/
 
-Error String::parse_url(String &r_scheme, String &r_host, int &r_port, String &r_path) const {
-	// Splits the URL into scheme, host, port, path. Strip credentials when present.
+Error String::parse_url(String &r_scheme, String &r_host, int &r_port, String &r_path, String &r_fragment) const {
+	// Splits the URL into scheme, host, port, path, fragment. Strip credentials when present.
 	String base = *this;
 	r_scheme = "";
 	r_host = "";
 	r_port = 0;
 	r_path = "";
+	r_fragment = "";
+
 	int pos = base.find("://");
 	// Scheme
 	if (pos != -1) {
-		r_scheme = base.substr(0, pos + 3).to_lower();
-		base = base.substr(pos + 3, base.length() - pos - 3);
+		bool is_scheme_valid = true;
+		for (int i = 0; i < pos; i++) {
+			if (!is_ascii_alphanumeric_char(base[i]) && base[i] != '+' && base[i] != '-' && base[i] != '.') {
+				is_scheme_valid = false;
+				break;
+			}
+		}
+		if (is_scheme_valid) {
+			r_scheme = base.substr(0, pos + 3).to_lower();
+			base = base.substr(pos + 3, base.length() - pos - 3);
+		}
+	}
+	pos = base.find("#");
+	// Fragment
+	if (pos != -1) {
+		r_fragment = base.substr(pos + 1);
+		base = base.substr(0, pos);
 	}
 	pos = base.find("/");
 	// Path

+ 1 - 1
core/string/ustring.h

@@ -452,7 +452,7 @@ public:
 	String c_escape_multiline() const;
 	String c_unescape() const;
 	String json_escape() const;
-	Error parse_url(String &r_scheme, String &r_host, int &r_port, String &r_path) const;
+	Error parse_url(String &r_scheme, String &r_host, int &r_port, String &r_path, String &r_fragment) const;
 
 	String property_name_encode() const;
 

+ 2 - 2
editor/debugger/editor_debugger_server.cpp

@@ -77,8 +77,8 @@ Error EditorDebuggerServerTCP::start(const String &p_uri) {
 
 	// Optionally override
 	if (!p_uri.is_empty() && p_uri != "tcp://") {
-		String scheme, path;
-		Error err = p_uri.parse_url(scheme, bind_host, bind_port, path);
+		String scheme, path, fragment;
+		Error err = p_uri.parse_url(scheme, bind_host, bind_port, path, fragment);
 		ERR_FAIL_COND_V(err != OK, ERR_INVALID_PARAMETER);
 		ERR_FAIL_COND_V(!bind_host.is_valid_ip_address() && bind_host != "*", ERR_INVALID_PARAMETER);
 	}

+ 2 - 1
editor/plugins/asset_library_editor_plugin.cpp

@@ -993,7 +993,8 @@ void EditorAssetLibrary::_request_image(ObjectID p_for, int p_asset_id, String p
 		String url_host;
 		int url_port;
 		String url_path;
-		Error err = trimmed_url.parse_url(url_scheme, url_host, url_port, url_path);
+		String url_fragment;
+		Error err = trimmed_url.parse_url(url_scheme, url_host, url_port, url_path, url_fragment);
 		if (err != OK) {
 			if (is_print_verbose_enabled()) {
 				ERR_PRINT(vformat("Asset Library: Invalid image URL '%s' for asset # %d.", trimmed_url, p_asset_id));

+ 2 - 2
modules/websocket/editor/editor_debugger_server_websocket.cpp

@@ -77,8 +77,8 @@ Error EditorDebuggerServerWebSocket::start(const String &p_uri) {
 
 	// Optionally override
 	if (!p_uri.is_empty() && p_uri != "ws://") {
-		String scheme, path;
-		Error err = p_uri.parse_url(scheme, bind_host, bind_port, path);
+		String scheme, path, fragment;
+		Error err = p_uri.parse_url(scheme, bind_host, bind_port, path, fragment);
 		ERR_FAIL_COND_V(err != OK, ERR_INVALID_PARAMETER);
 		ERR_FAIL_COND_V(!bind_host.is_valid_ip_address() && bind_host != "*", ERR_INVALID_PARAMETER);
 	}

+ 2 - 1
modules/websocket/emws_peer.cpp

@@ -68,8 +68,9 @@ Error EMWSPeer::connect_to_url(const String &p_url, Ref<TLSOptions> p_tls_option
 	String host;
 	String path;
 	String scheme;
+	String fragment;
 	int port = 0;
-	Error err = p_url.parse_url(scheme, host, port, path);
+	Error err = p_url.parse_url(scheme, host, port, path, fragment);
 	ERR_FAIL_COND_V_MSG(err != OK, err, "Invalid URL: " + p_url);
 
 	if (scheme.is_empty()) {

+ 2 - 1
modules/websocket/wsl_peer.cpp

@@ -482,8 +482,9 @@ Error WSLPeer::connect_to_url(const String &p_url, Ref<TLSOptions> p_options) {
 	String host;
 	String path;
 	String scheme;
+	String fragment;
 	int port = 0;
-	Error err = p_url.parse_url(scheme, host, port, path);
+	Error err = p_url.parse_url(scheme, host, port, path, fragment);
 	ERR_FAIL_COND_V_MSG(err != OK, err, "Invalid URL: " + p_url);
 	if (scheme.is_empty()) {
 		scheme = "ws://";

+ 2 - 1
scene/main/http_request.cpp

@@ -49,7 +49,8 @@ Error HTTPRequest::_parse_url(const String &p_url) {
 	redirections = 0;
 
 	String scheme;
-	Error err = p_url.parse_url(scheme, url, port, request_string);
+	String fragment;
+	Error err = p_url.parse_url(scheme, url, port, request_string, fragment);
 	ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Error parsing URL: '%s'.", p_url));
 
 	if (scheme == "https://") {

+ 40 - 0
tests/core/string/test_string.h

@@ -1988,6 +1988,46 @@ TEST_CASE("[String] Variant ptr indexed set") {
 	CHECK_EQ(s, String("azcd"));
 }
 
+TEST_CASE("[String] parse_url") {
+	String scheme, host, path, fragment;
+	int port;
+
+	SUBCASE("Typical URL") {
+		Error err = String("https://docs.godotengine.org/en/stable/").parse_url(scheme, host, port, path, fragment);
+		REQUIRE(err == OK);
+		CHECK_EQ(scheme, "https://");
+		CHECK_EQ(host, "docs.godotengine.org");
+		CHECK_EQ(port, 0);
+		CHECK_EQ(path, "/en/stable/");
+		CHECK_EQ(fragment, "");
+	}
+
+	SUBCASE("All Elements") {
+		Error err = String("https://www.example.com:8080/path/to/file.html#fragment").parse_url(scheme, host, port, path, fragment);
+		REQUIRE(err == OK);
+		CHECK_EQ(scheme, "https://");
+		CHECK_EQ(host, "www.example.com");
+		CHECK_EQ(port, 8080);
+		CHECK_EQ(path, "/path/to/file.html");
+		CHECK_EQ(fragment, "fragment");
+	}
+
+	SUBCASE("Invalid Scheme") {
+		Error err = String("http_://example.com").parse_url(scheme, host, port, path, fragment);
+		REQUIRE(err == ERR_INVALID_PARAMETER); // Host being empty is an error.
+	}
+
+	SUBCASE("Scheme vs Fragment") {
+		Error err = String("google.com/#goto=http://redirect_url/").parse_url(scheme, host, port, path, fragment);
+		REQUIRE(err == OK);
+		CHECK_EQ(scheme, "");
+		CHECK_EQ(host, "google.com");
+		CHECK_EQ(port, 0);
+		CHECK_EQ(path, "/");
+		CHECK_EQ(fragment, "goto=http://redirect_url/");
+	}
+}
+
 TEST_CASE("[Stress][String] Empty via ' == String()'") {
 	for (int i = 0; i < 100000; ++i) {
 		String str = "Hello World!";