From 9aced759668cfb693c66f53e7560f6c16084859e Mon Sep 17 00:00:00 2001 From: Relintai Date: Sun, 18 Dec 2022 14:03:07 +0100 Subject: [PATCH] Added HTTPSessionManagerDB using the disabled code in HTTPSessionManager, and cleaned up it's logic. --- modules/web/SCsub | 3 + modules/web/config.py | 2 + .../web/database/http_session_manager_db.cpp | 385 ++++++++++++++++++ .../web/database/http_session_manager_db.h | 72 ++++ modules/web/register_types.cpp | 11 + 5 files changed, 473 insertions(+) create mode 100644 modules/web/database/http_session_manager_db.cpp create mode 100644 modules/web/database/http_session_manager_db.h diff --git a/modules/web/SCsub b/modules/web/SCsub index 78a649ff1..e318fff2e 100644 --- a/modules/web/SCsub +++ b/modules/web/SCsub @@ -63,6 +63,9 @@ if env["tools"]: sources.append("editor/web_node_editor_web_server.cpp") sources.append("editor/web_node_editor_web_server_request.cpp") +if env['module_database_enabled']: + sources.append("database/http_session_manager_db.cpp") + if ARGUMENTS.get('custom_modules_shared', 'no') == 'yes': # Shared lib compilation module_env.Append(CCFLAGS=['-fPIC']) diff --git a/modules/web/config.py b/modules/web/config.py index 1fc6cb521..8c3ce6b93 100644 --- a/modules/web/config.py +++ b/modules/web/config.py @@ -75,6 +75,8 @@ def get_doc_classes(): "RedirectWebPage", "AliasWebPage", + + "HTTPSessionManagerDB", ] diff --git a/modules/web/database/http_session_manager_db.cpp b/modules/web/database/http_session_manager_db.cpp new file mode 100644 index 000000000..84b4aeda6 --- /dev/null +++ b/modules/web/database/http_session_manager_db.cpp @@ -0,0 +1,385 @@ +#include "http_session_manager_db.h" + +#include "../http/http_session.h" + +#include "modules/database/database.h" +#include "modules/database/database_connection.h" +#include "modules/database/database_manager.h" +#include "modules/database/query_builder.h" +#include "modules/database/query_result.h" +#include "modules/database/table_builder.h" + +#include "../http/web_server.h" +#include "../http/web_server_cookie.h" +#include "../http/web_server_request.h" + +#include "core/bind/core_bind.h" + +String HTTPSessionManagerDB::get_database_table_name() { + return _database_table_name; +} +void HTTPSessionManagerDB::set_database_table_name(const String &val) { + _database_table_name = val; +} + +String HTTPSessionManagerDB::get_database_data_table_name() { + return _database_data_table_name; +} +void HTTPSessionManagerDB::set_database_data_table_name(const String &val) { + _database_data_table_name = val; +} + +Ref HTTPSessionManagerDB::get_database() { + if (_database.is_valid()) { + return _database; + } + + return DatabaseManager::get_singleton()->get_ddb(); +} + +void HTTPSessionManagerDB::set_database(const Ref &db) { + _database = db; + + // todo send event to children when it's implemented? +} + +Ref HTTPSessionManagerDB::get_database_connection() { + Ref db = get_database(); + + ERR_FAIL_COND_V(!db.is_valid(), Ref()); + + Ref conn = db->get_connection(); + + ERR_FAIL_COND_V(!conn.is_valid(), Ref()); + + return conn; +} + +Ref HTTPSessionManagerDB::get_table_builder() { + Ref db = get_database(); + + ERR_FAIL_COND_V(!db.is_valid(), Ref()); + + Ref conn = db->get_connection(); + + ERR_FAIL_COND_V(!conn.is_valid(), Ref()); + + return conn->get_table_builder(); +} + +Ref HTTPSessionManagerDB::get_query_builder() { + Ref db = get_database(); + + ERR_FAIL_COND_V(!db.is_valid(), Ref()); + + Ref conn = db->get_connection(); + + ERR_FAIL_COND_V(!conn.is_valid(), Ref()); + + return conn->get_query_builder(); +} + +void HTTPSessionManagerDB::add_session(Ref session) { + if (!session.is_valid()) { + printf("HTTPSessionManagerDB::add_session: ERROR, session is null!\n"); + return; + } + + _mutex.lock(); + + _sessions_vec.push_back(session); + _sessions[session->session_id] = session; + + _mutex.unlock(); +} + +void HTTPSessionManagerDB::remove_session(Ref session) { + if (!session.is_valid()) { + printf("HTTPSessionManagerDB::remove_session: ERROR, session is null!\n"); + return; + } + + _mutex.lock(); + + _sessions.erase(session->session_id); + + for (int i = 0; i < _sessions_vec.size(); ++i) { + if (_sessions_vec[i] == session) { + _sessions_vec.remove(i); + _mutex.unlock(); + return; + } + } + + _mutex.unlock(); +} + +void HTTPSessionManagerDB::delete_session(const String &session_id) { + _mutex.lock(); + + Ref s = _sessions[session_id]; + + _sessions.erase(session_id); + + for (int i = 0; i < _sessions_vec.size(); ++i) { + Ref sess = _sessions_vec[i]; + + if (sess->session_id == session_id) { + _sessions_vec.remove(i); + + break; + } + } + + _mutex.unlock(); + + if (!s.is_valid()) { + return; + } + + if (!s->id) { + return; + } + + Ref b = get_query_builder(); + + b->del(_database_data_table_name)->where()->wpi("session_db_id", s->id)->end_command(); + b->del(_database_table_name)->where()->wpi("id", s->id)->end_command(); + b->run_query(); +} + +void HTTPSessionManagerDB::save_session(Ref session) { + Ref b = get_query_builder(); + + if (!session->id) { + b->insert(_database_table_name, "session_id"); + b->values(); + b->vals(session->session_id); + b->cvalues(); + b->end_command(); + b->select_last_insert_id(); + + session->id = b->run()->get_last_insert_rowid(); + + b->reset(); + } + + b->del(_database_data_table_name)->where()->wpi("session_db_id", session->id)->end_command(); + int id = session->id; + + HashMap *m = session->get_data(); + + const String *k = NULL; + + while ((k = m->next(k))) { + const Variant &val = m->get(*k); + + Variant::Type t = val.get_type(); + + // Maybe it should be allowed? + // Or maybe when adding stuff to the sessions the method should have a store = true bool, if false skip saving + if (t == Variant::OBJECT) { + continue; + } + + String vb64 = _Marshalls::get_singleton()->variant_to_base64(val); + + b->insert(_database_data_table_name, "session_db_id,key,value")->values()->vali(id)->vals(*k)->vals(vb64)->cvalues()->end_command(); + } + + b->run_query(); +} + +Ref HTTPSessionManagerDB::get_session(const String &session_id) { + return _sessions[session_id]; +} + +Ref HTTPSessionManagerDB::create_session() { + Ref session; + session.instance(); + + while (true) { + session->session_id = generate_session_id(session->session_id); + + _mutex.lock(); + + if (_sessions[session->session_id] == nullptr) { + _sessions_vec.push_back(session); + _sessions[session->session_id] = session; + + _mutex.unlock(); + + return session; + } + + _mutex.unlock(); + } + + save_session(session); + + return session; +} + +void HTTPSessionManagerDB::load_sessions() { + clear(); + + Ref b = get_query_builder(); + + b->select("id, session_id"); + b->from(_database_table_name); + // b->print(); + Ref r = b->run(); + + while (r->next_row()) { + int id = r->get_cell_int(0); + String session_id = r->get_cell(1); + + Ref s; + s.instance(); + s->id = id; + + s->session_id = session_id; + + add_session(s); + } + + b->reset(); + + b->select("session_db_id,key,value"); + b->from(_database_data_table_name); + // b->print(); + r = b->run(); + + while (r->next_row()) { + int session_db_id = r->get_cell_int(0); + + Ref s; + + for (int i = 0; i < _sessions_vec.size(); ++i) { + Ref ss = _sessions_vec[i]; + + if (ss.is_valid() && session_db_id == ss->id) { + s = ss; + break; + } + } + + if (!s.is_valid()) { + printf("Error: HTTPSessionManagerDB::load_sessions(): %d sid doesn't exists!\n", session_db_id); + + continue; + } + + String key = r->get_cell(1); + String vb64 = r->get_cell(2); + + Variant val = _Marshalls::get_singleton()->base64_to_variant(vb64); + + s->add(key, val); + } +} + +void HTTPSessionManagerDB::clear() { + _mutex.lock(); + + _sessions.clear(); + _sessions_vec.clear(); + + _mutex.unlock(); +} + +String HTTPSessionManagerDB::generate_session_id(const String &base) { + // todo make something simpler / better + + String sid = base; + + sid += itos(Math::rand()); + + return sid.sha256_text().substr(0, 20); +} + +void HTTPSessionManagerDB::migrate() { + drop_table(); + create_table(); +} + +void HTTPSessionManagerDB::create_table() { + Ref tb = get_table_builder(); + + tb->create_table(_database_table_name); + tb->integer("id")->auto_increment()->next_row(); + tb->varchar("session_id", 100)->next_row(); + tb->primary_key("id"); + tb->ccreate_table(); + // tb->print(); + tb->run_query(); + + tb->result = ""; + + tb->create_table(_database_data_table_name); + tb->integer("session_db_id")->not_null()->next_row(); + tb->varchar("key", 100)->next_row(); + tb->text("value")->not_null()->next_row(); + tb->foreign_key("session_db_id"); + tb->references(_database_table_name, "id"); + tb->ccreate_table(); + // tb->print(); + tb->run_query(); +} +void HTTPSessionManagerDB::drop_table() { + Ref tb = get_table_builder(); + + tb->drop_table_if_exists(_database_table_name)->run_query(); + tb->drop_table_if_exists(_database_data_table_name)->run_query(); +} + +HTTPSessionManagerDB::HTTPSessionManagerDB() { + _database_table_name = "sessions"; + _database_data_table_name = "session_data"; +} + +HTTPSessionManagerDB::~HTTPSessionManagerDB() { + clear(); +} + +void HTTPSessionManagerDB::_notification(const int what) { + switch (what) { + case NOTIFICATION_ENTER_TREE: { + DatabaseManager::get_singleton()->connect("migration", this, "migrate"); + } break; + case NOTIFICATION_EXIT_TREE: { + DatabaseManager::get_singleton()->disconnect("migration", this, "migrate"); + } break; + default: + break; + } +} + +void HTTPSessionManagerDB::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_database_table_name"), &HTTPSessionManagerDB::get_database_table_name); + ClassDB::bind_method(D_METHOD("set_database_table_name", "val"), &HTTPSessionManagerDB::set_database_table_name); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "database_table_name"), "set_database_table_name", "get_database_table_name"); + + ClassDB::bind_method(D_METHOD("get_database_data_table_name"), &HTTPSessionManagerDB::get_database_data_table_name); + ClassDB::bind_method(D_METHOD("set_database_data_table_name", "val"), &HTTPSessionManagerDB::set_database_data_table_name); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "database_data_table_name"), "set_database_data_table_name", "get_database_data_table_name"); + + ClassDB::bind_method(D_METHOD("get_database"), &HTTPSessionManagerDB::get_database); + ClassDB::bind_method(D_METHOD("set_database", "val"), &HTTPSessionManagerDB::set_database); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "database", PROPERTY_HINT_RESOURCE_TYPE, "Database", 0), "set_database", "get_database"); + + ClassDB::bind_method(D_METHOD("get_database_connection"), &HTTPSessionManagerDB::get_database_connection); + ClassDB::bind_method(D_METHOD("get_table_builder"), &HTTPSessionManagerDB::get_table_builder); + ClassDB::bind_method(D_METHOD("get_query_builder"), &HTTPSessionManagerDB::get_query_builder); + + ClassDB::bind_method(D_METHOD("add_session", "session"), &HTTPSessionManagerDB::add_session); + ClassDB::bind_method(D_METHOD("remove_session", "session"), &HTTPSessionManagerDB::remove_session); + ClassDB::bind_method(D_METHOD("delete_session", "session_id"), &HTTPSessionManagerDB::delete_session); + ClassDB::bind_method(D_METHOD("save_session", "session"), &HTTPSessionManagerDB::save_session); + ClassDB::bind_method(D_METHOD("get_session", "session_id"), &HTTPSessionManagerDB::get_session); + ClassDB::bind_method(D_METHOD("create_session"), &HTTPSessionManagerDB::create_session); + + ClassDB::bind_method(D_METHOD("load_sessions"), &HTTPSessionManagerDB::load_sessions); + ClassDB::bind_method(D_METHOD("clear"), &HTTPSessionManagerDB::clear); + ClassDB::bind_method(D_METHOD("generate_session_id", "base"), &HTTPSessionManagerDB::generate_session_id, ""); +} diff --git a/modules/web/database/http_session_manager_db.h b/modules/web/database/http_session_manager_db.h new file mode 100644 index 000000000..5b39b3d62 --- /dev/null +++ b/modules/web/database/http_session_manager_db.h @@ -0,0 +1,72 @@ +#ifndef HTTP_SESSION_MANAGER_DB_H +#define HTTP_SESSION_MANAGER_DB_H + +#include "core/containers/hash_map.h" +#include "core/containers/vector.h" +#include "core/os/mutex.h" +#include "core/string/ustring.h" + +#include "core/object/reference.h" +#include "scene/main/node.h" + +#include "../http/web_server_middleware.h" + +class HTTPSession; +class WebServerRequest; +class Database; +class DatabaseConnection; +class TableBuilder; +class QueryBuilder; + +class HTTPSessionManagerDB : public Node { + GDCLASS(HTTPSessionManagerDB, Node); + +public: + String get_database_table_name(); + void set_database_table_name(const String &val); + + String get_database_data_table_name(); + void set_database_data_table_name(const String &val); + + Ref get_database(); + void set_database(const Ref &db); + + Ref get_database_connection(); + Ref get_table_builder(); + Ref get_query_builder(); + + void add_session(Ref session); + void remove_session(Ref session); + void delete_session(const String &session_id); + void save_session(Ref session); + Ref get_session(const String &session_id); + Ref create_session(); + + void load_sessions(); + + void clear(); + + virtual String generate_session_id(const String &base = ""); + + virtual void migrate(); + virtual void create_table(); + virtual void drop_table(); + + HTTPSessionManagerDB(); + ~HTTPSessionManagerDB(); + + HashMap> _sessions; + Vector> _sessions_vec; + Mutex _mutex; + +protected: + void _notification(const int what); + + static void _bind_methods(); + + String _database_table_name; + String _database_data_table_name; + Ref _database; +}; + +#endif diff --git a/modules/web/register_types.cpp b/modules/web/register_types.cpp index 6c6797b2a..d1cf90fdd 100644 --- a/modules/web/register_types.cpp +++ b/modules/web/register_types.cpp @@ -65,6 +65,12 @@ SOFTWARE. #include "editor/web_node_editor_plugin.h" #endif +#include "modules/modules_enabled.gen.h" + +#ifdef MODULE_DATABASE_ENABLED +#include "database/http_session_manager_db.h" +#endif + void register_web_types() { ClassDB::register_class<_HTMLBuilder>(); ClassDB::register_class<_HTMLTag>(); @@ -129,6 +135,11 @@ void register_web_types() { ClassDB::register_class(); ClassDB::register_class(); +#ifdef MODULE_DATABASE_ENABLED + ClassDB::register_class(); + +#endif + #if TOOLS_ENABLED EditorPlugins::add_by_type(); #endif