Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 091ef6c8bf | |||
| cda2d9a0ff | |||
| c02ba25371 | |||
| 7dac7fb0de | |||
| 46574026fe | |||
| 9d0a5193a5 |
@@ -0,0 +1,34 @@
|
||||
#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>();
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#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_;
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
#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);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#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_;
|
||||
};
|
||||
@@ -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>();
|
||||
}
|
||||
@@ -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_;
|
||||
};
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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_;
|
||||
};
|
||||
@@ -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());
|
||||
}
|
||||
@@ -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_;
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
#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>();
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#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_;
|
||||
};
|
||||
@@ -0,0 +1,70 @@
|
||||
#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_;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#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_;
|
||||
};
|
||||
@@ -0,0 +1,89 @@
|
||||
#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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#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);
|
||||
};
|
||||
@@ -0,0 +1,95 @@
|
||||
#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();
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#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_;
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
#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();
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#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_;
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
#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())
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#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_;
|
||||
};
|
||||
@@ -1,16 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#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();
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#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;
|
||||
}
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
# Test DNS server on port 5300
|
||||
dig @127.0.0.1 -p 5300 example.com
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
#!/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
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
# Test HTTP server on port 8080
|
||||
curl -v http://127.0.0.1:8080/
|
||||
@@ -0,0 +1 @@
|
||||
Hello from file!
|
||||
@@ -2,16 +2,37 @@ set_languages "c++23"
|
||||
|
||||
add_rules("plugin.compile_commands.autoupdate", { outputdir = "." })
|
||||
|
||||
add_requires("boost", {
|
||||
system = true, -- sudo pacman -S boost
|
||||
|
||||
add_requires("cmake::Boost", {
|
||||
alias = "boost",
|
||||
system = true,
|
||||
configs = {
|
||||
all = false,
|
||||
system = true, -- asio
|
||||
components = { "program_options", "log" },
|
||||
},
|
||||
})
|
||||
|
||||
target "app"
|
||||
-- HTTP server target
|
||||
target "http_server"
|
||||
do
|
||||
set_kind "binary"
|
||||
add_files "src/*.cpp"
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user