[Net] Non-blocking WebSocket hostname resolution.

Hostname is now resolved during poll in WebSocketClient (wslay) to avoid
blocking during connect.
An attempt is still made to find the hostname in the resolver cache.

(cherry picked from commit 1ec96bc206)
This commit is contained in:
Jordan Schidlowsky 2022-01-29 00:29:27 +01:00 committed by Rémi Verschelde
parent 6d867b6d13
commit 14a1303f2d
No known key found for this signature in database
GPG key ID: C3336907360768E1
2 changed files with 57 additions and 22 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(IP_Address(p_host));
_ip_candidates.push_back(IP_Address(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.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 @@ NetworkedMultiplayerPeer::ConnectionStatus WSLClient::get_connection_status() co
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();
}
IP_Address 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;
bool _use_ssl = false;
IP::ResolverID _resolver_id = IP::RESOLVER_INVALID_ID;
void _do_handshake();
bool _verify_headers(String &r_protocol);