summaryrefslogtreecommitdiff
path: root/modules/websocket
diff options
context:
space:
mode:
authorJordan Schidlowsky <jordan@winterpixel.com>2022-01-29 00:29:27 +0100
committerFabio Alessandrelli <fabio.alessandrelli@gmail.com>2022-02-05 02:22:43 +0100
commit1ec96bc2063579f8e2f73944974cadcb15e348b0 (patch)
tree99f68e3602f3fcc151fcbe6ce03aa04c4d93d76f /modules/websocket
parent3db1d689ce11507d9597bb434891da95168f2c69 (diff)
[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.
Diffstat (limited to 'modules/websocket')
-rw-r--r--modules/websocket/wsl_client.cpp72
-rw-r--r--modules/websocket/wsl_client.h5
2 files changed, 56 insertions, 21 deletions
diff --git a/modules/websocket/wsl_client.cpp b/modules/websocket/wsl_client.cpp
index be1c75c354..bebb198126 100644
--- a/modules/websocket/wsl_client.cpp
+++ b/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 {
diff --git a/modules/websocket/wsl_client.h b/modules/websocket/wsl_client.h
index 4839d7ab9b..d846e6be00 100644
--- a/modules/websocket/wsl_client.h
+++ b/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);