Browse Source

Merge pull request #50365 from timothyqiu/ip-candidates

Try other resolved IPs if one fails to connect
Fabio Alessandrelli 4 years ago
parent
commit
61d681cfb9

+ 23 - 3
core/io/http_client_tcp.cpp

@@ -45,6 +45,8 @@ Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_ss
 	conn_port = p_port;
 	conn_port = p_port;
 	conn_host = p_host;
 	conn_host = p_host;
 
 
+	ip_candidates.clear();
+
 	ssl = p_ssl;
 	ssl = p_ssl;
 	ssl_verify_host = p_verify_host;
 	ssl_verify_host = p_verify_host;
 
 
@@ -234,6 +236,7 @@ void HTTPClientTCP::close() {
 		resolving = IP::RESOLVER_INVALID_ID;
 		resolving = IP::RESOLVER_INVALID_ID;
 	}
 	}
 
 
+	ip_candidates.clear();
 	response_headers.clear();
 	response_headers.clear();
 	response_str.clear();
 	response_str.clear();
 	body_size = -1;
 	body_size = -1;
@@ -256,10 +259,17 @@ Error HTTPClientTCP::poll() {
 					return OK; // Still resolving
 					return OK; // Still resolving
 
 
 				case IP::RESOLVER_STATUS_DONE: {
 				case IP::RESOLVER_STATUS_DONE: {
-					IPAddress host = IP::get_singleton()->get_resolve_item_address(resolving);
-					Error err = tcp_connection->connect_to_host(host, conn_port);
+					ip_candidates = IP::get_singleton()->get_resolve_item_addresses(resolving);
 					IP::get_singleton()->erase_resolve_item(resolving);
 					IP::get_singleton()->erase_resolve_item(resolving);
 					resolving = IP::RESOLVER_INVALID_ID;
 					resolving = IP::RESOLVER_INVALID_ID;
+
+					Error err = ERR_BUG; // Should be at least one entry.
+					while (ip_candidates.size() > 0) {
+						err = tcp_connection->connect_to_host(ip_candidates.front(), conn_port);
+						if (err == OK) {
+							break;
+						}
+					}
 					if (err) {
 					if (err) {
 						status = STATUS_CANT_CONNECT;
 						status = STATUS_CANT_CONNECT;
 						return err;
 						return err;
@@ -313,6 +323,7 @@ Error HTTPClientTCP::poll() {
 						if (ssl->get_status() == StreamPeerSSL::STATUS_CONNECTED) {
 						if (ssl->get_status() == StreamPeerSSL::STATUS_CONNECTED) {
 							// Handshake has been successful
 							// Handshake has been successful
 							handshaking = false;
 							handshaking = false;
+							ip_candidates.clear();
 							status = STATUS_CONNECTED;
 							status = STATUS_CONNECTED;
 							return OK;
 							return OK;
 						} else if (ssl->get_status() != StreamPeerSSL::STATUS_HANDSHAKING) {
 						} else if (ssl->get_status() != StreamPeerSSL::STATUS_HANDSHAKING) {
@@ -323,15 +334,24 @@ Error HTTPClientTCP::poll() {
 						}
 						}
 						// ... we will need to poll more for handshake to finish
 						// ... we will need to poll more for handshake to finish
 					} else {
 					} else {
+						ip_candidates.clear();
 						status = STATUS_CONNECTED;
 						status = STATUS_CONNECTED;
 					}
 					}
 					return OK;
 					return OK;
 				} break;
 				} break;
 				case StreamPeerTCP::STATUS_ERROR:
 				case StreamPeerTCP::STATUS_ERROR:
 				case StreamPeerTCP::STATUS_NONE: {
 				case StreamPeerTCP::STATUS_NONE: {
+					Error err = ERR_CANT_CONNECT;
+					while (ip_candidates.size() > 0) {
+						tcp_connection->disconnect_from_host();
+						err = tcp_connection->connect_to_host(ip_candidates.pop_front(), conn_port);
+						if (err == OK) {
+							return OK;
+						}
+					}
 					close();
 					close();
 					status = STATUS_CANT_CONNECT;
 					status = STATUS_CANT_CONNECT;
-					return ERR_CANT_CONNECT;
+					return err;
 				} break;
 				} break;
 			}
 			}
 		} break;
 		} break;

+ 1 - 0
core/io/http_client_tcp.h

@@ -37,6 +37,7 @@ class HTTPClientTCP : public HTTPClient {
 private:
 private:
 	Status status = STATUS_DISCONNECTED;
 	Status status = STATUS_DISCONNECTED;
 	IP::ResolverID resolving = IP::RESOLVER_INVALID_ID;
 	IP::ResolverID resolving = IP::RESOLVER_INVALID_ID;
+	Array ip_candidates;
 	int conn_port = -1;
 	int conn_port = -1;
 	String conn_host;
 	String conn_host;
 	bool ssl = false;
 	bool ssl = false;

+ 22 - 6
modules/websocket/wsl_client.cpp

@@ -161,22 +161,28 @@ Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
 	ERR_FAIL_COND_V(p_path.is_empty(), ERR_INVALID_PARAMETER);
 	ERR_FAIL_COND_V(p_path.is_empty(), ERR_INVALID_PARAMETER);
 
 
 	_peer = Ref<WSLPeer>(memnew(WSLPeer));
 	_peer = Ref<WSLPeer>(memnew(WSLPeer));
-	IPAddress addr;
 
 
-	if (!p_host.is_valid_ip_address()) {
-		addr = IP::get_singleton()->resolve_hostname(p_host);
+	if (p_host.is_valid_ip_address()) {
+		ip_candidates.clear();
+		ip_candidates.push_back(IPAddress(p_host));
 	} else {
 	} else {
-		addr = p_host;
+		ip_candidates = IP::get_singleton()->resolve_hostname_addresses(p_host);
 	}
 	}
 
 
-	ERR_FAIL_COND_V(!addr.is_valid(), ERR_INVALID_PARAMETER);
+	ERR_FAIL_COND_V(ip_candidates.is_empty(), ERR_INVALID_PARAMETER);
 
 
 	String port = "";
 	String port = "";
 	if ((p_port != 80 && !p_ssl) || (p_port != 443 && p_ssl)) {
 	if ((p_port != 80 && !p_ssl) || (p_port != 443 && p_ssl)) {
 		port = ":" + itos(p_port);
 		port = ":" + itos(p_port);
 	}
 	}
 
 
-	Error err = _tcp->connect_to_host(addr, p_port);
+	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);
+		if (err == OK) {
+			break;
+		}
+	}
 	if (err != OK) {
 	if (err != OK) {
 		_tcp->disconnect_from_host();
 		_tcp->disconnect_from_host();
 		_on_error();
 		_on_error();
@@ -185,6 +191,7 @@ Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
 	_connection = _tcp;
 	_connection = _tcp;
 	_use_ssl = p_ssl;
 	_use_ssl = p_ssl;
 	_host = p_host;
 	_host = p_host;
+	_port = p_port;
 	// Strip edges from protocols.
 	// Strip edges from protocols.
 	_protocols.resize(p_protocols.size());
 	_protocols.resize(p_protocols.size());
 	String *pw = _protocols.ptrw();
 	String *pw = _protocols.ptrw();
@@ -244,6 +251,7 @@ void WSLClient::poll() {
 			_on_error();
 			_on_error();
 			break;
 			break;
 		case StreamPeerTCP::STATUS_CONNECTED: {
 		case StreamPeerTCP::STATUS_CONNECTED: {
+			ip_candidates.clear();
 			Ref<StreamPeerSSL> ssl;
 			Ref<StreamPeerSSL> ssl;
 			if (_use_ssl) {
 			if (_use_ssl) {
 				if (_connection == _tcp) {
 				if (_connection == _tcp) {
@@ -274,6 +282,12 @@ void WSLClient::poll() {
 			_do_handshake();
 			_do_handshake();
 		} break;
 		} break;
 		case StreamPeerTCP::STATUS_ERROR:
 		case StreamPeerTCP::STATUS_ERROR:
+			while (ip_candidates.size() > 0) {
+				_tcp->disconnect_from_host();
+				if (_tcp->connect_to_host(ip_candidates.pop_front(), _port) == OK) {
+					return;
+				}
+			}
 			disconnect_from_host();
 			disconnect_from_host();
 			_on_error();
 			_on_error();
 			break;
 			break;
@@ -315,6 +329,8 @@ void WSLClient::disconnect_from_host(int p_code, String p_reason) {
 
 
 	memset(_resp_buf, 0, sizeof(_resp_buf));
 	memset(_resp_buf, 0, sizeof(_resp_buf));
 	_resp_pos = 0;
 	_resp_pos = 0;
+
+	ip_candidates.clear();
 }
 }
 
 
 IPAddress WSLClient::get_connected_host() const {
 IPAddress WSLClient::get_connected_host() const {

+ 2 - 0
modules/websocket/wsl_client.h

@@ -63,6 +63,8 @@ private:
 
 
 	String _key;
 	String _key;
 	String _host;
 	String _host;
+	int _port;
+	Array ip_candidates;
 	Vector<String> _protocols;
 	Vector<String> _protocols;
 	bool _use_ssl = false;
 	bool _use_ssl = false;