From b7cf03382007dbcca2fcda7913b167d09886b39b Mon Sep 17 00:00:00 2001 From: P4rth-B Date: Sun, 27 Apr 2025 19:07:33 +0530 Subject: [PATCH 1/2] feat(request-manager): add server request manager with proper synchronization --- include/server/connection_manager.hpp | 11 - include/server/fenris_server_struct.hpp | 79 ++++ include/server/request_manager.hpp | 42 ++ src/server/connection_manager.cpp | 10 +- src/server/fenris_server_struct.cpp | 94 +++++ src/server/request_manager.cpp | 493 ++++++++++++++++++++++++ 6 files changed, 712 insertions(+), 17 deletions(-) create mode 100644 include/server/fenris_server_struct.hpp create mode 100644 src/server/fenris_server_struct.cpp diff --git a/include/server/connection_manager.hpp b/include/server/connection_manager.hpp index 6682264..42fc224 100644 --- a/include/server/connection_manager.hpp +++ b/include/server/connection_manager.hpp @@ -19,17 +19,6 @@ namespace fenris { namespace server { -struct ClientInfo { - uint32_t client_id; - uint32_t socket; - std::string address; - std::string port; - std::string current_directory; - std::vector encryption_key; -}; - -class ClientHandler; - /** * @class ConnectionManager * @brief Manages server connections and handles client requests diff --git a/include/server/fenris_server_struct.hpp b/include/server/fenris_server_struct.hpp new file mode 100644 index 0000000..54309f4 --- /dev/null +++ b/include/server/fenris_server_struct.hpp @@ -0,0 +1,79 @@ +#ifndef FENRIS_SERVER_STRUCT_HPP +#define FENRIS_SERVER_STRUCT_HPP + +#include // for std::atomic +#include +#include // for std::shared_ptr +#include +#include +#include + +namespace fenris { +namespace server { + +const std::string DEFAULT_SERVER_DIR = "/fenris_server"; + +struct ClientInfo { + uint32_t client_id; + uint32_t socket; + std::string address; + std::string port; + std::string current_directory = "/"; + uint32_t depth = 0; // Depth of the current directory in the tree + bool keep_connection; + std::vector encryption_key; + std::shared_ptr + current_node; // Pointer to the current node in the file system tree + + ClientInfo(uint32_t client_id, uint32_t client_socket) + : client_id(client_id), socket(client_socket), keep_connection(true), + current_node(FST.root) + { + current_node->access_count++; + } + + ~ClientInfo() + { + // Ensure that current_node is root before deleting the client info + if (current_node) { + current_node->access_count--; + } + } +}; + +struct Node { + std::string name; // Name of the file or directory + bool is_directory; // Flag to indicate if it's a directory + std::vector> children; // Child nodes + std::weak_ptr parent; // Weak pointer to the parent node + std::atomic access_count{0}; + // Atomic counter which works as a sub directory access counter if + // (is_directory) or as a file readers counter if (!is_directory) + std::mutex node_mutex; // Mutex for write (if !is_directory) or read/write + // (if is_directory) +}; + +class FileSystemTree { + public: + FileSystemTree(); + + // Adds a new node to the tree + bool addNode(const std::string &path, bool is_directory); + + // Removes a node from the tree + bool removeNode(const std::string &path); + + // Finds a node by its path + std::shared_ptr findNode(const std::string &path); + + private: + std::shared_ptr root; // Root of the file system tree + + // Helper function to traverse the tree + std::shared_ptr traverse(const std::string &path); +}; + +} // namespace server +} // namespace fenris + +#endif // FENRIS_SERVER_HPP diff --git a/include/server/request_manager.hpp b/include/server/request_manager.hpp index 013dded..088d823 100644 --- a/include/server/request_manager.hpp +++ b/include/server/request_manager.hpp @@ -1,9 +1,51 @@ #ifndef FENRIS_SERVER_REQUEST_MANAGER_HPP #define FENRIS_SERVER_REQUEST_MANAGER_HPP +#include "common/file_operations.hpp" +#include "fenris.pb.h" +#include "server/fenris_server_struct.hpp" + namespace fenris { namespace server { +class ClientHandler { + public: + explicit ClientHandler(); + + void step_directory_with_mutex(std::string ¤t_directory, + std::string &new_directory, + uint32_t &depth, + std::shared_ptr ¤t_node); + + void traverse_back(std::string ¤t_directory, + uint32_t &depth, + std::shared_ptr ¤t_node); + + std::pair + change_directory(std::string current_directory, + std::string path, + uint32_t &depth, + std::shared_ptr ¤t_node); + + void destroy_node(std::string ¤t_directory, + std::shared_ptr ¤t_node); + + fenris::Response handle_request(const fenris::Request &request, + ClientInfo &client_info); + + std::vector get_handled_client_ids(); + + std::vector get_received_requests(); + + int get_request_count(); + + private: + int m_request_count; + std::mutex m_mutex; + std::vector m_handled_client_sockets; + std::vector m_received_requests; +}; + } // namespace server } // namespace fenris diff --git a/src/server/connection_manager.cpp b/src/server/connection_manager.cpp index 7331a06..c641343 100644 --- a/src/server/connection_manager.cpp +++ b/src/server/connection_manager.cpp @@ -4,8 +4,8 @@ #include "common/request.hpp" #include "common/response.hpp" #include "fenris.pb.h" +#include "server/fenris_server_struct.hpp" #include "server/request_manager.hpp" -#include "server/server.hpp" #include #include @@ -313,9 +313,7 @@ void ConnectionManager::handle_client(uint32_t client_socket, uint32_t client_id) { - ClientInfo client_info; - client_info.client_id = client_id; - client_info.socket = client_socket; + ClientInfo client_info(client_id, client_socket); // Set client socket to non-blocking if server is in non-blocking mode if (m_non_blocking_mode) { @@ -343,8 +341,8 @@ void ConnectionManager::handle_client(uint32_t client_socket, break; } - auto response = m_client_handler->handle_request(client_socket, - request_opt.value()); + auto response = + m_client_handler->handle_request(request_opt.value(), client_info); keep_connection = response.second; if (!send_response(client_info, response.first)) { diff --git a/src/server/fenris_server_struct.cpp b/src/server/fenris_server_struct.cpp new file mode 100644 index 0000000..b81b90a --- /dev/null +++ b/src/server/fenris_server_struct.cpp @@ -0,0 +1,94 @@ +#include "server/fenris_server_struct.hpp" +#include // for std::find_if +#include // for std::istringstream +#include // for std::runtime_error + +namespace fenris { +namespace server { + +FileSystemTree::FileSystemTree() +{ + root = std::make_shared(); + root->name = "/"; + root->is_directory = true; + root->access_count = 0; + root->parent.reset(); // Use reset() to clear the weak_ptr +} + +bool FileSystemTree::addNode(const std::string &path, bool is_directory) +{ + auto parent = traverse(path.substr(0, path.find_last_of('/'))); + if (!parent || !parent->is_directory) { + return false; + } + + auto new_node = std::make_shared(); + new_node->name = path.substr(path.find_last_of('/') + 1); + new_node->is_directory = is_directory; + new_node->access_count = 0; + new_node->parent = parent; + + parent->children.push_back(new_node); + return true; +} + +bool FileSystemTree::removeNode(const std::string &path) +{ + auto node = traverse(path); + if (!node || node->access_count > 0) { + return false; // Cannot remove a node being accessed + } + + auto parent = node->parent.lock(); + if (!parent) { + return false; + } + + parent->children.erase( + std::remove_if(parent->children.begin(), + parent->children.end(), + [&node](const std::shared_ptr &child) { + return child == node; + }), + parent->children.end()); + return true; +} + +std::shared_ptr FileSystemTree::findNode(const std::string &path) +{ + return traverse(path); +} + +std::shared_ptr FileSystemTree::traverse(const std::string &path) +{ + if (path == "/") { + return root; + } + + std::istringstream stream(path); + std::string segment; + auto current = root; + + while (std::getline(stream, segment, '/')) { + if (segment.empty()) { + continue; + } + + auto it = std::find_if(current->children.begin(), + current->children.end(), + [&segment](const std::shared_ptr &child) { + return child->name == segment; + }); + + if (it == current->children.end()) { + return nullptr; + } + + current = *it; + } + + return current; +} + +} // namespace server +} // namespace fenris diff --git a/src/server/request_manager.cpp b/src/server/request_manager.cpp index ef1b5d3..7f5a436 100644 --- a/src/server/request_manager.cpp +++ b/src/server/request_manager.cpp @@ -3,5 +3,498 @@ namespace fenris { namespace server { +ClientHandler::ClientHandler() : m_request_count(0) {} + +void ClientHandler::step_directory_with_mutex( + std::string ¤t_directory, + std::string &new_directory, + uint32_t &depth, + std::shared_ptr ¤t_node) +{ + if (new_directory == "..") { + // Go up one directory + if (current_directory != "/") { + current_directory.pop_back(); // Remove trailing slash + size_t pos = current_directory.find_last_of('/'); + if (pos != std::string::npos) { + pos++; + current_directory = current_directory.substr(0, pos); + depth--; + } + current_node->access_count--; + current_node = current_node->parent.lock(); + } + } else if (new_directory == ".") { + // Stay in the current directory + } else { + // Change to the new directory + auto it = + std::find_if(current_node->children.begin(), + current_node->children.end(), + [&new_directory](const std::shared_ptr &child) { + return ((child->name == new_directory) && + (child->is_directory == true)); + }); + if (it == current_node->children.end()) { + assert(1 == 0); + // Directory not found + } else { + current_node = *it; + current_node->access_count++; + current_directory += new_directory + "/"; + depth++; + } + } + current_directory = new_directory; +} + +void ClientHandler::traverse_back(std::string ¤t_directory, + uint32_t &depth, + std::shared_ptr ¤t_node) +{ + while (current_directory != "/") { + step_directory_with_mutex(current_directory, "..", depth, current_node); + } +} + +pair +ClientHandler::change_directory(std::string current_directory, + std::string path, + uint32_t &depth, + std::shared_ptr ¤t_node) +{ + if (path[path.size() - 1] == '/') { + path = path.substr(0, path.size() - 1); + } + uint32_t ind = 0; + if (path[0] == "/") { + traverse_back(current_directory, depth, current_node); + ind++; + } + while (path.find("/", ind) != std::string::npos) { + uint32_t x = path.find("/", ind); + step_directory_with_mutex(current_directory, + request.filename().substr(ind, x - ind), + depth, + current_node); + ind = x + 1; + } + return {current_directory, ind}; +} + +void ClientHandler::destroy_node(std::string ¤t_directory, + std::shared_ptr ¤t_node) +{ + traverse_back(current_directory, 0, current_node); + current_node->access_count--; +} + +fenris::Response ClientHandler::handle_request(const fenris::Request &request, + ClientInfo &client_info) +{ + + { + std::lock_guard lock(m_mutex); + m_handled_client_sockets.push_back(client_info.socket); + m_received_requests.push_back(request); + m_request_count++; + } + + fenris::Response response; + + switch (request.command()) { + case fenris::RequestType::PING: { + response.set_type(fenris::ResponseType::PONG); + response.set_success(true); + response.set_data("PING"); + return response; + } + case fenris::RequestType::TERMINATE: { + response.set_type(fenris::ResponseType::TERMINATED); + response.set_success(true); + response.set_data("Terminated successfully"); + traverse_back(client_info.current_directory, + client_info.depth, + client_info.current_node); + client_info.keep_connection = false; + return response; + } + default: + } + + std::shared_ptr new_node = FST.root; + new_node->access_count++; + uint32_t new_depth = 0; + try { + change_directory("/", + client_info.current_directory, + new_depth, + new_node); + auto [new_directory, ind] = + change_directory(client_info.current_directory, + request.filename(), + new_depth, + new_node); + } catch (const std::exception &e) { + response.set_type(fenris::ResponseType::ERROR); + response.set_success(false); + response.set_error_message("Invalid Path!"); + destroy_node(new_directory, new_node); + return response; + } + + std::string _file = request.filename().substring(ind); + std::string filename = DEFAULT_SERVER_DIR + new_directory + _file; + if (filename[filename.size() - 1] == '/') { + filename = filename.substr(0, filename.size() - 1); + _file = _file.substr(0, _file.size() - 1); + } + + switch (request.command()) { + case fenris::RequestType::CREATE_FILE: { + std::lock_guard lock(new_node->node_mutex); + auto result = fenris::common::create_file(filename); + if (result == fenris::common::FileOperationResult::SUCCESS) { + if (!FST.addNode(filename, false)) { + response.set_type(fenris::ResponseType::ERROR); + response.set_success(false); + response.set_error_message( + "FST not synchronized with file system."); + break; + } + response.set_type(fenris::ResponseType::SUCCESS); + response.set_success(true); + response.set_data("CREATE_FILE"); + } else if (result == + fenris::common::FileOperationResult::FILE_ALREADY_EXISTS) { + response.set_type(fenris::ResponseType::ERROR); + response.set_success(false); + response.set_error_message("File already exists"); + } else { + response.set_type(fenris::ResponseType::ERROR); + response.set_success(false); + response.set_error_message("Failed to create file"); + } + break; + } + case fenris::RequestType::READ_FILE: { + auto it = std::find_if(new_node->children.begin(), + new_node->children.end(), + [&_file](const std::shared_ptr &child) { + return ((child->name == _file) && + (child->is_directory == false)); + }); + + if (it == new_node->children.end()) { + response.set_type(fenris::ResponseType::ERROR); + response.set_success(false); + response.set_error_message("File not found"); + break; + } + { + std::lock_guard lock((*it)->node_mutex); + (*it)->access_count++; + } + + auto [content, result] = fenris::common::read_file(filename); + + (*it)->access_count--; + + if (result == fenris::common::FileOperationResult::SUCCESS) { + response.set_type(fenris::ResponseType::FILE_CONTENT); + response.set_success(true); + response.set_data(content.data(), content.size()); + } else if (result == + fenris::common::FileOperationResult::FILE_NOT_FOUND) { + response.set_type(fenris::ResponseType::ERROR); + response.set_success(false); + response.set_error_message("File not found"); + } else { + response.set_type(fenris::ResponseType::ERROR); + response.set_success(false); + response.set_error_message("Failed to read file"); + } + break; + } + case fenris::RequestType::WRITE_FILE: { + + auto it = std::find_if(new_node->children.begin(), + new_node->children.end(), + [&_file](const std::shared_ptr &child) { + return ((child->name == _file) && + (child->is_directory == false)); + }); + + if (it == new_node->children.end()) { + std::lock_guard lock(new_node->node_mutex); + auto result = fenris::common::create_file(filename); + if (result == fenris::common::FileOperationResult::SUCCESS) { + if (!FST.addNode(filename, false)) { + response.set_type(fenris::ResponseType::ERROR); + response.set_success(false); + response.set_error_message( + "FST not synchronized with file system."); + break; + } + it = std::find_if(new_node->children.begin(), + new_node->children.end(), + [&_file](const std::shared_ptr &child) { + return ((child->name == _file) && + (child->is_directory == false)); + }); + } else if (result == fenris::common::FileOperationResult:: + FILE_ALREADY_EXISTS) { + response.set_type(fenris::ResponseType::ERROR); + response.set_success(false); + response.set_error_message("This should not happen"); + break; + } else { + response.set_type(fenris::ResponseType::ERROR); + response.set_success(false); + response.set_error_message("Failed to create file"); + break; + } + } + + std::lock_guard lock((*it)->node_mutex); + while ((*it)->access_count > 0) { + // Wait for access count to be zero + } + + auto result = fenris::common::write_file( + filename, + {request.data().begin(), request.data().end()}); + if (result == fenris::common::FileOperationResult::SUCCESS) { + response.set_type(fenris::ResponseType::SUCCESS); + response.set_success(true); + response.set_data("The file has been written successfully"); + } else if (result == + fenris::common::FileOperationResult::PERMISSION_DENIED) { + response.set_type(fenris::ResponseType::ERROR); + response.set_success(false); + response.set_error_message( + "Permission denied to write to the file"); + } else { + response.set_type(fenris::ResponseType::ERROR); + response.set_success(false); + response.set_error_message("Failed to write file"); + } + break; + } + case fenris::RequestType::DELETE_FILE: { + std::lock_guard lock(new_node->node_mutex); + auto it = std::find_if(new_node->children.begin(), + new_node->children.end(), + [&_file](const std::shared_ptr &child) { + return ((child->name == _file) && + (child->is_directory == false)); + }); + + if (it == new_node->children.end()) { + response.set_type(fenris::ResponseType::ERROR); + response.set_success(false); + response.set_error_message("File not found"); + break; + } + std::lock_guard lock((*it)->node_mutex); + while ((*it)->access_count > 0) { + // Wait for access count to be zero + } + auto result = fenris::common::delete_file(filename); + if (result == fenris::common::FileOperationResult::SUCCESS) { + response.set_type(fenris::ResponseType::SUCCESS); + response.set_success(true); + response.set_data("DELETE_FILE"); + } else if (result == + fenris::common::FileOperationResult::FILE_NOT_FOUND) { + response.set_type(fenris::ResponseType::ERROR); + response.set_success(false); + response.set_error_message("File not found"); + } else { + response.set_type(fenris::ResponseType::ERROR); + response.set_success(false); + response.set_error_message("Failed to delete file"); + } + break; + } + case fenris::RequestType::INFO_FILE: { + + auto it = std::find_if(new_node->children.begin(), + new_node->children.end(), + [&_file](const std::shared_ptr &child) { + return ((child->name == _file) && + (child->is_directory == false)); + }); + + if (it == new_node->children.end()) { + response.set_type(fenris::ResponseType::ERROR); + response.set_success(false); + response.set_error_message("File not found"); + break; + } + { + std::lock_guard lock((*it)->node_mutex); + (*it)->access_count++; + } + + auto [content, result] = fenris::common::get_file_info(filename); + + (*it)->access_count--; + + if (result == fenris::common::FileOperationResult::SUCCESS) { + response.set_type(fenris::ResponseType::FILE_INFO); + response.set_success(true); + response.set_file_info(content); + } else if (result == + fenris::common::FileOperationResult::FILE_NOT_FOUND) { + response.set_type(fenris::ResponseType::ERROR); + response.set_success(false); + response.set_error_message("File not found"); + } else { + response.set_type(fenris::ResponseType::ERROR); + response.set_success(false); + response.set_error_message("Failed to fetch file info"); + } + break; + } + case fenris::RequestType::CREATE_DIR: { + std::lock_guard lock(new_node->node_mutex); + auto result = fenris::common::create_directory(filename); + if (result == fenris::common::FileOperationResult::SUCCESS) { + FST.addNode(filename, true); + response.set_type(fenris::ResponseType::SUCCESS); + response.set_success(true); + response.set_data("CREATE_DIR"); + } else if (result == fenris::common::FileOperationResult:: + DIRECTORY_ALREADY_EXISTS) { + response.set_type(fenris::ResponseType::ERROR); + response.set_success(false); + response.set_error_message("Directory already exists"); + } else { + response.set_type(fenris::ResponseType::ERROR); + response.set_success(false); + response.set_error_message("Failed to create directory"); + } + break; + } + case fenris::RequestType::LIST_DIR: { + std::lock_guard lock(new_node->node_mutex); + auto [entries, result] = fenris::common::list_directory(filename); + if (result == fenris::common::FileOperationResult::SUCCESS) { + response.set_type(fenris::ResponseType::DIR_LISTING); + response.set_success(true); + + // fenris::DirectoryListing dir_listing; + // for (const auto &entry : entries) { + // auto *file_info = dir_listing.add_entries(); + // file_info->set_name(entry); + // } + // response.mutable_details()->set_allocated_directory_listing( + // &dir_listing); + + } else if (result == + fenris::common::FileOperationResult::FILE_NOT_FOUND) { + response.set_type(fenris::ResponseType::ERROR); + response.set_success(false); + response.set_error_message("Directory not found"); + } + + else if (result == fenris::common::FileOperationResult::INVALID_PATH) { + response.set_type(fenris::ResponseType::ERROR); + response.set_success(false); + response.set_error_message("Path is not a directory"); + } else { + response.set_type(fenris::ResponseType::ERROR); + response.set_success(false); + response.set_error_message("Failed to list directory"); + } + break; + } + case fenris::RequestType::CHANGE_DIR: { + try { + step_directory_with_mutex(new_directory, + _file, + new_depth, + new_node); + } catch (const std::exception &e) { + response.set_type(fenris::ResponseType::ERROR); + response.set_success(false); + response.set_error_message("Invalid Path!"); + destroy_node(new_directory, new_node); + return response; + } + swap(new_directory, client_info.current_directory); + swap(new_node, client_info.current_node); + swap(new_depth, client_info.depth); + response.set_type(fenris::ResponseType::SUCCESS); + response.set_success(true); + response.set_data("Changed directory successfully"); + break; + } + case fenris::RequestType::DELETE_DIR: { + std::lock_guard lock(new_node->node_mutex); + auto it = std::find_if(new_node->children.begin(), + new_node->children.end(), + [&_file](const std::shared_ptr &child) { + return child->name == _file; + }); + if (it == new_node->children.end()) { + response.set_type(fenris::ResponseType::ERROR); + response.set_success(false); + response.set_error_message("Directory does not exist"); + break; + } else { + if ((*it)->access_count > 0) { + response.set_type(fenris::ResponseType::ERROR); + response.set_success(false); + response.set_error_message("Directory is in use"); + break; + } + } + auto result = fenris::common::delete_directory(filename, true); + if (result == fenris::common::FileOperationResult::SUCCESS) { + response.set_type(fenris::ResponseType::SUCCESS); + response.set_success(true); + response.set_data("DELETE_DIRECTORY"); + FST.removeNode(filename); + } else if (result == + fenris::common::FileOperationResult::DIRECTORY_NOT_EMPTY) { + response.set_type(fenris::ResponseType::ERROR); + response.set_success(false); + response.set_error_message("Directory is not empty"); + } else { + response.set_type(fenris::ResponseType::ERROR); + response.set_success(false); + response.set_error_message("Failed to delete directory"); + } + break; + } + default: + response.set_type(fenris::ResponseType::ERROR); + response.set_success(false); + response.set_error_message("Unknown command"); + } + + return response; +} + +std::vector ClientHandler::get_handled_client_ids() +{ + std::lock_guard lock(m_mutex); + return m_handled_client_sockets; +} + +std::vector ClientHandler::get_received_requests() +{ + std::lock_guard lock(m_mutex); + return m_received_requests; +} + +int ClientHandler::get_request_count() +{ + std::lock_guard lock(m_mutex); + return m_request_count; +} + } // namespace server } // namespace fenris From 7690250002f01285b2dbe7655fb18eeb31fb8ed5 Mon Sep 17 00:00:00 2001 From: Praneeth Sarode Date: Wed, 30 Apr 2025 21:47:26 +0530 Subject: [PATCH 2/2] refactor(server-request-manager): fix build errors and major refactors Signed-off-by: Praneeth Sarode Signed-off-by: P4rth-B --- ...nris_server_struct.hpp => client_info.hpp} | 93 +++-- include/server/connection_manager.hpp | 48 ++- include/server/request_manager.hpp | 22 +- include/server/server.hpp | 1 - src/server/CMakeLists.txt | 12 +- src/server/client_info.cpp | 125 +++++++ src/server/connection_manager.cpp | 11 +- src/server/fenris_server_struct.cpp | 94 ----- src/server/request_manager.cpp | 345 +++++++----------- .../server/server_connection_manager_test.cpp | 73 ++-- 10 files changed, 389 insertions(+), 435 deletions(-) rename include/server/{fenris_server_struct.hpp => client_info.hpp} (55%) create mode 100644 src/server/client_info.cpp delete mode 100644 src/server/fenris_server_struct.cpp diff --git a/include/server/fenris_server_struct.hpp b/include/server/client_info.hpp similarity index 55% rename from include/server/fenris_server_struct.hpp rename to include/server/client_info.hpp index 54309f4..9e10b2e 100644 --- a/include/server/fenris_server_struct.hpp +++ b/include/server/client_info.hpp @@ -1,9 +1,9 @@ -#ifndef FENRIS_SERVER_STRUCT_HPP -#define FENRIS_SERVER_STRUCT_HPP +#ifndef FENRIS_CLIENT_INFO_HPP +#define FENRIS_CLIENT_INFO_HPP -#include // for std::atomic +#include #include -#include // for std::shared_ptr +#include #include #include #include @@ -13,6 +13,45 @@ namespace server { const std::string DEFAULT_SERVER_DIR = "/fenris_server"; +struct Node { + std::string name; + bool is_directory; + std::vector> children; + std::weak_ptr parent; + std::atomic access_count{0}; + std::mutex node_mutex; +}; + +class FileSystemTree { + public: + FileSystemTree(); + + // Adds a new node to the tree + bool add_node(const std::string &path, bool is_directory); + + // Removes a node from the tree + bool remove_node(const std::string &path); + + // Finds a node by its path + std::shared_ptr find_node(const std::string &path); + + // Finds a file in the current node's children + std::shared_ptr find_file(const std::shared_ptr ¤t_node, + const std::string &file); + + std::shared_ptr + find_directory(const std::shared_ptr ¤t_node, + const std::string &dir); + + std::shared_ptr root; // Root of the file system tree + + private: + std::mutex tree_mutex; // Mutex for thread-safe access to the tree + + // Helper function to traverse the tree + std::shared_ptr traverse(const std::string &path); +}; + struct ClientInfo { uint32_t client_id; uint32_t socket; @@ -21,15 +60,23 @@ struct ClientInfo { std::string current_directory = "/"; uint32_t depth = 0; // Depth of the current directory in the tree bool keep_connection; + std::vector encryption_key; std::shared_ptr current_node; // Pointer to the current node in the file system tree ClientInfo(uint32_t client_id, uint32_t client_socket) : client_id(client_id), socket(client_socket), keep_connection(true), - current_node(FST.root) + current_node(nullptr) { - current_node->access_count++; + } + + // Function to set the current node + void set_current_node(std::shared_ptr node) + { + if (current_node) { + current_node->access_count++; + } } ~ClientInfo() @@ -41,39 +88,7 @@ struct ClientInfo { } }; -struct Node { - std::string name; // Name of the file or directory - bool is_directory; // Flag to indicate if it's a directory - std::vector> children; // Child nodes - std::weak_ptr parent; // Weak pointer to the parent node - std::atomic access_count{0}; - // Atomic counter which works as a sub directory access counter if - // (is_directory) or as a file readers counter if (!is_directory) - std::mutex node_mutex; // Mutex for write (if !is_directory) or read/write - // (if is_directory) -}; - -class FileSystemTree { - public: - FileSystemTree(); - - // Adds a new node to the tree - bool addNode(const std::string &path, bool is_directory); - - // Removes a node from the tree - bool removeNode(const std::string &path); - - // Finds a node by its path - std::shared_ptr findNode(const std::string &path); - - private: - std::shared_ptr root; // Root of the file system tree - - // Helper function to traverse the tree - std::shared_ptr traverse(const std::string &path); -}; - } // namespace server } // namespace fenris -#endif // FENRIS_SERVER_HPP +#endif // FENRIS_CLIENT_INFO_HPP diff --git a/include/server/connection_manager.hpp b/include/server/connection_manager.hpp index 42fc224..9106464 100644 --- a/include/server/connection_manager.hpp +++ b/include/server/connection_manager.hpp @@ -4,6 +4,7 @@ #include "common/crypto_manager.hpp" #include "common/logging.hpp" #include "fenris.pb.h" +#include "server/client_info.hpp" #include #include @@ -19,6 +20,26 @@ namespace fenris { namespace server { +/** + * @class IClientHandler + * @brief Interface for handling client requests + * + * Implement this interface to process client requests in your file system + */ +class IClientHandler { + public: + virtual ~IClientHandler() = default; + + /** + * @brief Process a client request. + * @param client_socket Socket descriptor for the client connection. + * @param request The deserialized client request. + * @return A Response object to be sent back to the client. + */ + virtual fenris::Response handle_request(const fenris::Request &request, + ClientInfo &client_info) = 0; +}; + /** * @class ConnectionManager * @brief Manages server connections and handles client requests @@ -64,7 +85,7 @@ class ConnectionManager { * @brief Set handler for client connections * @param handler Function that processes client requests */ - void set_client_handler(std::unique_ptr handler); + void set_client_handler(std::unique_ptr handler); /** * @brief Get number of active clients @@ -133,7 +154,7 @@ class ConnectionManager { std::string m_hostname; std::string m_port; - std::unique_ptr m_client_handler; + std::unique_ptr m_client_handler; int32_t m_server_socket{-1}; std::atomic m_running{false}; std::thread m_listen_thread; @@ -149,29 +170,6 @@ class ConnectionManager { std::atomic m_next_client_id{1}; }; -/** - * @class ClientHandler - * @brief Interface for handling client requests - * - * Implement this interface to process client requests in your file system - */ -class ClientHandler { - public: - virtual ~ClientHandler() = default; - - /** - * @brief Process a client request. - * @param client_socket Socket descriptor for the client connection. - * @param request The deserialized client request. - * @return A Response object to be sent back to the client. - * The 'success' field should indicate if the operation succeeded. - * Return a default or error response if processing fails - * internally. - */ - virtual std::pair - handle_request(uint32_t client_socket, const fenris::Request &request) = 0; -}; - } // namespace server } // namespace fenris diff --git a/include/server/request_manager.hpp b/include/server/request_manager.hpp index 088d823..13ed478 100644 --- a/include/server/request_manager.hpp +++ b/include/server/request_manager.hpp @@ -3,17 +3,18 @@ #include "common/file_operations.hpp" #include "fenris.pb.h" -#include "server/fenris_server_struct.hpp" +#include "server/client_info.hpp" +#include "server/connection_manager.hpp" namespace fenris { namespace server { -class ClientHandler { +class ClientHandler : public IClientHandler { public: explicit ClientHandler(); - void step_directory_with_mutex(std::string ¤t_directory, - std::string &new_directory, + bool step_directory_with_mutex(std::string ¤t_directory, + const std::string &new_directory, uint32_t &depth, std::shared_ptr ¤t_node); @@ -28,22 +29,15 @@ class ClientHandler { std::shared_ptr ¤t_node); void destroy_node(std::string ¤t_directory, + uint32_t &depth, std::shared_ptr ¤t_node); fenris::Response handle_request(const fenris::Request &request, ClientInfo &client_info); - std::vector get_handled_client_ids(); + void initialize_file_system_tree(); - std::vector get_received_requests(); - - int get_request_count(); - - private: - int m_request_count; - std::mutex m_mutex; - std::vector m_handled_client_sockets; - std::vector m_received_requests; + FileSystemTree FST; }; } // namespace server diff --git a/include/server/server.hpp b/include/server/server.hpp index 5d4baee..4373bd0 100644 --- a/include/server/server.hpp +++ b/include/server/server.hpp @@ -8,7 +8,6 @@ namespace fenris { namespace server { - } // namespace server } // namespace fenris diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt index 2179f78..f9b398b 100644 --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -7,9 +7,9 @@ verbose_message("Setting up server executable...") set(SERVER_SOURCES main.cpp cache_manager.cpp + client_info.cpp connection_manager.cpp request_manager.cpp - response_manager.cpp server.cpp ) @@ -26,6 +26,16 @@ target_link_libraries(fenris_server fenris_common ) +# Client executable +add_executable(server main.cpp) + +# Link libraries to the client executable +target_link_libraries(server + PRIVATE + fenris_client + fenris_common + fenris_proto +) # Install the server executable install(TARGETS fenris_server RUNTIME DESTINATION bin diff --git a/src/server/client_info.cpp b/src/server/client_info.cpp new file mode 100644 index 0000000..c52de6d --- /dev/null +++ b/src/server/client_info.cpp @@ -0,0 +1,125 @@ +#include "server/client_info.hpp" +#include +#include +#include + +namespace fenris { +namespace server { + +FileSystemTree::FileSystemTree() +{ + root = std::make_shared(); + root->name = "/"; + root->is_directory = true; + root->access_count = 0; + root->parent.reset(); // Use reset() to clear the weak_ptr +} + +bool FileSystemTree::add_node(const std::string &path, bool is_directory) +{ + std::lock_guard lock(tree_mutex); + auto parent = traverse(path.substr(0, path.find_last_of('/'))); + if (!parent || !parent->is_directory) { + return false; + } + + auto new_node = std::make_shared(); + new_node->name = path.substr(path.find_last_of('/') + 1); + new_node->is_directory = is_directory; + new_node->access_count = 0; + new_node->parent = parent; + + parent->children.push_back(new_node); + + return true; +} + +bool FileSystemTree::remove_node(const std::string &path) +{ + std::lock_guard lock(tree_mutex); + auto node = traverse(path); + if (!node || node->access_count > 0) { + return false; // Cannot remove a node being accessed + } + + auto parent = node->parent.lock(); + if (parent) { + auto it = std::remove_if(parent->children.begin(), + parent->children.end(), + [&node](const std::shared_ptr &child) { + return child == node; + }); + parent->children.erase(it, parent->children.end()); + } + + return true; +} + +std::shared_ptr FileSystemTree::find_node(const std::string &path) +{ + std::lock_guard lock(tree_mutex); + return traverse(path); +} + +std::shared_ptr +FileSystemTree::find_file(const std::shared_ptr ¤t_node, + const std::string &file) +{ + std::lock_guard lock(tree_mutex); + auto it = std::find_if(current_node->children.begin(), + current_node->children.end(), + [&file](const std::shared_ptr &child) { + return ((child->name == file) && + (child->is_directory == false)); + }); + return (it != current_node->children.end()) ? *it : nullptr; +} + +std::shared_ptr +FileSystemTree::find_directory(const std::shared_ptr ¤t_node, + const std::string &dir) +{ + std::lock_guard lock(tree_mutex); + auto it = std::find_if(current_node->children.begin(), + current_node->children.end(), + [&dir](const std::shared_ptr &child) { + return ((child->name == dir) && + (child->is_directory == true)); + }); + return (it != current_node->children.end()) ? *it : nullptr; +} + +std::shared_ptr FileSystemTree::traverse(const std::string &path) +{ + if (path == "/") { + return root; + } + + std::istringstream stream(path); + std::string segment; + auto current = root; + + while (std::getline(stream, segment, '/')) { + if (segment.empty()) { + continue; + } + + std::lock_guard lock(current->node_mutex); + auto it = std::find_if(current->children.begin(), + current->children.end(), + [&segment](const std::shared_ptr &child) { + return child->name == segment; + }); + + if (it == current->children.end()) { + return nullptr; + } + + current = *it; + } + + return current; +} + +} // namespace server +} // namespace fenris diff --git a/src/server/connection_manager.cpp b/src/server/connection_manager.cpp index c641343..1717210 100644 --- a/src/server/connection_manager.cpp +++ b/src/server/connection_manager.cpp @@ -4,7 +4,7 @@ #include "common/request.hpp" #include "common/response.hpp" #include "fenris.pb.h" -#include "server/fenris_server_struct.hpp" +#include "server/client_info.hpp" #include "server/request_manager.hpp" #include @@ -196,7 +196,7 @@ void ConnectionManager::stop() } void ConnectionManager::set_client_handler( - std::unique_ptr handler) + std::unique_ptr handler) { m_client_handler = std::move(handler); } @@ -321,8 +321,6 @@ void ConnectionManager::handle_client(uint32_t client_socket, fcntl(client_socket, F_SETFL, flags | O_NONBLOCK); } - bool keep_connection = true; - if (!perform_key_exchange(client_info)) { m_logger->error("key exchange failed with client: {}", client_info.client_id); @@ -332,7 +330,7 @@ void ConnectionManager::handle_client(uint32_t client_socket, } // Process client requests - while (m_running && keep_connection) { + while (m_running && client_info.keep_connection) { auto request_opt = receive_request(client_info); if (!request_opt.has_value()) { @@ -343,9 +341,8 @@ void ConnectionManager::handle_client(uint32_t client_socket, auto response = m_client_handler->handle_request(request_opt.value(), client_info); - keep_connection = response.second; - if (!send_response(client_info, response.first)) { + if (!send_response(client_info, response)) { m_logger->error("failed to send response to client: {}", client_info.client_id); break; diff --git a/src/server/fenris_server_struct.cpp b/src/server/fenris_server_struct.cpp deleted file mode 100644 index b81b90a..0000000 --- a/src/server/fenris_server_struct.cpp +++ /dev/null @@ -1,94 +0,0 @@ -#include "server/fenris_server_struct.hpp" -#include // for std::find_if -#include // for std::istringstream -#include // for std::runtime_error - -namespace fenris { -namespace server { - -FileSystemTree::FileSystemTree() -{ - root = std::make_shared(); - root->name = "/"; - root->is_directory = true; - root->access_count = 0; - root->parent.reset(); // Use reset() to clear the weak_ptr -} - -bool FileSystemTree::addNode(const std::string &path, bool is_directory) -{ - auto parent = traverse(path.substr(0, path.find_last_of('/'))); - if (!parent || !parent->is_directory) { - return false; - } - - auto new_node = std::make_shared(); - new_node->name = path.substr(path.find_last_of('/') + 1); - new_node->is_directory = is_directory; - new_node->access_count = 0; - new_node->parent = parent; - - parent->children.push_back(new_node); - return true; -} - -bool FileSystemTree::removeNode(const std::string &path) -{ - auto node = traverse(path); - if (!node || node->access_count > 0) { - return false; // Cannot remove a node being accessed - } - - auto parent = node->parent.lock(); - if (!parent) { - return false; - } - - parent->children.erase( - std::remove_if(parent->children.begin(), - parent->children.end(), - [&node](const std::shared_ptr &child) { - return child == node; - }), - parent->children.end()); - return true; -} - -std::shared_ptr FileSystemTree::findNode(const std::string &path) -{ - return traverse(path); -} - -std::shared_ptr FileSystemTree::traverse(const std::string &path) -{ - if (path == "/") { - return root; - } - - std::istringstream stream(path); - std::string segment; - auto current = root; - - while (std::getline(stream, segment, '/')) { - if (segment.empty()) { - continue; - } - - auto it = std::find_if(current->children.begin(), - current->children.end(), - [&segment](const std::shared_ptr &child) { - return child->name == segment; - }); - - if (it == current->children.end()) { - return nullptr; - } - - current = *it; - } - - return current; -} - -} // namespace server -} // namespace fenris diff --git a/src/server/request_manager.cpp b/src/server/request_manager.cpp index 7f5a436..238923a 100644 --- a/src/server/request_manager.cpp +++ b/src/server/request_manager.cpp @@ -1,13 +1,11 @@ #include "server/request_manager.hpp" - +#include namespace fenris { namespace server { -ClientHandler::ClientHandler() : m_request_count(0) {} - -void ClientHandler::step_directory_with_mutex( +bool ClientHandler::step_directory_with_mutex( std::string ¤t_directory, - std::string &new_directory, + const std::string &new_directory, uint32_t &depth, std::shared_ptr ¤t_node) { @@ -28,24 +26,19 @@ void ClientHandler::step_directory_with_mutex( // Stay in the current directory } else { // Change to the new directory - auto it = - std::find_if(current_node->children.begin(), - current_node->children.end(), - [&new_directory](const std::shared_ptr &child) { - return ((child->name == new_directory) && - (child->is_directory == true)); - }); - if (it == current_node->children.end()) { - assert(1 == 0); + auto it = FST.find_directory(current_node, new_directory); + if (it == nullptr) { + assert(false); // Directory not found } else { - current_node = *it; + current_node = it; current_node->access_count++; current_directory += new_directory + "/"; depth++; } } current_directory = new_directory; + return true; } void ClientHandler::traverse_back(std::string ¤t_directory, @@ -57,7 +50,7 @@ void ClientHandler::traverse_back(std::string ¤t_directory, } } -pair +std::pair ClientHandler::change_directory(std::string current_directory, std::string path, uint32_t &depth, @@ -67,14 +60,15 @@ ClientHandler::change_directory(std::string current_directory, path = path.substr(0, path.size() - 1); } uint32_t ind = 0; - if (path[0] == "/") { + if (path[0] == '/') { traverse_back(current_directory, depth, current_node); ind++; } while (path.find("/", ind) != std::string::npos) { uint32_t x = path.find("/", ind); + std::string sub_path = path.substr(ind, x - ind); // Temporary variable step_directory_with_mutex(current_directory, - request.filename().substr(ind, x - ind), + sub_path, depth, current_node); ind = x + 1; @@ -83,68 +77,72 @@ ClientHandler::change_directory(std::string current_directory, } void ClientHandler::destroy_node(std::string ¤t_directory, + uint32_t &depth, std::shared_ptr ¤t_node) { - traverse_back(current_directory, 0, current_node); + traverse_back(current_directory, depth, current_node); current_node->access_count--; } fenris::Response ClientHandler::handle_request(const fenris::Request &request, ClientInfo &client_info) { - - { - std::lock_guard lock(m_mutex); - m_handled_client_sockets.push_back(client_info.socket); - m_received_requests.push_back(request); - m_request_count++; - } - fenris::Response response; + response.set_type(fenris::ResponseType::ERROR); + response.set_success(false); switch (request.command()) { case fenris::RequestType::PING: { response.set_type(fenris::ResponseType::PONG); response.set_success(true); - response.set_data("PING"); + response.set_data("PONG"); return response; } + case fenris::RequestType::TERMINATE: { response.set_type(fenris::ResponseType::TERMINATED); response.set_success(true); - response.set_data("Terminated successfully"); + response.set_data("Terminated successfully!"); + traverse_back(client_info.current_directory, client_info.depth, client_info.current_node); + client_info.keep_connection = false; return response; } default: + break; } std::shared_ptr new_node = FST.root; new_node->access_count++; + uint32_t new_depth = 0; + std::string new_directory = client_info.current_directory; + uint32_t ind = 0; try { change_directory("/", client_info.current_directory, new_depth, new_node); - auto [new_directory, ind] = + + std::tie(new_directory, ind) = change_directory(client_info.current_directory, request.filename(), new_depth, new_node); + } catch (const std::exception &e) { - response.set_type(fenris::ResponseType::ERROR); - response.set_success(false); response.set_error_message("Invalid Path!"); - destroy_node(new_directory, new_node); + destroy_node(new_directory, new_depth, new_node); return response; } - std::string _file = request.filename().substring(ind); + std::string _file = request.filename().substr(ind); + std::string filename = DEFAULT_SERVER_DIR + new_directory + _file; + if (filename[filename.size() - 1] == '/') { filename = filename.substr(0, filename.size() - 1); _file = _file.substr(0, _file.size() - 1); @@ -153,259 +151,208 @@ fenris::Response ClientHandler::handle_request(const fenris::Request &request, switch (request.command()) { case fenris::RequestType::CREATE_FILE: { std::lock_guard lock(new_node->node_mutex); - auto result = fenris::common::create_file(filename); - if (result == fenris::common::FileOperationResult::SUCCESS) { - if (!FST.addNode(filename, false)) { - response.set_type(fenris::ResponseType::ERROR); - response.set_success(false); + + auto result = common::create_file(filename); + + if (result == common::FileOperationResult::SUCCESS) { + + if (!FST.add_node(filename, false)) { response.set_error_message( "FST not synchronized with file system."); break; } + response.set_type(fenris::ResponseType::SUCCESS); response.set_success(true); - response.set_data("CREATE_FILE"); - } else if (result == - fenris::common::FileOperationResult::FILE_ALREADY_EXISTS) { - response.set_type(fenris::ResponseType::ERROR); - response.set_success(false); - response.set_error_message("File already exists"); + } else if (result == common::FileOperationResult::FILE_ALREADY_EXISTS) { + response.set_error_message("File already exists!"); } else { - response.set_type(fenris::ResponseType::ERROR); - response.set_success(false); - response.set_error_message("Failed to create file"); + response.set_error_message("Failed to create file!"); } + break; } case fenris::RequestType::READ_FILE: { - auto it = std::find_if(new_node->children.begin(), - new_node->children.end(), - [&_file](const std::shared_ptr &child) { - return ((child->name == _file) && - (child->is_directory == false)); - }); - - if (it == new_node->children.end()) { - response.set_type(fenris::ResponseType::ERROR); - response.set_success(false); + auto it = FST.find_file(new_node, _file); + + if (it == nullptr) { response.set_error_message("File not found"); break; } + { - std::lock_guard lock((*it)->node_mutex); - (*it)->access_count++; + std::lock_guard lock((it)->node_mutex); + (it)->access_count++; } - auto [content, result] = fenris::common::read_file(filename); + auto [content, result] = common::read_file(filename); - (*it)->access_count--; + { + std::lock_guard lock((it)->node_mutex); + (it)->access_count--; + } - if (result == fenris::common::FileOperationResult::SUCCESS) { + if (result == common::FileOperationResult::SUCCESS) { response.set_type(fenris::ResponseType::FILE_CONTENT); response.set_success(true); response.set_data(content.data(), content.size()); - } else if (result == - fenris::common::FileOperationResult::FILE_NOT_FOUND) { - response.set_type(fenris::ResponseType::ERROR); - response.set_success(false); + } else if (result == common::FileOperationResult::FILE_NOT_FOUND) { response.set_error_message("File not found"); } else { - response.set_type(fenris::ResponseType::ERROR); - response.set_success(false); response.set_error_message("Failed to read file"); } + break; } case fenris::RequestType::WRITE_FILE: { + auto it = FST.find_file(new_node, _file); - auto it = std::find_if(new_node->children.begin(), - new_node->children.end(), - [&_file](const std::shared_ptr &child) { - return ((child->name == _file) && - (child->is_directory == false)); - }); - - if (it == new_node->children.end()) { + if (it == nullptr) { std::lock_guard lock(new_node->node_mutex); - auto result = fenris::common::create_file(filename); - if (result == fenris::common::FileOperationResult::SUCCESS) { - if (!FST.addNode(filename, false)) { - response.set_type(fenris::ResponseType::ERROR); - response.set_success(false); + auto result = common::create_file(filename); + + if (result == common::FileOperationResult::SUCCESS) { + if (!FST.add_node(filename, false)) { response.set_error_message( "FST not synchronized with file system."); break; } - it = std::find_if(new_node->children.begin(), - new_node->children.end(), - [&_file](const std::shared_ptr &child) { - return ((child->name == _file) && - (child->is_directory == false)); - }); + + it = FST.find_file(new_node, _file); + } else if (result == fenris::common::FileOperationResult:: FILE_ALREADY_EXISTS) { - response.set_type(fenris::ResponseType::ERROR); - response.set_success(false); response.set_error_message("This should not happen"); break; } else { - response.set_type(fenris::ResponseType::ERROR); - response.set_success(false); response.set_error_message("Failed to create file"); break; } } - std::lock_guard lock((*it)->node_mutex); - while ((*it)->access_count > 0) { + std::lock_guard lock((it)->node_mutex); + while ((it)->access_count > 0) { // Wait for access count to be zero } - auto result = fenris::common::write_file( - filename, - {request.data().begin(), request.data().end()}); - if (result == fenris::common::FileOperationResult::SUCCESS) { + auto result = + common::write_file(filename, + {request.data().begin(), request.data().end()}); + if (result == common::FileOperationResult::SUCCESS) { response.set_type(fenris::ResponseType::SUCCESS); response.set_success(true); response.set_data("The file has been written successfully"); - } else if (result == - fenris::common::FileOperationResult::PERMISSION_DENIED) { - response.set_type(fenris::ResponseType::ERROR); - response.set_success(false); + + } else if (result == common::FileOperationResult::PERMISSION_DENIED) { response.set_error_message( "Permission denied to write to the file"); } else { - response.set_type(fenris::ResponseType::ERROR); - response.set_success(false); response.set_error_message("Failed to write file"); } break; } case fenris::RequestType::DELETE_FILE: { std::lock_guard lock(new_node->node_mutex); - auto it = std::find_if(new_node->children.begin(), - new_node->children.end(), - [&_file](const std::shared_ptr &child) { - return ((child->name == _file) && - (child->is_directory == false)); - }); - - if (it == new_node->children.end()) { - response.set_type(fenris::ResponseType::ERROR); - response.set_success(false); + // Check if the file exists in the current node + auto it = FST.find_file(new_node, _file); + + if (it == nullptr) { response.set_error_message("File not found"); break; } - std::lock_guard lock((*it)->node_mutex); - while ((*it)->access_count > 0) { - // Wait for access count to be zero + fenris::common::FileOperationResult result; + { + std::lock_guard lock((it)->node_mutex); + while ((it)->access_count > 0) { + // Wait for access count to be zero + } + result = fenris::common::delete_file(filename); } - auto result = fenris::common::delete_file(filename); + // `result` stores the outcome of the file deletion operation. if (result == fenris::common::FileOperationResult::SUCCESS) { response.set_type(fenris::ResponseType::SUCCESS); response.set_success(true); - response.set_data("DELETE_FILE"); - } else if (result == - fenris::common::FileOperationResult::FILE_NOT_FOUND) { - response.set_type(fenris::ResponseType::ERROR); - response.set_success(false); + } else if (result == common::FileOperationResult::FILE_NOT_FOUND) { response.set_error_message("File not found"); } else { - response.set_type(fenris::ResponseType::ERROR); - response.set_success(false); response.set_error_message("Failed to delete file"); } break; } case fenris::RequestType::INFO_FILE: { + auto it = FST.find_file(new_node, _file); - auto it = std::find_if(new_node->children.begin(), - new_node->children.end(), - [&_file](const std::shared_ptr &child) { - return ((child->name == _file) && - (child->is_directory == false)); - }); - - if (it == new_node->children.end()) { - response.set_type(fenris::ResponseType::ERROR); - response.set_success(false); + if (it == nullptr) { response.set_error_message("File not found"); break; } { - std::lock_guard lock((*it)->node_mutex); - (*it)->access_count++; + std::lock_guard lock((it)->node_mutex); + (it)->access_count++; } - auto [content, result] = fenris::common::get_file_info(filename); + auto [content, result] = common::get_file_info(filename); - (*it)->access_count--; + (it)->access_count--; - if (result == fenris::common::FileOperationResult::SUCCESS) { + if (result == common::FileOperationResult::SUCCESS) { response.set_type(fenris::ResponseType::FILE_INFO); response.set_success(true); - response.set_file_info(content); + fenris::FileInfo *file_info = response.mutable_file_info(); + file_info->set_name(content.name()); + file_info->set_size(content.size()); + file_info->set_is_directory(content.is_directory()); + file_info->set_modified_time(content.modified_time()); } else if (result == fenris::common::FileOperationResult::FILE_NOT_FOUND) { - response.set_type(fenris::ResponseType::ERROR); - response.set_success(false); response.set_error_message("File not found"); } else { - response.set_type(fenris::ResponseType::ERROR); - response.set_success(false); response.set_error_message("Failed to fetch file info"); } break; } case fenris::RequestType::CREATE_DIR: { std::lock_guard lock(new_node->node_mutex); - auto result = fenris::common::create_directory(filename); - if (result == fenris::common::FileOperationResult::SUCCESS) { - FST.addNode(filename, true); + + auto result = common::create_directory(filename); + if (result == common::FileOperationResult::SUCCESS) { + FST.add_node(filename, true); response.set_type(fenris::ResponseType::SUCCESS); response.set_success(true); response.set_data("CREATE_DIR"); - } else if (result == fenris::common::FileOperationResult:: - DIRECTORY_ALREADY_EXISTS) { - response.set_type(fenris::ResponseType::ERROR); - response.set_success(false); + } else if (result == + common::FileOperationResult::DIRECTORY_ALREADY_EXISTS) { response.set_error_message("Directory already exists"); } else { - response.set_type(fenris::ResponseType::ERROR); - response.set_success(false); response.set_error_message("Failed to create directory"); } break; } case fenris::RequestType::LIST_DIR: { std::lock_guard lock(new_node->node_mutex); - auto [entries, result] = fenris::common::list_directory(filename); - if (result == fenris::common::FileOperationResult::SUCCESS) { + auto [entries, result] = common::list_directory(filename); + if (result == common::FileOperationResult::SUCCESS) { response.set_type(fenris::ResponseType::DIR_LISTING); response.set_success(true); - // fenris::DirectoryListing dir_listing; - // for (const auto &entry : entries) { - // auto *file_info = dir_listing.add_entries(); - // file_info->set_name(entry); - // } - // response.mutable_details()->set_allocated_directory_listing( - // &dir_listing); + fenris::DirectoryListing *dir_listing = + response.mutable_directory_listing(); + for (const auto &entry : entries) { + fenris::FileInfo *file_info = dir_listing->add_entries(); + file_info->set_name(entry.name()); + file_info->set_size(entry.size()); + file_info->set_is_directory(entry.is_directory()); + file_info->set_modified_time(entry.modified_time()); + } } else if (result == fenris::common::FileOperationResult::FILE_NOT_FOUND) { - response.set_type(fenris::ResponseType::ERROR); - response.set_success(false); response.set_error_message("Directory not found"); } - else if (result == fenris::common::FileOperationResult::INVALID_PATH) { - response.set_type(fenris::ResponseType::ERROR); - response.set_success(false); + else if (result == common::FileOperationResult::INVALID_PATH) { response.set_error_message("Path is not a directory"); } else { - response.set_type(fenris::ResponseType::ERROR); - response.set_success(false); response.set_error_message("Failed to list directory"); } break; @@ -417,15 +364,13 @@ fenris::Response ClientHandler::handle_request(const fenris::Request &request, new_depth, new_node); } catch (const std::exception &e) { - response.set_type(fenris::ResponseType::ERROR); - response.set_success(false); response.set_error_message("Invalid Path!"); - destroy_node(new_directory, new_node); + destroy_node(new_directory, new_depth, new_node); return response; } - swap(new_directory, client_info.current_directory); - swap(new_node, client_info.current_node); - swap(new_depth, client_info.depth); + std::swap(new_directory, client_info.current_directory); + std::swap(new_node, client_info.current_node); + std::swap(new_depth, client_info.depth); response.set_type(fenris::ResponseType::SUCCESS); response.set_success(true); response.set_data("Changed directory successfully"); @@ -433,68 +378,36 @@ fenris::Response ClientHandler::handle_request(const fenris::Request &request, } case fenris::RequestType::DELETE_DIR: { std::lock_guard lock(new_node->node_mutex); - auto it = std::find_if(new_node->children.begin(), - new_node->children.end(), - [&_file](const std::shared_ptr &child) { - return child->name == _file; - }); - if (it == new_node->children.end()) { - response.set_type(fenris::ResponseType::ERROR); - response.set_success(false); + + auto it = FST.find_directory(new_node, _file); + if (it == nullptr) { response.set_error_message("Directory does not exist"); break; } else { - if ((*it)->access_count > 0) { - response.set_type(fenris::ResponseType::ERROR); - response.set_success(false); + if ((it)->access_count > 0) { response.set_error_message("Directory is in use"); break; } } - auto result = fenris::common::delete_directory(filename, true); - if (result == fenris::common::FileOperationResult::SUCCESS) { + auto result = common::delete_directory(filename, true); + if (result == common::FileOperationResult::SUCCESS) { response.set_type(fenris::ResponseType::SUCCESS); response.set_success(true); response.set_data("DELETE_DIRECTORY"); - FST.removeNode(filename); - } else if (result == - fenris::common::FileOperationResult::DIRECTORY_NOT_EMPTY) { - response.set_type(fenris::ResponseType::ERROR); - response.set_success(false); + FST.remove_node(filename); + } else if (result == common::FileOperationResult::DIRECTORY_NOT_EMPTY) { response.set_error_message("Directory is not empty"); } else { - response.set_type(fenris::ResponseType::ERROR); - response.set_success(false); response.set_error_message("Failed to delete directory"); } break; } default: - response.set_type(fenris::ResponseType::ERROR); - response.set_success(false); response.set_error_message("Unknown command"); } return response; } -std::vector ClientHandler::get_handled_client_ids() -{ - std::lock_guard lock(m_mutex); - return m_handled_client_sockets; -} - -std::vector ClientHandler::get_received_requests() -{ - std::lock_guard lock(m_mutex); - return m_received_requests; -} - -int ClientHandler::get_request_count() -{ - std::lock_guard lock(m_mutex); - return m_request_count; -} - } // namespace server } // namespace fenris diff --git a/tests/unittests/server/server_connection_manager_test.cpp b/tests/unittests/server/server_connection_manager_test.cpp index 6790f93..93a3d71 100644 --- a/tests/unittests/server/server_connection_manager_test.cpp +++ b/tests/unittests/server/server_connection_manager_test.cpp @@ -27,7 +27,7 @@ using namespace fenris::common::network; using namespace google::protobuf::util; // Mock implementation of ClientHandler for testing -class MockClientHandler : public ClientHandler { +class MockClientHandler : public IClientHandler { public: explicit MockClientHandler(bool keep_connection = true, int max_requests = 10) @@ -36,12 +36,11 @@ class MockClientHandler : public ClientHandler { { } - std::pair - handle_request(uint32_t client_socket, - const fenris::Request &request) override + fenris::Response handle_request(const fenris::Request &request, + ClientInfo &client_info) override { std::lock_guard lock(m_mutex); - m_handled_client_sockets.push_back(client_socket); + m_handled_client_sockets.push_back(client_info.socket); m_received_requests.push_back(request); m_request_count++; @@ -91,7 +90,8 @@ class MockClientHandler : public ClientHandler { } bool should_keep_connection = m_keep_connection && (m_request_count < m_max_requests); - return {response, should_keep_connection}; + client_info.keep_connection = should_keep_connection; + return response; } std::vector get_handled_client_ids() @@ -282,7 +282,6 @@ class ServerConnectionManagerTest : public ::testing::Test { protected: void SetUp() override { - m_port = 12345 + (rand() % 1000); m_port_str = std::to_string(m_port); @@ -314,8 +313,6 @@ class ServerConnectionManagerTest : public ::testing::Test { std::this_thread::sleep_for(std::chrono::milliseconds(200)); } - - for (int sock : m_client_sockets) { if (sock >= 0) { close(sock); @@ -324,28 +321,29 @@ class ServerConnectionManagerTest : public ::testing::Test { m_client_sockets.clear(); } - ClientInfo connect_test_client() + std::pair connect_test_client() { int client_sock = create_and_connect_client_socket("127.0.0.1", m_port); std::vector shared_key; - if (client_sock >= 0) { - m_client_sockets.push_back(client_sock); + if (client_sock < 0) { + return {ClientInfo(0, 0), false}; + } - if (!perform_client_key_exchange(client_sock, shared_key)) { - std::cerr << "Key exchange failed during test client connection" - << std::endl; - close(client_sock); - m_client_sockets.pop_back(); - return ClientInfo{}; - } - std::cout << "Key exchange successful for client socket: " - << client_sock << std::endl; + m_client_sockets.push_back(client_sock); + + if (!perform_client_key_exchange(client_sock, shared_key)) { + std::cerr << "Key exchange failed during test client connection" + << std::endl; + close(client_sock); + m_client_sockets.pop_back(); + return {ClientInfo(0, 0), false}; } - ClientInfo client_info; - client_info.client_id = m_client_sockets.size(); - client_info.socket = client_sock; + std::cout << "Key exchange successful for client socket: " + << client_sock << std::endl; + + ClientInfo client_info(m_client_sockets.size(), client_sock); client_info.encryption_key = std::vector(shared_key); client_info.address = "127.0.0.1"; client_info.port = m_port_str; @@ -353,7 +351,7 @@ class ServerConnectionManagerTest : public ::testing::Test { std::cout << "Client connected with ID: " << client_info.client_id << std::endl; - return client_info; + return {client_info, true}; } std::unique_ptr m_connection_manager; @@ -379,8 +377,8 @@ TEST_F(ServerConnectionManagerTest, AcceptClientConnection) m_connection_manager->start(); std::this_thread::sleep_for(std::chrono::milliseconds(100)); - ClientInfo client = connect_test_client(); - ASSERT_GE(client.socket, 0); + auto [client, success] = connect_test_client(); + ASSERT_TRUE(success); std::this_thread::sleep_for(std::chrono::milliseconds(100)); ASSERT_EQ(m_connection_manager->get_active_client_count(), 1); @@ -455,13 +453,12 @@ TEST_F(ServerConnectionManagerTest, MultipleClientConnections) ASSERT_EQ(response_opt->data(), "TERMINATE"); }; - ClientInfo client1 = connect_test_client(); - ClientInfo client2 = connect_test_client(); - ClientInfo client3 = connect_test_client(); - - ASSERT_GE(client1.socket, 0); - ASSERT_GE(client2.socket, 0); - ASSERT_GE(client3.socket, 0); + auto [client1, success1] = connect_test_client(); + ASSERT_TRUE(success1); + auto [client2, success2] = connect_test_client(); + ASSERT_TRUE(success2); + auto [client3, success3] = connect_test_client(); + ASSERT_TRUE(success3); std::this_thread::sleep_for(std::chrono::milliseconds(200)); ASSERT_EQ(m_connection_manager->get_active_client_count(), 3); @@ -491,8 +488,8 @@ TEST_F(ServerConnectionManagerTest, ClientDisconnection) m_connection_manager->start(); std::this_thread::sleep_for(std::chrono::milliseconds(100)); - ClientInfo client = connect_test_client(); - ASSERT_GE(client.socket, 0); + auto [client, success] = connect_test_client(); + ASSERT_TRUE(success); std::this_thread::sleep_for(std::chrono::milliseconds(100)); ASSERT_EQ(m_connection_manager->get_active_client_count(), 1); @@ -527,8 +524,8 @@ TEST_F(ServerConnectionManagerTest, HandleDifferentRequestTypes) m_connection_manager->start(); std::this_thread::sleep_for(std::chrono::milliseconds(100)); - ClientInfo client = connect_test_client(); - ASSERT_GE(client.socket, 0); + auto [client, success] = connect_test_client(); + ASSERT_TRUE(success); std::this_thread::sleep_for(std::chrono::milliseconds(100));