diff options
Diffstat (limited to 'modules/websocket')
34 files changed, 360 insertions, 238 deletions
diff --git a/modules/websocket/SCsub b/modules/websocket/SCsub index 4c022c43cf..dc0661995f 100644 --- a/modules/websocket/SCsub +++ b/modules/websocket/SCsub @@ -18,12 +18,11 @@ elif env["builtin_wslay"]: "wslay_net.c", "wslay_event.c", "wslay_queue.c", - "wslay_stack.c", "wslay_frame.c", ] thirdparty_sources = [thirdparty_dir + s for s in thirdparty_sources] - env_ws.Prepend(CPPPATH=[thirdparty_dir + "includes/"]) + env_ws.Prepend(CPPPATH=[thirdparty_dir]) env_ws.Append(CPPDEFINES=["HAVE_CONFIG_H"]) if env["platform"] == "windows" or env["platform"] == "uwp": @@ -42,6 +41,8 @@ elif env["builtin_wslay"]: module_obj = [] env_ws.add_source_files(module_obj, "*.cpp") +if env["tools"]: + env_ws.add_source_files(module_obj, "editor/*.cpp") env.modules_sources += module_obj # Needed to force rebuilding the module files when the thirdparty library is updated. diff --git a/modules/websocket/doc_classes/WebSocketClient.xml b/modules/websocket/doc_classes/WebSocketClient.xml index 4b515c12a1..ad2acf8a21 100644 --- a/modules/websocket/doc_classes/WebSocketClient.xml +++ b/modules/websocket/doc_classes/WebSocketClient.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="WebSocketClient" inherits="WebSocketMultiplayerPeer" version="4.0"> +<class name="WebSocketClient" inherits="WebSocketMultiplayerPeer" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> A WebSocket client implementation. </brief_description> @@ -39,13 +39,13 @@ <method name="get_connected_host" qualifiers="const"> <return type="String" /> <description> - Return the IP address of the currently connected host. + Returns the IP address of the currently connected host. </description> </method> <method name="get_connected_port" qualifiers="const"> <return type="int" /> <description> - Return the IP port of the currently connected host. + Returns the IP port of the currently connected host. </description> </method> </methods> diff --git a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml index 8d8ab220e2..4a617f4c82 100644 --- a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml +++ b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="WebSocketMultiplayerPeer" inherits="MultiplayerPeer" version="4.0"> +<class name="WebSocketMultiplayerPeer" inherits="MultiplayerPeer" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Base class for WebSocket server and client. </brief_description> diff --git a/modules/websocket/doc_classes/WebSocketPeer.xml b/modules/websocket/doc_classes/WebSocketPeer.xml index 2f455c32fd..6466654517 100644 --- a/modules/websocket/doc_classes/WebSocketPeer.xml +++ b/modules/websocket/doc_classes/WebSocketPeer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="WebSocketPeer" inherits="PacketPeer" version="4.0"> +<class name="WebSocketPeer" inherits="PacketPeer" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> A class representing a specific WebSocket connection. </brief_description> diff --git a/modules/websocket/doc_classes/WebSocketServer.xml b/modules/websocket/doc_classes/WebSocketServer.xml index f901b089ea..46b0274de3 100644 --- a/modules/websocket/doc_classes/WebSocketServer.xml +++ b/modules/websocket/doc_classes/WebSocketServer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="WebSocketServer" inherits="WebSocketMultiplayerPeer" version="4.0"> +<class name="WebSocketServer" inherits="WebSocketMultiplayerPeer" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> A WebSocket server implementation. </brief_description> @@ -60,6 +60,13 @@ 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="set_extra_headers"> + <return type="void" /> + <argument index="0" name="headers" type="PackedStringArray" default="PackedStringArray()" /> + <description> + Sets additional headers to be sent to clients during the HTTP handshake. + </description> + </method> <method name="stop"> <return type="void" /> <description> diff --git a/modules/websocket/editor_debugger_server_websocket.cpp b/modules/websocket/editor/editor_debugger_server_websocket.cpp index d248433d82..0443147d98 100644 --- a/modules/websocket/editor_debugger_server_websocket.cpp +++ b/modules/websocket/editor/editor_debugger_server_websocket.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 */ @@ -30,9 +30,13 @@ #include "editor_debugger_server_websocket.h" +#ifdef TOOLS_ENABLED + +#include "../remote_debugger_peer_websocket.h" #include "core/config/project_settings.h" +#include "editor/editor_log.h" +#include "editor/editor_node.h" #include "editor/editor_settings.h" -#include "modules/websocket/remote_debugger_peer_websocket.h" void EditorDebuggerServerWebSocket::_peer_connected(int p_id, String _protocol) { pending_peers.push_back(p_id); @@ -48,19 +52,47 @@ void EditorDebuggerServerWebSocket::poll() { server->poll(); } +String EditorDebuggerServerWebSocket::get_uri() const { + return endpoint; +} + Error EditorDebuggerServerWebSocket::start(const String &p_uri) { + // Default host and port + String bind_host = (String)EditorSettings::get_singleton()->get("network/debug/remote_host"); int bind_port = (int)EditorSettings::get_singleton()->get("network/debug/remote_port"); - String bind_host = EditorSettings::get_singleton()->get("network/debug/remote_host"); + + // Optionally override if (!p_uri.is_empty() && p_uri != "ws://") { String scheme, path; Error err = p_uri.parse_url(scheme, bind_host, bind_port, path); ERR_FAIL_COND_V(err != OK, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(!bind_host.is_valid_ip_address() && bind_host != "*", ERR_INVALID_PARAMETER); } + + // Set up the server server->set_bind_ip(bind_host); Vector<String> compatible_protocols; compatible_protocols.push_back("binary"); // compatibility with EMSCRIPTEN TCP-to-WebSocket layer. - return server->listen(bind_port, compatible_protocols); + + // Try listening on ports + const int max_attempts = 5; + for (int attempt = 1;; ++attempt) { + const Error err = server->listen(bind_port, compatible_protocols); + if (err == OK) { + break; + } + if (attempt >= max_attempts) { + EditorNode::get_log()->add_message(vformat("Cannot listen on port %d, remote debugging unavailable.", bind_port), EditorLog::MSG_TYPE_ERROR); + return err; + } + int last_port = bind_port++; + EditorNode::get_log()->add_message(vformat("Cannot listen on port %d, trying %d instead.", last_port, bind_port), EditorLog::MSG_TYPE_WARNING); + } + + // Endpoint that the client should connect to + endpoint = vformat("ws://%s:%d", bind_host, bind_port); + + return OK; } void EditorDebuggerServerWebSocket::stop() { @@ -99,3 +131,5 @@ EditorDebuggerServer *EditorDebuggerServerWebSocket::create(const String &p_prot ERR_FAIL_COND_V(p_protocol != "ws://", nullptr); return memnew(EditorDebuggerServerWebSocket); } + +#endif // TOOLS_ENABLED diff --git a/modules/websocket/editor_debugger_server_websocket.h b/modules/websocket/editor/editor_debugger_server_websocket.h index 14ab0109b2..7c0705302d 100644 --- a/modules/websocket/editor_debugger_server_websocket.h +++ b/modules/websocket/editor/editor_debugger_server_websocket.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 */ @@ -31,8 +31,10 @@ #ifndef EDITOR_DEBUGGER_SERVER_WEBSOCKET_H #define EDITOR_DEBUGGER_SERVER_WEBSOCKET_H +#ifdef TOOLS_ENABLED + +#include "../websocket_server.h" #include "editor/debugger/editor_debugger_server.h" -#include "modules/websocket/websocket_server.h" class EditorDebuggerServerWebSocket : public EditorDebuggerServer { GDCLASS(EditorDebuggerServerWebSocket, EditorDebuggerServer); @@ -40,6 +42,7 @@ class EditorDebuggerServerWebSocket : public EditorDebuggerServer { private: Ref<WebSocketServer> server; List<int> pending_peers; + String endpoint; public: static EditorDebuggerServer *create(const String &p_protocol); @@ -47,15 +50,18 @@ public: void _peer_connected(int p_peer, String p_protocol); void _peer_disconnected(int p_peer, bool p_was_clean); - void poll() override; - Error start(const String &p_uri) override; - void stop() override; - bool is_active() const override; - bool is_connection_available() const override; - Ref<RemoteDebuggerPeer> take_connection() override; + virtual void poll() override; + virtual String get_uri() const override; + virtual Error start(const String &p_uri = "") override; + virtual void stop() override; + virtual bool is_active() const override; + virtual bool is_connection_available() const override; + virtual Ref<RemoteDebuggerPeer> take_connection() override; EditorDebuggerServerWebSocket(); ~EditorDebuggerServerWebSocket(); }; +#endif // TOOLS_ENABLED + #endif // EDITOR_DEBUGGER_SERVER_WEBSOCKET_H diff --git a/modules/websocket/emws_client.cpp b/modules/websocket/emws_client.cpp index 5cd94e978f..e051a3b564 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-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 */ @@ -31,6 +31,7 @@ #ifdef JAVASCRIPT_ENABLED #include "emws_client.h" + #include "core/config/project_settings.h" #include "core/io/ip.h" #include "emscripten.h" @@ -45,8 +46,9 @@ void EMWSClient::_esws_on_message(void *obj, const uint8_t *p_data, int p_data_s EMWSClient *client = static_cast<EMWSClient *>(obj); Error err = static_cast<EMWSPeer *>(*client->get_peer(1))->read_msg(p_data, p_data_size, p_is_string == 1); - if (err == OK) + if (err == OK) { client->_on_peer_packet(); + } } void EMWSClient::_esws_on_error(void *obj) { @@ -71,8 +73,9 @@ Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, String proto_string; for (int i = 0; i < p_protocols.size(); i++) { - if (i != 0) + if (i != 0) { proto_string += ","; + } proto_string += p_protocols[i]; } @@ -109,8 +112,9 @@ Ref<WebSocketPeer> EMWSClient::get_peer(int p_peer_id) const { MultiplayerPeer::ConnectionStatus EMWSClient::get_connection_status() const { if (_peer->is_connected_to_host()) { - if (_is_connecting) + if (_is_connecting) { return CONNECTION_CONNECTING; + } return CONNECTION_CONNECTED; } diff --git a/modules/websocket/emws_client.h b/modules/websocket/emws_client.h index 3b0b8395b9..ca327a56fa 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-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 */ @@ -53,15 +53,15 @@ private: static void _esws_on_close(void *obj, int code, const char *reason, int was_clean); public: - Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets); - Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocol = Vector<String>(), const Vector<String> p_custom_headers = Vector<String>()); - Ref<WebSocketPeer> get_peer(int p_peer_id) const; - void disconnect_from_host(int p_code = 1000, String p_reason = ""); - IPAddress 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(); + Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) override; + Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocol = Vector<String>(), const Vector<String> p_custom_headers = Vector<String>()) override; + Ref<WebSocketPeer> get_peer(int p_peer_id) const override; + void disconnect_from_host(int p_code = 1000, String p_reason = "") override; + IPAddress get_connected_host() const override; + uint16_t get_connected_port() const override; + virtual ConnectionStatus get_connection_status() const override; + int get_max_packet_size() const override; + virtual void poll() override; EMWSClient(); ~EMWSClient(); }; diff --git a/modules/websocket/emws_peer.cpp b/modules/websocket/emws_peer.cpp index 035d036b90..86169f88e9 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-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 */ @@ -31,6 +31,7 @@ #ifdef JAVASCRIPT_ENABLED #include "emws_peer.h" + #include "core/io/ip.h" void EMWSPeer::set_sock(int p_sock, unsigned int p_in_buf_size, unsigned int p_in_pkt_size, unsigned int p_out_buf_size) { @@ -66,8 +67,9 @@ 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 (_in_buffer.packets_left() == 0) + if (_in_buffer.packets_left() == 0) { return ERR_UNAVAILABLE; + } int read = 0; Error err = _in_buffer.read_packet(_packet_buffer.ptrw(), _packet_buffer.size(), &_is_string, read); @@ -109,7 +111,7 @@ void EMWSPeer::close(int p_code, String p_reason) { IPAddress EMWSPeer::get_connected_host() const { ERR_FAIL_V_MSG(IPAddress(), "Not supported in HTML5 export."); -}; +} uint16_t EMWSPeer::get_connected_port() const { ERR_FAIL_V_MSG(0, "Not supported in HTML5 export."); diff --git a/modules/websocket/emws_peer.h b/modules/websocket/emws_peer.h index 6e93ea31a2..6bb4552c37 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-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 */ @@ -68,21 +68,21 @@ private: public: Error read_msg(const 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, unsigned int p_out_buf_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_current_outbound_buffered_amount() const; + virtual int get_available_packet_count() const override; + virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; + virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override; + virtual int get_max_packet_size() const override { return _packet_buffer.size(); }; + virtual int get_current_outbound_buffered_amount() const override; - virtual void close(int p_code = 1000, String p_reason = ""); - virtual bool is_connected_to_host() const; - virtual IPAddress get_connected_host() const; - virtual uint16_t get_connected_port() const; + virtual void close(int p_code = 1000, String p_reason = "") override; + virtual bool is_connected_to_host() const override; + virtual IPAddress get_connected_host() const override; + virtual uint16_t get_connected_port() const override; - virtual WriteMode get_write_mode() const; - virtual void set_write_mode(WriteMode p_mode); - virtual bool was_string_packet() const; - virtual void set_no_delay(bool p_enabled); + virtual WriteMode get_write_mode() const override; + virtual void set_write_mode(WriteMode p_mode) override; + virtual bool was_string_packet() const override; + virtual void set_no_delay(bool p_enabled) override; EMWSPeer(); ~EMWSPeer(); diff --git a/modules/websocket/emws_server.cpp b/modules/websocket/emws_server.cpp index 4a4f09a943..2033098cad 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-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 */ @@ -33,6 +33,9 @@ #include "emws_server.h" #include "core/os/os.h" +void EMWSServer::set_extra_headers(const Vector<String> &p_headers) { +} + Error EMWSServer::listen(int p_port, Vector<String> p_protocols, bool gd_mp_api) { return FAILED; } diff --git a/modules/websocket/emws_server.h b/modules/websocket/emws_server.h index d36e3a3557..ae31d9dbb0 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-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 */ @@ -41,17 +41,18 @@ class EMWSServer : public WebSocketServer { GDCIIMPL(EMWSServer, WebSocketServer); public: - Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets); - Error listen(int p_port, Vector<String> p_protocols = Vector<String>(), bool gd_mp_api = false); - void stop(); - bool is_listening() const; - bool has_peer(int p_id) const; - Ref<WebSocketPeer> get_peer(int p_id) const; - IPAddress get_peer_address(int p_peer_id) const; - int get_peer_port(int p_peer_id) const; - void disconnect_peer(int p_peer_id, int p_code = 1000, String p_reason = ""); - int get_max_packet_size() const; - virtual void poll(); + Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) override; + void set_extra_headers(const Vector<String> &p_headers) override; + Error listen(int p_port, Vector<String> p_protocols = Vector<String>(), bool gd_mp_api = false) override; + void stop() override; + bool is_listening() const override; + bool has_peer(int p_id) const override; + Ref<WebSocketPeer> get_peer(int p_id) const override; + IPAddress get_peer_address(int p_peer_id) const override; + int get_peer_port(int p_peer_id) const override; + void disconnect_peer(int p_peer_id, int p_code = 1000, String p_reason = "") override; + int get_max_packet_size() const override; + virtual void poll() override; virtual Vector<String> get_protocols() const; EMWSServer(); diff --git a/modules/websocket/library_godot_websocket.js b/modules/websocket/library_godot_websocket.js index dd2fd1e94f..57f1f10b02 100644 --- a/modules/websocket/library_godot_websocket.js +++ b/modules/websocket/library_godot_websocket.js @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 */ @@ -135,7 +135,7 @@ const GodotWebSocket = { if (!ref) { return; } - GodotWebSocket.close(p_id, 1001, ''); + GodotWebSocket.close(p_id, 3001, 'destroyed'); IDHandler.remove(p_id); ref.onopen = null; ref.onmessage = null; diff --git a/modules/websocket/packet_buffer.h b/modules/websocket/packet_buffer.h index e99a379767..7b4a164576 100644 --- a/modules/websocket/packet_buffer.h +++ b/modules/websocket/packet_buffer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 */ diff --git a/modules/websocket/register_types.cpp b/modules/websocket/register_types.cpp index 7c742b1b89..f562de111f 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-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 */ @@ -29,8 +29,10 @@ /*************************************************************************/ #include "register_types.h" + #include "core/config/project_settings.h" #include "core/error/error_macros.h" + #ifdef JAVASCRIPT_ENABLED #include "emscripten.h" #include "emws_client.h" @@ -40,10 +42,11 @@ #include "wsl_client.h" #include "wsl_server.h" #endif + #ifdef TOOLS_ENABLED #include "editor/debugger/editor_debugger_server.h" +#include "editor/editor_debugger_server_websocket.h" #include "editor/editor_node.h" -#include "editor_debugger_server_websocket.h" #endif #ifdef TOOLS_ENABLED @@ -52,25 +55,33 @@ static void _editor_init_callback() { } #endif -void register_websocket_types() { +void initialize_websocket_module(ModuleInitializationLevel p_level) { + if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) { #ifdef JAVASCRIPT_ENABLED - EMWSPeer::make_default(); - EMWSClient::make_default(); - EMWSServer::make_default(); + EMWSPeer::make_default(); + EMWSClient::make_default(); + EMWSServer::make_default(); #else - WSLPeer::make_default(); - WSLClient::make_default(); - WSLServer::make_default(); + WSLPeer::make_default(); + WSLClient::make_default(); + WSLServer::make_default(); #endif - GDREGISTER_VIRTUAL_CLASS(WebSocketMultiplayerPeer); - ClassDB::register_custom_instance_class<WebSocketServer>(); - ClassDB::register_custom_instance_class<WebSocketClient>(); - ClassDB::register_custom_instance_class<WebSocketPeer>(); + GDREGISTER_ABSTRACT_CLASS(WebSocketMultiplayerPeer); + ClassDB::register_custom_instance_class<WebSocketServer>(); + ClassDB::register_custom_instance_class<WebSocketClient>(); + ClassDB::register_custom_instance_class<WebSocketPeer>(); + } #ifdef TOOLS_ENABLED - EditorNode::add_init_callback(&_editor_init_callback); + if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) { + EditorNode::add_init_callback(&_editor_init_callback); + } #endif } -void unregister_websocket_types() {} +void uninitialize_websocket_module(ModuleInitializationLevel p_level) { + if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { + return; + } +} diff --git a/modules/websocket/register_types.h b/modules/websocket/register_types.h index 3884db67b7..dab42d6ed9 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-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 */ @@ -31,7 +31,9 @@ #ifndef WEBSOCKET_REGISTER_TYPES_H #define WEBSOCKET_REGISTER_TYPES_H -void register_websocket_types(); -void unregister_websocket_types(); +#include "modules/register_module_types.h" + +void initialize_websocket_module(ModuleInitializationLevel p_level); +void uninitialize_websocket_module(ModuleInitializationLevel p_level); #endif // WEBSOCKET_REGISTER_TYPES_H diff --git a/modules/websocket/remote_debugger_peer_websocket.cpp b/modules/websocket/remote_debugger_peer_websocket.cpp index c9591cc564..6319c3c664 100644 --- a/modules/websocket/remote_debugger_peer_websocket.cpp +++ b/modules/websocket/remote_debugger_peer_websocket.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 */ diff --git a/modules/websocket/remote_debugger_peer_websocket.h b/modules/websocket/remote_debugger_peer_websocket.h index 590d925dcc..3227065ded 100644 --- a/modules/websocket/remote_debugger_peer_websocket.h +++ b/modules/websocket/remote_debugger_peer_websocket.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 */ @@ -31,12 +31,13 @@ #ifndef REMOTE_DEBUGGER_PEER_WEBSOCKET_H #define REMOTE_DEBUGGER_PEER_WEBSOCKET_H +#include "core/debugger/remote_debugger_peer.h" + #ifdef JAVASCRIPT_ENABLED -#include "modules/websocket/emws_client.h" +#include "emws_client.h" #else -#include "modules/websocket/wsl_client.h" +#include "wsl_client.h" #endif -#include "core/debugger/remote_debugger_peer.h" class RemoteDebuggerPeerWebSocket : public RemoteDebuggerPeer { Ref<WebSocketClient> ws_client; @@ -50,14 +51,14 @@ public: static RemoteDebuggerPeer *create(const String &p_uri); Error connect_to_host(const String &p_uri); - bool is_peer_connected(); - int get_max_message_size() const; - bool has_message(); - Error put_message(const Array &p_arr); - Array get_message(); - void close(); - void poll(); - bool can_block() const; + bool is_peer_connected() override; + int get_max_message_size() const override; + bool has_message() override; + Error put_message(const Array &p_arr) override; + Array get_message() override; + void close() override; + void poll() override; + bool can_block() const override; RemoteDebuggerPeerWebSocket(Ref<WebSocketPeer> p_peer = Ref<WebSocketPeer>()); }; diff --git a/modules/websocket/websocket_client.cpp b/modules/websocket/websocket_client.cpp index bf35c91c7f..2734b4b88f 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-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 */ diff --git a/modules/websocket/websocket_client.h b/modules/websocket/websocket_client.h index c7f17f1ffb..d6c072ae16 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-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 */ diff --git a/modules/websocket/websocket_macros.h b/modules/websocket/websocket_macros.h index 2ca60a3b61..a01ae65c56 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-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 */ diff --git a/modules/websocket/websocket_multiplayer_peer.cpp b/modules/websocket/websocket_multiplayer_peer.cpp index e54bfbca12..7a3bbf1c47 100644 --- a/modules/websocket/websocket_multiplayer_peer.cpp +++ b/modules/websocket/websocket_multiplayer_peer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 */ diff --git a/modules/websocket/websocket_multiplayer_peer.h b/modules/websocket/websocket_multiplayer_peer.h index 380edf67ed..43d9d59f38 100644 --- a/modules/websocket/websocket_multiplayer_peer.h +++ b/modules/websocket/websocket_multiplayer_peer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 */ diff --git a/modules/websocket/websocket_peer.cpp b/modules/websocket/websocket_peer.cpp index ee13040821..a0af9303b8 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-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 */ diff --git a/modules/websocket/websocket_peer.h b/modules/websocket/websocket_peer.h index 517b8600d6..13fef2424f 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-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 */ diff --git a/modules/websocket/websocket_server.cpp b/modules/websocket/websocket_server.cpp index e7f90fdeba..b7851b02c4 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-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 */ @@ -42,6 +42,7 @@ WebSocketServer::~WebSocketServer() { void WebSocketServer::_bind_methods() { ClassDB::bind_method(D_METHOD("is_listening"), &WebSocketServer::is_listening); + ClassDB::bind_method(D_METHOD("set_extra_headers", "headers"), &WebSocketServer::set_extra_headers, DEFVAL(Vector<String>())); ClassDB::bind_method(D_METHOD("listen", "port", "protocols", "gd_mp_api"), &WebSocketServer::listen, DEFVAL(Vector<String>()), DEFVAL(false)); ClassDB::bind_method(D_METHOD("stop"), &WebSocketServer::stop); ClassDB::bind_method(D_METHOD("has_peer", "id"), &WebSocketServer::has_peer); @@ -67,7 +68,7 @@ void WebSocketServer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_handshake_timeout"), &WebSocketServer::get_handshake_timeout); ClassDB::bind_method(D_METHOD("set_handshake_timeout", "timeout"), &WebSocketServer::set_handshake_timeout); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "handshake_timeout"), "set_handshake_timeout", "get_handshake_timeout"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "handshake_timeout"), "set_handshake_timeout", "get_handshake_timeout"); 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"))); diff --git a/modules/websocket/websocket_server.h b/modules/websocket/websocket_server.h index c4d651471f..7bd80851f5 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-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 */ @@ -51,6 +51,7 @@ protected: uint32_t handshake_timeout = 3000; public: + virtual void set_extra_headers(const Vector<String> &p_headers) = 0; virtual Error listen(int p_port, const Vector<String> p_protocols = Vector<String>(), bool gd_mp_api = false) = 0; virtual void stop() = 0; virtual bool is_listening() const = 0; diff --git a/modules/websocket/wsl_client.cpp b/modules/websocket/wsl_client.cpp index 26c0176ea4..894ba7766f 100644 --- a/modules/websocket/wsl_client.cpp +++ b/modules/websocket/wsl_client.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 */ @@ -91,6 +91,7 @@ void WSLClient::_do_handshake() { data->id = 1; _peer->make_context(data, _in_buf_size, _in_pkt_size, _out_buf_size, _out_pkt_size); _peer->set_no_delay(true); + _status = CONNECTION_CONNECTED; _on_connect(protocol); break; } @@ -103,13 +104,14 @@ bool WSLClient::_verify_headers(String &r_protocol) { String s = (char *)_resp_buf; Vector<String> psa = s.split("\r\n"); int len = psa.size(); - ERR_FAIL_COND_V_MSG(len < 4, false, "Not enough response headers, got: " + itos(len) + ", expected >= 4."); + ERR_FAIL_COND_V_MSG(len < 4, false, "Not enough response headers. Got: " + itos(len) + ", expected >= 4."); Vector<String> req = psa[0].split(" ", false); - ERR_FAIL_COND_V_MSG(req.size() < 2, false, "Invalid protocol or status code."); + ERR_FAIL_COND_V_MSG(req.size() < 2, false, "Invalid protocol or status code. Got '" + psa[0] + "', expected 'HTTP/1.1 101'."); // Wrong protocol - ERR_FAIL_COND_V_MSG(req[0] != "HTTP/1.1" || req[1] != "101", false, "Invalid protocol or status code."); + ERR_FAIL_COND_V_MSG(req[0] != "HTTP/1.1", false, "Invalid protocol. Got: '" + req[0] + "', expected 'HTTP/1.1'."); + ERR_FAIL_COND_V_MSG(req[1] != "101", false, "Invalid status code. Got: '" + req[1] + "', expected '101'."); Map<String, String> headers; for (int i = 1; i < len; i++) { @@ -124,22 +126,24 @@ bool WSLClient::_verify_headers(String &r_protocol) { } } -#define _WSL_CHECK(NAME, VALUE) \ +#define WSL_CHECK(NAME, VALUE) \ ERR_FAIL_COND_V_MSG(!headers.has(NAME) || headers[NAME].to_lower() != VALUE, false, \ "Missing or invalid header '" + String(NAME) + "'. Expected value '" + VALUE + "'."); -#define _WSL_CHECK_NC(NAME, VALUE) \ +#define WSL_CHECK_NC(NAME, VALUE) \ ERR_FAIL_COND_V_MSG(!headers.has(NAME) || headers[NAME] != VALUE, false, \ "Missing or invalid header '" + String(NAME) + "'. Expected value '" + VALUE + "'."); - _WSL_CHECK("connection", "upgrade"); - _WSL_CHECK("upgrade", "websocket"); - _WSL_CHECK_NC("sec-websocket-accept", WSLPeer::compute_key_response(_key)); -#undef _WSL_CHECK_NC -#undef _WSL_CHECK + WSL_CHECK("connection", "upgrade"); + WSL_CHECK("upgrade", "websocket"); + WSL_CHECK_NC("sec-websocket-accept", WSLPeer::compute_key_response(_key)); +#undef WSL_CHECK_NC +#undef WSL_CHECK if (_protocols.size() == 0) { // We didn't request a custom protocol - ERR_FAIL_COND_V(headers.has("sec-websocket-protocol"), false); + ERR_FAIL_COND_V_MSG(headers.has("sec-websocket-protocol"), false, "Received unrequested sub-protocol -> " + headers["sec-websocket-protocol"]); } else { - ERR_FAIL_COND_V(!headers.has("sec-websocket-protocol"), false); + // We requested at least one custom protocol but didn't receive one + ERR_FAIL_COND_V_MSG(!headers.has("sec-websocket-protocol"), false, "Requested sub-protocol(s) but received none."); + // Check received sub-protocol was one of those requested. r_protocol = headers["sec-websocket-protocol"]; bool valid = false; for (int i = 0; i < _protocols.size(); i++) { @@ -150,6 +154,7 @@ bool WSLClient::_verify_headers(String &r_protocol) { break; } if (!valid) { + ERR_FAIL_V_MSG(false, "Received unrequested sub-protocol -> " + r_protocol); return false; } } @@ -163,22 +168,24 @@ Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port, _peer = Ref<WSLPeer>(memnew(WSLPeer)); if (p_host.is_valid_ip_address()) { - ip_candidates.clear(); - ip_candidates.push_back(IPAddress(p_host)); + _ip_candidates.push_back(IPAddress(p_host)); } else { - ip_candidates = IP::get_singleton()->resolve_hostname_addresses(p_host); - } - - ERR_FAIL_COND_V(ip_candidates.is_empty(), ERR_INVALID_PARAMETER); - - String port = ""; - if ((p_port != 80 && !p_ssl) || (p_port != 443 && p_ssl)) { - port = ":" + itos(p_port); + // Queue hostname for resolution. + _resolver_id = IP::get_singleton()->resolve_hostname_queue_item(p_host); + ERR_FAIL_COND_V(_resolver_id == IP::RESOLVER_INVALID_ID, ERR_INVALID_PARAMETER); + // Check if it was found in cache. + IP::ResolverStatus ip_status = IP::get_singleton()->get_resolve_item_status(_resolver_id); + if (ip_status == IP::RESOLVER_STATUS_DONE) { + _ip_candidates = IP::get_singleton()->get_resolve_item_addresses(_resolver_id); + IP::get_singleton()->erase_resolve_item(_resolver_id); + _resolver_id = IP::RESOLVER_INVALID_ID; + } } - Error err = ERR_BUG; // Should be at least one entry. - while (ip_candidates.size() > 0) { - err = _tcp->connect_to_host(ip_candidates.pop_front(), p_port); + // We assume OK while hostname resolution is pending. + Error err = _resolver_id != IP::RESOLVER_INVALID_ID ? OK : FAILED; + while (_ip_candidates.size()) { + err = _tcp->connect_to_host(_ip_candidates.pop_front(), p_port); if (err == OK) { break; } @@ -200,8 +207,11 @@ Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port, } _key = WSLPeer::generate_key(); - // TODO custom extra headers (allow overriding this too?) String request = "GET " + p_path + " HTTP/1.1\r\n"; + String port = ""; + if ((p_port != 80 && !p_ssl) || (p_port != 443 && p_ssl)) { + port = ":" + itos(p_port); + } request += "Host: " + p_host + port + "\r\n"; request += "Upgrade: websocket\r\n"; request += "Connection: Upgrade\r\n"; @@ -222,6 +232,7 @@ Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port, } request += "\r\n"; _request = request.utf8(); + _status = CONNECTION_CONNECTING; return OK; } @@ -231,6 +242,30 @@ int WSLClient::get_max_packet_size() const { } void WSLClient::poll() { + if (_resolver_id != IP::RESOLVER_INVALID_ID) { + IP::ResolverStatus ip_status = IP::get_singleton()->get_resolve_item_status(_resolver_id); + if (ip_status == IP::RESOLVER_STATUS_WAITING) { + return; + } + // Anything else is either a candidate or a failure. + Error err = FAILED; + if (ip_status == IP::RESOLVER_STATUS_DONE) { + _ip_candidates = IP::get_singleton()->get_resolve_item_addresses(_resolver_id); + while (_ip_candidates.size()) { + err = _tcp->connect_to_host(_ip_candidates.pop_front(), _port); + if (err == OK) { + break; + } + } + } + IP::get_singleton()->erase_resolve_item(_resolver_id); + _resolver_id = IP::RESOLVER_INVALID_ID; + if (err != OK) { + disconnect_from_host(); + _on_error(); + return; + } + } if (_peer->is_connected_to_host()) { _peer->poll(); if (!_peer->is_connected_to_host()) { @@ -244,6 +279,7 @@ void WSLClient::poll() { return; // Not connected. } + _tcp->poll(); switch (_tcp->get_status()) { case StreamPeerTCP::STATUS_NONE: // Clean close @@ -251,7 +287,7 @@ void WSLClient::poll() { _on_error(); break; case StreamPeerTCP::STATUS_CONNECTED: { - ip_candidates.clear(); + _ip_candidates.clear(); Ref<StreamPeerSSL> ssl; if (_use_ssl) { if (_connection == _tcp) { @@ -282,9 +318,9 @@ void WSLClient::poll() { _do_handshake(); } break; case StreamPeerTCP::STATUS_ERROR: - while (ip_candidates.size() > 0) { + while (_ip_candidates.size() > 0) { _tcp->disconnect_from_host(); - if (_tcp->connect_to_host(ip_candidates.pop_front(), _port) == OK) { + if (_tcp->connect_to_host(_ip_candidates.pop_front(), _port) == OK) { return; } } @@ -303,21 +339,19 @@ Ref<WebSocketPeer> WSLClient::get_peer(int p_peer_id) const { } MultiplayerPeer::ConnectionStatus WSLClient::get_connection_status() const { + // This is surprising, but keeps the current behaviour to allow clean close requests. + // TODO Refactor WebSocket and split Client/Server/Multiplayer like done in other peers. if (_peer->is_connected_to_host()) { return CONNECTION_CONNECTED; } - - if (_tcp->is_connected_to_host()) { - return CONNECTION_CONNECTING; - } - - return CONNECTION_DISCONNECTED; + return _status; } void WSLClient::disconnect_from_host(int p_code, String p_reason) { _peer->close(p_code, p_reason); _connection = Ref<StreamPeer>(nullptr); _tcp = Ref<StreamPeerTCP>(memnew(StreamPeerTCP)); + _status = CONNECTION_DISCONNECTED; _key = ""; _host = ""; @@ -330,7 +364,12 @@ void WSLClient::disconnect_from_host(int p_code, String p_reason) { memset(_resp_buf, 0, sizeof(_resp_buf)); _resp_pos = 0; - ip_candidates.clear(); + if (_resolver_id != IP::RESOLVER_INVALID_ID) { + IP::get_singleton()->erase_resolve_item(_resolver_id); + _resolver_id = IP::RESOLVER_INVALID_ID; + } + + _ip_candidates.clear(); } IPAddress WSLClient::get_connected_host() const { diff --git a/modules/websocket/wsl_client.h b/modules/websocket/wsl_client.h index 3972977910..22d7ffa839 100644 --- a/modules/websocket/wsl_client.h +++ b/modules/websocket/wsl_client.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 */ @@ -52,6 +52,7 @@ private: Ref<WSLPeer> _peer; Ref<StreamPeerTCP> _tcp; Ref<StreamPeer> _connection; + ConnectionStatus _status = CONNECTION_DISCONNECTED; CharString _request; int _requested = 0; @@ -59,28 +60,27 @@ private: uint8_t _resp_buf[WSL_MAX_HEADER_SIZE]; int _resp_pos = 0; - String _response; - String _key; String _host; - int _port; - Array ip_candidates; + uint16_t _port = 0; + Array _ip_candidates; Vector<String> _protocols; bool _use_ssl = false; + IP::ResolverID _resolver_id = IP::RESOLVER_INVALID_ID; void _do_handshake(); bool _verify_headers(String &r_protocol); public: - Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets); - Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocol = Vector<String>(), const Vector<String> p_custom_headers = Vector<String>()); - int get_max_packet_size() const; - Ref<WebSocketPeer> get_peer(int p_peer_id) const; - void disconnect_from_host(int p_code = 1000, String p_reason = ""); - IPAddress get_connected_host() const; - uint16_t get_connected_port() const; - virtual ConnectionStatus get_connection_status() const; - virtual void poll(); + Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) override; + Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocol = Vector<String>(), const Vector<String> p_custom_headers = Vector<String>()) override; + int get_max_packet_size() const override; + Ref<WebSocketPeer> get_peer(int p_peer_id) const override; + void disconnect_from_host(int p_code = 1000, String p_reason = "") override; + IPAddress get_connected_host() const override; + uint16_t get_connected_port() const override; + virtual ConnectionStatus get_connection_status() const override; + virtual void poll() override; WSLClient(); ~WSLClient(); diff --git a/modules/websocket/wsl_peer.cpp b/modules/websocket/wsl_peer.cpp index fc520ec57c..15df4d039c 100644 --- a/modules/websocket/wsl_peer.cpp +++ b/modules/websocket/wsl_peer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 */ @@ -146,17 +146,17 @@ void wsl_msg_recv_callback(wslay_event_context_ptr ctx, const struct wslay_event if (!peer_data->valid || peer_data->closing) { return; } - WSLPeer *peer = (WSLPeer *)peer_data->peer; + WSLPeer *peer = static_cast<WSLPeer *>(peer_data->peer); if (peer->parse_message(arg) != OK) { return; } if (peer_data->is_server) { - WSLServer *helper = (WSLServer *)peer_data->obj; + WSLServer *helper = static_cast<WSLServer *>(peer_data->obj); helper->_on_peer_packet(peer_data->id); } else { - WSLClient *helper = (WSLClient *)peer_data->obj; + WSLClient *helper = static_cast<WSLClient *>(peer_data->obj); helper->_on_peer_packet(); } } @@ -184,10 +184,10 @@ Error WSLPeer::parse_message(const wslay_event_on_msg_recv_arg *arg) { } if (!wslay_event_get_close_sent(_data->ctx)) { if (_data->is_server) { - WSLServer *helper = (WSLServer *)_data->obj; + WSLServer *helper = static_cast<WSLServer *>(_data->obj); helper->_on_close_request(_data->id, close_code, close_reason); } else { - WSLClient *helper = (WSLClient *)_data->obj; + WSLClient *helper = static_cast<WSLClient *>(_data->obj); helper->_on_close_request(close_code, close_reason); } } diff --git a/modules/websocket/wsl_peer.h b/modules/websocket/wsl_peer.h index 260d4b183d..abeecdd537 100644 --- a/modules/websocket/wsl_peer.h +++ b/modules/websocket/wsl_peer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 */ @@ -85,22 +85,22 @@ public: String close_reason; void poll(); // Used by client and server. - virtual int get_available_packet_count() const; - virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); - virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size); - virtual int get_max_packet_size() const { return _packet_buffer.size(); }; - virtual int get_current_outbound_buffered_amount() const; + virtual int get_available_packet_count() const override; + virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; + virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override; + virtual int get_max_packet_size() const override { return _packet_buffer.size(); }; + virtual int get_current_outbound_buffered_amount() const override; virtual void close_now(); - virtual void close(int p_code = 1000, String p_reason = ""); - virtual bool is_connected_to_host() const; - virtual IPAddress get_connected_host() const; - virtual uint16_t get_connected_port() const; - - virtual WriteMode get_write_mode() const; - virtual void set_write_mode(WriteMode p_mode); - virtual bool was_string_packet() const; - virtual void set_no_delay(bool p_enabled); + virtual void close(int p_code = 1000, String p_reason = "") override; + virtual bool is_connected_to_host() const override; + virtual IPAddress get_connected_host() const override; + virtual uint16_t get_connected_port() const override; + + virtual WriteMode get_write_mode() const override; + virtual void set_write_mode(WriteMode p_mode) override; + virtual bool was_string_packet() const override; + virtual void set_no_delay(bool p_enabled) override; void make_context(PeerData *p_data, unsigned int p_in_buf_size, unsigned int p_in_pkt_size, unsigned int p_out_buf_size, unsigned int p_out_pkt_size); Error parse_message(const wslay_event_on_msg_recv_arg *arg); diff --git a/modules/websocket/wsl_server.cpp b/modules/websocket/wsl_server.cpp index 514b2d055f..b58b2e4724 100644 --- a/modules/websocket/wsl_server.cpp +++ b/modules/websocket/wsl_server.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 */ @@ -58,17 +58,17 @@ bool WSLServer::PendingPeer::_parse_request(const Vector<String> p_protocols, St headers[name] = value; } } -#define _WSL_CHECK(NAME, VALUE) \ +#define WSL_CHECK(NAME, VALUE) \ ERR_FAIL_COND_V_MSG(!headers.has(NAME) || headers[NAME].to_lower() != VALUE, false, \ "Missing or invalid header '" + String(NAME) + "'. Expected value '" + VALUE + "'."); -#define _WSL_CHECK_EX(NAME) \ +#define WSL_CHECK_EX(NAME) \ ERR_FAIL_COND_V_MSG(!headers.has(NAME), false, "Missing header '" + String(NAME) + "'."); - _WSL_CHECK("upgrade", "websocket"); - _WSL_CHECK("sec-websocket-version", "13"); - _WSL_CHECK_EX("sec-websocket-key"); - _WSL_CHECK_EX("connection"); -#undef _WSL_CHECK_EX -#undef _WSL_CHECK + WSL_CHECK("upgrade", "websocket"); + WSL_CHECK("sec-websocket-version", "13"); + WSL_CHECK_EX("sec-websocket-key"); + WSL_CHECK_EX("connection"); +#undef WSL_CHECK_EX +#undef WSL_CHECK key = headers["sec-websocket-key"]; if (headers.has("sec-websocket-protocol")) { Vector<String> protos = headers["sec-websocket-protocol"].split(","); @@ -83,11 +83,11 @@ bool WSLServer::PendingPeer::_parse_request(const Vector<String> p_protocols, St break; } // Found a protocol - if (protocol != "") { + if (!protocol.is_empty()) { break; } } - if (protocol == "") { // Invalid protocol(s) requested + if (protocol.is_empty()) { // Invalid protocol(s) requested return false; } } else if (p_protocols.size() > 0) { // No protocol requested, but we need one @@ -96,7 +96,7 @@ bool WSLServer::PendingPeer::_parse_request(const Vector<String> p_protocols, St return true; } -Error WSLServer::PendingPeer::do_handshake(const Vector<String> p_protocols, uint64_t p_timeout, String &r_resource_name) { +Error WSLServer::PendingPeer::do_handshake(const Vector<String> p_protocols, uint64_t p_timeout, String &r_resource_name, const Vector<String> &p_extra_headers) { if (OS::get_singleton()->get_ticks_msec() - time > p_timeout) { print_verbose(vformat("WebSocket handshake timed out after %.3f seconds.", p_timeout * 0.001)); return ERR_TIMEOUT; @@ -138,9 +138,12 @@ Error WSLServer::PendingPeer::do_handshake(const Vector<String> p_protocols, uin s += "Upgrade: websocket\r\n"; s += "Connection: Upgrade\r\n"; s += "Sec-WebSocket-Accept: " + WSLPeer::compute_key_response(key) + "\r\n"; - if (protocol != "") { + if (!protocol.is_empty()) { s += "Sec-WebSocket-Protocol: " + protocol + "\r\n"; } + for (int i = 0; i < p_extra_headers.size(); i++) { + s += p_extra_headers[i] + "\r\n"; + } s += "\r\n"; response = s.utf8(); has_request = true; @@ -167,6 +170,10 @@ Error WSLServer::PendingPeer::do_handshake(const Vector<String> p_protocols, uin return OK; } +void WSLServer::set_extra_headers(const Vector<String> &p_headers) { + _extra_headers = p_headers; +} + Error WSLServer::listen(int p_port, const Vector<String> p_protocols, bool gd_mp_api) { ERR_FAIL_COND_V(is_listening(), ERR_ALREADY_IN_USE); @@ -183,7 +190,7 @@ Error WSLServer::listen(int p_port, const Vector<String> p_protocols, bool gd_mp void WSLServer::poll() { List<int> remove_ids; for (const KeyValue<int, Ref<WebSocketPeer>> &E : _peer_map) { - Ref<WSLPeer> peer = (WSLPeer *)E.value.ptr(); + Ref<WSLPeer> peer = const_cast<WSLPeer *>(static_cast<const WSLPeer *>(E.value.ptr())); peer->poll(); if (!peer->is_connected_to_host()) { _on_disconnect(E.key, peer->close_code != -1); @@ -199,7 +206,7 @@ void WSLServer::poll() { for (const Ref<PendingPeer> &E : _pending) { String resource_name; Ref<PendingPeer> ppeer = E; - Error err = ppeer->do_handshake(_protocols, handshake_timeout, resource_name); + Error err = ppeer->do_handshake(_protocols, handshake_timeout, resource_name, _extra_headers); if (err == ERR_BUSY) { continue; } else if (err != OK) { @@ -266,7 +273,7 @@ int WSLServer::get_max_packet_size() const { void WSLServer::stop() { _server->stop(); for (const KeyValue<int, Ref<WebSocketPeer>> &E : _peer_map) { - Ref<WSLPeer> peer = (WSLPeer *)E.value.ptr(); + Ref<WSLPeer> peer = const_cast<WSLPeer *>(static_cast<const WSLPeer *>(E.value.ptr())); peer->close_now(); } _pending.clear(); diff --git a/modules/websocket/wsl_server.h b/modules/websocket/wsl_server.h index 508b5a12a1..a920e9c665 100644 --- a/modules/websocket/wsl_server.h +++ b/modules/websocket/wsl_server.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 */ @@ -62,7 +62,7 @@ private: CharString response; int response_sent = 0; - Error do_handshake(const Vector<String> p_protocols, uint64_t p_timeout, String &r_resource_name); + Error do_handshake(const Vector<String> p_protocols, uint64_t p_timeout, String &r_resource_name, const Vector<String> &p_extra_headers); }; int _in_buf_size = DEF_BUF_SHIFT; @@ -73,19 +73,21 @@ private: List<Ref<PendingPeer>> _pending; Ref<TCPServer> _server; Vector<String> _protocols; + Vector<String> _extra_headers; public: - Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets); - Error listen(int p_port, const Vector<String> p_protocols = Vector<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; - IPAddress get_peer_address(int p_peer_id) const; - int get_peer_port(int p_peer_id) const; - void disconnect_peer(int p_peer_id, int p_code = 1000, String p_reason = ""); - virtual void poll(); + Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) override; + void set_extra_headers(const Vector<String> &p_headers) override; + Error listen(int p_port, const Vector<String> p_protocols = Vector<String>(), bool gd_mp_api = false) override; + void stop() override; + bool is_listening() const override; + int get_max_packet_size() const override; + bool has_peer(int p_id) const override; + Ref<WebSocketPeer> get_peer(int p_id) const override; + IPAddress get_peer_address(int p_peer_id) const override; + int get_peer_port(int p_peer_id) const override; + void disconnect_peer(int p_peer_id, int p_code = 1000, String p_reason = "") override; + virtual void poll() override; WSLServer(); ~WSLServer(); |