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
This commit is contained in:
Ваше Имя
2025-09-30 18:55:18 +04:00
parent 9d0a5193a5
commit 46574026fe
7 changed files with 159 additions and 0 deletions
+34
View File
@@ -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<T>()->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<int>();
}
std::string FileCli::getRootDir() const
{
return vm_["root"].as<std::string>();
}
+19
View File
@@ -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_;
};
+13
View File
@@ -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<FileSession>(std::move(socket), rootDir_)->start();
}
+16
View File
@@ -0,0 +1,16 @@
#pragma once
#include "tcp_server_base.hpp"
#include <string>
class FileServer : public TcpServerBase
{
public:
FileServer(IoContext &io, unsigned short port, std::string rootDir);
protected:
void createSession(TcpSocket socket) override;
private:
std::string rootDir_;
};
+35
View File
@@ -0,0 +1,35 @@
#include "file_session.hpp"
#include "session_base.hpp"
#include "logger.hpp"
#include <fstream>
#include <sstream>
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());
}
+15
View File
@@ -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_;
};
+27
View File
@@ -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();
}