summaryrefslogtreecommitdiff
path: root/modules/websocket
diff options
context:
space:
mode:
Diffstat (limited to 'modules/websocket')
-rw-r--r--modules/websocket/SCsub104
-rw-r--r--modules/websocket/doc_classes/WebSocketClient.xml23
-rw-r--r--modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml29
-rw-r--r--modules/websocket/doc_classes/WebSocketPeer.xml22
-rw-r--r--modules/websocket/doc_classes/WebSocketServer.xml24
-rw-r--r--modules/websocket/emws_client.cpp25
-rw-r--r--modules/websocket/emws_client.h9
-rw-r--r--modules/websocket/emws_peer.cpp68
-rw-r--r--modules/websocket/emws_peer.h27
-rw-r--r--modules/websocket/emws_server.cpp16
-rw-r--r--modules/websocket/emws_server.h7
-rw-r--r--modules/websocket/lws_client.cpp225
-rw-r--r--modules/websocket/lws_helper.h221
-rw-r--r--modules/websocket/lws_peer.cpp287
-rw-r--r--modules/websocket/lws_server.cpp204
-rw-r--r--modules/websocket/packet_buffer.h122
-rw-r--r--modules/websocket/register_types.cpp52
-rw-r--r--modules/websocket/register_types.h5
-rw-r--r--modules/websocket/websocket_client.cpp7
-rw-r--r--modules/websocket/websocket_client.h9
-rw-r--r--modules/websocket/websocket_macros.h15
-rw-r--r--modules/websocket/websocket_multiplayer_peer.cpp (renamed from modules/websocket/websocket_multiplayer.cpp)55
-rw-r--r--modules/websocket/websocket_multiplayer_peer.h (renamed from modules/websocket/websocket_multiplayer.h)18
-rw-r--r--modules/websocket/websocket_peer.cpp5
-rw-r--r--modules/websocket/websocket_peer.h6
-rw-r--r--modules/websocket/websocket_server.cpp5
-rw-r--r--modules/websocket/websocket_server.h9
-rw-r--r--modules/websocket/wsl_client.cpp344
-rw-r--r--modules/websocket/wsl_client.h (renamed from modules/websocket/lws_client.h)55
-rw-r--r--modules/websocket/wsl_peer.cpp339
-rw-r--r--modules/websocket/wsl_peer.h (renamed from modules/websocket/lws_peer.h)95
-rw-r--r--modules/websocket/wsl_server.cpp285
-rw-r--r--modules/websocket/wsl_server.h (renamed from modules/websocket/lws_server.h)65
33 files changed, 1496 insertions, 1286 deletions
diff --git a/modules/websocket/SCsub b/modules/websocket/SCsub
index c0985b3245..033169411f 100644
--- a/modules/websocket/SCsub
+++ b/modules/websocket/SCsub
@@ -5,90 +5,26 @@ Import('env_modules')
# Thirdparty source files
-env_lws = env_modules.Clone()
-
-if env['builtin_libwebsockets']:
- thirdparty_dir = "#thirdparty/libwebsockets/"
- helper_dir = "win32helpers/"
- thirdparty_sources = [
-
- "core/alloc.c",
- "core/context.c",
- "core/libwebsockets.c",
- "core/output.c",
- "core/pollfd.c",
- "core/service.c",
-
- "event-libs/poll/poll.c",
-
- "misc/base64-decode.c",
- "misc/lejp.c",
- "misc/sha-1.c",
-
- "roles/h1/ops-h1.c",
- "roles/http/header.c",
- "roles/http/client/client.c",
- "roles/http/client/client-handshake.c",
- "roles/http/server/fops-zip.c",
- "roles/http/server/lejp-conf.c",
- "roles/http/server/parsers.c",
- "roles/http/server/server.c",
- "roles/listen/ops-listen.c",
- "roles/pipe/ops-pipe.c",
- "roles/raw/ops-raw.c",
-
- "roles/ws/client-ws.c",
- "roles/ws/client-parser-ws.c",
- "roles/ws/ops-ws.c",
- "roles/ws/server-ws.c",
-
- "tls/tls.c",
- "tls/tls-client.c",
- "tls/tls-server.c",
-
- "tls/mbedtls/wrapper/library/ssl_cert.c",
- "tls/mbedtls/wrapper/library/ssl_pkey.c",
- "tls/mbedtls/wrapper/library/ssl_stack.c",
- "tls/mbedtls/wrapper/library/ssl_methods.c",
- "tls/mbedtls/wrapper/library/ssl_lib.c",
- "tls/mbedtls/wrapper/library/ssl_x509.c",
- "tls/mbedtls/wrapper/platform/ssl_port.c",
- "tls/mbedtls/wrapper/platform/ssl_pm.c",
- "tls/mbedtls/lws-genhash.c",
- "tls/mbedtls/mbedtls-client.c",
- "tls/mbedtls/lws-genrsa.c",
- "tls/mbedtls/ssl.c",
- "tls/mbedtls/mbedtls-server.c"
+env_ws = env_modules.Clone()
+
+if env['builtin_wslay'] and not env["platform"] == "javascript": # already builtin for javascript
+ wslay_dir = "#thirdparty/wslay/"
+ wslay_sources = [
+ "wslay_net.c",
+ "wslay_event.c",
+ "wslay_queue.c",
+ "wslay_stack.c",
+ "wslay_frame.c",
]
-
- if env_lws["platform"] == "android": # Builtin getifaddrs
- thirdparty_sources += ["misc/getifaddrs.c"]
-
- if env_lws["platform"] == "windows" or env_lws["platform"] == "uwp": # Winsock
- thirdparty_sources += ["plat/lws-plat-win.c", helper_dir + "getopt.c", helper_dir + "getopt_long.c", helper_dir + "gettimeofday.c"]
- else: # Unix socket
- thirdparty_sources += ["plat/lws-plat-unix.c"]
-
-
- thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
-
- if env_lws["platform"] == "javascript": # No need to add third party libraries at all
- pass
+ wslay_sources = [wslay_dir + s for s in wslay_sources]
+ env_ws.Prepend(CPPPATH=[wslay_dir + "includes/"])
+ env_ws.Append(CPPDEFINES=["HAVE_CONFIG_H"])
+ if env["platform"] == "windows" or env["platform"] == "uwp":
+ env_ws.Append(CPPDEFINES=["HAVE_WINSOCK2_H"])
else:
- env_lws.add_source_files(env.modules_sources, thirdparty_sources)
- env_lws.Append(CPPPATH=[thirdparty_dir])
-
- wrapper_includes = ["#thirdparty/libwebsockets/tls/mbedtls/wrapper/include/" + inc for inc in ["internal", "openssl", "platform", ""]]
- env_lws.Prepend(CPPPATH=wrapper_includes)
-
- if env['builtin_mbedtls']:
- mbedtls_includes = "#thirdparty/mbedtls/include"
- env_lws.Prepend(CPPPATH=[mbedtls_includes])
-
- if env_lws["platform"] == "windows" or env_lws["platform"] == "uwp":
- env_lws.Append(CPPPATH=[thirdparty_dir + helper_dir])
-
- if env_lws["platform"] == "uwp":
- env_lws.Append(CCFLAGS=["/DLWS_MINGW_SUPPORT"])
+ env_ws.Append(CPPDEFINES=["HAVE_NETINET_IN_H"])
+ env_wslay = env_ws.Clone()
+ env_wslay.disable_warnings()
+ env_wslay.add_source_files(env.modules_sources, wslay_sources)
-env_lws.add_source_files(env.modules_sources, "*.cpp")
+env_ws.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/websocket/doc_classes/WebSocketClient.xml b/modules/websocket/doc_classes/WebSocketClient.xml
index b3ea535495..c3baf9de83 100644
--- a/modules/websocket/doc_classes/WebSocketClient.xml
+++ b/modules/websocket/doc_classes/WebSocketClient.xml
@@ -1,18 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="WebSocketClient" inherits="WebSocketMultiplayerPeer" category="Core" version="3.1">
+<class name="WebSocketClient" inherits="WebSocketMultiplayerPeer" category="Core" version="3.2">
<brief_description>
- A WebSocket client implementation
+ A WebSocket client implementation.
</brief_description>
<description>
- This class implements a WebSocket client compatible with any RFC 6455 complaint WebSocket server.
+ This class implements a WebSocket client compatible with any RFC 6455-compliant WebSocket server.
This client can be optionally used as a network peer for the [MultiplayerAPI].
After starting the client ([method connect_to_url]), you will need to [method NetworkedMultiplayerPeer.poll] it at regular intervals (e.g. inside [method Node._process]).
- You will received appropriate signals when connecting, disconnecting, or when new data is available.
+ You will receive appropriate signals when connecting, disconnecting, or when new data is available.
</description>
<tutorials>
</tutorials>
- <demos>
- </demos>
<methods>
<method name="connect_to_url">
<return type="int" enum="Error">
@@ -24,8 +22,9 @@
<argument index="2" name="gd_mp_api" type="bool" default="false">
</argument>
<description>
- Connect to the given URL requesting one of the given [code]protocols[/code] as sub-protocol.
- If [code]true[/code] is passed as [code]gd_mp_api[/code], the client will behave like a network peer for the [MultiplayerAPI]. Note: connections to non Godot servers will not work, and [signal data_received] will not be emitted when this option is true.
+ 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]).
</description>
</method>
<method name="disconnect_from_host">
@@ -36,13 +35,14 @@
<argument index="1" name="reason" type="String" default="&quot;&quot;">
</argument>
<description>
- Disconnect this client from the connected host. See [method WebSocketPeer.close] for more info.
+ Disconnects this client from the connected host. See [method WebSocketPeer.close] for more information.
</description>
</method>
</methods>
<members>
<member name="verify_ssl" type="bool" setter="set_verify_ssl_enabled" getter="is_verify_ssl_enabled">
- Enable or disable SSL certificate verification. Note: You must specify the certificates to be used in the project settings for it to work when exported.
+ 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.
</member>
</members>
<signals>
@@ -67,7 +67,8 @@
</signal>
<signal name="data_received">
<description>
- Emitted when a WebSocket message is received. Note: This signal is NOT emitted when used as high level multiplayer peer.
+ Emitted when a WebSocket message is received.
+ [b]Note:[/b] This signal is [i]not[/i] emitted when used as high-level multiplayer peer.
</description>
</signal>
<signal name="server_close_request">
diff --git a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml
index 1a841f85ed..7070cfbdab 100644
--- a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml
+++ b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="WebSocketMultiplayerPeer" inherits="NetworkedMultiplayerPeer" category="Core" version="3.1">
+<class name="WebSocketMultiplayerPeer" inherits="NetworkedMultiplayerPeer" category="Core" version="3.2">
<brief_description>
Base class for WebSocket server and client.
</brief_description>
@@ -8,8 +8,6 @@
</description>
<tutorials>
</tutorials>
- <demos>
- </demos>
<methods>
<method name="get_peer" qualifiers="const">
<return type="WebSocketPeer">
@@ -20,13 +18,36 @@
Returns the [WebSocketPeer] associated to the given [code]peer_id[/code].
</description>
</method>
+ <method name="set_buffers">
+ <return type="int" enum="Error">
+ </return>
+ <argument index="0" name="input_buffer_size_kb" type="int">
+ </argument>
+ <argument index="1" name="input_max_packets" type="int">
+ </argument>
+ <argument index="2" name="output_buffer_size_kb" type="int">
+ </argument>
+ <argument index="3" name="output_max_packets" type="int">
+ </argument>
+ <description>
+ Configures the buffer sizes for this WebSocket peer. Default values can be specified in the Project Settings under [code]network/limits[/code]. For server, values are meant per connected peer.
+ The first two parameters define the size and queued packets limits of the input buffer, the last two of the output buffer.
+ Buffer sizes are expressed in KiB, so [code]4 = 2^12 = 4096 bytes[/code]. All parameters will be rounded up to the nearest power of two.
+ [b]Note:[/b] HTML5 exports only use the input buffer since the output one is managed by browsers.
+ </description>
+ </method>
</methods>
+ <members>
+ <member name="refuse_new_connections" type="bool" setter="set_refuse_new_connections" getter="is_refusing_new_connections" override="true" default="false" />
+ <member name="transfer_mode" type="int" setter="set_transfer_mode" getter="get_transfer_mode" override="true" enum="NetworkedMultiplayerPeer.TransferMode" default="2" />
+ </members>
<signals>
<signal name="peer_packet">
<argument index="0" name="peer_source" type="int">
</argument>
<description>
- Emitted when a packet is received from a peer. Note: this signal is only emitted when the client or server is configured to use Godot multiplayer API.
+ Emitted when a packet is received from a peer.
+ [b]Note:[/b] This signal is only emitted when the client or server is configured to use Godot multiplayer API.
</description>
</signal>
</signals>
diff --git a/modules/websocket/doc_classes/WebSocketPeer.xml b/modules/websocket/doc_classes/WebSocketPeer.xml
index cc59706916..dd95f7432e 100644
--- a/modules/websocket/doc_classes/WebSocketPeer.xml
+++ b/modules/websocket/doc_classes/WebSocketPeer.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="WebSocketPeer" inherits="PacketPeer" category="Core" version="3.1">
+<class name="WebSocketPeer" inherits="PacketPeer" category="Core" version="3.2">
<brief_description>
A class representing a specific WebSocket connection.
</brief_description>
@@ -9,8 +9,6 @@
</description>
<tutorials>
</tutorials>
- <demos>
- </demos>
<methods>
<method name="close">
<return type="void">
@@ -20,30 +18,32 @@
<argument index="1" name="reason" type="String" default="&quot;&quot;">
</argument>
<description>
- Close this WebSocket connection. [code]code[/code] is the status code for the closure (see RFC6455 section 7.4 for a list of valid status codes). [reason] is the human readable reason for closing the connection (can be any UTF8 string, must be less than 123 bytes).
- Note: To achieve a clean close, you will need to keep polling until either [signal WebSocketClient.connection_closed] or [signal WebSocketServer.client_disconnected] is received.
- Note: HTML5 export might not support all status codes. Please refer to browsers-specific documentation for more details.
+ Closes this WebSocket connection. [code]code[/code] is the status code for the closure (see RFC 6455 section 7.4 for a list of valid status codes). [code]reason[/code] is the human readable reason for closing the connection (can be any UTF-8 string that's smaller than 123 bytes).
+ [b]Note:[/b] To achieve a clean close, you will need to keep polling until either [signal WebSocketClient.connection_closed] or [signal WebSocketServer.client_disconnected] is received.
+ [b]Note:[/b] The HTML5 export might not support all status codes. Please refer to browser-specific documentation for more details.
</description>
</method>
<method name="get_connected_host" qualifiers="const">
<return type="String">
</return>
<description>
- Returns the IP Address of the connected peer. (Not available in HTML5 export)
+ Returns the IP address of the connected peer.
+ [b]Note:[/b] Not available in the HTML5 export.
</description>
</method>
<method name="get_connected_port" qualifiers="const">
<return type="int">
</return>
<description>
- Returns the remote port of the connected peer. (Not available in HTML5 export)
+ Returns the remote port of the connected peer.
+ [b]Note:[/b] Not available in the HTML5 export.
</description>
</method>
<method name="get_write_mode" qualifiers="const">
<return type="int" enum="WebSocketPeer.WriteMode">
</return>
<description>
- Get the current selected write mode. See [enum WriteMode].
+ Gets the current selected write mode. See [enum WriteMode].
</description>
</method>
<method name="is_connected_to_host" qualifiers="const">
@@ -72,10 +72,10 @@
</methods>
<constants>
<constant name="WRITE_MODE_TEXT" value="0" enum="WriteMode">
- Specify that WebSockets messages should be transferred as text payload (only valid UTF-8 is allowed).
+ Specifies that WebSockets messages should be transferred as text payload (only valid UTF-8 is allowed).
</constant>
<constant name="WRITE_MODE_BINARY" value="1" enum="WriteMode">
- Specify that WebSockets messages should be transferred as binary payload (any byte combination is allowed).
+ Specifies that WebSockets messages should be transferred as binary payload (any byte combination is allowed).
</constant>
</constants>
</class>
diff --git a/modules/websocket/doc_classes/WebSocketServer.xml b/modules/websocket/doc_classes/WebSocketServer.xml
index ba66fdd89b..63318e5874 100644
--- a/modules/websocket/doc_classes/WebSocketServer.xml
+++ b/modules/websocket/doc_classes/WebSocketServer.xml
@@ -1,17 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="WebSocketServer" inherits="WebSocketMultiplayerPeer" category="Core" version="3.1">
+<class name="WebSocketServer" inherits="WebSocketMultiplayerPeer" category="Core" version="3.2">
<brief_description>
- A WebSocket server implementation
+ A WebSocket server implementation.
</brief_description>
<description>
- This class implements a WebSocket server that can also support the high level multiplayer API.
+ This class implements a WebSocket server that can also support the high-level multiplayer API.
After starting the server ([method listen]), you will need to [method NetworkedMultiplayerPeer.poll] it at regular intervals (e.g. inside [method Node._process]). When clients connect, disconnect, or send data, you will receive the appropriate signal.
- Note: This class will not work in HTML5 exports due to browser restrictions.
+ [b]Note:[/b] This class will not work in HTML5 exports due to browser restrictions.
</description>
<tutorials>
</tutorials>
- <demos>
- </demos>
<methods>
<method name="disconnect_peer">
<return type="void">
@@ -23,7 +21,7 @@
<argument index="2" name="reason" type="String" default="&quot;&quot;">
</argument>
<description>
- Disconnects the peer identified by [code]id[/code] from the server. See [method WebSocketPeer.close] for more info.
+ Disconnects the peer identified by [code]id[/code] from the server. See [method WebSocketPeer.close] for more information.
</description>
</method>
<method name="get_peer_address" qualifiers="const">
@@ -70,16 +68,17 @@
<argument index="2" name="gd_mp_api" type="bool" default="false">
</argument>
<description>
- Start listening on the given port.
- You can specify the desired subprotocols via the "protocols" array. If the list empty (default), "binary" will be used.
- You can use this server as a network peer for [MultiplayerAPI] by passing [code]true[/code] as [code]gd_mp_api[/code]. Note: [signal data_received] will not be fired and clients other than Godot will not work in this case.
+ Starts listening on the given port.
+ You can specify the desired subprotocols via the "protocols" array. If the list empty (default), no sub-protocol will be requested.
+ If [code]true[/code] is passed as [code]gd_mp_api[/code], the server will behave like a network peer for the [MultiplayerAPI], connections from non-Godot clients 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(id)[/code] to communicate with the peer with given [code]id[/code] (e.g. [code]get_peer(id).get_available_packet_count[/code]).
</description>
</method>
<method name="stop">
<return type="void">
</return>
<description>
- Stop the server and clear its state.
+ Stops the server and clear its state.
</description>
</method>
</methods>
@@ -117,7 +116,8 @@
<argument index="0" name="id" type="int">
</argument>
<description>
- Emitted when a new message is received. Note: This signal is NOT emitted when used as high level multiplayer peer.
+ Emitted when a new message is received.
+ [b]Note:[/b] This signal is [i]not[/i] emitted when used as high-level multiplayer peer.
</description>
</signal>
</signals>
diff --git a/modules/websocket/emws_client.cpp b/modules/websocket/emws_client.cpp
index 8255ed7116..409cc9f699 100644
--- a/modules/websocket/emws_client.cpp
+++ b/modules/websocket/emws_client.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -27,10 +27,12 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+
#ifdef JAVASCRIPT_ENABLED
#include "emws_client.h"
#include "core/io/ip.h"
+#include "core/project_settings.h"
#include "emscripten.h"
extern "C" {
@@ -43,8 +45,9 @@ EMSCRIPTEN_KEEPALIVE void _esws_on_connect(void *obj, char *proto) {
EMSCRIPTEN_KEEPALIVE void _esws_on_message(void *obj, uint8_t *p_data, int p_data_size, int p_is_string) {
EMWSClient *client = static_cast<EMWSClient *>(obj);
- static_cast<EMWSPeer *>(*client->get_peer(1))->read_msg(p_data, p_data_size, p_is_string == 1);
- client->_on_peer_packet();
+ Error err = static_cast<EMWSPeer *>(*client->get_peer(1))->read_msg(p_data, p_data_size, p_is_string == 1);
+ if (err == OK)
+ client->_on_peer_packet();
}
EMSCRIPTEN_KEEPALIVE void _esws_on_error(void *obj) {
@@ -159,7 +162,7 @@ Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
}, _js_id, str.utf8().get_data(), proto_string.utf8().get_data());
/* clang-format on */
- static_cast<Ref<EMWSPeer> >(_peer)->set_sock(peer_sock);
+ static_cast<Ref<EMWSPeer> >(_peer)->set_sock(peer_sock, _in_buf_size, _in_pkt_size);
return OK;
};
@@ -198,7 +201,19 @@ uint16_t EMWSClient::get_connected_port() const {
return 1025;
};
+int EMWSClient::get_max_packet_size() const {
+ return (1 << _in_buf_size) - PROTO_SIZE;
+}
+
+Error EMWSClient::set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) {
+ _in_buf_size = nearest_shift(p_in_buffer - 1) + 10;
+ _in_pkt_size = nearest_shift(p_in_packets - 1);
+ return OK;
+}
+
EMWSClient::EMWSClient() {
+ _in_buf_size = nearest_shift((int)GLOBAL_GET(WSC_IN_BUF) - 1) + 10;
+ _in_pkt_size = nearest_shift((int)GLOBAL_GET(WSC_IN_PKT) - 1);
_is_connecting = false;
_peer = Ref<EMWSPeer>(memnew(EMWSPeer));
/* clang-format off */
diff --git a/modules/websocket/emws_client.h b/modules/websocket/emws_client.h
index b20633baff..1811d05eea 100644
--- a/modules/websocket/emws_client.h
+++ b/modules/websocket/emws_client.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -27,6 +27,7 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+
#ifndef EMWSCLIENT_H
#define EMWSCLIENT_H
@@ -41,17 +42,21 @@ class EMWSClient : public WebSocketClient {
GDCIIMPL(EMWSClient, WebSocketClient);
private:
+ int _in_buf_size;
+ int _in_pkt_size;
int _js_id;
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>());
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;
uint16_t get_connected_port() const;
virtual ConnectionStatus get_connection_status() const;
+ int get_max_packet_size() const;
virtual void poll();
EMWSClient();
~EMWSClient();
diff --git a/modules/websocket/emws_peer.cpp b/modules/websocket/emws_peer.cpp
index 68f41165eb..58d4c52688 100644
--- a/modules/websocket/emws_peer.cpp
+++ b/modules/websocket/emws_peer.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -27,16 +27,17 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+
#ifdef JAVASCRIPT_ENABLED
#include "emws_peer.h"
#include "core/io/ip.h"
-void EMWSPeer::set_sock(int p_sock) {
+void EMWSPeer::set_sock(int p_sock, unsigned int p_in_buf_size, unsigned int p_in_pkt_size) {
peer_sock = p_sock;
- in_buffer.clear();
- queue_count = 0;
+ _in_buffer.resize(p_in_pkt_size, p_in_buf_size);
+ _packet_buffer.resize((1 << p_in_buf_size));
}
void EMWSPeer::set_write_mode(WriteMode p_mode) {
@@ -47,18 +48,10 @@ EMWSPeer::WriteMode EMWSPeer::get_write_mode() const {
return write_mode;
}
-void EMWSPeer::read_msg(uint8_t *p_data, uint32_t p_size, bool p_is_string) {
-
- if (in_buffer.space_left() < p_size + 5) {
- ERR_EXPLAIN("Buffer full! Dropping data");
- ERR_FAIL();
- }
+Error EMWSPeer::read_msg(uint8_t *p_data, uint32_t p_size, bool p_is_string) {
uint8_t is_string = p_is_string ? 1 : 0;
- in_buffer.write((uint8_t *)&p_size, 4);
- in_buffer.write((uint8_t *)&is_string, 1);
- in_buffer.write(p_data, p_size);
- queue_count++;
+ return _in_buffer.write_packet(p_data, p_size, &is_string);
}
Error EMWSPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
@@ -89,40 +82,28 @@ Error EMWSPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
Error EMWSPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
- if (queue_count == 0)
+ if (_in_buffer.packets_left() == 0)
return ERR_UNAVAILABLE;
- uint32_t to_read = 0;
- uint32_t left = 0;
- uint8_t is_string = 0;
- r_buffer_size = 0;
+ PoolVector<uint8_t>::Write rw = _packet_buffer.write();
+ int read = 0;
+ Error err = _in_buffer.read_packet(rw.ptr(), _packet_buffer.size(), &_is_string, read);
+ ERR_FAIL_COND_V(err != OK, err);
- in_buffer.read((uint8_t *)&to_read, 4);
- --queue_count;
- left = in_buffer.data_left();
-
- if (left < to_read + 1) {
- in_buffer.advance_read(left);
- return FAILED;
- }
-
- in_buffer.read(&is_string, 1);
- _was_string = is_string == 1;
- in_buffer.read(packet_buffer, to_read);
- *r_buffer = packet_buffer;
- r_buffer_size = to_read;
+ *r_buffer = rw.ptr();
+ r_buffer_size = read;
return OK;
};
int EMWSPeer::get_available_packet_count() const {
- return queue_count;
+ return _in_buffer.packets_left();
};
bool EMWSPeer::was_string_packet() const {
- return _was_string;
+ return _is_string;
};
bool EMWSPeer::is_connected_to_host() const {
@@ -143,34 +124,29 @@ void EMWSPeer::close(int p_code, String p_reason) {
}, peer_sock, p_code, p_reason.utf8().get_data());
/* clang-format on */
}
+ _is_string = 0;
+ _in_buffer.clear();
peer_sock = -1;
- queue_count = 0;
- in_buffer.clear();
};
IP_Address EMWSPeer::get_connected_host() const {
- ERR_EXPLAIN("Not supported in HTML5 export");
- ERR_FAIL_V(IP_Address());
+ ERR_FAIL_V_MSG(IP_Address(), "Not supported in HTML5 export.");
};
uint16_t EMWSPeer::get_connected_port() const {
- ERR_EXPLAIN("Not supported in HTML5 export");
- ERR_FAIL_V(0);
+ ERR_FAIL_V_MSG(0, "Not supported in HTML5 export.");
};
EMWSPeer::EMWSPeer() {
peer_sock = -1;
- queue_count = 0;
- _was_string = false;
- in_buffer.resize(16);
write_mode = WRITE_MODE_BINARY;
+ close();
};
EMWSPeer::~EMWSPeer() {
- in_buffer.resize(0);
close();
};
diff --git a/modules/websocket/emws_peer.h b/modules/websocket/emws_peer.h
index a4b2c8f50b..6ceb69a1b7 100644
--- a/modules/websocket/emws_peer.h
+++ b/modules/websocket/emws_peer.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -27,6 +27,7 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+
#ifndef EMWSPEER_H
#define EMWSPEER_H
@@ -36,6 +37,7 @@
#include "core/io/packet_peer.h"
#include "core/ring_buffer.h"
#include "emscripten.h"
+#include "packet_buffer.h"
#include "websocket_peer.h"
class EMWSPeer : public WebSocketPeer {
@@ -43,25 +45,20 @@ class EMWSPeer : public WebSocketPeer {
GDCIIMPL(EMWSPeer, WebSocketPeer);
private:
- enum {
- PACKET_BUFFER_SIZE = 65536 - 5 // 4 bytes for the size, 1 for for type
- };
-
int peer_sock;
WriteMode write_mode;
- uint8_t packet_buffer[PACKET_BUFFER_SIZE];
- RingBuffer<uint8_t> in_buffer;
- int queue_count;
- bool _was_string;
+ PoolVector<uint8_t> _packet_buffer;
+ PacketBuffer<uint8_t> _in_buffer;
+ uint8_t _is_string;
public:
- void read_msg(uint8_t *p_data, uint32_t p_size, bool p_is_string);
- void set_sock(int sock);
+ Error read_msg(uint8_t *p_data, uint32_t p_size, bool p_is_string);
+ void set_sock(int p_sock, unsigned int p_in_buf_size, unsigned int p_in_pkt_size);
virtual int get_available_packet_count() const;
virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size);
virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size);
- virtual int get_max_packet_size() const { return PACKET_BUFFER_SIZE; };
+ virtual int get_max_packet_size() const { return _packet_buffer.size(); };
virtual void close(int p_code = 1000, String p_reason = "");
virtual bool is_connected_to_host() const;
@@ -72,10 +69,6 @@ public:
virtual void set_write_mode(WriteMode p_mode);
virtual bool was_string_packet() const;
- void set_wsi(struct lws *wsi);
- Error read_wsi(void *in, size_t len);
- Error write_wsi();
-
EMWSPeer();
~EMWSPeer();
};
diff --git a/modules/websocket/emws_server.cpp b/modules/websocket/emws_server.cpp
index ad4a758c0f..c4bb459ad0 100644
--- a/modules/websocket/emws_server.cpp
+++ b/modules/websocket/emws_server.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -27,6 +27,7 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+
#ifdef JAVASCRIPT_ENABLED
#include "emws_server.h"
@@ -71,6 +72,17 @@ int EMWSServer::get_peer_port(int p_peer_id) const {
void EMWSServer::disconnect_peer(int p_peer_id, int p_code, String p_reason) {
}
+void EMWSServer::poll() {
+}
+
+int EMWSServer::get_max_packet_size() const {
+ return 0;
+}
+
+Error EMWSServer::set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) {
+ return OK;
+}
+
EMWSServer::EMWSServer() {
}
diff --git a/modules/websocket/emws_server.h b/modules/websocket/emws_server.h
index 74b689a29b..a5e5b4090e 100644
--- a/modules/websocket/emws_server.h
+++ b/modules/websocket/emws_server.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -27,6 +27,7 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+
#ifndef EMWSSERVER_H
#define EMWSSERVER_H
@@ -41,6 +42,7 @@ class EMWSServer : public WebSocketServer {
GDCIIMPL(EMWSServer, WebSocketServer);
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);
void stop();
bool is_listening() const;
@@ -49,6 +51,7 @@ public:
IP_Address get_peer_address(int p_peer_id) const;
int get_peer_port(int p_peer_id) const;
void disconnect_peer(int p_peer_id, int p_code = 1000, String p_reason = "");
+ int get_max_packet_size() const;
virtual void poll();
virtual PoolVector<String> get_protocols() const;
diff --git a/modules/websocket/lws_client.cpp b/modules/websocket/lws_client.cpp
deleted file mode 100644
index cd814760e6..0000000000
--- a/modules/websocket/lws_client.cpp
+++ /dev/null
@@ -1,225 +0,0 @@
-/*************************************************************************/
-/* lws_client.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-#ifndef JAVASCRIPT_ENABLED
-
-#include "lws_client.h"
-#include "core/io/ip.h"
-#include "core/io/stream_peer_ssl.h"
-#include "tls/mbedtls/wrapper/include/openssl/ssl.h"
-
-Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocols) {
-
- ERR_FAIL_COND_V(context != NULL, FAILED);
-
- IP_Address addr;
-
- if (!p_host.is_valid_ip_address()) {
- addr = IP::get_singleton()->resolve_hostname(p_host);
- } else {
- addr = p_host;
- }
-
- ERR_FAIL_COND_V(!addr.is_valid(), ERR_INVALID_PARAMETER);
-
- // Prepare protocols
- _lws_make_protocols(this, &LWSClient::_lws_gd_callback, p_protocols, &_lws_ref);
-
- // Init lws client
- struct lws_context_creation_info info;
- struct lws_client_connect_info i;
-
- memset(&i, 0, sizeof i);
- memset(&info, 0, sizeof info);
-
- info.port = CONTEXT_PORT_NO_LISTEN;
- info.protocols = _lws_ref->lws_structs;
- info.gid = -1;
- info.uid = -1;
- //info.ws_ping_pong_interval = 5;
- info.user = _lws_ref;
-#if defined(LWS_OPENSSL_SUPPORT)
- info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
-#endif
- context = lws_create_context(&info);
-
- if (context == NULL) {
- _lws_free_ref(_lws_ref);
- _lws_ref = NULL;
- ERR_EXPLAIN("Unable to create lws context");
- ERR_FAIL_V(FAILED);
- }
-
- char abuf[1024];
- char hbuf[1024];
- char pbuf[2048];
- String addr_str = (String)addr;
- strncpy(abuf, addr_str.ascii().get_data(), 1024);
- strncpy(hbuf, p_host.utf8().get_data(), 1024);
- strncpy(pbuf, p_path.utf8().get_data(), 2048);
-
- i.context = context;
- if (p_protocols.size() > 0)
- i.protocol = _lws_ref->lws_names;
- else
- i.protocol = NULL;
- i.address = abuf;
- i.host = hbuf;
- i.path = pbuf;
- i.port = p_port;
-
- if (p_ssl) {
- i.ssl_connection = LCCSCF_USE_SSL;
- if (!verify_ssl)
- i.ssl_connection |= LCCSCF_ALLOW_SELFSIGNED;
- } else {
- i.ssl_connection = 0;
- }
-
- lws_client_connect_via_info(&i);
- return OK;
-};
-
-void LWSClient::poll() {
-
- _lws_poll();
-}
-
-int LWSClient::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) {
-
- Ref<LWSPeer> peer = static_cast<Ref<LWSPeer> >(_peer);
- LWSPeer::PeerData *peer_data = (LWSPeer::PeerData *)user;
-
- switch (reason) {
- case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS: {
- PoolByteArray arr = StreamPeerSSL::get_project_cert_array();
- if (arr.size() > 0)
- SSL_CTX_add_client_CA((SSL_CTX *)user, d2i_X509(NULL, &arr.read()[0], arr.size()));
- else if (verify_ssl)
- WARN_PRINTS("No CA cert specified in project settings, SSL will not work");
- } break;
-
- case LWS_CALLBACK_CLIENT_ESTABLISHED:
- peer->set_wsi(wsi);
- peer_data->peer_id = 0;
- peer_data->force_close = false;
- peer_data->clean_close = false;
- _on_connect(lws_get_protocol(wsi)->name);
- break;
-
- case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
- _on_error();
- destroy_context();
- return -1; // We should close the connection (would probably happen anyway)
-
- case LWS_CALLBACK_WS_PEER_INITIATED_CLOSE: {
- int code;
- String reason = peer->get_close_reason(in, len, code);
- peer_data->clean_close = true;
- _on_close_request(code, reason);
- return 0;
- }
-
- case LWS_CALLBACK_CLIENT_CLOSED:
- peer->close();
- destroy_context();
- _on_disconnect(peer_data->clean_close);
- return 0; // We can end here
-
- case LWS_CALLBACK_CLIENT_RECEIVE:
- peer->read_wsi(in, len);
- if (peer->get_available_packet_count() > 0)
- _on_peer_packet();
- break;
-
- case LWS_CALLBACK_CLIENT_WRITEABLE:
- if (peer_data->force_close) {
- peer->send_close_status(wsi);
- return -1;
- }
-
- peer->write_wsi();
- break;
-
- default:
- break;
- }
-
- return 0;
-}
-
-Ref<WebSocketPeer> LWSClient::get_peer(int p_peer_id) const {
-
- return _peer;
-}
-
-NetworkedMultiplayerPeer::ConnectionStatus LWSClient::get_connection_status() const {
-
- if (context == NULL)
- return CONNECTION_DISCONNECTED;
-
- if (_peer->is_connected_to_host())
- return CONNECTION_CONNECTED;
-
- return CONNECTION_CONNECTING;
-}
-
-void LWSClient::disconnect_from_host(int p_code, String p_reason) {
-
- if (context == NULL)
- return;
-
- _peer->close(p_code, p_reason);
-};
-
-IP_Address LWSClient::get_connected_host() const {
-
- return IP_Address();
-};
-
-uint16_t LWSClient::get_connected_port() const {
-
- return 1025;
-};
-
-LWSClient::LWSClient() {
- context = NULL;
- _lws_ref = NULL;
- _peer = Ref<LWSPeer>(memnew(LWSPeer));
-};
-
-LWSClient::~LWSClient() {
-
- invalidate_lws_ref(); // We do not want any more callback
- disconnect_from_host();
- destroy_context();
- _peer = Ref<LWSPeer>();
-};
-
-#endif // JAVASCRIPT_ENABLED
diff --git a/modules/websocket/lws_helper.h b/modules/websocket/lws_helper.h
deleted file mode 100644
index 70256ccf16..0000000000
--- a/modules/websocket/lws_helper.h
+++ /dev/null
@@ -1,221 +0,0 @@
-/*************************************************************************/
-/* lws_helper.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-#ifndef LWS_HELPER_H
-#define LWS_HELPER_H
-
-#define LWS_BUF_SIZE 65536
-#define LWS_PACKET_SIZE LWS_BUF_SIZE
-
-#include "core/io/stream_peer.h"
-#include "core/os/os.h"
-#include "core/reference.h"
-#include "core/ring_buffer.h"
-#include "lws_peer.h"
-
-struct _LWSRef {
- bool free_context;
- bool is_polling;
- bool is_valid;
- bool is_destroying;
- void *obj;
- struct lws_protocols *lws_structs;
- char *lws_names;
-};
-
-static _LWSRef *_lws_create_ref(void *obj) {
-
- _LWSRef *out = (_LWSRef *)memalloc(sizeof(_LWSRef));
- out->is_destroying = false;
- out->free_context = false;
- out->is_polling = false;
- out->obj = obj;
- out->is_valid = true;
- out->lws_structs = NULL;
- out->lws_names = NULL;
- return out;
-}
-
-static void _lws_free_ref(_LWSRef *ref) {
- // Free strings and structs
- memfree(ref->lws_structs);
- memfree(ref->lws_names);
- // Free ref
- memfree(ref);
-}
-
-static bool _lws_destroy(struct lws_context *context, _LWSRef *ref) {
- if (context == NULL || ref->is_destroying)
- return false;
-
- if (ref->is_polling) {
- ref->free_context = true;
- return false;
- }
-
- ref->is_destroying = true;
- lws_context_destroy(context);
- _lws_free_ref(ref);
- return true;
-}
-
-static bool _lws_poll(struct lws_context *context, _LWSRef *ref) {
-
- ERR_FAIL_COND_V(context == NULL, false);
- ERR_FAIL_COND_V(ref == NULL, false);
-
- ref->is_polling = true;
- lws_service(context, 0);
- ref->is_polling = false;
-
- if (!ref->free_context)
- return false; // Nothing to do
-
- bool is_valid = ref->is_valid; // Might have been destroyed by poll
-
- _lws_destroy(context, ref); // Will destroy context and ref
-
- return is_valid; // If the object should NULL its context and ref
-}
-
-/*
- * Prepare the protocol_structs to be fed to context.
- * Also prepare the protocol string used by the client.
- */
-static void _lws_make_protocols(void *p_obj, lws_callback_function *p_callback, PoolVector<String> p_names, _LWSRef **r_lws_ref) {
- // The input strings might go away after this call, we need to copy them.
- // We will clear them when destroying the context.
- int i;
- int len = p_names.size();
- size_t data_size = sizeof(struct LWSPeer::PeerData);
- PoolVector<String>::Read pnr = p_names.read();
-
- // This is a reference connecting the object with lws keep track of status, mallocs, etc.
- // Must survive as long the context.
- // Must be freed manually when context creation fails.
- _LWSRef *ref = _lws_create_ref(p_obj);
-
- // LWS protocol structs.
- ref->lws_structs = (struct lws_protocols *)memalloc(sizeof(struct lws_protocols) * (len + 2));
- memset(ref->lws_structs, 0, sizeof(struct lws_protocols) * (len + 2));
-
- CharString strings = p_names.join(",").ascii();
- int str_len = strings.length();
-
- // Joined string of protocols, double the size: comma separated first, NULL separated last
- ref->lws_names = (char *)memalloc((str_len + 1) * 2); // Plus the terminator
-
- char *names_ptr = ref->lws_names;
- struct lws_protocols *structs_ptr = ref->lws_structs;
-
- // Comma separated protocols string to be used in client Sec-WebSocket-Protocol header
- if (str_len > 0)
- copymem(names_ptr, strings.get_data(), str_len);
- names_ptr[str_len] = '\0'; // NULL terminator
-
- // NULL terminated protocol strings to be used in protocol structs
- if (str_len > 0)
- copymem(&names_ptr[str_len + 1], strings.get_data(), str_len);
- names_ptr[(str_len * 2) + 1] = '\0'; // NULL terminator
- int pos = str_len + 1;
-
- // The first protocol is the default for any http request (before upgrade).
- // It is also used as the websocket protocol when no subprotocol is specified.
- structs_ptr[0].name = "default";
- structs_ptr[0].callback = p_callback;
- structs_ptr[0].per_session_data_size = data_size;
- structs_ptr[0].rx_buffer_size = LWS_BUF_SIZE;
- structs_ptr[0].tx_packet_size = LWS_PACKET_SIZE;
- // Add user defined protocols
- for (i = 0; i < len; i++) {
- structs_ptr[i + 1].name = (const char *)&names_ptr[pos];
- structs_ptr[i + 1].callback = p_callback;
- structs_ptr[i + 1].per_session_data_size = data_size;
- structs_ptr[i + 1].rx_buffer_size = LWS_BUF_SIZE;
- structs_ptr[i + 1].tx_packet_size = LWS_PACKET_SIZE;
- pos += pnr[i].ascii().length() + 1;
- names_ptr[pos - 1] = '\0';
- }
- // Add protocols terminator
- structs_ptr[len + 1].name = NULL;
- structs_ptr[len + 1].callback = NULL;
- structs_ptr[len + 1].per_session_data_size = 0;
- structs_ptr[len + 1].rx_buffer_size = 0;
-
- *r_lws_ref = ref;
-}
-
-/* clang-format off */
-#define LWS_HELPER(CNAME) \
-protected: \
- struct _LWSRef *_lws_ref; \
- struct lws_context *context; \
- \
- static int _lws_gd_callback(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { \
- \
- if (wsi == NULL) { \
- return 0; \
- } \
- \
- struct _LWSRef *ref = (struct _LWSRef *)lws_context_user(lws_get_context(wsi)); \
- if (!ref->is_valid) \
- return 0; \
- CNAME *helper = (CNAME *)ref->obj; \
- return helper->_handle_cb(wsi, reason, user, in, len); \
- } \
- \
- void invalidate_lws_ref() { \
- if (_lws_ref != NULL) \
- _lws_ref->is_valid = false; \
- } \
- \
- void destroy_context() { \
- if (_lws_destroy(context, _lws_ref)) { \
- context = NULL; \
- _lws_ref = NULL; \
- } \
- } \
- \
-public: \
- virtual int _handle_cb(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len); \
- \
- void _lws_poll() { \
- ERR_FAIL_COND(context == NULL); \
- \
- if (::_lws_poll(context, _lws_ref)) { \
- context = NULL; \
- _lws_ref = NULL; \
- } \
- } \
- \
-protected:
-
-/* clang-format on */
-
-#endif // LWS_HELPER_H
diff --git a/modules/websocket/lws_peer.cpp b/modules/websocket/lws_peer.cpp
deleted file mode 100644
index 245b28b608..0000000000
--- a/modules/websocket/lws_peer.cpp
+++ /dev/null
@@ -1,287 +0,0 @@
-/*************************************************************************/
-/* lws_peer.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-#ifndef JAVASCRIPT_ENABLED
-
-#include "lws_peer.h"
-#include "core/io/ip.h"
-
-// Needed for socket_helpers on Android at least. UNIXes has it, just include if not windows
-#if !defined(WINDOWS_ENABLED)
-#include <netinet/in.h>
-#include <sys/socket.h>
-#endif
-
-#include "drivers/unix/socket_helpers.h"
-
-void LWSPeer::set_wsi(struct lws *p_wsi) {
- ERR_FAIL_COND(wsi != NULL);
-
- rbw.resize(16);
- rbr.resize(16);
- wsi = p_wsi;
-};
-
-void LWSPeer::set_write_mode(WriteMode p_mode) {
- write_mode = p_mode;
-}
-
-LWSPeer::WriteMode LWSPeer::get_write_mode() const {
- return write_mode;
-}
-
-Error LWSPeer::read_wsi(void *in, size_t len) {
-
- ERR_FAIL_COND_V(!is_connected_to_host(), FAILED);
-
- PeerData *peer_data = (PeerData *)(lws_wsi_user(wsi));
- uint32_t size = in_size;
- uint8_t is_string = lws_frame_is_binary(wsi) ? 0 : 1;
-
- if (rbr.space_left() < len + 5) {
- ERR_EXPLAIN("Buffer full! Dropping data");
- ERR_FAIL_V(FAILED);
- }
-
- copymem(&(input_buffer[size]), in, len);
- size += len;
-
- in_size = size;
- if (lws_is_final_fragment(wsi)) {
- rbr.write((uint8_t *)&size, 4);
- rbr.write((uint8_t *)&is_string, 1);
- rbr.write(input_buffer, size);
- in_count++;
- in_size = 0;
- }
-
- return OK;
-}
-
-Error LWSPeer::write_wsi() {
-
- ERR_FAIL_COND_V(!is_connected_to_host(), FAILED);
-
- PeerData *peer_data = (PeerData *)(lws_wsi_user(wsi));
- PoolVector<uint8_t> tmp;
- int left = rbw.data_left();
- uint32_t to_write = 0;
-
- if (left == 0 || out_count == 0)
- return OK;
-
- rbw.read((uint8_t *)&to_write, 4);
- out_count--;
-
- if (left < to_write) {
- rbw.advance_read(left);
- return FAILED;
- }
-
- tmp.resize(LWS_PRE + to_write);
- rbw.read(&(tmp.write()[LWS_PRE]), to_write);
- lws_write(wsi, &(tmp.write()[LWS_PRE]), to_write, (enum lws_write_protocol)write_mode);
- tmp.resize(0);
-
- if (out_count > 0)
- lws_callback_on_writable(wsi); // we want to write more!
-
- return OK;
-}
-
-Error LWSPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
-
- ERR_FAIL_COND_V(!is_connected_to_host(), FAILED);
-
- PeerData *peer_data = (PeerData *)lws_wsi_user(wsi);
- rbw.write((uint8_t *)&p_buffer_size, 4);
- rbw.write(p_buffer, MIN(p_buffer_size, rbw.space_left()));
- out_count++;
-
- lws_callback_on_writable(wsi); // notify that we want to write
- return OK;
-};
-
-Error LWSPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
-
- ERR_FAIL_COND_V(!is_connected_to_host(), FAILED);
-
- PeerData *peer_data = (PeerData *)lws_wsi_user(wsi);
-
- if (in_count == 0)
- return ERR_UNAVAILABLE;
-
- uint32_t to_read = 0;
- uint32_t left = 0;
- uint8_t is_string = 0;
- r_buffer_size = 0;
-
- rbr.read((uint8_t *)&to_read, 4);
- in_count--;
- left = rbr.data_left();
-
- if (left < to_read + 1) {
- rbr.advance_read(left);
- return FAILED;
- }
-
- rbr.read(&is_string, 1);
- rbr.read(packet_buffer, to_read);
- *r_buffer = packet_buffer;
- r_buffer_size = to_read;
- _was_string = is_string;
-
- return OK;
-};
-
-int LWSPeer::get_available_packet_count() const {
-
- if (!is_connected_to_host())
- return 0;
-
- return in_count;
-};
-
-bool LWSPeer::was_string_packet() const {
-
- return _was_string;
-};
-
-bool LWSPeer::is_connected_to_host() const {
-
- return wsi != NULL;
-};
-
-String LWSPeer::get_close_reason(void *in, size_t len, int &r_code) {
- String s;
- r_code = 0;
- if (len < 2) // From docs this should not happen
- return s;
-
- const uint8_t *b = (const uint8_t *)in;
- r_code = b[0] << 8 | b[1];
-
- if (len > 2) {
- const char *utf8 = (const char *)&b[2];
- s.parse_utf8(utf8, len - 2);
- }
- return s;
-}
-
-void LWSPeer::send_close_status(struct lws *p_wsi) {
- if (close_code == -1)
- return;
-
- int len = close_reason.size();
- ERR_FAIL_COND(len > 123); // Maximum allowed reason size in bytes
-
- lws_close_status code = (lws_close_status)close_code;
- unsigned char *reason = len > 0 ? (unsigned char *)close_reason.utf8().ptrw() : NULL;
-
- lws_close_reason(p_wsi, code, reason, len);
-
- close_code = -1;
- close_reason = "";
-}
-
-void LWSPeer::close(int p_code, String p_reason) {
- if (wsi != NULL) {
- close_code = p_code;
- close_reason = p_reason;
- PeerData *data = ((PeerData *)lws_wsi_user(wsi));
- data->force_close = true;
- data->clean_close = true;
- lws_callback_on_writable(wsi); // Notify that we want to disconnect
- } else {
- close_code = -1;
- close_reason = "";
- }
- wsi = NULL;
- rbw.resize(0);
- rbr.resize(0);
- in_count = 0;
- in_size = 0;
- out_count = 0;
- _was_string = false;
-};
-
-IP_Address LWSPeer::get_connected_host() const {
-
- ERR_FAIL_COND_V(!is_connected_to_host(), IP_Address());
-
- IP_Address ip;
- int port = 0;
-
- struct sockaddr_storage addr;
- socklen_t len = sizeof(addr);
-
- int fd = lws_get_socket_fd(wsi);
- ERR_FAIL_COND_V(fd == -1, IP_Address());
-
- int ret = getpeername(fd, (struct sockaddr *)&addr, &len);
- ERR_FAIL_COND_V(ret != 0, IP_Address());
-
- _set_ip_addr_port(ip, port, &addr);
-
- return ip;
-};
-
-uint16_t LWSPeer::get_connected_port() const {
-
- ERR_FAIL_COND_V(!is_connected_to_host(), 0);
-
- IP_Address ip;
- int port = 0;
-
- struct sockaddr_storage addr;
- socklen_t len = sizeof(addr);
-
- int fd = lws_get_socket_fd(wsi);
- ERR_FAIL_COND_V(fd == -1, 0);
-
- int ret = getpeername(fd, (struct sockaddr *)&addr, &len);
- ERR_FAIL_COND_V(ret != 0, 0);
-
- _set_ip_addr_port(ip, port, &addr);
-
- return port;
-};
-
-LWSPeer::LWSPeer() {
- wsi = NULL;
- write_mode = WRITE_MODE_BINARY;
- close();
-};
-
-LWSPeer::~LWSPeer() {
-
- close();
-};
-
-#endif // JAVASCRIPT_ENABLED
diff --git a/modules/websocket/lws_server.cpp b/modules/websocket/lws_server.cpp
deleted file mode 100644
index 58fa043346..0000000000
--- a/modules/websocket/lws_server.cpp
+++ /dev/null
@@ -1,204 +0,0 @@
-/*************************************************************************/
-/* lws_server.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-#ifndef JAVASCRIPT_ENABLED
-
-#include "lws_server.h"
-#include "core/os/os.h"
-
-Error LWSServer::listen(int p_port, PoolVector<String> p_protocols, bool gd_mp_api) {
-
- ERR_FAIL_COND_V(context != NULL, FAILED);
-
- _is_multiplayer = gd_mp_api;
-
- struct lws_context_creation_info info;
- memset(&info, 0, sizeof info);
-
- // Prepare lws protocol structs
- _lws_make_protocols(this, &LWSServer::_lws_gd_callback, p_protocols, &_lws_ref);
-
- info.port = p_port;
- info.user = _lws_ref;
- info.protocols = _lws_ref->lws_structs;
- info.gid = -1;
- info.uid = -1;
- //info.ws_ping_pong_interval = 5;
-
- context = lws_create_context(&info);
-
- if (context == NULL) {
- _lws_free_ref(_lws_ref);
- _lws_ref = NULL;
- ERR_EXPLAIN("Unable to create LWS context");
- ERR_FAIL_V(FAILED);
- }
-
- return OK;
-}
-
-bool LWSServer::is_listening() const {
- return context != NULL;
-}
-
-int LWSServer::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) {
-
- LWSPeer::PeerData *peer_data = (LWSPeer::PeerData *)user;
-
- switch (reason) {
- case LWS_CALLBACK_HTTP:
- // no http for now
- // closing immediately returning -1;
- return -1;
-
- case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
- // check header here?
- break;
-
- case LWS_CALLBACK_ESTABLISHED: {
- int32_t id = _gen_unique_id();
-
- Ref<LWSPeer> peer = Ref<LWSPeer>(memnew(LWSPeer));
- peer->set_wsi(wsi);
- _peer_map[id] = peer;
-
- peer_data->peer_id = id;
- peer_data->force_close = false;
- peer_data->clean_close = false;
- _on_connect(id, lws_get_protocol(wsi)->name);
- break;
- }
-
- case LWS_CALLBACK_WS_PEER_INITIATED_CLOSE: {
- if (peer_data == NULL)
- return 0;
-
- int32_t id = peer_data->peer_id;
- if (_peer_map.has(id)) {
- int code;
- Ref<LWSPeer> peer = _peer_map[id];
- String reason = peer->get_close_reason(in, len, code);
- peer_data->clean_close = true;
- _on_close_request(id, code, reason);
- }
- return 0;
- }
-
- case LWS_CALLBACK_CLOSED: {
- if (peer_data == NULL)
- return 0;
- int32_t id = peer_data->peer_id;
- bool clean = peer_data->clean_close;
- if (_peer_map.has(id)) {
- _peer_map[id]->close();
- _peer_map.erase(id);
- }
- _on_disconnect(id, clean);
- return 0; // we can end here
- }
-
- case LWS_CALLBACK_RECEIVE: {
- int32_t id = peer_data->peer_id;
- if (_peer_map.has(id)) {
- static_cast<Ref<LWSPeer> >(_peer_map[id])->read_wsi(in, len);
- if (_peer_map[id]->get_available_packet_count() > 0)
- _on_peer_packet(id);
- }
- break;
- }
-
- case LWS_CALLBACK_SERVER_WRITEABLE: {
- int id = peer_data->peer_id;
- if (peer_data->force_close) {
- if (_peer_map.has(id)) {
- Ref<LWSPeer> peer = _peer_map[id];
- peer->send_close_status(wsi);
- }
- return -1;
- }
-
- if (_peer_map.has(id))
- static_cast<Ref<LWSPeer> >(_peer_map[id])->write_wsi();
- break;
- }
-
- default:
- break;
- }
-
- return 0;
-}
-
-void LWSServer::stop() {
- if (context == NULL)
- return;
-
- _peer_map.clear();
- destroy_context();
- context = NULL;
-}
-
-bool LWSServer::has_peer(int p_id) const {
- return _peer_map.has(p_id);
-}
-
-Ref<WebSocketPeer> LWSServer::get_peer(int p_id) const {
- ERR_FAIL_COND_V(!has_peer(p_id), NULL);
- return _peer_map[p_id];
-}
-
-IP_Address LWSServer::get_peer_address(int p_peer_id) const {
- ERR_FAIL_COND_V(!has_peer(p_peer_id), IP_Address());
-
- return _peer_map[p_peer_id]->get_connected_host();
-}
-
-int LWSServer::get_peer_port(int p_peer_id) const {
- ERR_FAIL_COND_V(!has_peer(p_peer_id), 0);
-
- return _peer_map[p_peer_id]->get_connected_port();
-}
-
-void LWSServer::disconnect_peer(int p_peer_id, int p_code, String p_reason) {
- ERR_FAIL_COND(!has_peer(p_peer_id));
-
- get_peer(p_peer_id)->close(p_code, p_reason);
-}
-
-LWSServer::LWSServer() {
- context = NULL;
- _lws_ref = NULL;
-}
-
-LWSServer::~LWSServer() {
- invalidate_lws_ref(); // we do not want any more callbacks
- stop();
-}
-
-#endif // JAVASCRIPT_ENABLED
diff --git a/modules/websocket/packet_buffer.h b/modules/websocket/packet_buffer.h
new file mode 100644
index 0000000000..057fecfb56
--- /dev/null
+++ b/modules/websocket/packet_buffer.h
@@ -0,0 +1,122 @@
+/*************************************************************************/
+/* packet_buffer.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef PACKET_BUFFER_H
+#define PACKET_BUFFER_H
+
+#include "core/os/copymem.h"
+#include "core/ring_buffer.h"
+
+template <class T>
+class PacketBuffer {
+
+private:
+ typedef struct {
+ uint32_t size;
+ T info;
+ } _Packet;
+
+ RingBuffer<_Packet> _packets;
+ RingBuffer<uint8_t> _payload;
+
+public:
+ Error write_packet(const uint8_t *p_payload, uint32_t p_size, const T *p_info) {
+#ifdef TOOLS_ENABLED
+ // Verbose buffer warnings
+ if (p_payload && _payload.space_left() < (int32_t)p_size) {
+ ERR_PRINT("Buffer payload full! Dropping data.");
+ ERR_FAIL_V(ERR_OUT_OF_MEMORY);
+ }
+ if (p_info && _packets.space_left() < 1) {
+ ERR_PRINT("Too many packets in queue! Dropping data.");
+ ERR_FAIL_V(ERR_OUT_OF_MEMORY);
+ }
+#else
+ ERR_FAIL_COND_V(p_payload && (uint32_t)_payload.space_left() < p_size, ERR_OUT_OF_MEMORY);
+ ERR_FAIL_COND_V(p_info && _packets.space_left() < 1, ERR_OUT_OF_MEMORY);
+#endif
+
+ // If p_info is NULL, only the payload is written
+ if (p_info) {
+ _Packet p;
+ p.size = p_size;
+ copymem(&p.info, p_info, sizeof(T));
+ _packets.write(p);
+ }
+
+ // If p_payload is NULL, only the packet information is written.
+ if (p_payload) {
+ _payload.write((const uint8_t *)p_payload, p_size);
+ }
+
+ return OK;
+ }
+
+ Error read_packet(uint8_t *r_payload, int p_bytes, T *r_info, int &r_read) {
+ ERR_FAIL_COND_V(_packets.data_left() < 1, ERR_UNAVAILABLE);
+ _Packet p;
+ _packets.read(&p, 1);
+ ERR_FAIL_COND_V(_payload.data_left() < (int)p.size, ERR_BUG);
+ ERR_FAIL_COND_V(p_bytes < (int)p.size, ERR_OUT_OF_MEMORY);
+
+ r_read = p.size;
+ copymem(r_info, &p.info, sizeof(T));
+ _payload.read(r_payload, p.size);
+ return OK;
+ }
+
+ void discard_payload(int p_size) {
+ _packets.decrease_write(p_size);
+ }
+
+ void resize(int p_pkt_shift, int p_buf_shift) {
+ _packets.resize(p_pkt_shift);
+ _payload.resize(p_buf_shift);
+ }
+
+ int packets_left() const {
+ return _packets.data_left();
+ }
+
+ void clear() {
+ _payload.resize(0);
+ _packets.resize(0);
+ }
+
+ PacketBuffer() {
+ clear();
+ }
+
+ ~PacketBuffer() {
+ clear();
+ }
+};
+
+#endif // PACKET_BUFFER_H
diff --git a/modules/websocket/register_types.cpp b/modules/websocket/register_types.cpp
index 538cd40454..1c808f0d5c 100644
--- a/modules/websocket/register_types.cpp
+++ b/modules/websocket/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -27,47 +27,45 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+
#include "register_types.h"
#include "core/error_macros.h"
+#include "core/project_settings.h"
#ifdef JAVASCRIPT_ENABLED
#include "emscripten.h"
#include "emws_client.h"
#include "emws_peer.h"
#include "emws_server.h"
#else
-#include "lws_client.h"
-#include "lws_peer.h"
-#include "lws_server.h"
+#include "wsl_client.h"
+#include "wsl_server.h"
#endif
void register_websocket_types() {
+#define _SET_HINT(NAME, _VAL_, _MAX_) \
+ GLOBAL_DEF(NAME, _VAL_); \
+ ProjectSettings::get_singleton()->set_custom_property_info(NAME, PropertyInfo(Variant::INT, NAME, PROPERTY_HINT_RANGE, "2," #_MAX_ ",1,or_greater"));
+
+ // Client buffers project settings
+ _SET_HINT(WSC_IN_BUF, 64, 4096);
+ _SET_HINT(WSC_IN_PKT, 1024, 16384);
+ _SET_HINT(WSC_OUT_BUF, 64, 4096);
+ _SET_HINT(WSC_OUT_PKT, 1024, 16384);
+
+ // Server buffers project settings
+ _SET_HINT(WSS_IN_BUF, 64, 4096);
+ _SET_HINT(WSS_IN_PKT, 1024, 16384);
+ _SET_HINT(WSS_OUT_BUF, 64, 4096);
+ _SET_HINT(WSS_OUT_PKT, 1024, 16384);
+
#ifdef JAVASCRIPT_ENABLED
- EM_ASM({
- var IDHandler = {};
- IDHandler["ids"] = {};
- IDHandler["has"] = function(id) {
- return IDHandler.ids.hasOwnProperty(id);
- };
- IDHandler["add"] = function(obj) {
- var id = crypto.getRandomValues(new Int32Array(32))[0];
- IDHandler.ids[id] = obj;
- return id;
- };
- IDHandler["get"] = function(id) {
- return IDHandler.ids[id];
- };
- IDHandler["remove"] = function(id) {
- delete IDHandler.ids[id];
- };
- Module["IDHandler"] = IDHandler;
- });
EMWSPeer::make_default();
EMWSClient::make_default();
EMWSServer::make_default();
#else
- LWSPeer::make_default();
- LWSClient::make_default();
- LWSServer::make_default();
+ WSLPeer::make_default();
+ WSLClient::make_default();
+ WSLServer::make_default();
#endif
ClassDB::register_virtual_class<WebSocketMultiplayerPeer>();
diff --git a/modules/websocket/register_types.h b/modules/websocket/register_types.h
index 89ce93e286..0bcae50d14 100644
--- a/modules/websocket/register_types.h
+++ b/modules/websocket/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -27,5 +27,6 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+
void register_websocket_types();
void unregister_websocket_types();
diff --git a/modules/websocket/websocket_client.cpp b/modules/websocket/websocket_client.cpp
index f9b94dc519..4ff5404c61 100644
--- a/modules/websocket/websocket_client.cpp
+++ b/modules/websocket/websocket_client.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -27,6 +27,7 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+
#include "websocket_client.h"
GDCINULL(WebSocketClient);
@@ -136,7 +137,7 @@ void WebSocketClient::_bind_methods() {
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_PROPERTYNZ(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");
ADD_SIGNAL(MethodInfo("data_received"));
ADD_SIGNAL(MethodInfo("connection_established", PropertyInfo(Variant::STRING, "protocol")));
diff --git a/modules/websocket/websocket_client.h b/modules/websocket/websocket_client.h
index 948f128e9f..7ddb9468a5 100644
--- a/modules/websocket/websocket_client.h
+++ b/modules/websocket/websocket_client.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -27,11 +27,12 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+
#ifndef WEBSOCKET_CLIENT_H
#define WEBSOCKET_CLIENT_H
#include "core/error_list.h"
-#include "websocket_multiplayer.h"
+#include "websocket_multiplayer_peer.h"
#include "websocket_peer.h"
class WebSocketClient : public WebSocketMultiplayerPeer {
@@ -66,6 +67,8 @@ public:
void _on_disconnect(bool p_was_clean);
void _on_error();
+ virtual Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) = 0;
+
WebSocketClient();
~WebSocketClient();
};
diff --git a/modules/websocket/websocket_macros.h b/modules/websocket/websocket_macros.h
index d27fb4d778..56f278187f 100644
--- a/modules/websocket/websocket_macros.h
+++ b/modules/websocket/websocket_macros.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -27,9 +27,20 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+
#ifndef WEBSOCKETMACTOS_H
#define WEBSOCKETMACTOS_H
+#define WSC_IN_BUF "network/limits/websocket_client/max_in_buffer_kb"
+#define WSC_IN_PKT "network/limits/websocket_client/max_in_packets"
+#define WSC_OUT_BUF "network/limits/websocket_client/max_out_buffer_kb"
+#define WSC_OUT_PKT "network/limits/websocket_client/max_out_packets"
+
+#define WSS_IN_BUF "network/limits/websocket_server/max_in_buffer_kb"
+#define WSS_IN_PKT "network/limits/websocket_server/max_in_packets"
+#define WSS_OUT_BUF "network/limits/websocket_server/max_out_buffer_kb"
+#define WSS_OUT_PKT "network/limits/websocket_server/max_out_packets"
+
/* clang-format off */
#define GDCICLASS(CNAME) \
public:\
diff --git a/modules/websocket/websocket_multiplayer.cpp b/modules/websocket/websocket_multiplayer_peer.cpp
index b948c439df..dd86c758bf 100644
--- a/modules/websocket/websocket_multiplayer.cpp
+++ b/modules/websocket/websocket_multiplayer_peer.cpp
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* websocket_multiplayer.cpp */
+/* websocket_multiplayer_peer.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -27,7 +27,9 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "websocket_multiplayer.h"
+
+#include "websocket_multiplayer_peer.h"
+
#include "core/os/os.h"
WebSocketMultiplayerPeer::WebSocketMultiplayerPeer() {
@@ -85,6 +87,7 @@ void WebSocketMultiplayerPeer::_clear() {
void WebSocketMultiplayerPeer::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_buffers", "input_buffer_size_kb", "input_max_packets", "output_buffer_size_kb", "output_max_packets"), &WebSocketMultiplayerPeer::set_buffers);
ClassDB::bind_method(D_METHOD("get_peer", "peer_id"), &WebSocketMultiplayerPeer::get_peer);
ADD_SIGNAL(MethodInfo("peer_packet", PropertyInfo(Variant::INT, "peer_source")));
@@ -95,22 +98,16 @@ void WebSocketMultiplayerPeer::_bind_methods() {
//
int WebSocketMultiplayerPeer::get_available_packet_count() const {
- ERR_FAIL_COND_V(!_is_multiplayer, ERR_UNCONFIGURED);
+ ERR_FAIL_COND_V_MSG(!_is_multiplayer, ERR_UNCONFIGURED, "Please use get_peer(ID).get_available_packet_count to get available packet count from peers when not using the MultiplayerAPI.");
return _incoming_packets.size();
}
-int WebSocketMultiplayerPeer::get_max_packet_size() const {
-
- ERR_FAIL_COND_V(!_is_multiplayer, ERR_UNCONFIGURED);
-
- return MAX_PACKET_SIZE;
-}
-
Error WebSocketMultiplayerPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
+ ERR_FAIL_COND_V_MSG(!_is_multiplayer, ERR_UNCONFIGURED, "Please use get_peer(ID).get_packet/var to communicate with peers when not using the MultiplayerAPI.");
+
r_buffer_size = 0;
- ERR_FAIL_COND_V(!_is_multiplayer, ERR_UNCONFIGURED);
if (_current_packet.data != NULL) {
memfree(_current_packet.data);
@@ -128,7 +125,7 @@ Error WebSocketMultiplayerPeer::get_packet(const uint8_t **r_buffer, int &r_buff
Error WebSocketMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
- ERR_FAIL_COND_V(!_is_multiplayer, ERR_UNCONFIGURED);
+ ERR_FAIL_COND_V_MSG(!_is_multiplayer, ERR_UNCONFIGURED, "Please use get_peer(ID).put_packet/var to communicate with peers when not using the MultiplayerAPI.");
PoolVector<uint8_t> buffer = _make_pkt(SYS_NONE, get_unique_id(), _target_peer, p_buffer, p_buffer_size);
@@ -160,7 +157,7 @@ void WebSocketMultiplayerPeer::set_target_peer(int p_target_peer) {
int WebSocketMultiplayerPeer::get_packet_peer() const {
- ERR_FAIL_COND_V(!_is_multiplayer, 1);
+ ERR_FAIL_COND_V_MSG(!_is_multiplayer, 1, "This function is not available when not using the MultiplayerAPI.");
ERR_FAIL_COND_V(_incoming_packets.size() == 0, 1);
return _incoming_packets.front()->get().source;
@@ -190,7 +187,7 @@ void WebSocketMultiplayerPeer::_send_sys(Ref<WebSocketPeer> p_peer, uint8_t p_ty
p_peer->put_packet(&(message.read()[0]), message.size());
}
-PoolVector<uint8_t> WebSocketMultiplayerPeer::_make_pkt(uint32_t p_type, int32_t p_from, int32_t p_to, const uint8_t *p_data, uint32_t p_data_size) {
+PoolVector<uint8_t> WebSocketMultiplayerPeer::_make_pkt(uint8_t p_type, int32_t p_from, int32_t p_to, const uint8_t *p_data, uint32_t p_data_size) {
PoolVector<uint8_t> out;
out.resize(PROTO_SIZE + p_data_size);
@@ -213,7 +210,7 @@ void WebSocketMultiplayerPeer::_send_add(int32_t p_peer_id) {
_send_sys(get_peer(p_peer_id), SYS_ADD, 1);
for (Map<int, Ref<WebSocketPeer> >::Element *E = _peer_map.front(); E; E = E->next()) {
- uint32_t id = E->key();
+ int32_t id = E->key();
if (p_peer_id == id)
continue; // Skip the newwly added peer (already confirmed)
@@ -226,7 +223,7 @@ void WebSocketMultiplayerPeer::_send_add(int32_t p_peer_id) {
void WebSocketMultiplayerPeer::_send_del(int32_t p_peer_id) {
for (Map<int, Ref<WebSocketPeer> >::Element *E = _peer_map.front(); E; E = E->next()) {
- uint32_t id = E->key();
+ int32_t id = E->key();
if (p_peer_id != id)
_send_sys(get_peer(id), SYS_DEL, p_peer_id);
}
@@ -268,7 +265,10 @@ Error WebSocketMultiplayerPeer::_server_relay(int32_t p_from, int32_t p_to, cons
ERR_FAIL_COND_V(p_to == p_from, FAILED);
- return get_peer(p_to)->put_packet(p_buffer, p_buffer_size); // Sending to specific peer
+ Ref<WebSocketPeer> peer_to = get_peer(p_to);
+ ERR_FAIL_COND_V(peer_to.is_null(), FAILED);
+
+ return peer_to->put_packet(p_buffer, p_buffer_size); // Sending to specific peer
}
}
@@ -288,7 +288,7 @@ void WebSocketMultiplayerPeer::_process_multiplayer(Ref<WebSocketPeer> p_peer, u
data_size = size - PROTO_SIZE;
uint8_t type = 0;
- int32_t from = 0;
+ uint32_t from = 0;
int32_t to = 0;
copymem(&type, in_buffer, 1);
copymem(&from, &in_buffer[1], 4);
@@ -299,8 +299,6 @@ void WebSocketMultiplayerPeer::_process_multiplayer(Ref<WebSocketPeer> p_peer, u
ERR_FAIL_COND(type != SYS_NONE); // Only server sends sys messages
ERR_FAIL_COND(from != p_peer_id); // Someone is cheating
- _server_relay(from, to, in_buffer, size); // Relay if needed
-
if (to == 1) { // This is for the server
_store_pkt(from, to, in_buffer, data_size);
@@ -313,15 +311,11 @@ void WebSocketMultiplayerPeer::_process_multiplayer(Ref<WebSocketPeer> p_peer, u
} else if (to < 0) {
// All but one, for us if not excluded
- if (_peer_id != -p_peer_id)
+ if (_peer_id != -(int32_t)p_peer_id)
_store_pkt(from, to, in_buffer, data_size);
-
- } else {
-
- // Send to specific peer
- ERR_FAIL_COND(!_peer_map.has(to));
- get_peer(to)->put_packet(in_buffer, size);
}
+ // Relay if needed (i.e. "to" includes a peer that is not the server)
+ _server_relay(from, to, in_buffer, size);
} else {
@@ -353,8 +347,7 @@ void WebSocketMultiplayerPeer::_process_multiplayer(Ref<WebSocketPeer> p_peer, u
_peer_id = id;
break;
default:
- ERR_EXPLAIN("Invalid multiplayer message");
- ERR_FAIL();
+ ERR_FAIL_MSG("Invalid multiplayer message.");
break;
}
}
diff --git a/modules/websocket/websocket_multiplayer.h b/modules/websocket/websocket_multiplayer_peer.h
index 8edfc5296e..e3ab0784ab 100644
--- a/modules/websocket/websocket_multiplayer.h
+++ b/modules/websocket/websocket_multiplayer_peer.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* websocket_multiplayer.h */
+/* websocket_multiplayer_peer.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -27,6 +27,7 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+
#ifndef WEBSOCKET_MULTIPLAYER_PEER_H
#define WEBSOCKET_MULTIPLAYER_PEER_H
@@ -40,7 +41,7 @@ class WebSocketMultiplayerPeer : public NetworkedMultiplayerPeer {
GDCLASS(WebSocketMultiplayerPeer, NetworkedMultiplayerPeer);
private:
- PoolVector<uint8_t> _make_pkt(uint32_t p_type, int32_t p_from, int32_t p_to, const uint8_t *p_data, uint32_t p_data_size);
+ PoolVector<uint8_t> _make_pkt(uint8_t p_type, int32_t p_from, int32_t p_to, const uint8_t *p_data, uint32_t p_data_size);
void _store_pkt(int32_t p_source, int32_t p_dest, const uint8_t *p_data, uint32_t p_data_size);
Error _server_relay(int32_t p_from, int32_t p_to, const uint8_t *p_buffer, uint32_t p_buffer_size);
@@ -51,9 +52,7 @@ protected:
SYS_DEL = 2,
SYS_ID = 3,
- PROTO_SIZE = 9,
- SYS_PACKET_SIZE = 13,
- MAX_PACKET_SIZE = 65536 - 14 // 5 websocket, 9 multiplayer
+ PROTO_SIZE = 9
};
struct Packet {
@@ -83,7 +82,7 @@ public:
/* NetworkedMultiplayerPeer */
void set_transfer_mode(TransferMode p_mode);
TransferMode get_transfer_mode() const;
- void set_target_peer(int p_peer_id);
+ void set_target_peer(int p_target_peer);
int get_packet_peer() const;
int get_unique_id() const;
virtual bool is_server() const = 0;
@@ -93,11 +92,12 @@ public:
/* PacketPeer */
virtual int get_available_packet_count() const;
- virtual int get_max_packet_size() const;
+ virtual int get_max_packet_size() const = 0;
virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size);
virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size);
/* WebSocketPeer */
+ virtual Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) = 0;
virtual Ref<WebSocketPeer> get_peer(int p_peer_id) const = 0;
void _process_multiplayer(Ref<WebSocketPeer> p_peer, uint32_t p_peer_id);
diff --git a/modules/websocket/websocket_peer.cpp b/modules/websocket/websocket_peer.cpp
index 3ecb32ce19..15df1bf85a 100644
--- a/modules/websocket/websocket_peer.cpp
+++ b/modules/websocket/websocket_peer.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -27,6 +27,7 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+
#include "websocket_peer.h"
GDCINULL(WebSocketPeer);
diff --git a/modules/websocket/websocket_peer.h b/modules/websocket/websocket_peer.h
index 5918fda3c2..62c1f3e9a3 100644
--- a/modules/websocket/websocket_peer.h
+++ b/modules/websocket/websocket_peer.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -27,12 +27,12 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+
#ifndef WEBSOCKETPEER_H
#define WEBSOCKETPEER_H
#include "core/error_list.h"
#include "core/io/packet_peer.h"
-#include "core/ring_buffer.h"
#include "websocket_macros.h"
class WebSocketPeer : public PacketPeer {
diff --git a/modules/websocket/websocket_server.cpp b/modules/websocket/websocket_server.cpp
index c631ed70d5..ef5f6f5c20 100644
--- a/modules/websocket/websocket_server.cpp
+++ b/modules/websocket/websocket_server.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -27,6 +27,7 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+
#include "websocket_server.h"
GDCINULL(WebSocketServer);
diff --git a/modules/websocket/websocket_server.h b/modules/websocket/websocket_server.h
index 156f25897c..83c0c10419 100644
--- a/modules/websocket/websocket_server.h
+++ b/modules/websocket/websocket_server.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -27,11 +27,12 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+
#ifndef WEBSOCKET_H
#define WEBSOCKET_H
#include "core/reference.h"
-#include "websocket_multiplayer.h"
+#include "websocket_multiplayer_peer.h"
#include "websocket_peer.h"
class WebSocketServer : public WebSocketMultiplayerPeer {
@@ -61,6 +62,8 @@ 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);
+ virtual Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) = 0;
+
WebSocketServer();
~WebSocketServer();
};
diff --git a/modules/websocket/wsl_client.cpp b/modules/websocket/wsl_client.cpp
new file mode 100644
index 0000000000..0006a057e0
--- /dev/null
+++ b/modules/websocket/wsl_client.cpp
@@ -0,0 +1,344 @@
+/*************************************************************************/
+/* wsl_client.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef JAVASCRIPT_ENABLED
+
+#include "wsl_client.h"
+#include "core/io/ip.h"
+#include "core/project_settings.h"
+
+void WSLClient::_do_handshake() {
+ if (_requested < _request.size() - 1) {
+ int sent = 0;
+ Error err = _connection->put_partial_data(((const uint8_t *)_request.get_data() + _requested), _request.size() - _requested - 1, sent);
+ // Sending handshake failed
+ if (err != OK) {
+ disconnect_from_host();
+ _on_error();
+ return;
+ }
+ _requested += sent;
+
+ } else {
+ int read = 0;
+ while (true) {
+ if (_resp_pos >= WSL_MAX_HEADER_SIZE) {
+ // Header is too big
+ disconnect_from_host();
+ _on_error();
+ ERR_FAIL_MSG("Response headers too big.");
+ }
+ Error err = _connection->get_partial_data(&_resp_buf[_resp_pos], 1, read);
+ if (err == ERR_FILE_EOF) {
+ // We got a disconnect.
+ disconnect_from_host();
+ _on_error();
+ return;
+ } else if (err != OK) {
+ // Got some error.
+ disconnect_from_host();
+ _on_error();
+ return;
+ } else if (read != 1) {
+ // Busy, wait next poll.
+ break;
+ }
+ // Check "\r\n\r\n" header terminator
+ char *r = (char *)_resp_buf;
+ int l = _resp_pos;
+ if (l > 3 && r[l] == '\n' && r[l - 1] == '\r' && r[l - 2] == '\n' && r[l - 3] == '\r') {
+ r[l - 3] = '\0';
+ String protocol;
+ // Response is over, verify headers and create peer.
+ if (!_verify_headers(protocol)) {
+ disconnect_from_host();
+ _on_error();
+ ERR_FAIL_MSG("Invalid response headers.");
+ }
+ // Create peer.
+ WSLPeer::PeerData *data = memnew(struct WSLPeer::PeerData);
+ data->obj = this;
+ data->conn = _connection;
+ data->is_server = false;
+ data->id = 1;
+ _peer->make_context(data, _in_buf_size, _in_pkt_size, _out_buf_size, _out_pkt_size);
+ _on_connect(protocol);
+ break;
+ }
+ _resp_pos += 1;
+ }
+ }
+}
+
+bool WSLClient::_verify_headers(String &r_protocol) {
+ String s = (char *)_resp_buf;
+ Vector<String> psa = s.split("\r\n");
+ int len = psa.size();
+ ERR_FAIL_COND_V_MSG(len < 4, false, "Not enough response headers, got: " + itos(len) + ", expected >= 4.");
+
+ Vector<String> req = psa[0].split(" ", false);
+ ERR_FAIL_COND_V_MSG(req.size() < 2, false, "Invalid protocol or status code.");
+
+ // Wrong protocol
+ ERR_FAIL_COND_V_MSG(req[0] != "HTTP/1.1" || req[1] != "101", false, "Invalid protocol or status code.");
+
+ Map<String, String> headers;
+ for (int i = 1; i < len; i++) {
+ Vector<String> header = psa[i].split(":", false, 1);
+ ERR_FAIL_COND_V_MSG(header.size() != 2, false, "Invalid header -> " + psa[i] + ".");
+ String name = header[0].to_lower();
+ String value = header[1].strip_edges();
+ if (headers.has(name))
+ headers[name] += "," + value;
+ else
+ headers[name] = value;
+ }
+
+#define _WSL_CHECK(NAME, VALUE) \
+ ERR_FAIL_COND_V_MSG(!headers.has(NAME) || headers[NAME].to_lower() != VALUE, false, \
+ "Missing or invalid header '" + String(NAME) + "'. Expected value '" + VALUE + "'.");
+#define _WSL_CHECK_NC(NAME, VALUE) \
+ ERR_FAIL_COND_V_MSG(!headers.has(NAME) || headers[NAME] != VALUE, false, \
+ "Missing or invalid header '" + String(NAME) + "'. Expected value '" + VALUE + "'.");
+ _WSL_CHECK("connection", "upgrade");
+ _WSL_CHECK("upgrade", "websocket");
+ _WSL_CHECK_NC("sec-websocket-accept", WSLPeer::compute_key_response(_key));
+#undef _WSL_CHECK_NC
+#undef _WSL_CHECK
+ if (_protocols.size() == 0) {
+ // We didn't request a custom protocol
+ ERR_FAIL_COND_V(headers.has("sec-websocket-protocol"), false);
+ } else {
+ ERR_FAIL_COND_V(!headers.has("sec-websocket-protocol"), false);
+ r_protocol = headers["sec-websocket-protocol"];
+ bool valid = false;
+ for (int i = 0; i < _protocols.size(); i++) {
+ if (_protocols[i] != r_protocol)
+ continue;
+ valid = true;
+ break;
+ }
+ if (!valid)
+ return false;
+ }
+ return true;
+}
+
+Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocols) {
+
+ ERR_FAIL_COND_V(_connection.is_valid(), ERR_ALREADY_IN_USE);
+
+ _peer = Ref<WSLPeer>(memnew(WSLPeer));
+ IP_Address addr;
+
+ if (!p_host.is_valid_ip_address()) {
+ addr = IP::get_singleton()->resolve_hostname(p_host);
+ } else {
+ addr = p_host;
+ }
+
+ ERR_FAIL_COND_V(!addr.is_valid(), ERR_INVALID_PARAMETER);
+
+ String port = "";
+ if ((p_port != 80 && !p_ssl) || (p_port != 443 && p_ssl)) {
+ port = ":" + itos(p_port);
+ }
+
+ Error err = _tcp->connect_to_host(addr, p_port);
+ if (err != OK) {
+ _tcp->disconnect_from_host();
+ _on_error();
+ return err;
+ }
+ _connection = _tcp;
+ _use_ssl = p_ssl;
+ _host = p_host;
+ _protocols = p_protocols;
+
+ _key = WSLPeer::generate_key();
+ // TODO custom extra headers (allow overriding this too?)
+ String request = "GET " + p_path + " HTTP/1.1\r\n";
+ request += "Host: " + p_host + port + "\r\n";
+ request += "Upgrade: websocket\r\n";
+ request += "Connection: Upgrade\r\n";
+ request += "Sec-WebSocket-Key: " + _key + "\r\n";
+ request += "Sec-WebSocket-Version: 13\r\n";
+ if (p_protocols.size() > 0) {
+ request += "Sec-WebSocket-Protocol: ";
+ for (int i = 0; i < p_protocols.size(); i++) {
+ if (i != 0)
+ request += ",";
+ request += p_protocols[i];
+ }
+ request += "\r\n";
+ }
+ request += "\r\n";
+ _request = request.utf8();
+
+ return OK;
+}
+
+int WSLClient::get_max_packet_size() const {
+ return (1 << _out_buf_size) - PROTO_SIZE;
+}
+
+void WSLClient::poll() {
+ if (_peer->is_connected_to_host()) {
+ _peer->poll();
+ if (!_peer->is_connected_to_host()) {
+ disconnect_from_host();
+ _on_disconnect(_peer->close_code != -1);
+ }
+ return;
+ }
+
+ if (_connection.is_null())
+ return; // Not connected.
+
+ switch (_tcp->get_status()) {
+ case StreamPeerTCP::STATUS_NONE:
+ // Clean close
+ disconnect_from_host();
+ _on_error();
+ break;
+ case StreamPeerTCP::STATUS_CONNECTED: {
+ Ref<StreamPeerSSL> ssl;
+ if (_use_ssl) {
+ if (_connection == _tcp) {
+ // Start SSL handshake
+ 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) {
+ disconnect_from_host();
+ _on_error();
+ return;
+ }
+ _connection = ssl;
+ } else {
+ ssl = static_cast<Ref<StreamPeerSSL> >(_connection);
+ ERR_FAIL_COND(ssl.is_null()); // Bug?
+ ssl->poll();
+ }
+ if (ssl->get_status() == StreamPeerSSL::STATUS_HANDSHAKING)
+ return; // Need more polling.
+ else if (ssl->get_status() != StreamPeerSSL::STATUS_CONNECTED) {
+ disconnect_from_host();
+ _on_error();
+ return; // Error.
+ }
+ }
+ // Do websocket handshake.
+ _do_handshake();
+ } break;
+ case StreamPeerTCP::STATUS_ERROR:
+ disconnect_from_host();
+ _on_error();
+ break;
+ case StreamPeerTCP::STATUS_CONNECTING:
+ break; // Wait for connection
+ }
+}
+
+Ref<WebSocketPeer> WSLClient::get_peer(int p_peer_id) const {
+
+ ERR_FAIL_COND_V(p_peer_id != 1, NULL);
+
+ return _peer;
+}
+
+NetworkedMultiplayerPeer::ConnectionStatus WSLClient::get_connection_status() const {
+
+ if (_peer->is_connected_to_host())
+ return CONNECTION_CONNECTED;
+
+ if (_tcp->is_connected_to_host())
+ return CONNECTION_CONNECTING;
+
+ return CONNECTION_DISCONNECTED;
+}
+
+void WSLClient::disconnect_from_host(int p_code, String p_reason) {
+
+ _peer->close(p_code, p_reason);
+ _connection = Ref<StreamPeer>(NULL);
+ _tcp = Ref<StreamPeerTCP>(memnew(StreamPeerTCP));
+
+ _key = "";
+ _host = "";
+ _protocols.resize(0);
+ _use_ssl = false;
+
+ _request = "";
+ _requested = 0;
+
+ memset(_resp_buf, 0, sizeof(_resp_buf));
+ _resp_pos = 0;
+}
+
+IP_Address WSLClient::get_connected_host() const {
+
+ return IP_Address();
+}
+
+uint16_t WSLClient::get_connected_port() const {
+
+ return 1025;
+}
+
+Error WSLClient::set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) {
+ ERR_FAIL_COND_V_MSG(_connection.is_valid(), FAILED, "Buffers sizes can only be set before listening or connecting.");
+
+ _in_buf_size = nearest_shift(p_in_buffer - 1) + 10;
+ _in_pkt_size = nearest_shift(p_in_packets - 1);
+ _out_buf_size = nearest_shift(p_out_buffer - 1) + 10;
+ _out_pkt_size = nearest_shift(p_out_packets - 1);
+ return OK;
+}
+
+WSLClient::WSLClient() {
+ _in_buf_size = nearest_shift((int)GLOBAL_GET(WSC_IN_BUF) - 1) + 10;
+ _in_pkt_size = nearest_shift((int)GLOBAL_GET(WSC_IN_PKT) - 1);
+ _out_buf_size = nearest_shift((int)GLOBAL_GET(WSC_OUT_BUF) - 1) + 10;
+ _out_pkt_size = nearest_shift((int)GLOBAL_GET(WSC_OUT_PKT) - 1);
+
+ _peer.instance();
+ _tcp.instance();
+ disconnect_from_host();
+}
+
+WSLClient::~WSLClient() {
+
+ _peer->close_now();
+ _peer->invalidate();
+ disconnect_from_host();
+}
+
+#endif // JAVASCRIPT_ENABLED
diff --git a/modules/websocket/lws_client.h b/modules/websocket/wsl_client.h
index 1bbc19f352..57dfd635b7 100644
--- a/modules/websocket/lws_client.h
+++ b/modules/websocket/wsl_client.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* lws_client.h */
+/* wsl_client.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -27,24 +27,53 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef LWSCLIENT_H
-#define LWSCLIENT_H
+
+#ifndef WSLCLIENT_H
+#define WSLCLIENT_H
#ifndef JAVASCRIPT_ENABLED
#include "core/error_list.h"
-#include "lws_helper.h"
-#include "lws_peer.h"
+#include "core/io/stream_peer_ssl.h"
+#include "core/io/stream_peer_tcp.h"
#include "websocket_client.h"
+#include "wsl_peer.h"
+#include "wslay/wslay.h"
+
+class WSLClient : public WebSocketClient {
+
+ GDCIIMPL(WSLClient, WebSocketClient);
+
+private:
+ int _in_buf_size;
+ int _in_pkt_size;
+ int _out_buf_size;
+ int _out_pkt_size;
+
+ Ref<WSLPeer> _peer;
+ Ref<StreamPeerTCP> _tcp;
+ Ref<StreamPeer> _connection;
+
+ CharString _request;
+ int _requested;
+
+ uint8_t _resp_buf[WSL_MAX_HEADER_SIZE];
+ int _resp_pos;
-class LWSClient : public WebSocketClient {
+ String _response;
- GDCIIMPL(LWSClient, WebSocketClient);
+ String _key;
+ String _host;
+ PoolVector<String> _protocols;
+ bool _use_ssl;
- LWS_HELPER(LWSClient);
+ void _do_handshake();
+ bool _verify_headers(String &r_protocol);
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>());
+ 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 = "");
IP_Address get_connected_host() const;
@@ -52,10 +81,10 @@ public:
virtual ConnectionStatus get_connection_status() const;
virtual void poll();
- LWSClient();
- ~LWSClient();
+ WSLClient();
+ ~WSLClient();
};
#endif // JAVASCRIPT_ENABLED
-#endif // LWSCLIENT_H
+#endif // WSLCLIENT_H
diff --git a/modules/websocket/wsl_peer.cpp b/modules/websocket/wsl_peer.cpp
new file mode 100644
index 0000000000..74fb901232
--- /dev/null
+++ b/modules/websocket/wsl_peer.cpp
@@ -0,0 +1,339 @@
+/*************************************************************************/
+/* wsl_peer.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef JAVASCRIPT_ENABLED
+
+#include "wsl_peer.h"
+
+#include "wsl_client.h"
+#include "wsl_server.h"
+
+#include "core/crypto/crypto_core.h"
+#include "core/math/random_number_generator.h"
+#include "core/os/os.h"
+
+String WSLPeer::generate_key() {
+ // Random key
+ RandomNumberGenerator rng;
+ rng.set_seed(OS::get_singleton()->get_unix_time());
+ PoolVector<uint8_t> bkey;
+ int len = 16; // 16 bytes, as per RFC
+ bkey.resize(len);
+ PoolVector<uint8_t>::Write w = bkey.write();
+ for (int i = 0; i < len; i++) {
+ w[i] = (uint8_t)rng.randi_range(0, 255);
+ }
+ return CryptoCore::b64_encode_str(&w[0], len);
+}
+
+String WSLPeer::compute_key_response(String p_key) {
+ String key = p_key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; // Magic UUID as per RFC
+ Vector<uint8_t> sha = key.sha1_buffer();
+ return CryptoCore::b64_encode_str(sha.ptr(), sha.size());
+}
+
+void WSLPeer::_wsl_destroy(struct PeerData **p_data) {
+ if (!p_data || !(*p_data))
+ return;
+ struct PeerData *data = *p_data;
+ if (data->polling) {
+ data->destroy = true;
+ return;
+ }
+ wslay_event_context_free(data->ctx);
+ memdelete(data);
+ *p_data = NULL;
+}
+
+bool WSLPeer::_wsl_poll(struct PeerData *p_data) {
+ p_data->polling = true;
+ int err = 0;
+ if ((err = wslay_event_recv(p_data->ctx)) != 0 || (err = wslay_event_send(p_data->ctx)) != 0) {
+ print_verbose("Websocket (wslay) poll error: " + itos(err));
+ p_data->destroy = true;
+ }
+ p_data->polling = false;
+
+ if (p_data->destroy || (wslay_event_get_close_sent(p_data->ctx) && wslay_event_get_close_received(p_data->ctx))) {
+ bool valid = p_data->valid;
+ _wsl_destroy(&p_data);
+ return valid;
+ }
+ return false;
+}
+
+ssize_t wsl_recv_callback(wslay_event_context_ptr ctx, uint8_t *data, size_t len, int flags, void *user_data) {
+ struct WSLPeer::PeerData *peer_data = (struct WSLPeer::PeerData *)user_data;
+ if (!peer_data->valid) {
+ wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE);
+ return -1;
+ }
+ Ref<StreamPeer> conn = peer_data->conn;
+ int read = 0;
+ Error err = conn->get_partial_data(data, len, read);
+ if (err != OK) {
+ print_verbose("Websocket get data error: " + itos(err) + ", read (should be 0!): " + itos(read));
+ wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE);
+ return -1;
+ }
+ if (read == 0) {
+ wslay_event_set_error(ctx, WSLAY_ERR_WOULDBLOCK);
+ return -1;
+ }
+ return read;
+}
+
+ssize_t wsl_send_callback(wslay_event_context_ptr ctx, const uint8_t *data, size_t len, int flags, void *user_data) {
+ struct WSLPeer::PeerData *peer_data = (struct WSLPeer::PeerData *)user_data;
+ if (!peer_data->valid) {
+ wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE);
+ return -1;
+ }
+ Ref<StreamPeer> conn = peer_data->conn;
+ int sent = 0;
+ Error err = conn->put_partial_data(data, len, sent);
+ if (err != OK) {
+ wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE);
+ return -1;
+ }
+ if (sent == 0) {
+ wslay_event_set_error(ctx, WSLAY_ERR_WOULDBLOCK);
+ return -1;
+ }
+ return sent;
+}
+
+int wsl_genmask_callback(wslay_event_context_ptr ctx, uint8_t *buf, size_t len, void *user_data) {
+ RandomNumberGenerator rng;
+ // TODO maybe use crypto in the future?
+ rng.set_seed(OS::get_singleton()->get_unix_time());
+ for (unsigned int i = 0; i < len; i++) {
+ buf[i] = (uint8_t)rng.randi_range(0, 255);
+ }
+ return 0;
+}
+
+void wsl_msg_recv_callback(wslay_event_context_ptr ctx, const struct wslay_event_on_msg_recv_arg *arg, void *user_data) {
+ struct WSLPeer::PeerData *peer_data = (struct WSLPeer::PeerData *)user_data;
+ if (!peer_data->valid) {
+ return;
+ }
+ WSLPeer *peer = (WSLPeer *)peer_data->peer;
+
+ if (peer->parse_message(arg) != OK)
+ return;
+
+ if (peer_data->is_server) {
+ WSLServer *helper = (WSLServer *)peer_data->obj;
+ helper->_on_peer_packet(peer_data->id);
+ } else {
+ WSLClient *helper = (WSLClient *)peer_data->obj;
+ helper->_on_peer_packet();
+ }
+}
+
+wslay_event_callbacks wsl_callbacks = {
+ wsl_recv_callback,
+ wsl_send_callback,
+ wsl_genmask_callback,
+ NULL, /* on_frame_recv_start_callback */
+ NULL, /* on_frame_recv_callback */
+ NULL, /* on_frame_recv_end_callback */
+ wsl_msg_recv_callback
+};
+
+Error WSLPeer::parse_message(const wslay_event_on_msg_recv_arg *arg) {
+ uint8_t is_string = 0;
+ if (arg->opcode == WSLAY_TEXT_FRAME) {
+ is_string = 1;
+ } else if (arg->opcode == WSLAY_CONNECTION_CLOSE) {
+ close_code = arg->status_code;
+ size_t len = arg->msg_length;
+ close_reason = "";
+ if (len > 2 /* first 2 bytes = close code */) {
+ close_reason.parse_utf8((char *)arg->msg + 2, len - 2);
+ }
+ if (!wslay_event_get_close_sent(_data->ctx)) {
+ if (_data->is_server) {
+ WSLServer *helper = (WSLServer *)_data->obj;
+ helper->_on_close_request(_data->id, close_code, close_reason);
+ } else {
+ WSLClient *helper = (WSLClient *)_data->obj;
+ helper->_on_close_request(close_code, close_reason);
+ }
+ }
+ return ERR_FILE_EOF;
+ } else if (arg->opcode != WSLAY_BINARY_FRAME) {
+ // Ping or pong
+ return ERR_SKIP;
+ }
+ _in_buffer.write_packet(arg->msg, arg->msg_length, &is_string);
+ return OK;
+}
+
+void WSLPeer::make_context(PeerData *p_data, unsigned int p_in_buf_size, unsigned int p_in_pkt_size, unsigned int p_out_buf_size, unsigned int p_out_pkt_size) {
+ ERR_FAIL_COND(_data != NULL);
+ ERR_FAIL_COND(p_data == NULL);
+
+ _in_buffer.resize(p_in_pkt_size, p_in_buf_size);
+ _packet_buffer.resize((1 << MAX(p_in_buf_size, p_out_buf_size)));
+
+ _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);
+ else
+ wslay_event_context_client_init(&(_data->ctx), &wsl_callbacks, _data);
+ wslay_event_config_set_max_recv_msg_length(_data->ctx, (1 << p_in_buf_size));
+}
+
+void WSLPeer::set_write_mode(WriteMode p_mode) {
+ write_mode = p_mode;
+}
+
+WSLPeer::WriteMode WSLPeer::get_write_mode() const {
+ return write_mode;
+}
+
+void WSLPeer::poll() {
+ if (!_data)
+ return;
+
+ if (_wsl_poll(_data)) {
+ _data = NULL;
+ }
+}
+
+Error WSLPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
+
+ ERR_FAIL_COND_V(!is_connected_to_host(), FAILED);
+
+ struct wslay_event_msg msg; // Should I use fragmented?
+ msg.opcode = write_mode == WRITE_MODE_TEXT ? WSLAY_TEXT_FRAME : WSLAY_BINARY_FRAME;
+ msg.msg = p_buffer;
+ msg.msg_length = p_buffer_size;
+
+ wslay_event_queue_msg(_data->ctx, &msg);
+ return OK;
+}
+
+Error WSLPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
+
+ r_buffer_size = 0;
+
+ ERR_FAIL_COND_V(!is_connected_to_host(), FAILED);
+
+ if (_in_buffer.packets_left() == 0)
+ return ERR_UNAVAILABLE;
+
+ int read = 0;
+ PoolVector<uint8_t>::Write rw = _packet_buffer.write();
+ _in_buffer.read_packet(rw.ptr(), _packet_buffer.size(), &_is_string, read);
+
+ *r_buffer = rw.ptr();
+ r_buffer_size = read;
+
+ return OK;
+}
+
+int WSLPeer::get_available_packet_count() const {
+
+ if (!is_connected_to_host())
+ return 0;
+
+ return _in_buffer.packets_left();
+}
+
+bool WSLPeer::was_string_packet() const {
+
+ return _is_string;
+}
+
+bool WSLPeer::is_connected_to_host() const {
+
+ return _data != NULL;
+}
+
+void WSLPeer::close_now() {
+ close(1000, "");
+ _wsl_destroy(&_data);
+}
+
+void WSLPeer::close(int p_code, String p_reason) {
+ if (_data && !wslay_event_get_close_sent(_data->ctx)) {
+ CharString cs = p_reason.utf8();
+ wslay_event_queue_close(_data->ctx, p_code, (uint8_t *)cs.ptr(), cs.size());
+ wslay_event_send(_data->ctx);
+ }
+
+ _in_buffer.clear();
+ _packet_buffer.resize(0);
+}
+
+IP_Address WSLPeer::get_connected_host() const {
+
+ ERR_FAIL_COND_V(!is_connected_to_host(), IP_Address());
+
+ IP_Address ip;
+ return ip;
+}
+
+uint16_t WSLPeer::get_connected_port() const {
+
+ ERR_FAIL_COND_V(!is_connected_to_host(), 0);
+
+ uint16_t port = 0;
+ return port;
+}
+
+void WSLPeer::invalidate() {
+ if (_data)
+ _data->valid = false;
+}
+
+WSLPeer::WSLPeer() {
+ _data = NULL;
+ _is_string = 0;
+ close_code = -1;
+ write_mode = WRITE_MODE_BINARY;
+}
+
+WSLPeer::~WSLPeer() {
+
+ close();
+ invalidate();
+ _wsl_destroy(&_data);
+ _data = NULL;
+}
+
+#endif // JAVASCRIPT_ENABLED
diff --git a/modules/websocket/lws_peer.h b/modules/websocket/wsl_peer.h
index 571445db01..d51b304fe1 100644
--- a/modules/websocket/lws_peer.h
+++ b/modules/websocket/wsl_peer.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* lws_peer.h */
+/* wsl_peer.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -27,54 +27,77 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef LWSPEER_H
-#define LWSPEER_H
+
+#ifndef WSLPEER_H
+#define WSLPEER_H
#ifndef JAVASCRIPT_ENABLED
#include "core/error_list.h"
#include "core/io/packet_peer.h"
#include "core/ring_buffer.h"
-#include "libwebsockets.h"
-#include "lws_config.h"
+#include "packet_buffer.h"
#include "websocket_peer.h"
+#include "wslay/wslay.h"
-class LWSPeer : public WebSocketPeer {
+#define WSL_MAX_HEADER_SIZE 4096
- GDCIIMPL(LWSPeer, WebSocketPeer);
+class WSLPeer : public WebSocketPeer {
-private:
- enum {
- PACKET_BUFFER_SIZE = 65536 - 5 // 4 bytes for the size, 1 for the type
+ GDCIIMPL(WSLPeer, WebSocketPeer);
+
+public:
+ struct PeerData {
+ bool polling;
+ bool destroy;
+ bool valid;
+ bool is_server;
+ void *obj;
+ void *peer;
+ Ref<StreamPeer> conn;
+ int id;
+ wslay_event_context_ptr ctx;
+
+ PeerData() {
+ polling = false;
+ destroy = false;
+ valid = false;
+ is_server = false;
+ id = 1;
+ ctx = NULL;
+ obj = NULL;
+ peer = NULL;
+ }
};
- uint8_t packet_buffer[PACKET_BUFFER_SIZE];
- struct lws *wsi;
+ static String compute_key_response(String p_key);
+ static String generate_key();
+
+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.
+ PacketBuffer<uint8_t> _in_buffer;
+
+ PoolVector<uint8_t> _packet_buffer;
+
WriteMode write_mode;
- bool _was_string;
+public:
int close_code;
String close_reason;
-
-public:
- struct PeerData {
- uint32_t peer_id;
- bool force_close;
- bool clean_close;
- };
-
- RingBuffer<uint8_t> rbw;
- RingBuffer<uint8_t> rbr;
- uint8_t input_buffer[PACKET_BUFFER_SIZE];
- uint32_t in_size;
- int in_count;
- int out_count;
+ void poll(); // Used by client and server.
virtual int get_available_packet_count() const;
virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size);
virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size);
- virtual int get_max_packet_size() const { return PACKET_BUFFER_SIZE; };
+ virtual int get_max_packet_size() const { return _packet_buffer.size(); };
+ virtual void close_now();
virtual void close(int p_code = 1000, String p_reason = "");
virtual bool is_connected_to_host() const;
virtual IP_Address get_connected_host() const;
@@ -84,14 +107,12 @@ public:
virtual void set_write_mode(WriteMode p_mode);
virtual bool was_string_packet() const;
- void set_wsi(struct lws *wsi);
- Error read_wsi(void *in, size_t len);
- Error write_wsi();
- void send_close_status(struct lws *wsi);
- String get_close_reason(void *in, size_t len, int &r_code);
+ void make_context(PeerData *p_data, unsigned int p_in_buf_size, unsigned int p_in_pkt_size, unsigned int p_out_buf_size, unsigned int p_out_pkt_size);
+ Error parse_message(const wslay_event_on_msg_recv_arg *arg);
+ void invalidate();
- LWSPeer();
- ~LWSPeer();
+ WSLPeer();
+ ~WSLPeer();
};
#endif // JAVASCRIPT_ENABLED
diff --git a/modules/websocket/wsl_server.cpp b/modules/websocket/wsl_server.cpp
new file mode 100644
index 0000000000..efb526eed1
--- /dev/null
+++ b/modules/websocket/wsl_server.cpp
@@ -0,0 +1,285 @@
+/*************************************************************************/
+/* wsl_server.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef JAVASCRIPT_ENABLED
+
+#include "wsl_server.h"
+#include "core/os/os.h"
+#include "core/project_settings.h"
+
+WSLServer::PendingPeer::PendingPeer() {
+ time = 0;
+ has_request = false;
+ response_sent = 0;
+ req_pos = 0;
+ memset(req_buf, 0, sizeof(req_buf));
+}
+
+bool WSLServer::PendingPeer::_parse_request(const PoolStringArray 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.");
+
+ Vector<String> req = psa[0].split(" ", false);
+ ERR_FAIL_COND_V_MSG(req.size() < 2, false, "Invalid protocol or status code.");
+
+ // Wrong protocol
+ ERR_FAIL_COND_V_MSG(req[0] != "GET" || req[2] != "HTTP/1.1", false, "Invalid method or HTTP version.");
+
+ Map<String, String> headers;
+ for (int i = 1; i < len; i++) {
+ Vector<String> header = psa[i].split(":", false, 1);
+ ERR_FAIL_COND_V_MSG(header.size() != 2, false, "Invalid header -> " + psa[i]);
+ String name = header[0].to_lower();
+ String value = header[1].strip_edges();
+ if (headers.has(name))
+ headers[name] += "," + value;
+ else
+ headers[name] = value;
+ }
+#define _WSL_CHECK(NAME, VALUE) \
+ ERR_FAIL_COND_V_MSG(!headers.has(NAME) || headers[NAME].to_lower() != VALUE, false, \
+ "Missing or invalid header '" + String(NAME) + "'. Expected value '" + VALUE + "'.");
+#define _WSL_CHECK_EX(NAME) \
+ ERR_FAIL_COND_V_MSG(!headers.has(NAME), false, "Missing header '" + String(NAME) + "'.");
+ _WSL_CHECK("upgrade", "websocket");
+ _WSL_CHECK("sec-websocket-version", "13");
+ _WSL_CHECK_EX("sec-websocket-key");
+ _WSL_CHECK_EX("connection");
+#undef _WSL_CHECK_EX
+#undef _WSL_CHECK
+ key = headers["sec-websocket-key"];
+ if (headers.has("sec-websocket-protocol")) {
+ Vector<String> protos = headers["sec-websocket-protocol"].split(",");
+ for (int i = 0; i < protos.size(); i++) {
+ // Check if we have the given protocol
+ for (int j = 0; j < p_protocols.size(); j++) {
+ if (protos[i] != p_protocols[j])
+ continue;
+ protocol = protos[i];
+ break;
+ }
+ // Found a protocol
+ if (protocol != "")
+ break;
+ }
+ if (protocol == "") // Invalid protocol(s) requested
+ return false;
+ } else if (p_protocols.size() > 0) // No protocol requested, but we need one
+ return false;
+ return true;
+}
+
+Error WSLServer::PendingPeer::do_handshake(PoolStringArray p_protocols) {
+ if (OS::get_singleton()->get_ticks_msec() - time > WSL_SERVER_TIMEOUT)
+ return ERR_TIMEOUT;
+ if (!has_request) {
+ int read = 0;
+ while (true) {
+ ERR_FAIL_COND_V_MSG(req_pos >= WSL_MAX_HEADER_SIZE, ERR_OUT_OF_MEMORY, "Response headers too big.");
+ Error err = connection->get_partial_data(&req_buf[req_pos], 1, read);
+ if (err != OK) // Got an error
+ return FAILED;
+ else if (read != 1) // Busy, wait next poll
+ return ERR_BUSY;
+ char *r = (char *)req_buf;
+ int l = req_pos;
+ if (l > 3 && r[l] == '\n' && r[l - 1] == '\r' && r[l - 2] == '\n' && r[l - 3] == '\r') {
+ r[l - 3] = '\0';
+ if (!_parse_request(p_protocols)) {
+ return FAILED;
+ }
+ String s = "HTTP/1.1 101 Switching Protocols\r\n";
+ s += "Upgrade: websocket\r\n";
+ s += "Connection: Upgrade\r\n";
+ s += "Sec-WebSocket-Accept: " + WSLPeer::compute_key_response(key) + "\r\n";
+ if (protocol != "")
+ s += "Sec-WebSocket-Protocol: " + protocol + "\r\n";
+ s += "\r\n";
+ response = s.utf8();
+ has_request = true;
+ break;
+ }
+ req_pos += 1;
+ }
+ }
+ if (has_request && response_sent < response.size() - 1) {
+ int sent = 0;
+ Error err = connection->put_partial_data((const uint8_t *)response.get_data() + response_sent, response.size() - response_sent - 1, sent);
+ if (err != OK) {
+ return err;
+ }
+ response_sent += sent;
+ }
+ if (response_sent < response.size() - 1)
+ return ERR_BUSY;
+ return OK;
+}
+
+Error WSLServer::listen(int p_port, PoolVector<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;
+ _server->listen(p_port);
+
+ return OK;
+}
+
+void WSLServer::poll() {
+
+ List<int> remove_ids;
+ for (Map<int, Ref<WebSocketPeer> >::Element *E = _peer_map.front(); E; E = E->next()) {
+ Ref<WSLPeer> peer = (WSLPeer *)E->get().ptr();
+ peer->poll();
+ if (!peer->is_connected_to_host()) {
+ _on_disconnect(E->key(), peer->close_code != -1);
+ remove_ids.push_back(E->key());
+ }
+ }
+ for (List<int>::Element *E = remove_ids.front(); E; E = E->next()) {
+ _peer_map.erase(E->get());
+ }
+ remove_ids.clear();
+
+ List<Ref<PendingPeer> > remove_peers;
+ for (List<Ref<PendingPeer> >::Element *E = _pending.front(); E; E = E->next()) {
+ Ref<PendingPeer> ppeer = E->get();
+ Error err = ppeer->do_handshake(_protocols);
+ if (err == ERR_BUSY) {
+ continue;
+ } else if (err != OK) {
+ remove_peers.push_back(ppeer);
+ continue;
+ }
+ // Creating new peer
+ int32_t id = _gen_unique_id();
+
+ WSLPeer::PeerData *data = memnew(struct WSLPeer::PeerData);
+ data->obj = this;
+ data->conn = ppeer->connection;
+ data->is_server = true;
+ data->id = id;
+
+ Ref<WSLPeer> ws_peer = memnew(WSLPeer);
+ ws_peer->make_context(data, _in_buf_size, _in_pkt_size, _out_buf_size, _out_pkt_size);
+
+ _peer_map[id] = ws_peer;
+ remove_peers.push_back(ppeer);
+ _on_connect(id, ppeer->protocol);
+ }
+ for (List<Ref<PendingPeer> >::Element *E = remove_peers.front(); E; E = E->next()) {
+ _pending.erase(E->get());
+ }
+ remove_peers.clear();
+
+ if (!_server->is_listening())
+ return;
+
+ while (_server->is_connection_available()) {
+ Ref<StreamPeer> 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;
+ peer->time = OS::get_singleton()->get_ticks_msec();
+ _pending.push_back(peer);
+ }
+}
+
+bool WSLServer::is_listening() const {
+ return _server->is_listening();
+}
+
+int WSLServer::get_max_packet_size() const {
+ return (1 << _out_buf_size) - PROTO_SIZE;
+}
+
+void WSLServer::stop() {
+ _server->stop();
+ for (Map<int, Ref<WebSocketPeer> >::Element *E = _peer_map.front(); E; E = E->next()) {
+ Ref<WSLPeer> peer = (WSLPeer *)E->get().ptr();
+ peer->close_now();
+ }
+ _pending.clear();
+ _peer_map.clear();
+}
+
+bool WSLServer::has_peer(int p_id) const {
+ return _peer_map.has(p_id);
+}
+
+Ref<WebSocketPeer> WSLServer::get_peer(int p_id) const {
+ ERR_FAIL_COND_V(!has_peer(p_id), NULL);
+ return _peer_map[p_id];
+}
+
+IP_Address WSLServer::get_peer_address(int p_peer_id) const {
+ ERR_FAIL_COND_V(!has_peer(p_peer_id), IP_Address());
+
+ return _peer_map[p_peer_id]->get_connected_host();
+}
+
+int WSLServer::get_peer_port(int p_peer_id) const {
+ ERR_FAIL_COND_V(!has_peer(p_peer_id), 0);
+
+ return _peer_map[p_peer_id]->get_connected_port();
+}
+
+void WSLServer::disconnect_peer(int p_peer_id, int p_code, String p_reason) {
+ ERR_FAIL_COND(!has_peer(p_peer_id));
+
+ get_peer(p_peer_id)->close(p_code, p_reason);
+}
+
+Error WSLServer::set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) {
+ ERR_FAIL_COND_V_MSG(_server->is_listening(), FAILED, "Buffers sizes can only be set before listening or connecting.");
+
+ _in_buf_size = nearest_shift(p_in_buffer - 1) + 10;
+ _in_pkt_size = nearest_shift(p_in_packets - 1);
+ _out_buf_size = nearest_shift(p_out_buffer - 1) + 10;
+ _out_pkt_size = nearest_shift(p_out_packets - 1);
+ return OK;
+}
+
+WSLServer::WSLServer() {
+ _in_buf_size = nearest_shift((int)GLOBAL_GET(WSS_IN_BUF) - 1) + 10;
+ _in_pkt_size = nearest_shift((int)GLOBAL_GET(WSS_IN_PKT) - 1);
+ _out_buf_size = nearest_shift((int)GLOBAL_GET(WSS_OUT_BUF) - 1) + 10;
+ _out_pkt_size = nearest_shift((int)GLOBAL_GET(WSS_OUT_PKT) - 1);
+ _server.instance();
+}
+
+WSLServer::~WSLServer() {
+ stop();
+}
+
+#endif // JAVASCRIPT_ENABLED
diff --git a/modules/websocket/lws_server.h b/modules/websocket/wsl_server.h
index 346773ebc4..2ceb941073 100644
--- a/modules/websocket/lws_server.h
+++ b/modules/websocket/wsl_server.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* lws_server.h */
+/* wsl_server.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -27,40 +27,73 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef LWSSERVER_H
-#define LWSSERVER_H
+
+#ifndef WSLSERVER_H
+#define WSLSERVER_H
#ifndef JAVASCRIPT_ENABLED
-#include "core/reference.h"
-#include "lws_helper.h"
-#include "lws_peer.h"
#include "websocket_server.h"
+#include "wsl_peer.h"
+
+#include "core/io/stream_peer_tcp.h"
+#include "core/io/tcp_server.h"
-class LWSServer : public WebSocketServer {
+#define WSL_SERVER_TIMEOUT 1000
- GDCIIMPL(LWSServer, WebSocketServer);
+class WSLServer : public WebSocketServer {
- LWS_HELPER(LWSServer);
+ GDCIIMPL(WSLServer, WebSocketServer);
private:
- Map<int, Ref<LWSPeer> > peer_map;
+ class PendingPeer : public Reference {
+
+ private:
+ bool _parse_request(const PoolStringArray p_protocols);
+
+ public:
+ Ref<StreamPeer> connection;
+
+ int time;
+ uint8_t req_buf[WSL_MAX_HEADER_SIZE];
+ int req_pos;
+ String key;
+ String protocol;
+ bool has_request;
+ CharString response;
+ int response_sent;
+
+ PendingPeer();
+
+ Error do_handshake(const PoolStringArray p_protocols);
+ };
+
+ int _in_buf_size;
+ int _in_pkt_size;
+ int _out_buf_size;
+ int _out_pkt_size;
+
+ List<Ref<PendingPeer> > _pending;
+ Ref<TCP_Server> _server;
+ PoolStringArray _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);
void stop();
bool is_listening() const;
+ int get_max_packet_size() const;
bool has_peer(int p_id) const;
Ref<WebSocketPeer> get_peer(int p_id) const;
IP_Address get_peer_address(int p_peer_id) const;
int get_peer_port(int p_peer_id) const;
void disconnect_peer(int p_peer_id, int p_code = 1000, String p_reason = "");
- virtual void poll() { _lws_poll(); }
+ virtual void poll();
- LWSServer();
- ~LWSServer();
+ WSLServer();
+ ~WSLServer();
};
#endif // JAVASCRIPT_ENABLED
-#endif // LWSSERVER_H
+#endif // WSLSERVER_H