Parse fragment from URL

This commit is contained in:
Haoyu Qiu 2024-05-22 10:22:50 +08:00
parent 506d6e427a
commit 6516ca6b11
9 changed files with 74 additions and 13 deletions

View File

@ -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

View File

@ -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;

View File

@ -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);
}

View File

@ -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));

View File

@ -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);
}

View File

@ -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()) {

View File

@ -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://";

View File

@ -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://") {

View File

@ -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!";