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

String: Parse fragment from URL #92237

Merged
merged 1 commit into from
Oct 1, 2024
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
25 changes: 21 additions & 4 deletions core/string/ustring.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion core/string/ustring.h
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ class String {
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;

Expand Down
4 changes: 2 additions & 2 deletions editor/debugger/editor_debugger_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
3 changes: 2 additions & 1 deletion editor/plugins/asset_library_editor_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
3 changes: 2 additions & 1 deletion modules/websocket/emws_peer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down
3 changes: 2 additions & 1 deletion modules/websocket/wsl_peer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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://";
Expand Down
3 changes: 2 additions & 1 deletion scene/main/http_request.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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://") {
Expand Down
40 changes: 40 additions & 0 deletions tests/core/string/test_string.h
Original file line number Diff line number Diff line change
Expand Up @@ -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!";
Expand Down
Loading