From 2feba341be3c13c84a153b9e9b1824293c259177 Mon Sep 17 00:00:00 2001 From: Relintai Date: Thu, 7 Jul 2022 21:44:14 +0200 Subject: [PATCH] Reworked the WebServer to automatically find it's root webnode, and HTTPSessionManager. Also Added notifications for when the server starts and stops, and implemented the write lock for it. --- modules/web/http/web_server.cpp | 123 +++++++++++++++++++++++--------- modules/web/http/web_server.h | 33 ++++++--- 2 files changed, 114 insertions(+), 42 deletions(-) diff --git a/modules/web/http/web_server.cpp b/modules/web/http/web_server.cpp index e304d6fe8..90907e17d 100644 --- a/modules/web/http/web_server.cpp +++ b/modules/web/http/web_server.cpp @@ -1,47 +1,30 @@ #include "web_server.h" +#include "core/class_db.h" #include "web_node.h" #include "web_server_request.h" #include "http_session_manager.h" -NodePath WebServer::get_web_root_path() const { - return _web_root_path; -} -void WebServer::set_web_root_path(const NodePath &path) { - _web_root_path = path; +bool WebServer::get_is_running() { + return _is_running; } WebNode *WebServer::get_web_root() { return _web_root; } -void WebServer::set_web_root(WebNode *root) { - _web_root = root; -} - Node *WebServer::get_web_root_bind() { return _web_root; } -void WebServer::set_web_root_bind(Node *root) { - WebNode *web_root = Object::cast_to(root); - - _web_root = web_root; -} HTTPSessionManager *WebServer::get_session_manager() { return _session_manager; } -void WebServer::set_session_manager(HTTPSessionManager *sess_man) { - _session_manager = sess_man; -} Node *WebServer::get_session_manager_bind() { return _session_manager; } -void WebServer::set_session_manager_bind(Node *sess_man) { - _session_manager = Object::cast_to(sess_man); -} void WebServer::server_handle_request(Ref request) { ERR_FAIL_COND(!_web_root); @@ -58,6 +41,21 @@ void WebServer::request_write_lock() { _write_lock_requested = true; } +void WebServer::refresh_root() { + _web_root = nullptr; + _session_manager = nullptr; + + for (int i = 0; i < get_child_count(); ++i) { + if (!_web_root) { + _web_root = Object::cast_to(get_child(i)); + } + + if (!_session_manager) { + _session_manager = Object::cast_to(get_child(i)); + } + } +} + void WebServer::start() { call("_start"); } @@ -66,35 +64,92 @@ void WebServer::stop() { } void WebServer::_start() { - //look up root node, and sessionmanager, if not set. - - if (!_web_root && _web_root_path != NodePath()) { - _web_root = Object::cast_to(get_node_or_null(_web_root_path)); + if (_is_running) { + return; } + + _is_running = true; + + refresh_root(); } void WebServer::_stop() { + if (!_is_running) { + return; + } + + _is_running = false; +} + +String WebServer::get_configuration_warning() const { + int webnode_count = 0; + int session_manager_count = 0; + + for (int i = 0; i < get_child_count(); ++i) { + if (!_web_root) { + WebNode *wn = Object::cast_to(get_child(i)); + + if (wn) { + ++webnode_count; + } + } + + if (!_session_manager) { + HTTPSessionManager *sm = Object::cast_to(get_child(i)); + + if (sm) { + ++session_manager_count; + } + } + } + + String err; + + if (webnode_count == 0) { + err += "You need one (and only one) WebNode as the child for the webserver to work. (A WebRoot Node is recommended by default.)"; + } else if (webnode_count > 1) { + err += "You have more than one root WebNode as the child of the server. Please move them under a new common WebNode, else the server will only use the first one!"; + } + + if (session_manager_count > 1) { + if (err != "") { + err += "\n"; + } + + err += "You have more than one HTTPSessionManager nodes as child, the server will only be able use the first one. Please remove the other."; + } + + return err; } WebServer::WebServer() { + _is_running = false; + _web_root = nullptr; + _session_manager = nullptr; _write_lock_requested = false; + + set_process_internal(true); } WebServer::~WebServer() { } +void WebServer::_notification(int p_what) { + if (p_what == NOTIFICATION_INTERNAL_PROCESS) { + if (_write_lock_requested) { + _rw_lock.write_lock(); + notification(NOTIFICATION_WEB_SERVER_WRITE_LOCK_ACQUIRED); + //the root could have changed. + refresh_root(); + _rw_lock.write_unlock(); + } + } +} + void WebServer::_bind_methods() { - ClassDB::bind_method(D_METHOD("get_web_root_path"), &WebServer::get_web_root_path); - ClassDB::bind_method(D_METHOD("set_web_root_path", "val"), &WebServer::set_web_root_path); - ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "web_root_path"), "set_web_root_path", "get_web_root_path"); - ClassDB::bind_method(D_METHOD("get_web_root"), &WebServer::get_web_root_bind); - ClassDB::bind_method(D_METHOD("set_web_root", "val"), &WebServer::set_web_root_bind); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "web_root", PROPERTY_HINT_RESOURCE_TYPE, "WebRoot", 0), "set_web_root", "get_web_root"); - ClassDB::bind_method(D_METHOD("get_session_manager"), &WebServer::get_session_manager_bind); - ClassDB::bind_method(D_METHOD("set_session_manager", "val"), &WebServer::set_session_manager_bind); ClassDB::bind_method(D_METHOD("server_handle_request", "request"), &WebServer::server_handle_request); ClassDB::bind_method(D_METHOD("request_write_lock"), &WebServer::request_write_lock); @@ -105,4 +160,8 @@ void WebServer::_bind_methods() { ClassDB::bind_method(D_METHOD("stop"), &WebServer::stop); ClassDB::bind_method(D_METHOD("_start"), &WebServer::_start); ClassDB::bind_method(D_METHOD("_stop"), &WebServer::_stop); + + BIND_CONSTANT(NOTIFICATION_WEB_SERVER_STARTED); + BIND_CONSTANT(NOTIFICATION_WEB_SERVER_STOPPED); + BIND_CONSTANT(NOTIFICATION_WEB_SERVER_WRITE_LOCK_ACQUIRED); } diff --git a/modules/web/http/web_server.h b/modules/web/http/web_server.h index ab52446d1..bd808b70e 100644 --- a/modules/web/http/web_server.h +++ b/modules/web/http/web_server.h @@ -13,40 +13,53 @@ class WebServer : public Node { GDCLASS(WebServer, Node); public: - NodePath get_web_root_path() const; - void set_web_root_path(const NodePath &path); + enum { + NOTIFICATION_WEB_SERVER_STARTED = 2000, + NOTIFICATION_WEB_SERVER_STOPPED = 2001, + NOTIFICATION_WEB_SERVER_WRITE_LOCK_ACQUIRED = 2002, + }; + + bool get_is_running(); WebNode *get_web_root(); - void set_web_root(WebNode *root); - Node *get_web_root_bind(); - void set_web_root_bind(Node *root); HTTPSessionManager *get_session_manager(); - void set_session_manager(HTTPSessionManager *sess_man); - Node *get_session_manager_bind(); - void set_session_manager_bind(Node *sess_man); void server_handle_request(Ref request); + // The server is expected to be running in a heavily multi threaded setting, + // if you want to safely change to root, or to heavily change the tree + // you need to lock the tree, however triggering a change like this using a web interface + // from withing a server would cause a deadlock, so if you want to do something like this + // you can call request_write_lock() while handling a request, then + // do the desired change when your node receives NOTIFICATION_WEB_SERVER_WRITE_LOCK_ACQUIRED + // Also note that every WebNode has a lock similar to this for the same reason, + // if you want to only change a certain WebNode's children, only lock that particular node. void request_write_lock(); + void refresh_root(); + void start(); void stop(); virtual void _start(); virtual void _stop(); + String get_configuration_warning() const; + WebServer(); ~WebServer(); protected: + void _notification(int p_what); + static void _bind_methods(); - NodePath _web_root_path; - WebNode *_web_root; + bool _is_running; + WebNode *_web_root; HTTPSessionManager *_session_manager; //TODO this should be atomic