diff options
Diffstat (limited to 'modules/websocket')
30 files changed, 842 insertions, 494 deletions
diff --git a/modules/websocket/SCsub b/modules/websocket/SCsub index c0985b3245..0345e533bc 100644 --- a/modules/websocket/SCsub +++ b/modules/websocket/SCsub @@ -7,88 +7,92 @@ Import('env_modules') env_lws = env_modules.Clone() -if env['builtin_libwebsockets']: +if env['builtin_libwebsockets'] and not env["platform"] == "javascript": # already builtin for javascript thirdparty_dir = "#thirdparty/libwebsockets/" - helper_dir = "win32helpers/" + helper_dir = "#thirdparty/libwebsockets/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" + "lib/core/adopt.c", + "lib/core/alloc.c", + "lib/core/connect.c", + "lib/core/context.c", + "lib/core/dummy-callback.c", + "lib/core/libwebsockets.c", + "lib/core/output.c", + "lib/core/pollfd.c", + "lib/core/service.c", + + "lib/event-libs/poll/poll.c", + + "lib/misc/base64-decode.c", + "lib/misc/lejp.c", + "lib/misc/sha-1.c", + + "lib/roles/h1/ops-h1.c", + "lib/roles/http/header.c", + "lib/roles/http/client/client.c", + "lib/roles/http/client/client-handshake.c", + "lib/roles/http/server/fops-zip.c", + "lib/roles/http/server/lejp-conf.c", + "lib/roles/http/server/parsers.c", + "lib/roles/http/server/server.c", + "lib/roles/listen/ops-listen.c", + "lib/roles/pipe/ops-pipe.c", + "lib/roles/raw-skt/ops-raw-skt.c", + "lib/roles/raw-file/ops-raw-file.c", + + "lib/roles/ws/client-ws.c", + "lib/roles/ws/client-parser-ws.c", + "lib/roles/ws/ops-ws.c", + "lib/roles/ws/server-ws.c", + + "lib/tls/tls.c", + "lib/tls/tls-client.c", + "lib/tls/tls-server.c", + + "lib/tls/mbedtls/wrapper/library/ssl_cert.c", + "lib/tls/mbedtls/wrapper/library/ssl_pkey.c", + "lib/tls/mbedtls/wrapper/library/ssl_stack.c", + "lib/tls/mbedtls/wrapper/library/ssl_methods.c", + "lib/tls/mbedtls/wrapper/library/ssl_lib.c", + "lib/tls/mbedtls/wrapper/library/ssl_x509.c", + "lib/tls/mbedtls/wrapper/platform/ssl_port.c", + "lib/tls/mbedtls/wrapper/platform/ssl_pm.c", + "lib/tls/mbedtls/lws-genhash.c", + "lib/tls/mbedtls/mbedtls-client.c", + "lib/tls/mbedtls/lws-genrsa.c", + "lib/tls/mbedtls/ssl.c", + "lib/tls/mbedtls/mbedtls-server.c" ] - if env_lws["platform"] == "android": # Builtin getifaddrs - thirdparty_sources += ["misc/getifaddrs.c"] + if env["platform"] == "android": # Builtin getifaddrs + thirdparty_sources += ["lib/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["platform"] == "windows" or env["platform"] == "uwp": # Winsock + thirdparty_sources += Glob(thirdparty_dir + "lib/plat/windows/*.c") + [helper_dir + src for src in ["getopt.c", "getopt_long.c", "gettimeofday.c"]] + else: # Unix socket + thirdparty_sources += Glob(thirdparty_dir + "lib/plat/unix/*.c") - thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] + env_lws.Append(CPPPATH=[thirdparty_dir + 'include/']) - if env_lws["platform"] == "javascript": # No need to add third party libraries at all - pass - else: - env_lws.add_source_files(env.modules_sources, thirdparty_sources) - env_lws.Append(CPPPATH=[thirdparty_dir]) + if env['builtin_mbedtls']: + mbedtls_includes = "#thirdparty/mbedtls/include" + env_lws.Prepend(CPPPATH=[mbedtls_includes]) - wrapper_includes = ["#thirdparty/libwebsockets/tls/mbedtls/wrapper/include/" + inc for inc in ["internal", "openssl", "platform", ""]] - env_lws.Prepend(CPPPATH=wrapper_includes) + wrapper_includes = ["#thirdparty/libwebsockets/lib/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["platform"] == "windows" or env["platform"] == "uwp": + env_lws.Append(CPPPATH=[helper_dir]) - if env_lws["platform"] == "windows" or env_lws["platform"] == "uwp": - env_lws.Append(CPPPATH=[thirdparty_dir + helper_dir]) + if env["platform"] == "uwp": + env_lws.Append(CCFLAGS=["/DLWS_MINGW_SUPPORT"]) - if env_lws["platform"] == "uwp": - env_lws.Append(CCFLAGS=["/DLWS_MINGW_SUPPORT"]) + env_thirdparty = env_lws.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.Append(CPPPATH=[thirdparty_dir + 'lib/']) + env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) env_lws.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/websocket/doc_classes/WebSocketClient.xml b/modules/websocket/doc_classes/WebSocketClient.xml index 2e11e1a44c..ffb6d40e30 100644 --- a/modules/websocket/doc_classes/WebSocketClient.xml +++ b/modules/websocket/doc_classes/WebSocketClient.xml @@ -25,14 +25,19 @@ </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: connnections to non Godot servers will not work, and [signal data_received] will not be emitted when this option is true. + 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"> <return type="void"> </return> + <argument index="0" name="code" type="int" default="1000"> + </argument> + <argument index="1" name="reason" type="String" default=""""> + </argument> <description> - Disconnect from the server if currently connected. + Disconnect this client from the connected host. See [method WebSocketPeer.close] for more info. </description> </method> </methods> @@ -43,8 +48,10 @@ </members> <signals> <signal name="connection_closed"> + <argument index="0" name="was_clean_close" type="bool"> + </argument> <description> - Emitted when the connection to the server is closed. + Emitted when the connection to the server is closed. [code]was_clean_close[/code] will be [code]true[/code] if the connection was shutdown cleanly. </description> </signal> <signal name="connection_error"> @@ -64,6 +71,15 @@ Emitted when a WebSocket message is received. Note: This signal is NOT emitted when used as high level multiplayer peer. </description> </signal> + <signal name="server_close_request"> + <argument index="0" name="code" type="int"> + </argument> + <argument index="1" name="reason" type="String"> + </argument> + <description> + Emitted when the server requests a clean close. You should keep polling until you get a [signal connection_closed] signal to achieve the clean close. See [method WebSocketPeer.close] for more details. + </description> + </signal> </signals> <constants> </constants> diff --git a/modules/websocket/doc_classes/WebSocketPeer.xml b/modules/websocket/doc_classes/WebSocketPeer.xml index 85a08e0c0b..5dda012899 100644 --- a/modules/websocket/doc_classes/WebSocketPeer.xml +++ b/modules/websocket/doc_classes/WebSocketPeer.xml @@ -15,8 +15,14 @@ <method name="close"> <return type="void"> </return> + <argument index="0" name="code" type="int" default="1000"> + </argument> + <argument index="1" name="reason" type="String" default=""""> + </argument> <description> - Close this WebSocket connection, actively disconnecting the peer. + 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). [code]reason[/code] 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. </description> </method> <method name="get_connected_host" qualifiers="const"> diff --git a/modules/websocket/doc_classes/WebSocketServer.xml b/modules/websocket/doc_classes/WebSocketServer.xml index a1061e446b..2932bf782a 100644 --- a/modules/websocket/doc_classes/WebSocketServer.xml +++ b/modules/websocket/doc_classes/WebSocketServer.xml @@ -18,8 +18,12 @@ </return> <argument index="0" name="id" type="int"> </argument> + <argument index="1" name="code" type="int" default="1000"> + </argument> + <argument index="2" name="reason" type="String" default=""""> + </argument> <description> - Disconnects the given peer. + Disconnects the peer identified by [code]id[/code] from the server. See [method WebSocketPeer.close] for more info. </description> </method> <method name="get_peer_address" qualifiers="const"> @@ -68,7 +72,8 @@ <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 true as "gd_mp_api". Note: [signal data_received] will not be fired and clients other than Godot will not work in this case. + 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"> @@ -80,6 +85,17 @@ </method> </methods> <signals> + <signal name="client_close_request"> + <argument index="0" name="id" type="int"> + </argument> + <argument index="1" name="code" type="int"> + </argument> + <argument index="2" name="reason" type="String"> + </argument> + <description> + Emitted when a client requests a clean close. You should keep polling until you get a [signal client_disconnected] signal with the same [code]id[/code] to achieve the clean close. See [method WebSocketPeer.close] for more details. + </description> + </signal> <signal name="client_connected"> <argument index="0" name="id" type="int"> </argument> @@ -92,8 +108,10 @@ <signal name="client_disconnected"> <argument index="0" name="id" type="int"> </argument> + <argument index="1" name="was_clean_close" type="bool"> + </argument> <description> - Emitted when a client disconnects. + Emitted when a client disconnects. [code]was_clean_close[/code] will be [code]true[/code] if the connection was shutdown cleanly. </description> </signal> <signal name="data_received"> diff --git a/modules/websocket/emws_client.cpp b/modules/websocket/emws_client.cpp index 00c36ebb47..aafae8f1c2 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) { @@ -55,32 +58,31 @@ EMSCRIPTEN_KEEPALIVE void _esws_on_error(void *obj) { EMSCRIPTEN_KEEPALIVE void _esws_on_close(void *obj, int code, char *reason, int was_clean) { EMWSClient *client = static_cast<EMWSClient *>(obj); + client->_on_close_request(code, String(reason)); client->_is_connecting = false; - client->_on_disconnect(); + client->_on_disconnect(was_clean != 0); } } Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocols) { + String proto_string = p_protocols.join(","); String str = "ws://"; - String proto_string = ""; if (p_ssl) str = "wss://"; str += p_host + ":" + itos(p_port) + p_path; - for (int i = 0; i < p_protocols.size(); i++) { - proto_string += p_protocols[i]; - proto_string += ","; - } - if (proto_string == "") - proto_string = "binary,"; - - proto_string = proto_string.substr(0, proto_string.length() - 1); _is_connecting = true; /* clang-format off */ int peer_sock = EM_ASM_INT({ - var socket = new WebSocket(UTF8ToString($1), UTF8ToString($2).split(",")); + var proto_str = UTF8ToString($2); + var socket = null; + if (proto_str) { + socket = new WebSocket(UTF8ToString($1), proto_str.split(",")); + } else { + socket = new WebSocket(UTF8ToString($1)); + } var c_ptr = Module.IDHandler.get($0); socket.binaryType = "arraybuffer"; @@ -147,7 +149,7 @@ Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, if (!Module.IDHandler.has($0)) return; // Godot Object is gone! var was_clean = 0; - if (event.was_clean) + if (event.wasClean) was_clean = 1; ccall("_esws_on_close", "void", @@ -160,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; }; @@ -184,9 +186,9 @@ NetworkedMultiplayerPeer::ConnectionStatus EMWSClient::get_connection_status() c return CONNECTION_DISCONNECTED; }; -void EMWSClient::disconnect_from_host() { +void EMWSClient::disconnect_from_host(int p_code, String p_reason) { - _peer->close(); + _peer->close(p_code, p_reason); }; IP_Address EMWSClient::get_connected_host() const { @@ -199,7 +201,13 @@ uint16_t EMWSClient::get_connected_port() const { return 1025; }; +int EMWSClient::get_max_packet_size() const { + return (1 << _in_buf_size) - PROTO_SIZE; +} + 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 6c2fa23b53..9bc29c1913 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,6 +42,8 @@ class EMWSClient : public WebSocketClient { GDCIIMPL(EMWSClient, WebSocketClient); private: + int _in_buf_size; + int _in_pkt_size; int _js_id; public: @@ -48,10 +51,11 @@ public: 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(); + 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 e0b987b4d7..a9f38f1a82 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 { @@ -130,20 +111,22 @@ bool EMWSPeer::is_connected_to_host() const { return peer_sock != -1; }; -void EMWSPeer::close() { +void EMWSPeer::close(int p_code, String p_reason) { if (peer_sock != -1) { /* clang-format off */ EM_ASM({ var sock = Module.IDHandler.get($0); - sock.close(); + var code = $1; + var reason = UTF8ToString($2); + sock.close(code, reason); Module.IDHandler.remove($0); - }, peer_sock); + }, 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 { @@ -160,15 +143,12 @@ uint16_t EMWSPeer::get_connected_port() const { 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 e06f725265..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,27 +45,22 @@ 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(); + virtual void close(int p_code = 1000, String p_reason = ""); virtual bool is_connected_to_host() const; virtual IP_Address get_connected_host() const; virtual uint16_t get_connected_port() 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 3eb93e4152..0eef1c4ba9 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" @@ -68,7 +69,14 @@ int EMWSServer::get_peer_port(int p_peer_id) const { return 0; } -void EMWSServer::disconnect_peer(int p_peer_id) { +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; } EMWSServer::EMWSServer() { diff --git a/modules/websocket/emws_server.h b/modules/websocket/emws_server.h index 9ec4ce72c8..bb101cd155 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 @@ -48,7 +49,8 @@ public: 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); + 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 index ac31daa108..d09558ab22 100644 --- a/modules/websocket/lws_client.cpp +++ b/modules/websocket/lws_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,12 +27,17 @@ /* 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" +#include "core/project_settings.h" +#if defined(LWS_OPENSSL_SUPPORT) +// Not openssl, just the mbedtls wrapper +#include "openssl/ssl.h" +#endif Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocols) { @@ -48,12 +53,10 @@ Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, ERR_FAIL_COND_V(!addr.is_valid(), ERR_INVALID_PARAMETER); - // prepare protocols - if (p_protocols.size() == 0) // default to binary protocol - p_protocols.append("binary"); + // Prepare protocols _lws_make_protocols(this, &LWSClient::_lws_gd_callback, p_protocols, &_lws_ref); - // init lws client + // Init lws client struct lws_context_creation_info info; struct lws_client_connect_info i; @@ -78,20 +81,11 @@ Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, 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; - i.protocol = _lws_ref->lws_names; - i.address = abuf; - i.host = hbuf; - i.path = pbuf; - i.port = p_port; + if (p_protocols.size() > 0) + i.protocol = _lws_ref->lws_names; + else + i.protocol = NULL; if (p_ssl) { i.ssl_connection = LCCSCF_USE_SSL; @@ -101,10 +95,24 @@ Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, i.ssl_connection = 0; } + // These CharStrings needs to survive till we call lws_client_connect_via_info + CharString addr_ch = ((String)addr).ascii(); + CharString host_ch = p_host.utf8(); + CharString path_ch = p_path.utf8(); + i.address = addr_ch.get_data(); + i.host = host_ch.get_data(); + i.path = path_ch.get_data(); + i.port = p_port; + lws_client_connect_via_info(&i); + return OK; }; +int LWSClient::get_max_packet_size() const { + return (1 << _out_buf_size) - PROTO_SIZE; +} + void LWSClient::poll() { _lws_poll(); @@ -116,6 +124,7 @@ int LWSClient::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi LWSPeer::PeerData *peer_data = (LWSPeer::PeerData *)user; switch (reason) { +#if defined(LWS_OPENSSL_SUPPORT) case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS: { PoolByteArray arr = StreamPeerSSL::get_project_cert_array(); if (arr.size() > 0) @@ -123,24 +132,33 @@ int LWSClient::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi else if (verify_ssl) WARN_PRINTS("No CA cert specified in project settings, SSL will not work"); } break; - +#endif case LWS_CALLBACK_CLIENT_ESTABLISHED: - peer->set_wsi(wsi); + peer->set_wsi(wsi, _in_buf_size, _in_pkt_size, _out_buf_size, _out_pkt_size); 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) + return -1; // We should close the connection (would probably happen anyway) + + case LWS_CALLBACK_WS_PEER_INITIATED_CLOSE: { + int code; + String reason2 = peer->get_close_reason(in, len, code); + peer_data->clean_close = true; + _on_close_request(code, reason2); + return 0; + } case LWS_CALLBACK_CLIENT_CLOSED: peer->close(); destroy_context(); - _on_disconnect(); - return 0; // we can end here + _on_disconnect(peer_data->clean_close); + return 0; // We can end here case LWS_CALLBACK_CLIENT_RECEIVE: peer->read_wsi(in, len); @@ -149,8 +167,10 @@ int LWSClient::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi break; case LWS_CALLBACK_CLIENT_WRITEABLE: - if (peer_data->force_close) + if (peer_data->force_close) { + peer->send_close_status(wsi); return -1; + } peer->write_wsi(); break; @@ -178,13 +198,12 @@ NetworkedMultiplayerPeer::ConnectionStatus LWSClient::get_connection_status() co return CONNECTION_CONNECTING; } -void LWSClient::disconnect_from_host() { +void LWSClient::disconnect_from_host(int p_code, String p_reason) { if (context == NULL) return; - _peer->close(); - destroy_context(); + _peer->close(p_code, p_reason); }; IP_Address LWSClient::get_connected_host() const { @@ -198,6 +217,11 @@ uint16_t LWSClient::get_connected_port() const { }; LWSClient::LWSClient() { + _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); + context = NULL; _lws_ref = NULL; _peer = Ref<LWSPeer>(memnew(LWSPeer)); @@ -207,6 +231,7 @@ LWSClient::~LWSClient() { invalidate_lws_ref(); // We do not want any more callback disconnect_from_host(); + destroy_context(); _peer = Ref<LWSPeer>(); }; diff --git a/modules/websocket/lws_client.h b/modules/websocket/lws_client.h index 8850683cb5..b3a1237550 100644 --- a/modules/websocket/lws_client.h +++ b/modules/websocket/lws_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 LWSCLIENT_H #define LWSCLIENT_H @@ -43,10 +44,17 @@ class LWSClient : public WebSocketClient { LWS_HELPER(LWSClient); +private: + int _in_buf_size; + int _in_pkt_size; + int _out_buf_size; + int _out_pkt_size; + public: 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(); + 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; diff --git a/modules/websocket/lws_helper.cpp b/modules/websocket/lws_helper.cpp new file mode 100644 index 0000000000..a652779960 --- /dev/null +++ b/modules/websocket/lws_helper.cpp @@ -0,0 +1,157 @@ +/*************************************************************************/ +/* lws_helper.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. */ +/*************************************************************************/ + +#if !defined(JAVASCRIPT_ENABLED) + +#include "lws_helper.h" + +_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; +} + +void _lws_free_ref(_LWSRef *ref) { + // Free strings and structs + memfree(ref->lws_structs); + memfree(ref->lws_names); + // Free ref + memfree(ref); +} + +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; +} + +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. + */ +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; +} + +#endif diff --git a/modules/websocket/lws_helper.h b/modules/websocket/lws_helper.h index 85a1e3769f..265dc4e6ad 100644 --- a/modules/websocket/lws_helper.h +++ b/modules/websocket/lws_helper.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 LWS_HELPER_H #define LWS_HELPER_H @@ -49,132 +50,18 @@ struct _LWSRef { 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. 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; - - copymem(names_ptr, strings.get_data(), str_len); - names_ptr[str_len] = '\0'; /* NULL terminator */ - /* NULL terminated strings to be used in protocol structs */ - 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 always http-only */ - structs_ptr[0].name = "http-only"; - 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; -} +_LWSRef *_lws_create_ref(void *obj); +void _lws_free_ref(_LWSRef *ref); +bool _lws_destroy(struct lws_context *context, _LWSRef *ref); +bool _lws_poll(struct lws_context *context, _LWSRef *ref); +void _lws_make_protocols(void *p_obj, lws_callback_function *p_callback, PoolVector<String> p_names, _LWSRef **r_lws_ref); /* clang-format off */ #define LWS_HELPER(CNAME) \ protected: \ struct _LWSRef *_lws_ref; \ struct lws_context *context; \ + bool _keep_servicing; \ \ static int _lws_gd_callback(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { \ \ @@ -186,6 +73,7 @@ protected: \ if (!ref->is_valid) \ return 0; \ CNAME *helper = (CNAME *)ref->obj; \ + helper->_keep_servicing = true; \ return helper->_handle_cb(wsi, reason, user, in, len); \ } \ \ @@ -206,11 +94,14 @@ public: \ \ void _lws_poll() { \ ERR_FAIL_COND(context == NULL); \ - \ - if (::_lws_poll(context, _lws_ref)) { \ - context = NULL; \ - _lws_ref = NULL; \ - } \ + do { \ + _keep_servicing = false; \ + if (::_lws_poll(context, _lws_ref)) { \ + context = NULL; \ + _lws_ref = NULL; \ + break; \ + } \ + } while (_keep_servicing); \ } \ \ protected: diff --git a/modules/websocket/lws_peer.cpp b/modules/websocket/lws_peer.cpp index 0989357258..a7c85450fa 100644 --- a/modules/websocket/lws_peer.cpp +++ b/modules/websocket/lws_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,9 +27,11 @@ /* 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 @@ -38,13 +40,14 @@ #include <sys/socket.h> #endif -#include "drivers/unix/socket_helpers.h" +#include "drivers/unix/net_socket_posix.h" -void LWSPeer::set_wsi(struct lws *p_wsi) { +void LWSPeer::set_wsi(struct lws *p_wsi, 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(wsi != NULL); - rbw.resize(16); - rbr.resize(16); + _in_buffer.resize(p_in_pkt_size, p_in_buf_size); + _out_buffer.resize(p_out_pkt_size, p_out_buf_size); + _packet_buffer.resize((1 << MAX(p_in_buf_size, p_out_buf_size)) + LWS_PRE); wsi = p_wsi; }; @@ -60,25 +63,29 @@ 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 (lws_is_first_fragment(wsi)) + _in_size = 0; + else if (_in_size == -1) // Trash this frame + return ERR_FILE_CORRUPT; - if (rbr.space_left() < len + 5) { - ERR_EXPLAIN("Buffer full! Dropping data"); - ERR_FAIL_V(FAILED); + Error err = _in_buffer.write_packet((const uint8_t *)in, len, NULL); + + if (err != OK) { + _in_buffer.discard_payload(_in_size); + _in_size = -1; + ERR_FAIL_V(err); } - copymem(&(input_buffer[size]), in, len); - size += len; + _in_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; + uint8_t is_string = lws_frame_is_binary(wsi) ? 0 : 1; + err = _in_buffer.write_packet(NULL, _in_size, &is_string); + if (err != OK) { + _in_buffer.discard_payload(_in_size); + _in_size = -1; + ERR_FAIL_V(err); + } } return OK; @@ -88,28 +95,21 @@ 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; + int count = _out_buffer.packets_left(); - if (left == 0 || out_count == 0) + if (count == 0) return OK; - rbw.read((uint8_t *)&to_write, 4); - out_count--; - - if (left < to_write) { - rbw.advance_read(left); - return FAILED; - } + int read = 0; + uint8_t is_string = 0; + PoolVector<uint8_t>::Write rw = _packet_buffer.write(); + _out_buffer.read_packet(&(rw[LWS_PRE]), _packet_buffer.size() - LWS_PRE, &is_string, read); - 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); + enum lws_write_protocol mode = is_string ? LWS_WRITE_TEXT : LWS_WRITE_BINARY; + lws_write(wsi, &(rw[LWS_PRE]), read, mode); - if (out_count > 0) + if (count > 1) lws_callback_on_writable(wsi); // we want to write more! return OK; @@ -119,43 +119,27 @@ 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++; - + uint8_t is_string = write_mode == WRITE_MODE_TEXT; + _out_buffer.write_packet(p_buffer, p_buffer_size, &is_string); 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); + r_buffer_size = 0; - PeerData *peer_data = (PeerData *)lws_wsi_user(wsi); + ERR_FAIL_COND_V(!is_connected_to_host(), FAILED); - if (in_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; - - rbr.read((uint8_t *)&to_read, 4); - in_count--; - left = rbr.data_left(); + int read = 0; + PoolVector<uint8_t>::Write rw = _packet_buffer.write(); + _in_buffer.read_packet(rw.ptr(), _packet_buffer.size(), &_is_string, read); - 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; + *r_buffer = rw.ptr(); + r_buffer_size = read; return OK; }; @@ -165,12 +149,12 @@ int LWSPeer::get_available_packet_count() const { if (!is_connected_to_host()) return 0; - return in_count; + return _in_buffer.packets_left(); }; bool LWSPeer::was_string_packet() const { - return _was_string; + return _is_string; }; bool LWSPeer::is_connected_to_host() const { @@ -178,19 +162,56 @@ bool LWSPeer::is_connected_to_host() const { return wsi != NULL; }; -void LWSPeer::close() { +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; - lws_callback_on_writable(wsi); // notify that we want to disconnect + 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; + _in_buffer.clear(); + _out_buffer.clear(); + _in_size = 0; + _is_string = 0; + _packet_buffer.resize(0); }; IP_Address LWSPeer::get_connected_host() const { @@ -198,7 +219,7 @@ IP_Address LWSPeer::get_connected_host() const { ERR_FAIL_COND_V(!is_connected_to_host(), IP_Address()); IP_Address ip; - int port = 0; + uint16_t port = 0; struct sockaddr_storage addr; socklen_t len = sizeof(addr); @@ -209,7 +230,7 @@ IP_Address LWSPeer::get_connected_host() const { int ret = getpeername(fd, (struct sockaddr *)&addr, &len); ERR_FAIL_COND_V(ret != 0, IP_Address()); - _set_ip_addr_port(ip, port, &addr); + NetSocketPosix::_set_ip_port(&addr, ip, port); return ip; }; @@ -219,7 +240,7 @@ uint16_t LWSPeer::get_connected_port() const { ERR_FAIL_COND_V(!is_connected_to_host(), 0); IP_Address ip; - int port = 0; + uint16_t port = 0; struct sockaddr_storage addr; socklen_t len = sizeof(addr); @@ -230,7 +251,7 @@ uint16_t LWSPeer::get_connected_port() const { int ret = getpeername(fd, (struct sockaddr *)&addr, &len); ERR_FAIL_COND_V(ret != 0, 0); - _set_ip_addr_port(ip, port, &addr); + NetSocketPosix::_set_ip_port(&addr, ip, port); return port; }; diff --git a/modules/websocket/lws_peer.h b/modules/websocket/lws_peer.h index d7d46e3076..6771c13094 100644 --- a/modules/websocket/lws_peer.h +++ b/modules/websocket/lws_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 LWSPEER_H #define LWSPEER_H @@ -37,6 +38,7 @@ #include "core/ring_buffer.h" #include "libwebsockets.h" #include "lws_config.h" +#include "packet_buffer.h" #include "websocket_peer.h" class LWSPeer : public WebSocketPeer { @@ -44,34 +46,33 @@ class LWSPeer : public WebSocketPeer { GDCIIMPL(LWSPeer, WebSocketPeer); private: - enum { - PACKET_BUFFER_SIZE = 65536 - 5 // 4 bytes for the size, 1 for the type - }; + int _in_size; + uint8_t _is_string; + // Our packet info is just a boolean (is_string), using uint8_t for it. + PacketBuffer<uint8_t> _in_buffer; + PacketBuffer<uint8_t> _out_buffer; + + PoolVector<uint8_t> _packet_buffer; - uint8_t packet_buffer[PACKET_BUFFER_SIZE]; struct lws *wsi; WriteMode write_mode; - bool _was_string; + + 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; - 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(); + virtual void close(int p_code = 1000, String p_reason = ""); virtual bool is_connected_to_host() const; virtual IP_Address get_connected_host() const; virtual uint16_t get_connected_port() const; @@ -80,9 +81,11 @@ public: virtual void set_write_mode(WriteMode p_mode); virtual bool was_string_packet() const; - void set_wsi(struct lws *wsi); + void set_wsi(struct lws *wsi, unsigned int _in_buf_size, unsigned int _in_pkt_size, unsigned int _out_buf_size, unsigned int _out_pkt_size); 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); LWSPeer(); ~LWSPeer(); diff --git a/modules/websocket/lws_server.cpp b/modules/websocket/lws_server.cpp index bb724bce9c..61ccdca74a 100644 --- a/modules/websocket/lws_server.cpp +++ b/modules/websocket/lws_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,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. */ /*************************************************************************/ + #ifndef JAVASCRIPT_ENABLED #include "lws_server.h" #include "core/os/os.h" +#include "core/project_settings.h" Error LWSServer::listen(int p_port, PoolVector<String> p_protocols, bool gd_mp_api) { @@ -41,9 +43,6 @@ Error LWSServer::listen(int p_port, PoolVector<String> p_protocols, bool gd_mp_a struct lws_context_creation_info info; memset(&info, 0, sizeof info); - if (p_protocols.size() == 0) // default to binary protocol - p_protocols.append(String("binary")); - // Prepare lws protocol structs _lws_make_protocols(this, &LWSServer::_lws_gd_callback, p_protocols, &_lws_ref); @@ -70,6 +69,10 @@ bool LWSServer::is_listening() const { return context != NULL; } +int LWSServer::get_max_packet_size() const { + return (1 << _out_buf_size) - PROTO_SIZE; +} + 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; @@ -88,25 +91,41 @@ int LWSServer::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi int32_t id = _gen_unique_id(); Ref<LWSPeer> peer = Ref<LWSPeer>(memnew(LWSPeer)); - peer->set_wsi(wsi); + peer->set_wsi(wsi, _in_buf_size, _in_pkt_size, _out_buf_size, _out_pkt_size); _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 reason2 = peer->get_close_reason(in, len, code); + peer_data->clean_close = true; + _on_close_request(id, code, reason2); + } + 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); + _on_disconnect(id, clean); return 0; // we can end here } @@ -121,10 +140,15 @@ int LWSServer::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi } case LWS_CALLBACK_SERVER_WRITEABLE: { - if (peer_data->force_close) + 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; + } - int id = peer_data->peer_id; if (_peer_map.has(id)) static_cast<Ref<LWSPeer> >(_peer_map[id])->write_wsi(); break; @@ -167,13 +191,17 @@ int LWSServer::get_peer_port(int p_peer_id) const { return _peer_map[p_peer_id]->get_connected_port(); } -void LWSServer::disconnect_peer(int p_peer_id) { +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(); + get_peer(p_peer_id)->close(p_code, p_reason); } LWSServer::LWSServer() { + _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); context = NULL; _lws_ref = NULL; } diff --git a/modules/websocket/lws_server.h b/modules/websocket/lws_server.h index 9e3fb9b775..5096ee0f88 100644 --- a/modules/websocket/lws_server.h +++ b/modules/websocket/lws_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 LWSSERVER_H #define LWSSERVER_H @@ -45,16 +46,21 @@ class LWSServer : public WebSocketServer { private: Map<int, Ref<LWSPeer> > peer_map; + int _in_buf_size; + int _in_pkt_size; + int _out_buf_size; + int _out_pkt_size; public: 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); + void disconnect_peer(int p_peer_id, int p_code = 1000, String p_reason = ""); virtual void poll() { _lws_poll(); } LWSServer(); diff --git a/modules/websocket/packet_buffer.h b/modules/websocket/packet_buffer.h new file mode 100644 index 0000000000..47786a87a6 --- /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 && _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 721f3cc330..ed6cc5638e 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,8 +27,10 @@ /* 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 "error_macros.h" +#include "core/error_macros.h" +#include "core/project_settings.h" #ifdef JAVASCRIPT_ENABLED #include "emscripten.h" #include "emws_client.h" @@ -41,6 +43,22 @@ #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 = {}; 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 7701163085..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); @@ -107,12 +108,17 @@ void WebSocketClient::_on_connect(String p_protocol) { } } -void WebSocketClient::_on_disconnect() { +void WebSocketClient::_on_close_request(int p_code, String p_reason) { + + emit_signal("server_close_request", p_code, p_reason); +} + +void WebSocketClient::_on_disconnect(bool p_was_clean) { if (_is_multiplayer) { emit_signal("connection_failed"); } else { - emit_signal("connection_closed"); + emit_signal("connection_closed", p_was_clean); } } @@ -127,14 +133,15 @@ void WebSocketClient::_on_error() { void WebSocketClient::_bind_methods() { ClassDB::bind_method(D_METHOD("connect_to_url", "url", "protocols", "gd_mp_api"), &WebSocketClient::connect_to_url, DEFVAL(PoolVector<String>()), DEFVAL(false)); - ClassDB::bind_method(D_METHOD("disconnect_from_host"), &WebSocketClient::disconnect_from_host); + ClassDB::bind_method(D_METHOD("disconnect_from_host", "code", "reason"), &WebSocketClient::disconnect_from_host, DEFVAL(1000), DEFVAL("")); 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"))); - ADD_SIGNAL(MethodInfo("connection_closed")); + ADD_SIGNAL(MethodInfo("server_close_request", PropertyInfo(Variant::INT, "code"), PropertyInfo(Variant::STRING, "reason"))); + ADD_SIGNAL(MethodInfo("connection_closed", PropertyInfo(Variant::BOOL, "was_clean_close"))); ADD_SIGNAL(MethodInfo("connection_error")); } diff --git a/modules/websocket/websocket_client.h b/modules/websocket/websocket_client.h index 6165f37d40..c464d97c7f 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 { @@ -53,7 +54,7 @@ public: virtual void poll() = 0; virtual Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>()) = 0; - virtual void disconnect_from_host() = 0; + virtual void disconnect_from_host(int p_code = 1000, String p_reason = "") = 0; virtual IP_Address get_connected_host() const = 0; virtual uint16_t get_connected_port() const = 0; @@ -62,7 +63,8 @@ public: void _on_peer_packet(); void _on_connect(String p_protocol); - void _on_disconnect(); + void _on_close_request(int p_code, String p_reason); + void _on_disconnect(bool p_was_clean); void _on_error(); 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..6aab8a7e81 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() { @@ -95,22 +97,18 @@ void WebSocketMultiplayerPeer::_bind_methods() { // int WebSocketMultiplayerPeer::get_available_packet_count() const { + ERR_EXPLAIN("Please use get_peer(ID).get_available_packet_count to get available packet count from peers when not using the MultiplayerAPI."); ERR_FAIL_COND_V(!_is_multiplayer, ERR_UNCONFIGURED); return _incoming_packets.size(); } -int WebSocketMultiplayerPeer::get_max_packet_size() const { +Error WebSocketMultiplayerPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { + ERR_EXPLAIN("Please use get_peer(ID).get_packet/var to communicate with peers when not using the MultiplayerAPI."); 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) { - r_buffer_size = 0; - ERR_FAIL_COND_V(!_is_multiplayer, ERR_UNCONFIGURED); if (_current_packet.data != NULL) { memfree(_current_packet.data); @@ -128,6 +126,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_EXPLAIN("Please use get_peer(ID).put_packet/var to communicate with peers when not using the MultiplayerAPI."); ERR_FAIL_COND_V(!_is_multiplayer, ERR_UNCONFIGURED); PoolVector<uint8_t> buffer = _make_pkt(SYS_NONE, get_unique_id(), _target_peer, p_buffer, p_buffer_size); @@ -160,6 +159,7 @@ void WebSocketMultiplayerPeer::set_target_peer(int p_target_peer) { int WebSocketMultiplayerPeer::get_packet_peer() const { + ERR_EXPLAIN("This function is not available when not using the MultiplayerAPI."); ERR_FAIL_COND_V(!_is_multiplayer, 1); ERR_FAIL_COND_V(_incoming_packets.size() == 0, 1); @@ -213,7 +213,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 +226,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); } @@ -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); @@ -313,7 +313,7 @@ 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 { diff --git a/modules/websocket/websocket_multiplayer.h b/modules/websocket/websocket_multiplayer_peer.h index 8edfc5296e..b050449ee0 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 @@ -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 { @@ -93,7 +92,7 @@ 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); diff --git a/modules/websocket/websocket_peer.cpp b/modules/websocket/websocket_peer.cpp index 61f783e377..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); @@ -42,7 +43,7 @@ void WebSocketPeer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_write_mode", "mode"), &WebSocketPeer::set_write_mode); ClassDB::bind_method(D_METHOD("is_connected_to_host"), &WebSocketPeer::is_connected_to_host); ClassDB::bind_method(D_METHOD("was_string_packet"), &WebSocketPeer::was_string_packet); - ClassDB::bind_method(D_METHOD("close"), &WebSocketPeer::close); + ClassDB::bind_method(D_METHOD("close", "code", "reason"), &WebSocketPeer::close, DEFVAL(1000), DEFVAL("")); ClassDB::bind_method(D_METHOD("get_connected_host"), &WebSocketPeer::get_connected_host); ClassDB::bind_method(D_METHOD("get_connected_port"), &WebSocketPeer::get_connected_port); diff --git a/modules/websocket/websocket_peer.h b/modules/websocket/websocket_peer.h index ad451e9cc7..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 { @@ -58,7 +58,7 @@ public: virtual WriteMode get_write_mode() const = 0; virtual void set_write_mode(WriteMode p_mode) = 0; - virtual void close() = 0; + virtual void close(int p_code = 1000, String p_reason = "") = 0; virtual bool is_connected_to_host() const = 0; virtual IP_Address get_connected_host() const = 0; diff --git a/modules/websocket/websocket_server.cpp b/modules/websocket/websocket_server.cpp index 53dd7b51b7..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); @@ -46,9 +47,10 @@ void WebSocketServer::_bind_methods() { ClassDB::bind_method(D_METHOD("has_peer", "id"), &WebSocketServer::has_peer); ClassDB::bind_method(D_METHOD("get_peer_address", "id"), &WebSocketServer::get_peer_address); ClassDB::bind_method(D_METHOD("get_peer_port", "id"), &WebSocketServer::get_peer_port); - ClassDB::bind_method(D_METHOD("disconnect_peer", "id"), &WebSocketServer::disconnect_peer); + ClassDB::bind_method(D_METHOD("disconnect_peer", "id", "code", "reason"), &WebSocketServer::disconnect_peer, DEFVAL(1000), DEFVAL("")); - ADD_SIGNAL(MethodInfo("client_disconnected", PropertyInfo(Variant::INT, "id"))); + ADD_SIGNAL(MethodInfo("client_close_request", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::INT, "code"), PropertyInfo(Variant::STRING, "reason"))); + ADD_SIGNAL(MethodInfo("client_disconnected", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::BOOL, "was_clean_close"))); ADD_SIGNAL(MethodInfo("client_connected", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "protocol"))); ADD_SIGNAL(MethodInfo("data_received", PropertyInfo(Variant::INT, "id"))); } @@ -85,13 +87,18 @@ void WebSocketServer::_on_connect(int32_t p_peer_id, String p_protocol) { } } -void WebSocketServer::_on_disconnect(int32_t p_peer_id) { +void WebSocketServer::_on_disconnect(int32_t p_peer_id, bool p_was_clean) { if (_is_multiplayer) { // Send delete to clients _send_del(p_peer_id); emit_signal("peer_disconnected", p_peer_id); } else { - emit_signal("client_disconnected", p_peer_id); + emit_signal("client_disconnected", p_peer_id, p_was_clean); } } + +void WebSocketServer::_on_close_request(int32_t p_peer_id, int p_code, String p_reason) { + + emit_signal("client_close_request", p_peer_id, p_code, p_reason); +} diff --git a/modules/websocket/websocket_server.h b/modules/websocket/websocket_server.h index 64935f8a58..7a94c4047b 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 { @@ -54,11 +55,12 @@ public: virtual IP_Address get_peer_address(int p_peer_id) const = 0; virtual int get_peer_port(int p_peer_id) const = 0; - virtual void disconnect_peer(int p_peer_id) = 0; + virtual void disconnect_peer(int p_peer_id, int p_code = 1000, String p_reason = "") = 0; void _on_peer_packet(int32_t p_peer_id); void _on_connect(int32_t p_peer_id, String p_protocol); - void _on_disconnect(int32_t p_peer_id); + 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); WebSocketServer(); ~WebSocketServer(); |