Browse Source

Merge pull request #48205 from Faless/net/4.x_url_parsing

[Net] Implement String::parse_url for parsing URLs.
Fabio Alessandrelli 4 years ago
parent
commit
015fc2ad4f

+ 65 - 0
core/string/ustring.cpp

@@ -240,6 +240,71 @@ String String::word_wrap(int p_chars_per_line) const {
 	return ret;
 	return ret;
 }
 }
 
 
+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.
+	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) {
 void String::copy_from(const char *p_cstr) {
 	// copy Latin-1 encoded c-string directly
 	// copy Latin-1 encoded c-string directly
 	if (!p_cstr) {
 	if (!p_cstr) {

+ 1 - 0
core/string/ustring.h

@@ -416,6 +416,7 @@ public:
 	String c_unescape() const;
 	String c_unescape() const;
 	String json_escape() const;
 	String json_escape() const;
 	String word_wrap(int p_chars_per_line) 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;
 	String property_name_encode() const;
 
 

+ 8 - 24
modules/websocket/websocket_client.cpp

@@ -43,34 +43,18 @@ Error WebSocketClient::connect_to_url(String p_url, const Vector<String> p_proto
 
 
 	String host = p_url;
 	String host = p_url;
 	String path = "/";
 	String path = "/";
-	int p_len = -1;
+	String scheme = "";
 	int port = 80;
 	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);
 	return connect_to_host(host, path, port, ssl, p_protocols, p_custom_headers);
 }
 }
 
 

+ 10 - 27
scene/main/http_request.cpp

@@ -40,9 +40,7 @@ Error HTTPRequest::_request() {
 }
 }
 
 
 Error HTTPRequest::_parse_url(const String &p_url) {
 Error HTTPRequest::_parse_url(const String &p_url) {
-	url = p_url;
 	use_ssl = false;
 	use_ssl = false;
-
 	request_string = "";
 	request_string = "";
 	port = 80;
 	port = 80;
 	request_sent = false;
 	request_sent = false;
@@ -52,35 +50,20 @@ Error HTTPRequest::_parse_url(const String &p_url) {
 	downloaded.set(0);
 	downloaded.set(0);
 	redirections = 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;
 		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;
 	return OK;
 }
 }