2
0
Эх сурвалжийг харах

Merge pull request #32683 from Faless/ws/improve_pr

WebSocket improvements, SSL server, custom headers.
Rémi Verschelde 6 жил өмнө
parent
commit
be446038bb

+ 20 - 0
modules/websocket/doc_classes/WebSocketClient.xml

@@ -21,10 +21,13 @@
 			</argument>
 			<argument index="2" name="gd_mp_api" type="bool" default="false">
 			</argument>
+			<argument index="3" name="custom_headers" type="PoolStringArray" default="PoolStringArray(  )">
+			</argument>
 			<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.
 				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]).
+				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>
 		</method>
 		<method name="disconnect_from_host">
@@ -38,8 +41,25 @@
 				Disconnects this client from the connected host. See [method WebSocketPeer.close] for more information.
 			</description>
 		</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>
 	<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">
 			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.

+ 11 - 0
modules/websocket/doc_classes/WebSocketServer.xml

@@ -82,6 +82,17 @@
 			</description>
 		</method>
 	</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>
 		<signal name="client_close_request">
 			<argument index="0" name="id" type="int">

+ 11 - 4
modules/websocket/emws_client.cpp

@@ -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 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://";
+		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;
 
 	_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 {
 
-	return IP_Address();
+	ERR_FAIL_V_MSG(IP_Address(), "Not supported in HTML5 export.");
 };
 
 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 {

+ 1 - 1
modules/websocket/emws_client.h

@@ -50,7 +50,7 @@ public:
 	bool _is_connecting;
 
 	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;
 	void disconnect_from_host(int p_code = 1000, String p_reason = "");
 	IP_Address get_connected_host() const;

+ 21 - 3
modules/websocket/websocket_client.cpp

@@ -40,7 +40,7 @@ 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;
 
 	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);
 	}
 
-	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) {
@@ -85,6 +85,17 @@ bool WebSocketClient::is_verify_ssl_enabled() const {
 	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 {
 
 	return false;
@@ -132,13 +143,20 @@ void WebSocketClient::_on_error() {
 }
 
 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("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("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");
 
+	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("connection_established", PropertyInfo(Variant::STRING, "protocol")));
 	ADD_SIGNAL(MethodInfo("server_close_request", PropertyInfo(Variant::INT, "code"), PropertyInfo(Variant::STRING, "reason")));

+ 6 - 2
modules/websocket/websocket_client.h

@@ -31,6 +31,7 @@
 #ifndef WEBSOCKET_CLIENT_H
 #define WEBSOCKET_CLIENT_H
 
+#include "core/crypto/crypto.h"
 #include "core/error_list.h"
 #include "websocket_multiplayer_peer.h"
 #include "websocket_peer.h"
@@ -43,17 +44,20 @@ class WebSocketClient : public WebSocketMultiplayerPeer {
 protected:
 	Ref<WebSocketPeer> _peer;
 	bool verify_ssl;
+	Ref<X509Certificate> ssl_cert;
 
 	static void _bind_methods();
 
 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);
 	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 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 IP_Address get_connected_host() const = 0;
 	virtual uint16_t get_connected_port() const = 0;

+ 40 - 1
modules/websocket/websocket_server.cpp

@@ -42,19 +42,58 @@ WebSocketServer::~WebSocketServer() {
 void WebSocketServer::_bind_methods() {
 
 	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("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_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("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_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("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 {
 	if (is_listening())
 		return CONNECTION_CONNECTED;

+ 15 - 1
modules/websocket/websocket_server.h

@@ -31,6 +31,7 @@
 #ifndef WEBSOCKET_H
 #define WEBSOCKET_H
 
+#include "core/crypto/crypto.h"
 #include "core/reference.h"
 #include "websocket_multiplayer_peer.h"
 #include "websocket_peer.h"
@@ -43,9 +44,13 @@ class WebSocketServer : public WebSocketMultiplayerPeer {
 protected:
 	static void _bind_methods();
 
+	Ref<CryptoKey> private_key;
+	Ref<X509Certificate> ssl_cert;
+	Ref<X509Certificate> ca_chain;
+
 public:
 	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 bool is_listening() 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_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;
 
 	WebSocketServer();

+ 13 - 6
modules/websocket/wsl_client.cpp

@@ -86,6 +86,7 @@ void WSLClient::_do_handshake() {
 				WSLPeer::PeerData *data = memnew(struct WSLPeer::PeerData);
 				data->obj = this;
 				data->conn = _connection;
+				data->tcp = _tcp;
 				data->is_server = false;
 				data->id = 1;
 				_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;
 }
 
-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);
 
@@ -180,7 +181,8 @@ Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
 	_connection = _tcp;
 	_use_ssl = p_ssl;
 	_host = p_host;
-	_protocols = p_protocols;
+	_protocols.clear();
+	_protocols.append_array(p_protocols);
 
 	_key = WSLPeer::generate_key();
 	// 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";
 	}
+	for (int i = 0; i < p_custom_headers.size(); i++) {
+		request += p_custom_headers[i] + "\r\n";
+	}
 	request += "\r\n";
 	_request = request.utf8();
 
@@ -236,7 +241,7 @@ void WSLClient::poll() {
 					ssl = Ref<StreamPeerSSL>(StreamPeerSSL::create());
 					ERR_FAIL_COND_MSG(ssl.is_null(), "SSL is not available in this build.");
 					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();
 						_on_error();
 						return;
@@ -293,7 +298,7 @@ void WSLClient::disconnect_from_host(int p_code, String p_reason) {
 
 	_key = "";
 	_host = "";
-	_protocols.resize(0);
+	_protocols.clear();
 	_use_ssl = false;
 
 	_request = "";
@@ -305,12 +310,14 @@ void WSLClient::disconnect_from_host(int p_code, String p_reason) {
 
 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 {
 
-	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) {

+ 2 - 2
modules/websocket/wsl_client.h

@@ -64,7 +64,7 @@ private:
 
 	String _key;
 	String _host;
-	PoolVector<String> _protocols;
+	Vector<String> _protocols;
 	bool _use_ssl;
 
 	void _do_handshake();
@@ -72,7 +72,7 @@ private:
 
 public:
 	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;
 	Ref<WebSocketPeer> get_peer(int p_peer_id) const;
 	void disconnect_from_host(int p_code = 1000, String p_reason = "");

+ 4 - 7
modules/websocket/wsl_peer.cpp

@@ -208,7 +208,6 @@ void WSLPeer::make_context(PeerData *p_data, unsigned int p_in_buf_size, unsigne
 	_data = p_data;
 	_data->peer = this;
 	_data->valid = true;
-	_connection = Ref<StreamPeer>(_data->conn);
 
 	if (_data->is_server)
 		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 {
 
-	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 ip;
+	return _data->tcp->get_connected_host();
 }
 
 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 port;
+	return _data->tcp->get_connected_port();
 }
 
 void WSLPeer::invalidate() {

+ 2 - 1
modules/websocket/wsl_peer.h

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

+ 28 - 6
modules/websocket/wsl_server.cpp

@@ -35,6 +35,7 @@
 #include "core/project_settings.h"
 
 WSLServer::PendingPeer::PendingPeer() {
+	use_ssl = false;
 	time = 0;
 	has_request = false;
 	response_sent = 0;
@@ -42,7 +43,7 @@ WSLServer::PendingPeer::PendingPeer() {
 	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");
 	int len = psa.size();
 	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;
 }
 
-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)
 		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) {
 		int read = 0;
 		while (true) {
@@ -143,11 +154,11 @@ Error WSLServer::PendingPeer::do_handshake(PoolStringArray p_protocols) {
 	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);
 
 	_is_multiplayer = gd_mp_api;
-	_protocols = p_protocols;
+	_protocols.append_array(p_protocols);
 	_server->listen(p_port);
 
 	return OK;
@@ -185,6 +196,7 @@ void WSLServer::poll() {
 		WSLPeer::PeerData *data = memnew(struct WSLPeer::PeerData);
 		data->obj = this;
 		data->conn = ppeer->connection;
+		data->tcp = ppeer->tcp;
 		data->is_server = true;
 		data->id = id;
 
@@ -204,12 +216,21 @@ void WSLServer::poll() {
 		return;
 
 	while (_server->is_connection_available()) {
-		Ref<StreamPeer> conn = _server->take_connection();
+		Ref<StreamPeerTCP> conn = _server->take_connection();
 		if (is_refusing_new_connections())
 			continue; // Conn will go out-of-scope and be closed.
 
 		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();
 		_pending.push_back(peer);
 	}
@@ -231,6 +252,7 @@ void WSLServer::stop() {
 	}
 	_pending.clear();
 	_peer_map.clear();
+	_protocols.clear();
 }
 
 bool WSLServer::has_peer(int p_id) const {

+ 7 - 4
modules/websocket/wsl_server.h

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