Compare commits

..

No commits in common. "0b794fac407b6938dc89cee5341d3316f069eb59" and "d9967b69e8b0cf62bd8fe245e6e6210935a98a68" have entirely different histories.

13 changed files with 139 additions and 318 deletions

View File

@ -38,8 +38,6 @@ add_executable(terminal-web-server
src/terminal_api_driver.cpp src/terminal_api_driver.cpp
src/auth/resources.cpp src/auth/resources.cpp
src/auth/resources.h src/auth/resources.h
src/auth/jwt.cpp
src/auth/jwt.h
) )
find_package(Boost 1.53.0 COMPONENTS system thread filesystem log log_setup REQUIRED) find_package(Boost 1.53.0 COMPONENTS system thread filesystem log log_setup REQUIRED)

View File

@ -1,5 +0,0 @@
//
// Created by vlad on 04.11.2024.
//
#include "jwt.h"

View File

@ -1,23 +0,0 @@
#ifndef JWT_H
#define JWT_H
#include <string>
#include "resources.h"
namespace http::auth::jwt {
class Jwt {
public:
static Jwt fromCookies(const std::string& cookie);
static Jwt fromString(const std::string& cookie);
static Jwt fromUser(const std::string& User);
bool isValid();
std::string getUsername();
std::string asCookie();
~Jwt();
};
}
#endif //JWT_H

View File

@ -3,13 +3,3 @@
// //
#include "resources.h" #include "resources.h"
#include <utility>
// http::auth::AuentificationRequiredResource::AuentificationRequiredResource(const std::string &path, AuthProvider& provider, resource::respGenerator generator): BasicResource(path), generator_(std::move(generator)) {
// }
//
// void http::auth::AuentificationRequiredResource::handle(const server::Request &req, server::Reply &rep) {
// }
//
// http::auth::AuentificationRequiredResource::~AuentificationRequiredResource() = default;

View File

@ -4,86 +4,25 @@
namespace http::auth { namespace http::auth {
/** /**
* Класс пользователя, содержит логин/хеш_пароля/настройки пользователя/права. * Класс пользовательских разрешений,
* Хеш пароля представляется в виде строки
*/ */
class User { class UserPremision{};
private:
uint32_t perms;
public:
const std::string username;
std::string passwordHash;
User(const std::string& username, const std::string& passwordHash);
/** /**
* Проверить пароль на соответствие хешу * Класс пользователя, содержит логин/хеш_пароля/настройки пользователя/права
* @param pass
* @return
*/ */
bool checkPassword(const std::string& pass); class User{};
/**
* Установка пароля
* @param pass строка с исходным паролем
*/
void setPassword(const std::string& pass);
static constexpr uint32_t SUPERUSER = 0x0001;
static constexpr uint32_t WATCH_STATISTICS = 0x0002; // мониторинг модема
static constexpr uint32_t RESET_PACKET_STATISTICS = 0x0004; // сброс статистики пакетов
static constexpr uint32_t WATCH_SETTINGS = 0x0008; // просмотр настроек , если недоступно, то вкладки с настройками не будет
static constexpr uint32_t EDIT_SETTINGS = 0x0010; // редактирование настроек, установка параметров модулятора/демодулятора/dma/cinc
static constexpr uint32_t UPDATE_FIRMWARE = 0x0020; // обновление прошивки
/**
* Проверить, что у пользователя есть нужное право. Если это суперпользователь, то у него по умолчанию все права есть.
* @param p набор прав, из констант данного класса.
* @return
*/
bool checkPremisions(uint32_t p);
void setPremisions(uint32_t p);
void resetPremisions(uint32_t p);
~User();
};
/** /**
* Класс аутентификации. Управляет всеми сессиями, создает новые при логине, удаляет при логауте. * Класс аутентификации. Управляет всеми сессиями, создает новые при логине, удаляет при логауте.
* @note Класс устанавливает заголовок 'Set-Cookie' в ответе, и этот заголовок должен дойти до пользователя! * @note Класс устанавливает заголовок 'Set-Cookie' в ответе, и этот заголовок должен дойти до пользователя!
*/ */
class AuthProvider { class AuthProvider {
public:
AuthProvider();
/**
* Авторизовать пользователя.
*
* @param rep
* @return true, в случае успешной авторизации
*/
bool doAuth(const std::string& username, const std::string& password, server::Reply& rep);
std::shared_ptr<User> getSession(server::Request& req);
~AuthProvider();
}; };
class AuentificationRequiredResource final: public resource::BasicResource { class NeedAuentificationResource: public resource::BasicResource {};
public:
explicit AuentificationRequiredResource(const std::string& path, std::shared_ptr<AuthProvider> provider, resource::respGenerator generator);
void handle(const server::Request &req, server::Reply &rep) override;
~AuentificationRequiredResource() override;
private:
resource::respGenerator generator_;
std::shared_ptr<AuthProvider> provider_;
};
} }
#endif //RESOURCES_H #endif //RESOURCES_H

View File

@ -16,7 +16,6 @@
#include <fstream> #include <fstream>
#include "terminal_api_driver.h" #include "terminal_api_driver.h"
#include "auth/resources.h"
namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp> namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp>
@ -73,66 +72,16 @@ void init_logging() {
log::add_common_attributes(); log::add_common_attributes();
} }
class ServerResources { static void initResources(http::server::Server& s, std::shared_ptr<api_driver::ApiDriver>& api) {
std::unique_ptr<http::resource::StaticFileFactory> sf; s.resources.emplace_back(std::make_unique<http::resource::StaticFileResource>("/", "static/main.html", mime_types::text_html));
std::unique_ptr<api_driver::ApiDriver> api; s.resources.emplace_back(std::make_unique<http::resource::StaticFileResource>("/login", "static/login.html", mime_types::text_html));
public: s.resources.emplace_back(std::make_unique<http::resource::StaticFileResource>("/favicon.ico", "static/favicon.png", mime_types::image_png));
static constexpr const char* INDEX_HTML = "static/main.html"; s.resources.emplace_back(std::make_unique<http::resource::StaticFileResource>("/images/krokodil_vzryvaetsya_hd.gif", "static/krokodil.gif", mime_types::image_gif));
static constexpr const char* LOGIN_HTML = "static/login.html";
static constexpr const char* LOGIN_FAILED_HTML = "static/login-failed.html";
// картинки, их даже можно кешировать s.resources.emplace_back(std::make_unique<http::resource::StaticFileResource>("/style.css", "static/style.css", mime_types::text_css));
static constexpr const char* FAVICON_ICO = "static/favicon.png"; s.resources.emplace_back(std::make_unique<http::resource::StaticFileResource>("/fields.css", "static/fields.css", mime_types::text_css));
static constexpr const char* KROKODIL_GIF = "static/krokodil.gif"; s.resources.emplace_back(std::make_unique<http::resource::StaticFileResource>("/js/vue.js", "static/js/vue.js", mime_types::javascript));
static constexpr const char* VUE_JS = "static/js/vue.js"; // это тоже можно кешировать
// а эти стили нельзя кешировать в отладочной версии
static constexpr const char* STYLE_CSS = "static/style.css";
static constexpr const char* FIELDS_CSS = "static/fields.css";
ServerResources(const ServerResources&) = delete;
ServerResources(): sf(std::make_unique<http::resource::StaticFileFactory>()), api(std::make_unique<api_driver::ApiDriver>()) {
sf->registerFile(INDEX_HTML, mime_types::text_html, false);
sf->registerFile(LOGIN_HTML, mime_types::text_html, false);
sf->registerFile(LOGIN_FAILED_HTML, mime_types::text_html, false);
sf->registerFile(FAVICON_ICO, mime_types::image_png, true);
sf->registerFile(KROKODIL_GIF, mime_types::image_gif, true);
sf->registerFile(VUE_JS, mime_types::javascript, true);
#if USE_DEBUG
constexpr bool allowCacheCss = false;
#else
constexpr bool allowCacheCss = true;
#endif
sf->registerFile(STYLE_CSS, mime_types::text_css, allowCacheCss);
sf->registerFile(FIELDS_CSS, mime_types::text_css, allowCacheCss);
}
void registerResources(http::server::Server& s) {
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/", [this](const auto& req, auto& rep) {
boost::ignore_unused(req);
sf->serve(INDEX_HTML, rep);
}));
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/login", [this](const auto& req, auto& rep) {
if (req.method == "GET") {
sf->serve(LOGIN_HTML, rep);
} else if (req.method == "POST") {
sf->serve(LOGIN_FAILED_HTML, rep);
} else {
http::server::stockReply(http::server::bad_request, rep);
}
}));
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/favicon.ico", [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(FAVICON_ICO, rep); }));
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/images/krokodil_vzryvaetsya_hd.gif", [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(KROKODIL_GIF, rep); }));
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/style.css", [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(STYLE_CSS, rep); }));
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/fields.css", [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(FIELDS_CSS, rep); }));
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/js/vue.js", [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(VUE_JS, rep); }));
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/api/statistics", [](const auto& req, auto& rep) { s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/api/statistics", [](const auto& req, auto& rep) {
if (req.method != "GET") { if (req.method != "GET") {
@ -146,7 +95,7 @@ public:
rep.content.insert(rep.content.end(), json, json + strlen(json)); rep.content.insert(rep.content.end(), json, json + strlen(json));
})); }));
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/api/get/statistics", [this](const auto& req, auto& rep) { s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/api/get/statistics", [api](const auto& req, auto& rep) {
if (req.method != "GET") { if (req.method != "GET") {
http::server::stockReply(http::server::bad_request, rep); http::server::stockReply(http::server::bad_request, rep);
} }
@ -160,7 +109,7 @@ public:
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size()); rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
})); }));
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/api/resetPacketStatistics", [this](const auto& req, auto& rep) { s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/api/resetPacketStatistics", [api](const auto& req, auto& rep) {
if (req.method != "POST") { if (req.method != "POST") {
http::server::stockReply(http::server::bad_request, rep); http::server::stockReply(http::server::bad_request, rep);
} }
@ -173,7 +122,7 @@ public:
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size()); rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
})); }));
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/api/get/settings", [this](const auto& req, auto& rep) { s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/api/get/settings", [api](const auto& req, auto& rep) {
if (req.method != "GET") { if (req.method != "GET") {
http::server::stockReply(http::server::bad_request, rep); http::server::stockReply(http::server::bad_request, rep);
} }
@ -188,10 +137,6 @@ public:
})); }));
} }
~ServerResources() = default;
};
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
try { try {
prctl(PR_SET_NAME, "main", 0, 0, 0); prctl(PR_SET_NAME, "main", 0, 0, 0);
@ -214,14 +159,14 @@ int main(int argc, char *argv[]) {
BOOST_LOG_TRIVIAL(info) << "Starting RELEASE build" << argv[0]; BOOST_LOG_TRIVIAL(info) << "Starting RELEASE build" << argv[0];
#endif #endif
ServerResources resources; auto api = std::make_shared<api_driver::ApiDriver>();
// Initialise the server. // Initialise the server.
std::unique_ptr<http::server::Server> s; std::unique_ptr<http::server::Server> s;
if (strcmp(argv[1], "nossl") == 0) { if (strcmp(argv[1], "nossl") == 0) {
s = std::make_unique<http::server::Server>(argv[2], argv[3]); s = std::make_unique<http::server::Server>(argv[2], argv[3]);
resources.registerResources(*s); initResources(*s, api);
s->run(); s->run();
} else if (strcmp(argv[1], "ssl") == 0) { } else if (strcmp(argv[1], "ssl") == 0) {
@ -242,7 +187,7 @@ int main(int argc, char *argv[]) {
ctx->use_tmp_dh(boost::asio::buffer(dh)); ctx->use_tmp_dh(boost::asio::buffer(dh));
s = std::make_unique<http::server::Server>(argv[2], argv[3], ctx); s = std::make_unique<http::server::Server>(argv[2], argv[3], ctx);
resources.registerResources(*s); initResources(*s, api);
s->run(); s->run();
} else { } else {
std::cerr << "Unsupported ssl mode: " << argv[1] << std::endl; std::cerr << "Unsupported ssl mode: " << argv[1] << std::endl;

View File

@ -13,7 +13,7 @@
namespace http::server { namespace http::server {
using request_handler = std::function<void(const Request &req, Reply &rep)>; using request_handler = std::function<void(const Request &req, reply &rep)>;
class ConnectionManager; class ConnectionManager;
/// Represents a single Connection from a client. Base class /// Represents a single Connection from a client. Base class
@ -71,7 +71,7 @@ namespace http::server {
RequestParser request_parser_; RequestParser request_parser_;
/// The reply to be sent back to the client. /// The reply to be sent back to the client.
Reply reply_; reply reply_;
}; };
class SslConnection final : public ConnectionBase, public std::enable_shared_from_this<SslConnection> { class SslConnection final : public ConnectionBase, public std::enable_shared_from_this<SslConnection> {
@ -113,7 +113,7 @@ namespace http::server {
RequestParser request_parser_; RequestParser request_parser_;
/// The reply to be sent back to the client. /// The reply to be sent back to the client.
Reply reply_; reply reply_;
}; };
typedef std::shared_ptr<ConnectionBase> connection_ptr; typedef std::shared_ptr<ConnectionBase> connection_ptr;

View File

@ -68,7 +68,7 @@ namespace http::server {
const char crlf[] = {'\r', '\n'}; const char crlf[] = {'\r', '\n'};
} // namespace misc_strings } // namespace misc_strings
std::vector<boost::asio::const_buffer> Reply::to_buffers() const { std::vector<boost::asio::const_buffer> reply::to_buffers() const {
std::vector<boost::asio::const_buffer> buffers; std::vector<boost::asio::const_buffer> buffers;
buffers.push_back(status_strings::to_buffer(status)); buffers.push_back(status_strings::to_buffer(status));
for (const auto & h : headers) { for (const auto & h : headers) {
@ -85,77 +85,77 @@ namespace http::server {
namespace stock_replies { namespace stock_replies {
constexpr char ok[] = ""; constexpr char ok[] = "";
constexpr char created[] = constexpr char created[] =
"<!DOCTYPE html>\n<html>" "<html>"
"<head><title>Created</title></head>" "<head><title>Created</title></head>"
"<body><h1>201 Created</h1></body>" "<body><h1>201 Created</h1></body>"
"</html>"; "</html>";
constexpr char accepted[] = constexpr char accepted[] =
"<!DOCTYPE html>\n<html>" "<html>"
"<head><title>Accepted</title></head>" "<head><title>Accepted</title></head>"
"<body><h1>202 Accepted</h1></body>" "<body><h1>202 Accepted</h1></body>"
"</html>"; "</html>";
constexpr char no_content[] = constexpr char no_content[] =
"<!DOCTYPE html>\n<html>" "<html>"
"<head><title>No Content</title></head>" "<head><title>No Content</title></head>"
"<body><h1>204 Content</h1></body>" "<body><h1>204 Content</h1></body>"
"</html>"; "</html>";
constexpr char multiple_choices[] = constexpr char multiple_choices[] =
"<!DOCTYPE html>\n<html>" "<html>"
"<head><title>Multiple Choices</title></head>" "<head><title>Multiple Choices</title></head>"
"<body><h1>300 Multiple Choices</h1></body>" "<body><h1>300 Multiple Choices</h1></body>"
"</html>"; "</html>";
constexpr char moved_permanently[] = constexpr char moved_permanently[] =
"<!DOCTYPE html>\n<html>" "<html>"
"<head><title>Moved Permanently</title></head>" "<head><title>Moved Permanently</title></head>"
"<body><h1>301 Moved Permanently</h1></body>" "<body><h1>301 Moved Permanently</h1></body>"
"</html>"; "</html>";
constexpr char moved_temporarily[] = constexpr char moved_temporarily[] =
"<!DOCTYPE html>\n<html>" "<html>"
"<head><title>Moved Temporarily</title></head>" "<head><title>Moved Temporarily</title></head>"
"<body><h1>302 Moved Temporarily</h1></body>" "<body><h1>302 Moved Temporarily</h1></body>"
"</html>"; "</html>";
constexpr char not_modified[] = constexpr char not_modified[] =
"<!DOCTYPE html>\n<html>" "<html>"
"<head><title>Not Modified</title></head>" "<head><title>Not Modified</title></head>"
"<body><h1>304 Not Modified</h1></body>" "<body><h1>304 Not Modified</h1></body>"
"</html>"; "</html>";
constexpr char bad_request[] = constexpr char bad_request[] =
"<!DOCTYPE html>\n<html>" "<html>"
"<head><title>Bad Request</title></head>" "<head><title>Bad Request</title></head>"
"<body><h1>400 Bad Request</h1></body>" "<body><h1>400 Bad Request</h1></body>"
"</html>"; "</html>";
constexpr char unauthorized[] = constexpr char unauthorized[] =
"<!DOCTYPE html>\n<html>" "<html>"
"<head><title>Unauthorized</title></head>" "<head><title>Unauthorized</title></head>"
"<body><h1>401 Unauthorized</h1></body>" "<body><h1>401 Unauthorized</h1></body>"
"</html>"; "</html>";
constexpr char forbidden[] = constexpr char forbidden[] =
"<!DOCTYPE html>\n<html>" "<html>"
"<head><title>Forbidden</title></head>" "<head><title>Forbidden</title></head>"
"<body><h1>403 Forbidden</h1></body>" "<body><h1>403 Forbidden</h1></body>"
"</html>"; "</html>";
constexpr char not_found[] = constexpr char not_found[] =
"<!DOCTYPE html>\n<html>" "<html>"
"<head><title>Not Found</title></head>" "<head><title>Not Found</title></head>"
"<body><h1>404 Not Found</h1></body>" "<body><h1>404 Not Found</h1></body>"
"</html>"; "</html>";
constexpr char internal_server_error[] = constexpr char internal_server_error[] =
"<!DOCTYPE html>\n<html>" "<html>"
"<head><title>Internal Server Error</title></head>" "<head><title>Internal Server Error</title></head>"
"<body><h1>500 Internal Server Error</h1></body>" "<body><h1>500 Internal Server Error</h1></body>"
"</html>"; "</html>";
constexpr char not_implemented[] = constexpr char not_implemented[] =
"<!DOCTYPE html>\n<html>" "<html>"
"<head><title>Not Implemented</title></head>" "<head><title>Not Implemented</title></head>"
"<body><h1>501 Not Implemented</h1></body>" "<body><h1>501 Not Implemented</h1></body>"
"</html>"; "</html>";
constexpr char bad_gateway[] = constexpr char bad_gateway[] =
"<!DOCTYPE html>\n<html>" "<html>"
"<head><title>Bad Gateway</title></head>" "<head><title>Bad Gateway</title></head>"
"<body><h1>502 Bad Gateway</h1></body>" "<body><h1>502 Bad Gateway</h1></body>"
"</html>"; "</html>";
constexpr char service_unavailable[] = constexpr char service_unavailable[] =
"<!DOCTYPE html>\n<html>" "<html>"
"<head><title>Service Unavailable</title></head>" "<head><title>Service Unavailable</title></head>"
"<body><h1>503 Service Unavailable</h1></body>" "<body><h1>503 Service Unavailable</h1></body>"
"</html>"; "</html>";
@ -206,7 +206,7 @@ namespace http::server {
} }
} // namespace stock_replies } // namespace stock_replies
void stockReply(status_type status, Reply& rep) { void stockReply(status_type status, reply& rep) {
rep.status = status; rep.status = status;
rep.headers.clear(); rep.headers.clear();
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::text_html)}); rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::text_html)});

View File

@ -28,7 +28,7 @@ namespace http::server {
}; };
/// A reply to be sent to a client. /// A reply to be sent to a client.
struct Reply { struct reply {
status_type status; status_type status;
/// The headers to be included in the reply. /// The headers to be included in the reply.
@ -44,7 +44,7 @@ namespace http::server {
}; };
/// Get a stock reply. /// Get a stock reply.
void stockReply(status_type status, Reply& rep); void stockReply(status_type status, reply& rep);
} // namespace http::Server } // namespace http::Server

View File

@ -1,9 +1,6 @@
#include "resource.h" #include "resource.h"
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
#include <fstream> #include <fstream>
#include <utility>
#include "../../dependencies/control_system/common/protocol_commands.h"
static void loadFile(const std::string& path, std::vector<char>& content) { static void loadFile(const std::string& path, std::vector<char>& content) {
std::ifstream is(path, std::ios::in | std::ios::binary); std::ifstream is(path, std::ios::in | std::ios::binary);
@ -22,60 +19,44 @@ static void loadFile(const std::string& path, std::vector<char>& content) {
} }
} }
http::resource::BasicResource::BasicResource(std::string path): path(std::move(path)) {} http::resource::StaticFileResource::StaticFileResource(const std::string &path, const std::string &filePath, server::mime_types::Mime type): type(type) {
this->path = path;
http::resource::StaticFileFactory::StaticFileDef::StaticFileDef(std::string path, server::mime_types::Mime type, bool allowCache): path(std::move(path)), type(type), allowCache(allowCache) {
#ifdef USE_DEBUG #ifdef USE_DEBUG
if (allowCache) { BOOST_LOG_TRIVIAL(info) << "Skip loading file " << filePath << " (http path: " << path << ")";
BOOST_LOG_TRIVIAL(info) << "Load static file " << this->path; this->filePath = filePath;
loadFile(this->path, this->content);
} else {
BOOST_LOG_TRIVIAL(info) << "Skip loading static file " << this->path;
}
#else #else
BOOST_LOG_TRIVIAL(info) << "Load static file " << this->path; BOOST_LOG_TRIVIAL(info) << "Load file " << filePath << " (http path: " << path << ")";
loadFile(this->path, this->content); loadFile(filePath, this->content);
#endif #endif
} }
http::resource::StaticFileFactory::StaticFileDef::~StaticFileDef() = default;
http::resource::StaticFileFactory::StaticFileFactory() = default; void http::resource::StaticFileResource::handle(const server::Request &req, server::reply &rep) {
if (req.method != "GET") {
void http::resource::StaticFileFactory::registerFile(const std::string &path, server::mime_types::Mime type, bool allowCache) { stockReply(server::bad_request, rep);
this->files.emplace_back(path, type, allowCache);
}
void http::resource::StaticFileFactory::serve(const std::string &path, server::Reply &rep) {
for (auto& f: this->files) {
if (f.path == path) {
#ifdef USE_DEBUG
if (f.allowCache) {
rep.content.clear();
rep.content.insert(rep.content.end(), f.content.begin(), f.content.end());
} else {
BOOST_LOG_TRIVIAL(debug) << "Reload file " << path << " (http path: " << path << ")";
loadFile(f.path, rep.content);
}
#else
rep.content.clear();
rep.content.insert(rep.content.end(), f.content.begin(), f.content.end());
#endif
rep.status = server::ok;
// rep.headers.clear();
rep.headers.push_back({.name = "Content-Type", .value = server::mime_types::toString(f.type)});
if (f.allowCache) {
rep.headers.push_back({.name = "Cache-Control", .value = "max-age=86400"}); // сутки, думаю хватит
}
return; return;
} }
}
// TODO сделать поддержку range
#ifdef USE_DEBUG
BOOST_LOG_TRIVIAL(debug) << "Reload file " << filePath << " (http path: " << path << ")";
loadFile(this->filePath, rep.content);
#else
rep.content.clear();
rep.content.insert(rep.content.end(), this->content.begin(), this->content.end());
#endif
rep.status = server::ok;
rep.headers.clear();
// TODO сделать cache control
rep.headers.push_back({.name = "Content-Type", .value = toString(this->type)});
} }
http::resource::StaticFileFactory::~StaticFileFactory() = default; http::resource::StaticFileResource::~StaticFileResource() = default;
http::resource::GenericResource::GenericResource(const std::string &path, respGenerator generator): BasicResource(path), generator_(std::move(generator)) {} http::resource::GenericResource::GenericResource(const std::string &path, const respGenerator &generator): generator_(generator) {
this->path = path;
}
void http::resource::GenericResource::handle(const server::Request &req, server::Reply &rep) { void http::resource::GenericResource::handle(const server::Request &req, server::reply &rep) {
this->generator_(req, rep); this->generator_(req, rep);
} }

View File

@ -12,38 +12,34 @@ namespace http::resource {
*/ */
class BasicResource { class BasicResource {
public: public:
BasicResource(std::string path);
std::string path; std::string path;
virtual void handle(const server::Request &req, server::Reply &rep) = 0; virtual void handle(const server::Request &req, server::reply &rep) = 0;
virtual ~BasicResource() = default; virtual ~BasicResource() = default;
}; };
class StaticFileFactory { /**
class StaticFileDef { * Класс ресурса статического файла
public: */
StaticFileDef(std::string path, server::mime_types::Mime type, bool allowCache = true); class StaticFileResource final : public BasicResource {
private:
std::string path;
server::mime_types::Mime type; server::mime_types::Mime type;
bool allowCache; #ifdef USE_DEBUG
std::string filePath;
#else
std::vector<char> content; std::vector<char> content;
#endif
~StaticFileDef();
};
std::vector<StaticFileDef> files;
public: public:
StaticFileFactory(); StaticFileResource(const std::string& path, const std::string& filePath, server::mime_types::Mime type);
void registerFile(const std::string& path, server::mime_types::Mime type, bool allowCache = true); void handle(const server::Request &req, server::reply &rep) override;
void serve(const std::string& path, server::Reply& rep); ~StaticFileResource() override;
~StaticFileFactory();
}; };
using respGenerator = std::function<void(const server::Request &req, server::Reply &rep)>; using respGenerator = std::function<void(const server::Request &req, server::reply &rep)>;
/** /**
* Класс ресурса для POST-запросов * Класс ресурса для POST-запросов
@ -53,9 +49,9 @@ namespace http::resource {
respGenerator generator_; respGenerator generator_;
public: public:
GenericResource(const std::string& path, respGenerator generator); GenericResource(const std::string& path, const respGenerator& generator);
void handle(const server::Request &req, server::Reply &rep) override; void handle(const server::Request &req, server::reply &rep) override;
~GenericResource() override; ~GenericResource() override;
}; };

View File

@ -111,7 +111,7 @@ namespace http::server {
}); });
} }
void Server::requestHandler(const Request &req, Reply &rep) { void Server::requestHandler(const Request &req, reply &rep) {
// Request path must be absolute and not contain "..". // Request path must be absolute and not contain "..".
if (req.url->path.empty() || req.url->path[0] != '/' || req.url->path.find("..") != std::string::npos) { if (req.url->path.empty() || req.url->path[0] != '/' || req.url->path.find("..") != std::string::npos) {
stockReply(bad_request, rep); stockReply(bad_request, rep);

View File

@ -74,7 +74,7 @@ namespace http::server {
ConnectionManager connection_manager_; ConnectionManager connection_manager_;
/// Handle a request and produce a reply. /// Handle a request and produce a reply.
void requestHandler(const Request &req, Reply &rep); void requestHandler(const Request &req, reply &rep);
}; };
} // namespace http::Server } // namespace http::Server