Merge pull request #32683 from Faless/ws/improve_pr

WebSocket improvements, SSL server, custom headers.
This commit is contained in:
Rémi Verschelde 2019-10-11 08:28:47 +02:00 committed by GitHub
commit be446038bb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 181 additions and 38 deletions

View file

@ -21,10 +21,13 @@
</argument> </argument>
<argument index="2" name="gd_mp_api" type="bool" default="false"> <argument index="2" name="gd_mp_api" type="bool" default="false">
</argument> </argument>
<argument index="3" name="custom_headers" type="PoolStringArray" default="PoolStringArray( )">
</argument>
<description> <description>
Connects to the given URL requesting one of the given [code]protocols[/code] as sub-protocol. If the list empty (default), no sub-protocol will be requested. Connects to the given URL requesting one of the given [code]protocols[/code] as sub-protocol. If the list empty (default), no sub-protocol will be requested.
If [code]true[/code] is passed as [code]gd_mp_api[/code], the client will behave like a network peer for the [MultiplayerAPI], connections to non-Godot servers will not work, and [signal data_received] will not be emitted. If [code]true[/code] is passed as [code]gd_mp_api[/code], the client will behave like a network peer for the [MultiplayerAPI], connections to non-Godot servers will not work, and [signal data_received] will not be emitted.
If [code]false[/code] is passed instead (default), you must call [PacketPeer] functions ([code]put_packet[/code], [code]get_packet[/code], etc.) on the [WebSocketPeer] returned via [code]get_peer(1)[/code] and not on this object directly (e.g. [code]get_peer(1).put_packet(data)[/code]). If [code]false[/code] is passed instead (default), you must call [PacketPeer] functions ([code]put_packet[/code], [code]get_packet[/code], etc.) on the [WebSocketPeer] returned via [code]get_peer(1)[/code] and not on this object directly (e.g. [code]get_peer(1).put_packet(data)[/code]).
You can optionally pass a list of [code]custom_headers[/code] to be added to the handshake HTTP request (not supported in HTML5 platform).
</description> </description>
</method> </method>
<method name="disconnect_from_host"> <method name="disconnect_from_host">
@ -38,8 +41,25 @@
Disconnects this client from the connected host. See [method WebSocketPeer.close] for more information. Disconnects this client from the connected host. See [method WebSocketPeer.close] for more information.
</description> </description>
</method> </method>
<method name="get_connected_host" qualifiers="const">
<return type="String">
</return>
<description>
Return the IP address of the currently connected host.
</description>
</method>
<method name="get_connected_port" qualifiers="const">
<return type="int">
</return>
<description>
Return the IP port of the currently connected host.
</description>
</method>
</methods> </methods>
<members> <members>
<member name="trusted_ssl_certificate" type="X509Certificate" setter="set_trusted_ssl_certificate" getter="get_trusted_ssl_certificate">
If specified, this [X509Certificate] will be the only one accepted when connecting to an SSL host. Any other certificate provided by the server will be regarded as invalid.
</member>
<member name="verify_ssl" type="bool" setter="set_verify_ssl_enabled" getter="is_verify_ssl_enabled"> <member name="verify_ssl" type="bool" setter="set_verify_ssl_enabled" getter="is_verify_ssl_enabled">
If [code]true[/code], SSL certificate verification is enabled. If [code]true[/code], SSL certificate verification is enabled.
[b]Note:[/b] You must specify the certificates to be used in the Project Settings for it to work when exported. [b]Note:[/b] You must specify the certificates to be used in the Project Settings for it to work when exported.

View file

@ -82,6 +82,17 @@
</description> </description>
</method> </method>
</methods> </methods>
<members>
<member name="ca_chain" type="X509Certificate" setter="set_ca_chain" getter="get_ca_chain">
When using SSL (see [member private_key] and [member ssl_certificate]), you can set this to a valid [X509Certificate] to be provided as additional CA chain information during the SSL handshake.
</member>
<member name="private_key" type="CryptoKey" setter="set_private_key" getter="get_private_key">
When set to a valid [CryptoKey] (along with [member ssl_certificate]) will cause the server to require SSL instead of regular TCP (i.e. the `wss://` protocol).
</member>
<member name="ssl_certificate" type="X509Certificate" setter="set_ssl_certificate" getter="get_ssl_certificate">
When set to a valid [X509Certificate] (along with [member private_key]) will cause the server to require SSL instead of regular TCP (i.e. the `wss://` protocol).
</member>
</members>
<signals> <signals>
<signal name="client_close_request"> <signal name="client_close_request">
<argument index="0" name="id" type="int"> <argument index="0" name="id" type="int">

View file

@ -64,13 +64,20 @@ EMSCRIPTEN_KEEPALIVE void _esws_on_close(void *obj, int code, char *reason, int
} }
} }
Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocols) { Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const PoolVector<String> p_protocols, const Vector<String> p_custom_headers) {
String proto_string = p_protocols.join(","); String proto_string = p_protocols.join(",");
String str = "ws://"; String str = "ws://";
if (p_ssl) if (p_custom_headers.size()) {
WARN_PRINT_ONCE("Custom headers are not supported in in HTML5 platform.");
}
if (p_ssl) {
str = "wss://"; str = "wss://";
if (ssl_cert.is_valid()) {
WARN_PRINT_ONCE("Custom SSL certificate is not supported in HTML5 platform.");
}
}
str += p_host + ":" + itos(p_port) + p_path; str += p_host + ":" + itos(p_port) + p_path;
_is_connecting = true; _is_connecting = true;
@ -193,12 +200,12 @@ void EMWSClient::disconnect_from_host(int p_code, String p_reason) {
IP_Address EMWSClient::get_connected_host() const { IP_Address EMWSClient::get_connected_host() const {
return IP_Address(); ERR_FAIL_V_MSG(IP_Address(), "Not supported in HTML5 export.");
}; };
uint16_t EMWSClient::get_connected_port() const { uint16_t EMWSClient::get_connected_port() const {
return 1025; ERR_FAIL_V_MSG(0, "Not supported in HTML5 export.");
}; };
int EMWSClient::get_max_packet_size() const { int EMWSClient::get_max_packet_size() const {

View file

@ -50,7 +50,7 @@ public:
bool _is_connecting; bool _is_connecting;
Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets); Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets);
Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>()); Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const PoolVector<String> p_protocol = PoolVector<String>(), const Dictionary p_custom_headers = Dictionary());
Ref<WebSocketPeer> get_peer(int p_peer_id) const; Ref<WebSocketPeer> get_peer(int p_peer_id) const;
void disconnect_from_host(int p_code = 1000, String p_reason = ""); void disconnect_from_host(int p_code = 1000, String p_reason = "");
IP_Address get_connected_host() const; IP_Address get_connected_host() const;

View file

@ -40,7 +40,7 @@ WebSocketClient::WebSocketClient() {
WebSocketClient::~WebSocketClient() { WebSocketClient::~WebSocketClient() {
} }
Error WebSocketClient::connect_to_url(String p_url, PoolVector<String> p_protocols, bool gd_mp_api) { Error WebSocketClient::connect_to_url(String p_url, const Vector<String> p_protocols, bool gd_mp_api, const Vector<String> p_custom_headers) {
_is_multiplayer = gd_mp_api; _is_multiplayer = gd_mp_api;
String host = p_url; String host = p_url;
@ -72,7 +72,7 @@ Error WebSocketClient::connect_to_url(String p_url, PoolVector<String> p_protoco
host = host.substr(0, p_len); host = host.substr(0, p_len);
} }
return connect_to_host(host, path, port, ssl, p_protocols); return connect_to_host(host, path, port, ssl, p_protocols, p_custom_headers);
} }
void WebSocketClient::set_verify_ssl_enabled(bool p_verify_ssl) { void WebSocketClient::set_verify_ssl_enabled(bool p_verify_ssl) {
@ -85,6 +85,17 @@ bool WebSocketClient::is_verify_ssl_enabled() const {
return verify_ssl; return verify_ssl;
} }
Ref<X509Certificate> WebSocketClient::get_trusted_ssl_certificate() const {
return ssl_cert;
}
void WebSocketClient::set_trusted_ssl_certificate(Ref<X509Certificate> p_cert) {
ERR_FAIL_COND(get_connection_status() != CONNECTION_DISCONNECTED);
ssl_cert = p_cert;
}
bool WebSocketClient::is_server() const { bool WebSocketClient::is_server() const {
return false; return false;
@ -132,13 +143,20 @@ void WebSocketClient::_on_error() {
} }
void WebSocketClient::_bind_methods() { void WebSocketClient::_bind_methods() {
ClassDB::bind_method(D_METHOD("connect_to_url", "url", "protocols", "gd_mp_api"), &WebSocketClient::connect_to_url, DEFVAL(PoolVector<String>()), DEFVAL(false)); ClassDB::bind_method(D_METHOD("connect_to_url", "url", "protocols", "gd_mp_api", "custom_headers"), &WebSocketClient::connect_to_url, DEFVAL(Vector<String>()), DEFVAL(false), DEFVAL(Vector<String>()));
ClassDB::bind_method(D_METHOD("disconnect_from_host", "code", "reason"), &WebSocketClient::disconnect_from_host, DEFVAL(1000), DEFVAL("")); ClassDB::bind_method(D_METHOD("disconnect_from_host", "code", "reason"), &WebSocketClient::disconnect_from_host, DEFVAL(1000), DEFVAL(""));
ClassDB::bind_method(D_METHOD("get_connected_host"), &WebSocketClient::get_connected_host);
ClassDB::bind_method(D_METHOD("get_connected_port"), &WebSocketClient::get_connected_port);
ClassDB::bind_method(D_METHOD("set_verify_ssl_enabled", "enabled"), &WebSocketClient::set_verify_ssl_enabled); ClassDB::bind_method(D_METHOD("set_verify_ssl_enabled", "enabled"), &WebSocketClient::set_verify_ssl_enabled);
ClassDB::bind_method(D_METHOD("is_verify_ssl_enabled"), &WebSocketClient::is_verify_ssl_enabled); ClassDB::bind_method(D_METHOD("is_verify_ssl_enabled"), &WebSocketClient::is_verify_ssl_enabled);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "verify_ssl", PROPERTY_HINT_NONE, "", 0), "set_verify_ssl_enabled", "is_verify_ssl_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "verify_ssl", PROPERTY_HINT_NONE, "", 0), "set_verify_ssl_enabled", "is_verify_ssl_enabled");
ClassDB::bind_method(D_METHOD("get_trusted_ssl_certificate"), &WebSocketClient::get_trusted_ssl_certificate);
ClassDB::bind_method(D_METHOD("set_trusted_ssl_certificate"), &WebSocketClient::set_trusted_ssl_certificate);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "trusted_ssl_certificate", PROPERTY_HINT_RESOURCE_TYPE, "X509Certificate", 0), "set_trusted_ssl_certificate", "get_trusted_ssl_certificate");
ADD_SIGNAL(MethodInfo("data_received")); ADD_SIGNAL(MethodInfo("data_received"));
ADD_SIGNAL(MethodInfo("connection_established", PropertyInfo(Variant::STRING, "protocol"))); ADD_SIGNAL(MethodInfo("connection_established", PropertyInfo(Variant::STRING, "protocol")));
ADD_SIGNAL(MethodInfo("server_close_request", PropertyInfo(Variant::INT, "code"), PropertyInfo(Variant::STRING, "reason"))); ADD_SIGNAL(MethodInfo("server_close_request", PropertyInfo(Variant::INT, "code"), PropertyInfo(Variant::STRING, "reason")));

View file

@ -31,6 +31,7 @@
#ifndef WEBSOCKET_CLIENT_H #ifndef WEBSOCKET_CLIENT_H
#define WEBSOCKET_CLIENT_H #define WEBSOCKET_CLIENT_H
#include "core/crypto/crypto.h"
#include "core/error_list.h" #include "core/error_list.h"
#include "websocket_multiplayer_peer.h" #include "websocket_multiplayer_peer.h"
#include "websocket_peer.h" #include "websocket_peer.h"
@ -43,17 +44,20 @@ class WebSocketClient : public WebSocketMultiplayerPeer {
protected: protected:
Ref<WebSocketPeer> _peer; Ref<WebSocketPeer> _peer;
bool verify_ssl; bool verify_ssl;
Ref<X509Certificate> ssl_cert;
static void _bind_methods(); static void _bind_methods();
public: public:
Error connect_to_url(String p_url, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false); Error connect_to_url(String p_url, const Vector<String> p_protocols = Vector<String>(), bool gd_mp_api = false, const Vector<String> p_custom_headers = Vector<String>());
void set_verify_ssl_enabled(bool p_verify_ssl); void set_verify_ssl_enabled(bool p_verify_ssl);
bool is_verify_ssl_enabled() const; bool is_verify_ssl_enabled() const;
Ref<X509Certificate> get_trusted_ssl_certificate() const;
void set_trusted_ssl_certificate(Ref<X509Certificate> p_cert);
virtual void poll() = 0; virtual void poll() = 0;
virtual Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>()) = 0; virtual Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocol = Vector<String>(), const Vector<String> p_custom_headers = Vector<String>()) = 0;
virtual void disconnect_from_host(int p_code = 1000, String p_reason = "") = 0; virtual void disconnect_from_host(int p_code = 1000, String p_reason = "") = 0;
virtual IP_Address get_connected_host() const = 0; virtual IP_Address get_connected_host() const = 0;
virtual uint16_t get_connected_port() const = 0; virtual uint16_t get_connected_port() const = 0;

View file

@ -42,19 +42,58 @@ WebSocketServer::~WebSocketServer() {
void WebSocketServer::_bind_methods() { void WebSocketServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_listening"), &WebSocketServer::is_listening); ClassDB::bind_method(D_METHOD("is_listening"), &WebSocketServer::is_listening);
ClassDB::bind_method(D_METHOD("listen", "port", "protocols", "gd_mp_api"), &WebSocketServer::listen, DEFVAL(PoolVector<String>()), DEFVAL(false)); ClassDB::bind_method(D_METHOD("listen", "port", "protocols", "gd_mp_api"), &WebSocketServer::listen, DEFVAL(Vector<String>()), DEFVAL(false));
ClassDB::bind_method(D_METHOD("stop"), &WebSocketServer::stop); ClassDB::bind_method(D_METHOD("stop"), &WebSocketServer::stop);
ClassDB::bind_method(D_METHOD("has_peer", "id"), &WebSocketServer::has_peer); ClassDB::bind_method(D_METHOD("has_peer", "id"), &WebSocketServer::has_peer);
ClassDB::bind_method(D_METHOD("get_peer_address", "id"), &WebSocketServer::get_peer_address); ClassDB::bind_method(D_METHOD("get_peer_address", "id"), &WebSocketServer::get_peer_address);
ClassDB::bind_method(D_METHOD("get_peer_port", "id"), &WebSocketServer::get_peer_port); ClassDB::bind_method(D_METHOD("get_peer_port", "id"), &WebSocketServer::get_peer_port);
ClassDB::bind_method(D_METHOD("disconnect_peer", "id", "code", "reason"), &WebSocketServer::disconnect_peer, DEFVAL(1000), DEFVAL("")); ClassDB::bind_method(D_METHOD("disconnect_peer", "id", "code", "reason"), &WebSocketServer::disconnect_peer, DEFVAL(1000), DEFVAL(""));
ClassDB::bind_method(D_METHOD("get_private_key"), &WebSocketServer::get_private_key);
ClassDB::bind_method(D_METHOD("set_private_key"), &WebSocketServer::set_private_key);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "private_key", PROPERTY_HINT_RESOURCE_TYPE, "CryptoKey", 0), "set_private_key", "get_private_key");
ClassDB::bind_method(D_METHOD("get_ssl_certificate"), &WebSocketServer::get_ssl_certificate);
ClassDB::bind_method(D_METHOD("set_ssl_certificate"), &WebSocketServer::set_ssl_certificate);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "ssl_certificate", PROPERTY_HINT_RESOURCE_TYPE, "X509Certificate", 0), "set_ssl_certificate", "get_ssl_certificate");
ClassDB::bind_method(D_METHOD("get_ca_chain"), &WebSocketServer::get_ca_chain);
ClassDB::bind_method(D_METHOD("set_ca_chain"), &WebSocketServer::set_ca_chain);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "ca_chain", PROPERTY_HINT_RESOURCE_TYPE, "X509Certificate", 0), "set_ca_chain", "get_ca_chain");
ADD_SIGNAL(MethodInfo("client_close_request", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::INT, "code"), PropertyInfo(Variant::STRING, "reason"))); ADD_SIGNAL(MethodInfo("client_close_request", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::INT, "code"), PropertyInfo(Variant::STRING, "reason")));
ADD_SIGNAL(MethodInfo("client_disconnected", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::BOOL, "was_clean_close"))); ADD_SIGNAL(MethodInfo("client_disconnected", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::BOOL, "was_clean_close")));
ADD_SIGNAL(MethodInfo("client_connected", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "protocol"))); ADD_SIGNAL(MethodInfo("client_connected", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "protocol")));
ADD_SIGNAL(MethodInfo("data_received", PropertyInfo(Variant::INT, "id"))); ADD_SIGNAL(MethodInfo("data_received", PropertyInfo(Variant::INT, "id")));
} }
Ref<CryptoKey> WebSocketServer::get_private_key() const {
return private_key;
}
void WebSocketServer::set_private_key(Ref<CryptoKey> p_key) {
ERR_FAIL_COND(is_listening());
private_key = p_key;
}
Ref<X509Certificate> WebSocketServer::get_ssl_certificate() const {
return ssl_cert;
}
void WebSocketServer::set_ssl_certificate(Ref<X509Certificate> p_cert) {
ERR_FAIL_COND(is_listening());
ssl_cert = p_cert;
}
Ref<X509Certificate> WebSocketServer::get_ca_chain() const {
return ca_chain;
}
void WebSocketServer::set_ca_chain(Ref<X509Certificate> p_ca_chain) {
ERR_FAIL_COND(is_listening());
ca_chain = p_ca_chain;
}
NetworkedMultiplayerPeer::ConnectionStatus WebSocketServer::get_connection_status() const { NetworkedMultiplayerPeer::ConnectionStatus WebSocketServer::get_connection_status() const {
if (is_listening()) if (is_listening())
return CONNECTION_CONNECTED; return CONNECTION_CONNECTED;

View file

@ -31,6 +31,7 @@
#ifndef WEBSOCKET_H #ifndef WEBSOCKET_H
#define WEBSOCKET_H #define WEBSOCKET_H
#include "core/crypto/crypto.h"
#include "core/reference.h" #include "core/reference.h"
#include "websocket_multiplayer_peer.h" #include "websocket_multiplayer_peer.h"
#include "websocket_peer.h" #include "websocket_peer.h"
@ -43,9 +44,13 @@ class WebSocketServer : public WebSocketMultiplayerPeer {
protected: protected:
static void _bind_methods(); static void _bind_methods();
Ref<CryptoKey> private_key;
Ref<X509Certificate> ssl_cert;
Ref<X509Certificate> ca_chain;
public: public:
virtual void poll() = 0; virtual void poll() = 0;
virtual Error listen(int p_port, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false) = 0; virtual Error listen(int p_port, const Vector<String> p_protocols = Vector<String>(), bool gd_mp_api = false) = 0;
virtual void stop() = 0; virtual void stop() = 0;
virtual bool is_listening() const = 0; virtual bool is_listening() const = 0;
virtual bool has_peer(int p_id) const = 0; virtual bool has_peer(int p_id) const = 0;
@ -62,6 +67,15 @@ public:
void _on_disconnect(int32_t p_peer_id, bool p_was_clean); void _on_disconnect(int32_t p_peer_id, bool p_was_clean);
void _on_close_request(int32_t p_peer_id, int p_code, String p_reason); void _on_close_request(int32_t p_peer_id, int p_code, String p_reason);
Ref<CryptoKey> get_private_key() const;
void set_private_key(Ref<CryptoKey> p_key);
Ref<X509Certificate> get_ssl_certificate() const;
void set_ssl_certificate(Ref<X509Certificate> p_cert);
Ref<X509Certificate> get_ca_chain() const;
void set_ca_chain(Ref<X509Certificate> p_ca_chain);
virtual Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) = 0; virtual Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) = 0;
WebSocketServer(); WebSocketServer();

View file

@ -86,6 +86,7 @@ void WSLClient::_do_handshake() {
WSLPeer::PeerData *data = memnew(struct WSLPeer::PeerData); WSLPeer::PeerData *data = memnew(struct WSLPeer::PeerData);
data->obj = this; data->obj = this;
data->conn = _connection; data->conn = _connection;
data->tcp = _tcp;
data->is_server = false; data->is_server = false;
data->id = 1; data->id = 1;
_peer->make_context(data, _in_buf_size, _in_pkt_size, _out_buf_size, _out_pkt_size); _peer->make_context(data, _in_buf_size, _in_pkt_size, _out_buf_size, _out_pkt_size);
@ -151,7 +152,7 @@ bool WSLClient::_verify_headers(String &r_protocol) {
return true; return true;
} }
Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocols) { Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocols, const Vector<String> p_custom_headers) {
ERR_FAIL_COND_V(_connection.is_valid(), ERR_ALREADY_IN_USE); ERR_FAIL_COND_V(_connection.is_valid(), ERR_ALREADY_IN_USE);
@ -180,7 +181,8 @@ 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;
_protocols = p_protocols; _protocols.clear();
_protocols.append_array(p_protocols);
_key = WSLPeer::generate_key(); _key = WSLPeer::generate_key();
// TODO custom extra headers (allow overriding this too?) // TODO custom extra headers (allow overriding this too?)
@ -199,6 +201,9 @@ Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
} }
request += "\r\n"; request += "\r\n";
} }
for (int i = 0; i < p_custom_headers.size(); i++) {
request += p_custom_headers[i] + "\r\n";
}
request += "\r\n"; request += "\r\n";
_request = request.utf8(); _request = request.utf8();
@ -236,7 +241,7 @@ void WSLClient::poll() {
ssl = Ref<StreamPeerSSL>(StreamPeerSSL::create()); ssl = Ref<StreamPeerSSL>(StreamPeerSSL::create());
ERR_FAIL_COND_MSG(ssl.is_null(), "SSL is not available in this build."); ERR_FAIL_COND_MSG(ssl.is_null(), "SSL is not available in this build.");
ssl->set_blocking_handshake_enabled(false); ssl->set_blocking_handshake_enabled(false);
if (ssl->connect_to_stream(_tcp, verify_ssl, _host) != OK) { if (ssl->connect_to_stream(_tcp, verify_ssl, _host, ssl_cert) != OK) {
disconnect_from_host(); disconnect_from_host();
_on_error(); _on_error();
return; return;
@ -293,7 +298,7 @@ void WSLClient::disconnect_from_host(int p_code, String p_reason) {
_key = ""; _key = "";
_host = ""; _host = "";
_protocols.resize(0); _protocols.clear();
_use_ssl = false; _use_ssl = false;
_request = ""; _request = "";
@ -305,12 +310,14 @@ void WSLClient::disconnect_from_host(int p_code, String p_reason) {
IP_Address WSLClient::get_connected_host() const { IP_Address WSLClient::get_connected_host() const {
return IP_Address(); ERR_FAIL_COND_V(!_peer->is_connected_to_host(), IP_Address());
return _peer->get_connected_host();
} }
uint16_t WSLClient::get_connected_port() const { uint16_t WSLClient::get_connected_port() const {
return 1025; ERR_FAIL_COND_V(!_peer->is_connected_to_host(), 0);
return _peer->get_connected_port();
} }
Error WSLClient::set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) { Error WSLClient::set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) {

View file

@ -64,7 +64,7 @@ private:
String _key; String _key;
String _host; String _host;
PoolVector<String> _protocols; Vector<String> _protocols;
bool _use_ssl; bool _use_ssl;
void _do_handshake(); void _do_handshake();
@ -72,7 +72,7 @@ private:
public: public:
Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets); Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets);
Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>()); Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocol = Vector<String>(), const Vector<String> p_custom_headers = Vector<String>());
int get_max_packet_size() const; int get_max_packet_size() const;
Ref<WebSocketPeer> get_peer(int p_peer_id) const; Ref<WebSocketPeer> get_peer(int p_peer_id) const;
void disconnect_from_host(int p_code = 1000, String p_reason = ""); void disconnect_from_host(int p_code = 1000, String p_reason = "");

View file

@ -208,7 +208,6 @@ void WSLPeer::make_context(PeerData *p_data, unsigned int p_in_buf_size, unsigne
_data = p_data; _data = p_data;
_data->peer = this; _data->peer = this;
_data->valid = true; _data->valid = true;
_connection = Ref<StreamPeer>(_data->conn);
if (_data->is_server) if (_data->is_server)
wslay_event_context_server_init(&(_data->ctx), &wsl_callbacks, _data); wslay_event_context_server_init(&(_data->ctx), &wsl_callbacks, _data);
@ -302,18 +301,16 @@ void WSLPeer::close(int p_code, String p_reason) {
IP_Address WSLPeer::get_connected_host() const { IP_Address WSLPeer::get_connected_host() const {
ERR_FAIL_COND_V(!is_connected_to_host(), IP_Address()); ERR_FAIL_COND_V(!is_connected_to_host() || _data->tcp.is_null(), IP_Address());
IP_Address ip; return _data->tcp->get_connected_host();
return ip;
} }
uint16_t WSLPeer::get_connected_port() const { uint16_t WSLPeer::get_connected_port() const {
ERR_FAIL_COND_V(!is_connected_to_host(), 0); ERR_FAIL_COND_V(!is_connected_to_host() || _data->tcp.is_null(), 0);
uint16_t port = 0; return _data->tcp->get_connected_port();
return port;
} }
void WSLPeer::invalidate() { void WSLPeer::invalidate() {

View file

@ -35,6 +35,7 @@
#include "core/error_list.h" #include "core/error_list.h"
#include "core/io/packet_peer.h" #include "core/io/packet_peer.h"
#include "core/io/stream_peer_tcp.h"
#include "core/ring_buffer.h" #include "core/ring_buffer.h"
#include "packet_buffer.h" #include "packet_buffer.h"
#include "websocket_peer.h" #include "websocket_peer.h"
@ -55,6 +56,7 @@ public:
void *obj; void *obj;
void *peer; void *peer;
Ref<StreamPeer> conn; Ref<StreamPeer> conn;
Ref<StreamPeerTCP> tcp;
int id; int id;
wslay_event_context_ptr ctx; wslay_event_context_ptr ctx;
@ -77,7 +79,6 @@ private:
static bool _wsl_poll(struct PeerData *p_data); static bool _wsl_poll(struct PeerData *p_data);
static void _wsl_destroy(struct PeerData **p_data); static void _wsl_destroy(struct PeerData **p_data);
Ref<StreamPeer> _connection;
struct PeerData *_data; struct PeerData *_data;
uint8_t _is_string; uint8_t _is_string;
// Our packet info is just a boolean (is_string), using uint8_t for it. // Our packet info is just a boolean (is_string), using uint8_t for it.

View file

@ -35,6 +35,7 @@
#include "core/project_settings.h" #include "core/project_settings.h"
WSLServer::PendingPeer::PendingPeer() { WSLServer::PendingPeer::PendingPeer() {
use_ssl = false;
time = 0; time = 0;
has_request = false; has_request = false;
response_sent = 0; response_sent = 0;
@ -42,7 +43,7 @@ WSLServer::PendingPeer::PendingPeer() {
memset(req_buf, 0, sizeof(req_buf)); memset(req_buf, 0, sizeof(req_buf));
} }
bool WSLServer::PendingPeer::_parse_request(const PoolStringArray p_protocols) { bool WSLServer::PendingPeer::_parse_request(const Vector<String> p_protocols) {
Vector<String> psa = String((char *)req_buf).split("\r\n"); Vector<String> psa = String((char *)req_buf).split("\r\n");
int len = psa.size(); int len = psa.size();
ERR_FAIL_COND_V_MSG(len < 4, false, "Not enough response headers, got: " + itos(len) + ", expected >= 4."); ERR_FAIL_COND_V_MSG(len < 4, false, "Not enough response headers, got: " + itos(len) + ", expected >= 4.");
@ -97,9 +98,19 @@ bool WSLServer::PendingPeer::_parse_request(const PoolStringArray p_protocols) {
return true; return true;
} }
Error WSLServer::PendingPeer::do_handshake(PoolStringArray p_protocols) { Error WSLServer::PendingPeer::do_handshake(const Vector<String> p_protocols) {
if (OS::get_singleton()->get_ticks_msec() - time > WSL_SERVER_TIMEOUT) if (OS::get_singleton()->get_ticks_msec() - time > WSL_SERVER_TIMEOUT)
return ERR_TIMEOUT; return ERR_TIMEOUT;
if (use_ssl) {
Ref<StreamPeerSSL> ssl = static_cast<Ref<StreamPeerSSL> >(connection);
if (ssl.is_null())
return FAILED;
ssl->poll();
if (ssl->get_status() == StreamPeerSSL::STATUS_HANDSHAKING)
return ERR_BUSY;
else if (ssl->get_status() != StreamPeerSSL::STATUS_CONNECTED)
return FAILED;
}
if (!has_request) { if (!has_request) {
int read = 0; int read = 0;
while (true) { while (true) {
@ -143,11 +154,11 @@ Error WSLServer::PendingPeer::do_handshake(PoolStringArray p_protocols) {
return OK; return OK;
} }
Error WSLServer::listen(int p_port, PoolVector<String> p_protocols, bool gd_mp_api) { Error WSLServer::listen(int p_port, const Vector<String> p_protocols, bool gd_mp_api) {
ERR_FAIL_COND_V(is_listening(), ERR_ALREADY_IN_USE); ERR_FAIL_COND_V(is_listening(), ERR_ALREADY_IN_USE);
_is_multiplayer = gd_mp_api; _is_multiplayer = gd_mp_api;
_protocols = p_protocols; _protocols.append_array(p_protocols);
_server->listen(p_port); _server->listen(p_port);
return OK; return OK;
@ -185,6 +196,7 @@ void WSLServer::poll() {
WSLPeer::PeerData *data = memnew(struct WSLPeer::PeerData); WSLPeer::PeerData *data = memnew(struct WSLPeer::PeerData);
data->obj = this; data->obj = this;
data->conn = ppeer->connection; data->conn = ppeer->connection;
data->tcp = ppeer->tcp;
data->is_server = true; data->is_server = true;
data->id = id; data->id = id;
@ -204,12 +216,21 @@ void WSLServer::poll() {
return; return;
while (_server->is_connection_available()) { while (_server->is_connection_available()) {
Ref<StreamPeer> conn = _server->take_connection(); Ref<StreamPeerTCP> conn = _server->take_connection();
if (is_refusing_new_connections()) if (is_refusing_new_connections())
continue; // Conn will go out-of-scope and be closed. continue; // Conn will go out-of-scope and be closed.
Ref<PendingPeer> peer = memnew(PendingPeer); Ref<PendingPeer> peer = memnew(PendingPeer);
peer->connection = conn; if (private_key.is_valid() && ssl_cert.is_valid()) {
Ref<StreamPeerSSL> ssl = Ref<StreamPeerSSL>(StreamPeerSSL::create());
ssl->set_blocking_handshake_enabled(false);
ssl->accept_stream(conn, private_key, ssl_cert, ca_chain);
peer->connection = ssl;
peer->use_ssl = true;
} else {
peer->connection = conn;
}
peer->tcp = conn;
peer->time = OS::get_singleton()->get_ticks_msec(); peer->time = OS::get_singleton()->get_ticks_msec();
_pending.push_back(peer); _pending.push_back(peer);
} }
@ -231,6 +252,7 @@ void WSLServer::stop() {
} }
_pending.clear(); _pending.clear();
_peer_map.clear(); _peer_map.clear();
_protocols.clear();
} }
bool WSLServer::has_peer(int p_id) const { bool WSLServer::has_peer(int p_id) const {

View file

@ -36,6 +36,7 @@
#include "websocket_server.h" #include "websocket_server.h"
#include "wsl_peer.h" #include "wsl_peer.h"
#include "core/io/stream_peer_ssl.h"
#include "core/io/stream_peer_tcp.h" #include "core/io/stream_peer_tcp.h"
#include "core/io/tcp_server.h" #include "core/io/tcp_server.h"
@ -49,10 +50,12 @@ private:
class PendingPeer : public Reference { class PendingPeer : public Reference {
private: private:
bool _parse_request(const PoolStringArray p_protocols); bool _parse_request(const Vector<String> p_protocols);
public: public:
Ref<StreamPeerTCP> tcp;
Ref<StreamPeer> connection; Ref<StreamPeer> connection;
bool use_ssl;
int time; int time;
uint8_t req_buf[WSL_MAX_HEADER_SIZE]; uint8_t req_buf[WSL_MAX_HEADER_SIZE];
@ -65,7 +68,7 @@ private:
PendingPeer(); PendingPeer();
Error do_handshake(const PoolStringArray p_protocols); Error do_handshake(const Vector<String> p_protocols);
}; };
int _in_buf_size; int _in_buf_size;
@ -75,11 +78,11 @@ private:
List<Ref<PendingPeer> > _pending; List<Ref<PendingPeer> > _pending;
Ref<TCP_Server> _server; Ref<TCP_Server> _server;
PoolStringArray _protocols; Vector<String> _protocols;
public: public:
Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets); Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets);
Error listen(int p_port, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false); Error listen(int p_port, const Vector<String> p_protocols = Vector<String>(), bool gd_mp_api = false);
void stop(); void stop();
bool is_listening() const; bool is_listening() const;
int get_max_packet_size() const; int get_max_packet_size() const;