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

[Net] Non-blocking WebSocket hostname resolution.
This commit is contained in:
Rémi Verschelde 2022-02-05 18:23:11 +01:00 committed by GitHub
commit eb4b4a317b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 56 additions and 21 deletions

View File

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

View File

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