1 Commits

Author SHA1 Message Date
Ваше Имя 88520915dc feat(utils/logger): add logging utility with console and file output 2025-10-08 20:24:17 +04:00
43 changed files with 25 additions and 1184 deletions
-27
View File
@@ -1,27 +0,0 @@
#pragma once
#include <boost/asio.hpp>
#include <boost/program_options.hpp>
#include <boost/system/error_code.hpp>
// --- Error handling ---
using ErrorCode = boost::system::error_code;
// --- IO Context ---
using IoContext = boost::asio::io_context;
// --- TCP ---
using TcpSocket = boost::asio::ip::tcp::socket;
using TcpAcceptor = boost::asio::ip::tcp::acceptor;
using TcpEndpoint = boost::asio::ip::tcp::endpoint;
// --- UDP (для DNS) ---
using UdpSocket = boost::asio::ip::udp::socket;
using UdpEndpoint = boost::asio::ip::udp::endpoint;
// --- Таймеры ---
using SteadyTimer = boost::asio::steady_timer;
// --- Program options ---
using OptionsDescription = boost::program_options::options_description;
using VariablesMap = boost::program_options::variables_map;
-34
View File
@@ -1,34 +0,0 @@
#include "cli_base.hpp"
#include <boost/program_options/parsers.hpp>
#include <iostream>
CliBase::CliBase(const std::string &title)
: desc_(title)
, help_(false)
{
}
void CliBase::parse(int argc, char *argv[])
{
using namespace boost::program_options;
setupOptions(); // must be first
parsed_options parsed = parse_command_line(argc, argv, desc_);
store(parsed, vm_);
notify(vm_);
if (vm_.count("help")) {
help_ = true;
}
}
bool CliBase::isHelp() const
{
return help_;
}
void CliBase::printHelp() const
{
std::cout << desc_ << "\n";
}
-27
View File
@@ -1,27 +0,0 @@
#pragma once
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/variables_map.hpp>
#include <string>
class CliBase
{
public:
CliBase(const std::string &title);
virtual ~CliBase() = default;
// parse argc/argv
void parse(int argc, char *argv[]);
bool isHelp() const;
void printHelp() const;
protected:
// children add their specific options here
virtual void setupOptions() = 0;
boost::program_options::options_description desc_;
boost::program_options::variables_map vm_;
bool help_;
};
-9
View File
@@ -1,9 +0,0 @@
#pragma once
class Server
{
public:
virtual ~Server() = default;
virtual void start() = 0;
virtual void stop() = 0;
};
-70
View File
@@ -1,70 +0,0 @@
#include "session_base.hpp"
#include "logger.hpp"
#include <boost/asio/write.hpp>
SessionBase::SessionBase(TcpSocket socket)
: socket_(std::move(socket))
{
}
void SessionBase::start()
{
doRead();
}
void SessionBase::doRead()
{
using namespace boost::asio;
auto self = shared_from_this();
auto handler = [this, self](ErrorCode ec, std::size_t bytesTransferred) {
onRead(ec, bytesTransferred);
};
socket_.async_read_some(buffer(buffer_), handler);
}
void SessionBase::onRead(ErrorCode ec, std::size_t bytesTransferred)
{
if (ec) {
Logger::error("Read error: " + ec.message());
return;
}
std::string rawRequest(buffer_.data(), bytesTransferred);
Logger::info("Received request:\n" + rawRequest);
try {
handleRequest(rawRequest);
} catch (const std::exception &ex) {
Logger::error(std::string("Request handling failed: ") + ex.what());
}
}
void SessionBase::doWrite(const std::string &response)
{
using namespace boost::asio;
auto self = shared_from_this();
auto onWriteHandler =
[this, self](ErrorCode ec, std::size_t /*bytesTransferred*/) {
if (ec) {
Logger::error("Write error: " + ec.message());
}
ErrorCode shutdownEc;
if (socket_.shutdown(TcpSocket::shutdown_both, shutdownEc)) {
Logger::warn("Shutdown error: " + shutdownEc.message());
}
ErrorCode closeEc;
if (socket_.close(closeEc)) {
Logger::warn("Close error: " + closeEc.message());
} else {
Logger::info("Connection closed");
}
};
async_write(socket_, buffer(response), onWriteHandler);
}
-25
View File
@@ -1,25 +0,0 @@
#pragma once
#include "aliases.hpp"
#include <array>
#include <memory>
#include <string>
class SessionBase : public std::enable_shared_from_this<SessionBase>
{
public:
explicit SessionBase(TcpSocket socket);
virtual ~SessionBase() = default;
void start();
protected:
virtual void handleRequest(const std::string_view rawRequest) = 0;
void doRead();
void onRead(ErrorCode ec, std::size_t bytesTransferred);
void doWrite(const std::string &response);
TcpSocket socket_;
std::array<char, 8192> buffer_;
};
-23
View File
@@ -1,23 +0,0 @@
#include "tcp_server_base.hpp"
#include "logger.hpp"
TcpServerBase::TcpServerBase(IoContext &io, unsigned short port)
: acceptor_(io, TcpEndpoint(boost::asio::ip::tcp::v4(), port))
{
}
void TcpServerBase::start()
{
auto doAccept = [this]() {
acceptor_.async_accept([this](ErrorCode ec, TcpSocket socket) {
if (!ec) {
Logger::info("New client connected");
createSession(std::move(socket));
} else {
Logger::error("Accept error: " + ec.message());
}
start(); // continue accepting
});
};
doAccept();
}
-17
View File
@@ -1,17 +0,0 @@
#pragma once
#include "aliases.hpp"
class TcpServerBase
{
public:
TcpServerBase(IoContext &io, unsigned short port);
virtual ~TcpServerBase() = default;
void start();
protected:
virtual void createSession(TcpSocket socket) = 0;
TcpAcceptor acceptor_;
};
-46
View File
@@ -1,46 +0,0 @@
#include "udp_server_base.hpp"
#include "logger.hpp"
UdpServerBase::UdpServerBase(IoContext &io, unsigned short port)
: socket_(io, UdpEndpoint(boost::asio::ip::udp::v4(), port))
{
}
void UdpServerBase::start()
{
doReceive();
}
void UdpServerBase::doReceive()
{
using namespace boost::asio;
auto onReceive = [this](ErrorCode ec, std::size_t bytesReceived) {
if (!ec && bytesReceived > 0) {
std::string_view data(buffer_.data(), bytesReceived);
handleRequest(data, remoteEndpoint_);
} else if (ec) {
Logger::error("UDP receive error: " + ec.message());
}
doReceive(); // continue listening
};
socket_.async_receive_from(buffer(buffer_), remoteEndpoint_, onReceive);
}
void UdpServerBase::doSend(
const std::string &response, const UdpEndpoint &target
)
{
using namespace boost::asio;
auto bufferToSend = std::make_shared<std::string>(response);
auto onSend = [bufferToSend](ErrorCode ec, std::size_t /*bytesSent*/) {
if (ec) {
Logger::error("UDP send error: " + ec.message());
}
};
socket_.async_send_to(buffer(*bufferToSend), target, onSend);
}
-23
View File
@@ -1,23 +0,0 @@
#pragma once
#include "aliases.hpp"
class UdpServerBase
{
public:
UdpServerBase(IoContext &io, unsigned short port);
virtual ~UdpServerBase() = default;
void start();
protected:
virtual void
handleRequest(const std::string_view data, const UdpEndpoint &sender) = 0;
void doReceive();
void doSend(const std::string &response, const UdpEndpoint &target);
UdpSocket socket_;
std::array<char, 4096> buffer_;
UdpEndpoint remoteEndpoint_;
};
-34
View File
@@ -1,34 +0,0 @@
#include "dns_cli.hpp"
// helper for default values
constexpr auto withDefault = [](auto value) {
using T = decltype(value);
return boost::program_options::value<T>()->default_value(value);
};
DnsCli::DnsCli()
: CliBase("DNS Server Options")
, port_(5300)
, ip_("127.0.0.1")
{
}
void DnsCli::setupOptions()
{
// clang-format off
desc_.add_options()
("help,h", "Show help message")
("port,p", withDefault(5300), "UDP port to listen on")
("ip,i", withDefault(std::string("127.0.0.1")), "IP address to return in A record");
// clang-format on
}
int DnsCli::getPort() const
{
return vm_["port"].as<int>();
}
std::string DnsCli::getIp() const
{
return vm_["ip"].as<std::string>();
}
-20
View File
@@ -1,20 +0,0 @@
#pragma once
#include "cli_base.hpp"
#include <string>
class DnsCli : public CliBase
{
public:
DnsCli();
int getPort() const;
std::string getIp() const;
protected:
void setupOptions() override;
private:
int port_;
std::string ip_;
};
-57
View File
@@ -1,57 +0,0 @@
#include "dns_server.hpp"
#include "logger.hpp"
DnsServer::DnsServer(IoContext &io, unsigned short port, std::string answerIp)
: UdpServerBase(io, port)
, answerIp_(std::move(answerIp))
{
}
void DnsServer::handleRequest(
const std::string_view data, const UdpEndpoint &sender
)
{
if (data.size() < 12) {
Logger::warn("Invalid DNS packet");
return;
}
// response buffer
std::string resp;
// copy ID
resp.append(data.substr(0, 2));
// flags: QR=1 (response), AA=1
resp.push_back('\x81');
resp.push_back('\x80');
// QDCOUNT=1, ANCOUNT=1, NSCOUNT=0, ARCOUNT=0
resp.append("\0\1\0\1\0\0\0\0", 8);
// copy question (from offset 12 till end of query)
resp.append(data.substr(12));
// answer section
resp.append("\xc0\x0c"); // name pointer to question
resp.append("\0\1"); // TYPE A
resp.append("\0\1"); // CLASS IN
resp.append("\0\0\0\x3c"); // TTL = 60
resp.append("\0\x04"); // RDLENGTH = 4
// answer IP (from CLI or default 127.0.0.1)
unsigned int b1, b2, b3, b4;
if (sscanf(answerIp_.c_str(), "%u.%u.%u.%u", &b1, &b2, &b3, &b4) != 4) {
b1 = 127;
b2 = 0;
b3 = 0;
b4 = 1;
}
resp.push_back(static_cast<char>(b1));
resp.push_back(static_cast<char>(b2));
resp.push_back(static_cast<char>(b3));
resp.push_back(static_cast<char>(b4));
Logger::info("DNS query answered with A " + answerIp_);
doSend(resp, sender);
}
-18
View File
@@ -1,18 +0,0 @@
#pragma once
#include "udp_server_base.hpp"
#include <string>
class DnsServer : public UdpServerBase
{
public:
DnsServer(IoContext &io, unsigned short port, std::string answerIp);
protected:
void handleRequest(
const std::string_view data, const UdpEndpoint &sender
) override;
private:
std::string answerIp_;
};
-34
View File
@@ -1,34 +0,0 @@
#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
@@ -1,19 +0,0 @@
#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
@@ -1,13 +0,0 @@
#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
@@ -1,16 +0,0 @@
#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
@@ -1,35 +0,0 @@
#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
@@ -1,15 +0,0 @@
#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_;
};
-34
View File
@@ -1,34 +0,0 @@
#include "http_cli.hpp"
// helper for default values
constexpr auto withDefault = [](auto value) {
using T = decltype(value);
return boost::program_options::value<T>()->default_value(value);
};
HttpCli::HttpCli()
: CliBase("HTTP Server Options")
, port_(8080)
, rootDir_("./www")
{
}
void HttpCli::setupOptions()
{
// clang-format off
desc_.add_options()
("help,h", "Show help message")
("port,p", withDefault(8080), "Port to listen on")
("root,r", withDefault(std::string("./www")), "Root directory for files");
// clang-format on
}
int HttpCli::getPort() const
{
return vm_["port"].as<int>();
}
std::string HttpCli::getRootDir() const
{
return vm_["root"].as<std::string>();
}
-19
View File
@@ -1,19 +0,0 @@
#pragma once
#include "cli_base.hpp"
class HttpCli : public CliBase
{
public:
HttpCli();
int getPort() const;
std::string getRootDir() const;
protected:
void setupOptions() override;
private:
int port_;
std::string rootDir_;
};
-70
View File
@@ -1,70 +0,0 @@
#include "http_request.hpp"
#include <stdexcept>
void HttpRequest::setMethod(const std::string_view method)
{
method_ = method;
}
const std::string HttpRequest::getMethod() const
{
return method_;
}
void HttpRequest::setPath(const std::string_view path)
{
path_ = path;
}
const std::string HttpRequest::getPath() const
{
return path_;
}
void HttpRequest::setVersion(const std::string_view version)
{
version_ = version;
}
const std::string HttpRequest::getVersion() const
{
return version_;
}
void HttpRequest::addHeader(
const std::string_view key, const std::string_view value
)
{
headers_[std::string(key)] = std::string(value);
}
bool HttpRequest::hasHeader(const std::string_view key) const
{
return headers_.find(std::string(key)) != headers_.end();
}
std::string HttpRequest::getHeader(const std::string_view key) const
{
auto it = headers_.find(std::string(key));
if (it == headers_.end()) {
throw std::runtime_error("Header not found: " + std::string(key));
}
return it->second;
}
const std::unordered_map<std::string, std::string> &
HttpRequest::getHeaders() const
{
return headers_;
}
void HttpRequest::setBody(const std::string_view body)
{
body_ = body;
}
const std::string HttpRequest::getBody() const
{
return body_;
}
-34
View File
@@ -1,34 +0,0 @@
#pragma once
#include <string>
#include <unordered_map>
class HttpRequest
{
public:
HttpRequest() = default;
void setMethod(const std::string_view method);
const std::string getMethod() const;
void setPath(const std::string_view path);
const std::string getPath() const;
void setVersion(const std::string_view version);
const std::string getVersion() const;
void addHeader(const std::string_view key, const std::string_view value);
bool hasHeader(const std::string_view key) const;
std::string getHeader(const std::string_view key) const;
const std::unordered_map<std::string, std::string> &getHeaders() const;
void setBody(const std::string_view body);
const std::string getBody() const;
private:
std::string method_; // GET, POST
std::string path_; // /index.html
std::string version_; // HTTP/1.1
std::unordered_map<std::string, std::string> headers_;
std::string body_;
};
-89
View File
@@ -1,89 +0,0 @@
#include "http_request_parser.hpp"
#include <boost/algorithm/string/trim.hpp>
#include <sstream>
#include <stdexcept>
HttpRequest HttpRequestParser::parse(const std::string &raw)
{
std::istringstream stream(raw);
HttpRequest request;
std::string line;
if (!std::getline(stream, line)) {
throw std::runtime_error("Invalid HTTP request: empty");
}
if (!line.empty() && line.back() == '\r') {
line.pop_back();
}
parseRequestLine(line, request);
parseHeaders(stream, request);
parseBody(stream, request);
return request;
}
// parse "GET /index.html HTTP/1.1"
void HttpRequestParser::parseRequestLine(
const std::string &line, HttpRequest &req
)
{
std::istringstream requestLine(line);
std::string method, path, version;
requestLine >> method >> path >> version;
if (method.empty() || path.empty() || version.empty()) {
throw std::runtime_error("Invalid HTTP request line");
}
req.setMethod(method);
req.setPath(path);
req.setVersion(version);
}
// parse headers until empty line
void HttpRequestParser::parseHeaders(
std::istringstream &stream, HttpRequest &req
)
{
std::string line;
while (std::getline(stream, line)) {
if (!line.empty() && line.back() == '\r') {
line.pop_back();
}
if (line.empty()) {
break; // end of headers
}
parseHeaderLine(line, req);
}
}
// parse single "Key: Value" header
void HttpRequestParser::parseHeaderLine(
const std::string &line, HttpRequest &req
)
{
auto colon = line.find(':');
if (colon == std::string::npos) {
return; // skip invalid header
}
std::string key = line.substr(0, colon);
std::string value = line.substr(colon + 1);
boost::algorithm::trim(key);
boost::algorithm::trim(value);
req.addHeader(key, value);
}
// parse optional body after headers
void HttpRequestParser::parseBody(std::istringstream &stream, HttpRequest &req)
{
std::string body;
std::getline(stream, body, '\0');
if (!body.empty()) {
req.setBody(body);
}
}
-16
View File
@@ -1,16 +0,0 @@
#pragma once
#include "http_request.hpp"
#include <string>
class HttpRequestParser
{
public:
static HttpRequest parse(const std::string &raw);
private:
static void parseRequestLine(const std::string &line, HttpRequest &req);
static void parseHeaderLine(const std::string &line, HttpRequest &req);
static void parseHeaders(std::istringstream &stream, HttpRequest &req);
static void parseBody(std::istringstream &stream, HttpRequest &req);
};
-95
View File
@@ -1,95 +0,0 @@
#include "http_response.hpp"
#include <sstream>
#include <stdexcept>
HttpResponse::HttpResponse()
: statusCode_(200)
, reasonPhrase_("OK")
, version_("HTTP/1.1")
{
}
void HttpResponse::setStatusCode(int code)
{
statusCode_ = code;
}
int HttpResponse::getStatusCode() const
{
return statusCode_;
}
void HttpResponse::setReasonPhrase(const std::string_view phrase)
{
reasonPhrase_ = phrase;
}
const std::string_view HttpResponse::getReasonPhrase() const
{
return reasonPhrase_;
}
void HttpResponse::setVersion(const std::string_view version)
{
version_ = version;
}
const std::string_view HttpResponse::getVersion() const
{
return version_;
}
void HttpResponse::addHeader(
const std::string_view key, const std::string_view value
)
{
headers_[std::string(key)] = std::string(value);
}
bool HttpResponse::hasHeader(const std::string_view key) const
{
return headers_.find(std::string(key)) != headers_.end();
}
std::string HttpResponse::getHeader(const std::string_view key) const
{
auto it = headers_.find(std::string(key));
if (it == headers_.end()) {
throw std::runtime_error("Header not found: " + std::string(key));
}
return it->second;
}
const std::unordered_map<std::string, std::string> &
HttpResponse::getHeaders() const
{
return headers_;
}
void HttpResponse::setBody(const std::string_view body)
{
body_ = body;
}
const std::string_view HttpResponse::getBody() const
{
return body_;
}
std::string HttpResponse::toString() const
{
std::ostringstream oss;
oss << version_ << " " << statusCode_ << " " << reasonPhrase_ << "\r\n";
for (const auto &[key, value] : headers_) {
oss << key << ": " << value << "\r\n";
}
oss << "\r\n";
oss << body_;
return oss.str();
}
-36
View File
@@ -1,36 +0,0 @@
#pragma once
#include <string>
#include <unordered_map>
class HttpResponse
{
public:
HttpResponse();
void setStatusCode(int code);
int getStatusCode() const;
void setReasonPhrase(const std::string_view phrase);
const std::string_view getReasonPhrase() const;
void setVersion(const std::string_view version);
const std::string_view getVersion() const;
void addHeader(const std::string_view key, const std::string_view value);
bool hasHeader(const std::string_view key) const;
std::string getHeader(const std::string_view key) const;
const std::unordered_map<std::string, std::string> &getHeaders() const;
void setBody(const std::string_view body);
const std::string_view getBody() const;
std::string toString() const;
private:
int statusCode_;
std::string reasonPhrase_;
std::string version_;
std::unordered_map<std::string, std::string> headers_;
std::string body_;
};
-15
View File
@@ -1,15 +0,0 @@
#include "http_server.hpp"
#include "http_server.hpp"
#include "http_session.hpp"
HttpServer::HttpServer(IoContext &io, unsigned short port, std::string rootDir)
: TcpServerBase(io, port)
, rootDir_(std::move(rootDir))
{
}
void HttpServer::createSession(TcpSocket socket)
{
std::make_shared<HttpSession>(std::move(socket), rootDir_)->start();
}
-15
View File
@@ -1,15 +0,0 @@
#pragma once
#include "tcp_server_base.hpp"
class HttpServer : public TcpServerBase
{
public:
HttpServer(IoContext &io, unsigned short port, std::string rootDir);
protected:
void createSession(TcpSocket socket) override;
private:
std::string rootDir_;
};
-61
View File
@@ -1,61 +0,0 @@
#include "http_session.hpp"
#include "session_base.hpp"
#include "http_request.hpp"
#include "http_request_parser.hpp"
#include "http_response.hpp"
#include "logger.hpp"
#include <fstream>
HttpSession::HttpSession(TcpSocket socket, std::string rootDir)
: SessionBase(std::move(socket))
, rootDir_(std::move(rootDir))
{
}
void HttpSession::handleRequest(const std::string_view rawRequest)
{
HttpRequest req = HttpRequestParser::parse(std::string(rawRequest));
HttpResponse res;
if (req.getPath().rfind("/file/", 0) == 0) {
// remove "/file/" prefix
std::string filePath = rootDir_ + "/" + req.getPath().substr(6);
std::ifstream file(filePath, std::ios::binary);
if (file) {
std::ostringstream ss;
ss << file.rdbuf();
std::string content = ss.str();
res.setStatusCode(200);
res.setReasonPhrase("OK");
res.setBody(content);
res.addHeader("Content-Type", "text/plain");
res.addHeader("Content-Length", std::to_string(content.size()));
} else {
res.setStatusCode(404);
res.setReasonPhrase("Not Found");
res.setBody("File not found");
res.addHeader("Content-Type", "text/plain");
res.addHeader(
"Content-Length", std::to_string(res.getBody().size())
);
}
} else {
res.setStatusCode(200);
res.setReasonPhrase("OK");
res.setBody("Hello, World!");
res.addHeader("Content-Type", "text/plain");
res.addHeader("Content-Length", std::to_string(res.getBody().size()));
}
std::string responseStr = res.toString();
doWrite(responseStr);
Logger::info(
req.getMethod() + " " + req.getPath() + " -> "
+ std::to_string(res.getStatusCode())
);
}
-15
View File
@@ -1,15 +0,0 @@
#pragma once
#include "session_base.hpp"
class HttpSession : public SessionBase
{
public:
HttpSession(TcpSocket socket, std::string rootDir);
protected:
void handleRequest(const std::string_view rawRequest) override;
private:
std::string rootDir_;
};
+16
View File
@@ -0,0 +1,16 @@
#include <boost/asio.hpp>
#include <iostream>
int main()
{
std::cout << "Hello, Boost + C++23 + xmake!\n";
boost::asio::io_context io;
boost::asio::steady_timer timer(io, std::chrono::seconds(1));
timer.async_wait([](const boost::system::error_code &) {
std::cout << "Timer fired after 1 second!\n";
});
io.run();
return 0;
}
-26
View File
@@ -1,26 +0,0 @@
#include "dns_cli.hpp"
#include "dns_server.hpp"
#include "logger.hpp"
int main(int argc, char *argv[])
{
DnsCli cli;
cli.parse(argc, argv);
if (cli.isHelp()) {
cli.printHelp();
return 0;
}
Logger::init(true, "dns_server.log");
Logger::info(
"DNS server starting on port " + std::to_string(cli.getPort())
+ ", answering with IP " + cli.getIp()
);
IoContext io;
DnsServer server(io, cli.getPort(), cli.getIp());
server.start();
io.run();
}
-27
View File
@@ -1,27 +0,0 @@
#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();
}
-29
View File
@@ -1,29 +0,0 @@
#include "http_cli.hpp"
#include "http_server.hpp"
#include "logger.hpp"
int main(int argc, char **argv)
{
HttpCli cli;
cli.parse(argc, argv);
if (cli.isHelp()) {
cli.printHelp();
return 0;
}
Logger::init(true, "http_server.log");
IoContext io;
HttpServer server(io, cli.getPort(), cli.getRootDir());
Logger::info(
"Http server starting on port " + std::to_string(cli.getPort())
+ ", root dir: " + cli.getRootDir()
);
server.start();
io.run();
return 0;
}
-3
View File
@@ -1,3 +0,0 @@
#!/bin/sh
# Test DNS server on port 5300
dig @127.0.0.1 -p 5300 example.com
-4
View File
@@ -1,4 +0,0 @@
#!/bin/sh
# Test File server on port 9090
# Send file name "test.txt" to server and print response
echo "test.txt" | nc 127.0.0.1 9090
-3
View File
@@ -1,3 +0,0 @@
#!/bin/sh
# Test HTTP server on port 8080
curl -v http://127.0.0.1:8080/
-1
View File
@@ -1 +0,0 @@
Hello from file!
+9 -30
View File
@@ -2,37 +2,16 @@ set_languages "c++23"
add_rules("plugin.compile_commands.autoupdate", { outputdir = "." }) add_rules("plugin.compile_commands.autoupdate", { outputdir = "." })
add_requires("cmake::Boost", { add_requires("boost", {
alias = "boost", system = true, -- sudo pacman -S boost
system = true,
configs = { configs = {
components = { "program_options", "log" }, all = false,
system = true, -- asio
}, },
}) })
-- HTTP server target target "app"
target "http_server" set_kind "binary"
do add_files "src/*.cpp"
set_kind "binary" add_links "boost_system"
add_files("src/main/main_http.cpp", "src/http/*.cpp", "src/common/*.cpp")
add_includedirs("src", "src/http", "src/common", { public = true })
add_packages "boost"
end
-- File server target
target "file_server"
do
set_kind "binary"
add_files("src/main/main_file.cpp", "src/file/*.cpp", "src/common/*.cpp")
add_includedirs("src", "src/file", "src/common", { public = true })
add_packages "boost"
end
-- DNS server target
target "dns_server"
do
set_kind "binary"
add_files("src/main/main_dns.cpp", "src/dns/*.cpp", "src/common/*.cpp")
add_includedirs("src", "src/dns", "src/common", { public = true })
add_packages "boost"
end