From 46574026fe3b3ec3dacf1605ff03611467a20da3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B0=D1=88=D0=B5=20=D0=98=D0=BC=D1=8F?= Date: Tue, 30 Sep 2025 18:55:18 +0400 Subject: [PATCH] feat(file server): implement file server with CLI options Implement file server with CLI options for port and root directory, server initializes sessions, session handles file requests, and main function orchestrates setup and execution --- src/file/file_cli.cpp | 34 ++++++++++++++++++++++++++++++++++ src/file/file_cli.hpp | 19 +++++++++++++++++++ src/file/file_server.cpp | 13 +++++++++++++ src/file/file_server.hpp | 16 ++++++++++++++++ src/file/file_session.cpp | 35 +++++++++++++++++++++++++++++++++++ src/file/file_session.hpp | 15 +++++++++++++++ src/main/main_file.cpp | 27 +++++++++++++++++++++++++++ 7 files changed, 159 insertions(+) create mode 100644 src/file/file_cli.cpp create mode 100644 src/file/file_cli.hpp create mode 100644 src/file/file_server.cpp create mode 100644 src/file/file_server.hpp create mode 100644 src/file/file_session.cpp create mode 100644 src/file/file_session.hpp create mode 100644 src/main/main_file.cpp diff --git a/src/file/file_cli.cpp b/src/file/file_cli.cpp new file mode 100644 index 0000000..0a064b2 --- /dev/null +++ b/src/file/file_cli.cpp @@ -0,0 +1,34 @@ +#include "file_cli.hpp" + +// helper for default values +constexpr auto withDefault = [](auto value) { + using T = decltype(value); + return boost::program_options::value()->default_value(value); +}; + +FileCli::FileCli() + : CliBase("File Server Options") + , port_(9090) + , rootDir_("./data") +{ +} + +void FileCli::setupOptions() +{ + // clang-format off + desc_.add_options() + ("help,h", "Show help message") + ("port,p", withDefault(9090), "Port to listen on") + ("root,r", withDefault(std::string("./data")), "Root directory for files"); + // clang-format on +} + +int FileCli::getPort() const +{ + return vm_["port"].as(); +} + +std::string FileCli::getRootDir() const +{ + return vm_["root"].as(); +} diff --git a/src/file/file_cli.hpp b/src/file/file_cli.hpp new file mode 100644 index 0000000..0aa9968 --- /dev/null +++ b/src/file/file_cli.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include "cli_base.hpp" + +class FileCli : public CliBase +{ + public: + FileCli(); + + int getPort() const; + std::string getRootDir() const; + + protected: + void setupOptions() override; + + private: + int port_; + std::string rootDir_; +}; diff --git a/src/file/file_server.cpp b/src/file/file_server.cpp new file mode 100644 index 0000000..d0198f2 --- /dev/null +++ b/src/file/file_server.cpp @@ -0,0 +1,13 @@ +#include "file_server.hpp" +#include "file_session.hpp" + +FileServer::FileServer(IoContext &io, unsigned short port, std::string rootDir) + : TcpServerBase(io, port) + , rootDir_(std::move(rootDir)) +{ +} + +void FileServer::createSession(TcpSocket socket) +{ + std::make_shared(std::move(socket), rootDir_)->start(); +} diff --git a/src/file/file_server.hpp b/src/file/file_server.hpp new file mode 100644 index 0000000..ed8991c --- /dev/null +++ b/src/file/file_server.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include "tcp_server_base.hpp" +#include + +class FileServer : public TcpServerBase +{ + public: + FileServer(IoContext &io, unsigned short port, std::string rootDir); + + protected: + void createSession(TcpSocket socket) override; + + private: + std::string rootDir_; +}; diff --git a/src/file/file_session.cpp b/src/file/file_session.cpp new file mode 100644 index 0000000..9525b42 --- /dev/null +++ b/src/file/file_session.cpp @@ -0,0 +1,35 @@ +#include "file_session.hpp" +#include "session_base.hpp" +#include "logger.hpp" +#include +#include + +FileSession::FileSession(TcpSocket socket, std::string rootDir) + : SessionBase(std::move(socket)) + , rootDir_(std::move(rootDir)) +{ +} + +void FileSession::handleRequest(const std::string_view rawRequest) +{ + // strip trailing newlines + std::string fileName(rawRequest); + while (!fileName.empty() + && (fileName.back() == '\r' || fileName.back() == '\n')) { + fileName.pop_back(); + } + + std::string filePath = rootDir_ + "/" + fileName; + std::ifstream file(filePath, std::ios::binary); + + std::ostringstream ss; + if (file) { + ss << file.rdbuf(); + Logger::info("File served: " + filePath); + } else { + ss << "File not found: " << fileName; + Logger::warn("File not found: " + filePath); + } + + doWrite(ss.str()); +} diff --git a/src/file/file_session.hpp b/src/file/file_session.hpp new file mode 100644 index 0000000..42a20a3 --- /dev/null +++ b/src/file/file_session.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include "session_base.hpp" + +class FileSession : public SessionBase +{ + public: + FileSession(TcpSocket socket, std::string rootDir); + + protected: + void handleRequest(const std::string_view rawRequest) override; + + private: + std::string rootDir_; +}; diff --git a/src/main/main_file.cpp b/src/main/main_file.cpp new file mode 100644 index 0000000..96c5a24 --- /dev/null +++ b/src/main/main_file.cpp @@ -0,0 +1,27 @@ +#include "file_cli.hpp" +#include "file_server.hpp" +#include "logger.hpp" + +int main(int argc, char *argv[]) +{ + FileCli cli; + cli.parse(argc, argv); + + if (cli.isHelp()) { + cli.printHelp(); + return 0; + } + + Logger::init(true, "file_server.log"); + + IoContext io; + FileServer server(io, cli.getPort(), cli.getRootDir()); + + Logger::info( + "File server starting on port " + std::to_string(cli.getPort()) + + ", root dir: " + cli.getRootDir() + ); + + server.start(); + io.run(); +}