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(); +}