소스 검색

Merge pull request #57649 from Faless/net/4.x_ws_queue_hostres

[Net] Non-blocking WebSocket hostname resolution.
Rémi Verschelde 3 년 전
부모
커밋
eb4b4a317b
2개의 변경된 파일56개의 추가작업 그리고 21개의 파일을 삭제
  1. 53 19
      modules/websocket/wsl_client.cpp
  2. 3 2
      modules/websocket/wsl_client.h

+ 53 - 19
modules/websocket/wsl_client.cpp

@@ -163,22 +163,24 @@ Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
 	_peer = Ref<WSLPeer>(memnew(WSLPeer));
 
 	if (p_host.is_valid_ip_address()) {
-		ip_candidates.clear();
-		ip_candidates.push_back(IPAddress(p_host));
+		_ip_candidates.push_back(IPAddress(p_host));
 	} else {
-		ip_candidates = IP::get_singleton()->resolve_hostname_addresses(p_host);
-	}
-
-	ERR_FAIL_COND_V(ip_candidates.is_empty(), ERR_INVALID_PARAMETER);
-
-	String port = "";
-	if ((p_port != 80 && !p_ssl) || (p_port != 443 && p_ssl)) {
-		port = ":" + itos(p_port);
+		// Queue hostname for resolution.
+		_resolver_id = IP::get_singleton()->resolve_hostname_queue_item(p_host);
+		ERR_FAIL_COND_V(_resolver_id == IP::RESOLVER_INVALID_ID, ERR_INVALID_PARAMETER);
+		// Check if it was found in cache.
+		IP::ResolverStatus ip_status = IP::get_singleton()->get_resolve_item_status(_resolver_id);
+		if (ip_status == IP::RESOLVER_STATUS_DONE) {
+			_ip_candidates = IP::get_singleton()->get_resolve_item_addresses(_resolver_id);
+			IP::get_singleton()->erase_resolve_item(_resolver_id);
+			_resolver_id = IP::RESOLVER_INVALID_ID;
+		}
 	}
 
-	Error err = ERR_BUG; // Should be at least one entry.
-	while (ip_candidates.size() > 0) {
-		err = _tcp->connect_to_host(ip_candidates.pop_front(), p_port);
+	// We assume OK while hostname resultion is pending.
+	Error err = _resolver_id != IP::RESOLVER_INVALID_ID ? OK : FAILED;
+	while (_ip_candidates.size()) {
+		err = _tcp->connect_to_host(_ip_candidates.pop_front(), p_port);
 		if (err == OK) {
 			break;
 		}
@@ -200,8 +202,11 @@ Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
 	}
 
 	_key = WSLPeer::generate_key();
-	// TODO custom extra headers (allow overriding this too?)
 	String request = "GET " + p_path + " HTTP/1.1\r\n";
+	String port = "";
+	if ((p_port != 80 && !p_ssl) || (p_port != 443 && p_ssl)) {
+		port = ":" + itos(p_port);
+	}
 	request += "Host: " + p_host + port + "\r\n";
 	request += "Upgrade: websocket\r\n";
 	request += "Connection: Upgrade\r\n";
@@ -231,6 +236,30 @@ int WSLClient::get_max_packet_size() const {
 }
 
 void WSLClient::poll() {
+	if (_resolver_id != IP::RESOLVER_INVALID_ID) {
+		IP::ResolverStatus ip_status = IP::get_singleton()->get_resolve_item_status(_resolver_id);
+		if (ip_status == IP::RESOLVER_STATUS_WAITING) {
+			return;
+		}
+		// Anything else is either a candidate or a failure.
+		Error err = FAILED;
+		if (ip_status == IP::RESOLVER_STATUS_DONE) {
+			_ip_candidates = IP::get_singleton()->get_resolve_item_addresses(_resolver_id);
+			while (_ip_candidates.size()) {
+				err = _tcp->connect_to_host(_ip_candidates.pop_front(), _port);
+				if (err == OK) {
+					break;
+				}
+			}
+		}
+		IP::get_singleton()->erase_resolve_item(_resolver_id);
+		_resolver_id = IP::RESOLVER_INVALID_ID;
+		if (err != OK) {
+			disconnect_from_host();
+			_on_error();
+			return;
+		}
+	}
 	if (_peer->is_connected_to_host()) {
 		_peer->poll();
 		if (!_peer->is_connected_to_host()) {
@@ -251,7 +280,7 @@ void WSLClient::poll() {
 			_on_error();
 			break;
 		case StreamPeerTCP::STATUS_CONNECTED: {
-			ip_candidates.clear();
+			_ip_candidates.clear();
 			Ref<StreamPeerSSL> ssl;
 			if (_use_ssl) {
 				if (_connection == _tcp) {
@@ -282,9 +311,9 @@ void WSLClient::poll() {
 			_do_handshake();
 		} break;
 		case StreamPeerTCP::STATUS_ERROR:
-			while (ip_candidates.size() > 0) {
+			while (_ip_candidates.size() > 0) {
 				_tcp->disconnect_from_host();
-				if (_tcp->connect_to_host(ip_candidates.pop_front(), _port) == OK) {
+				if (_tcp->connect_to_host(_ip_candidates.pop_front(), _port) == OK) {
 					return;
 				}
 			}
@@ -307,7 +336,7 @@ MultiplayerPeer::ConnectionStatus WSLClient::get_connection_status() const {
 		return CONNECTION_CONNECTED;
 	}
 
-	if (_tcp->is_connected_to_host()) {
+	if (_tcp->is_connected_to_host() || _resolver_id != IP::RESOLVER_INVALID_ID) {
 		return CONNECTION_CONNECTING;
 	}
 
@@ -330,7 +359,12 @@ void WSLClient::disconnect_from_host(int p_code, String p_reason) {
 	memset(_resp_buf, 0, sizeof(_resp_buf));
 	_resp_pos = 0;
 
-	ip_candidates.clear();
+	if (_resolver_id != IP::RESOLVER_INVALID_ID) {
+		IP::get_singleton()->erase_resolve_item(_resolver_id);
+		_resolver_id = IP::RESOLVER_INVALID_ID;
+	}
+
+	_ip_candidates.clear();
 }
 
 IPAddress WSLClient::get_connected_host() const {

+ 3 - 2
modules/websocket/wsl_client.h

@@ -63,10 +63,11 @@ private:
 
 	String _key;
 	String _host;
-	int _port;
-	Array ip_candidates;
+	uint16_t _port;
+	Array _ip_candidates;
 	Vector<String> _protocols;
 	bool _use_ssl = false;
+	IP::ResolverID _resolver_id = IP::RESOLVER_INVALID_ID;
 
 	void _do_handshake();
 	bool _verify_headers(String &r_protocol);