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

[Net] Implement String::parse_url for parsing URLs. #48205

Merged
merged 1 commit into from
May 3, 2021
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
65 changes: 65 additions & 0 deletions core/string/ustring.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,71 @@ String String::word_wrap(int p_chars_per_line) const {
return ret;
}

Error String::parse_url(String &r_scheme, String &r_host, int &r_port, String &r_path) const {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be worth adding a short description similar to what you put in the PR, since it's not exposed and thus documented in the classref.

// Splits the URL into scheme, host, port, path. Strip credentials when present.
String base = *this;
r_scheme = "";
r_host = "";
r_port = 0;
r_path = "";
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);
}
pos = base.find("/");
// Path
if (pos != -1) {
r_path = base.substr(pos, base.length() - pos);
base = base.substr(0, pos);
}
// Host
pos = base.find("@");
if (pos != -1) {
// Strip credentials
base = base.substr(pos + 1, base.length() - pos - 1);
}
if (base.begins_with("[")) {
// Literal IPv6
pos = base.rfind("]");
if (pos == -1) {
return ERR_INVALID_PARAMETER;
}
r_host = base.substr(1, pos - 1);
base = base.substr(pos + 1, base.length() - pos - 1);
} else {
// Anything else
if (base.get_slice_count(":") > 1) {
return ERR_INVALID_PARAMETER;
}
pos = base.rfind(":");
if (pos == -1) {
r_host = base;
base = "";
} else {
r_host = base.substr(0, pos);
base = base.substr(pos, base.length() - pos);
}
}
if (r_host.is_empty()) {
return ERR_INVALID_PARAMETER;
}
r_host = r_host.to_lower();
// Port
if (base.begins_with(":")) {
base = base.substr(1, base.length() - 1);
if (!base.is_valid_integer()) {
return ERR_INVALID_PARAMETER;
}
r_port = base.to_int();
if (r_port < 1 || r_port > 65535) {
return ERR_INVALID_PARAMETER;
}
}
return OK;
}

void String::copy_from(const char *p_cstr) {
// copy Latin-1 encoded c-string directly
if (!p_cstr) {
Expand Down
1 change: 1 addition & 0 deletions core/string/ustring.h
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,7 @@ class String {
String c_unescape() const;
String json_escape() const;
String word_wrap(int p_chars_per_line) const;
Error parse_url(String &r_scheme, String &r_host, int &r_port, String &r_path) const;

String property_name_encode() const;

Expand Down
32 changes: 8 additions & 24 deletions modules/websocket/websocket_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,34 +43,18 @@ Error WebSocketClient::connect_to_url(String p_url, const Vector<String> p_proto

String host = p_url;
String path = "/";
int p_len = -1;
String scheme = "";
int port = 80;
bool ssl = false;
if (host.begins_with("wss://")) {
ssl = true; // we should implement this
host = host.substr(6, host.length() - 6);
port = 443;
} else {
ssl = false;
if (host.begins_with("ws://")) {
host = host.substr(5, host.length() - 5);
}
}
Error err = p_url.parse_url(scheme, host, port, path);
ERR_FAIL_COND_V_MSG(err != OK, err, "Invalid URL: " + p_url);

// Path
p_len = host.find("/");
if (p_len != -1) {
path = host.substr(p_len, host.length() - p_len);
host = host.substr(0, p_len);
bool ssl = false;
if (scheme == "wss://") {
ssl = true;
}

// Port
p_len = host.rfind(":");
if (p_len != -1 && p_len == host.find(":")) {
port = host.substr(p_len, host.length() - p_len).to_int();
host = host.substr(0, p_len);
if (port == 0) {
port = ssl ? 443 : 80;
}

return connect_to_host(host, path, port, ssl, p_protocols, p_custom_headers);
}

Expand Down
37 changes: 10 additions & 27 deletions scene/main/http_request.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,7 @@ Error HTTPRequest::_request() {
}

Error HTTPRequest::_parse_url(const String &p_url) {
url = p_url;
use_ssl = false;

request_string = "";
port = 80;
request_sent = false;
Expand All @@ -52,35 +50,20 @@ Error HTTPRequest::_parse_url(const String &p_url) {
downloaded.set(0);
redirections = 0;

String url_lower = url.to_lower();
if (url_lower.begins_with("http://")) {
url = url.substr(7, url.length() - 7);
} else if (url_lower.begins_with("https://")) {
url = url.substr(8, url.length() - 8);
String scheme;
Error err = p_url.parse_url(scheme, url, port, request_string);
ERR_FAIL_COND_V_MSG(err != OK, err, "Error parsing URL: " + p_url + ".");
if (scheme == "https://") {
use_ssl = true;
port = 443;
} else {
ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Malformed URL: " + url + ".");
} else if (scheme != "http://") {
ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Invalid URL scheme: " + scheme + ".");
}

ERR_FAIL_COND_V_MSG(url.length() < 1, ERR_INVALID_PARAMETER, "URL too short: " + url + ".");

int slash_pos = url.find("/");

if (slash_pos != -1) {
request_string = url.substr(slash_pos, url.length());
url = url.substr(0, slash_pos);
} else {
request_string = "/";
if (port == 0) {
port = use_ssl ? 443 : 80;
}

int colon_pos = url.find(":");
if (colon_pos != -1) {
port = url.substr(colon_pos + 1, url.length()).to_int();
url = url.substr(0, colon_pos);
ERR_FAIL_COND_V(port < 1 || port > 65535, ERR_INVALID_PARAMETER);
if (request_string.is_empty()) {
request_string = "/";
}

return OK;
}

Expand Down