From 0d157fb407a35f8afe6d6f0f4c2cc5cd5d5a1933 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Thu, 9 Apr 2020 18:30:32 +0200 Subject: Prepared generate_page for static-files plugin --- .gitignore | 2 ++ Makefile | 2 +- config.cpp | 30 ++++++++++++++++++++ config.h | 1 + http.cpp | 52 +++++++++++++++------------------- http.h | 3 +- https.cpp | 53 +++++++++++++++-------------------- https.h | 4 +-- plugin.cpp | 5 ++++ plugin.h | 6 +++- plugin_interface.h | 21 ++++++++++++-- plugins/static-files/static-files.cpp | 12 ++++++-- plugins/static-files/static-files.h | 8 +++++- response.cpp | 40 ++++++++++++++++++++++++-- response.h | 4 +-- server.cpp | 27 +++++++++++++++--- server.h | 13 +++++++-- webserver.conf | 1 + webserver.cpp | 2 +- 19 files changed, 202 insertions(+), 84 deletions(-) diff --git a/.gitignore b/.gitignore index adbd73c..bf43ad2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ *.o *.d +*.so +*.swp default.profraw test-webserver webserver diff --git a/Makefile b/Makefile index 234ddaf..cb58502 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ DISTROS=debian10 VERSION=$(shell dpkg-parsechangelog --show-field Version) PROJECTNAME=webserver -PLUGINS=static-files webbox # weblog cgi fcgi +PLUGINS=static-files #webbox # weblog cgi fcgi CXX=clang++-10 diff --git a/config.cpp b/config.cpp index 0d1eb34..9468e39 100644 --- a/config.cpp +++ b/config.cpp @@ -215,6 +215,36 @@ std::string Config::DocRoot(const Socket& socket, const std::string& requested_h return result; } +std::string Config::GetPlugin(const Socket& socket, const std::string& requested_host, const std::string& requested_path) const +{ + // TODO: speed this up + std::string host{requested_host}; + std::string result; + + auto pos {host.find(':')}; + if (pos != host.npos) { + host = host.substr(0, pos); + } + + for (const auto& site: m_sites) { + if (std::find(socket.serve_sites.begin(), socket.serve_sites.end(), site.name) != socket.serve_sites.end()) { + for (const auto& m_host: site.hosts) { + if (m_host == host) { + for (const auto& path: site.paths) { + if (boost::starts_with(requested_path, path.requested)) { + const auto& root { path.params.at("plugin")}; + if (root.size() > result.size()) + result = root; + } + } + } + } + } + } + + return result; +} + bool Config::PluginIsConfigured(const std::string& name) const { for (const auto& site: m_sites) { diff --git a/config.h b/config.h index 72ce9b1..4f788d8 100644 --- a/config.h +++ b/config.h @@ -76,6 +76,7 @@ class Config /// param[in] requested_host e.g. www.domain.com:8080 or www.domain.com std::string DocRoot(const Socket& socket, const std::string& requested_host, const std::string& requested_path) const; + std::string GetPlugin(const Socket& socket, const std::string& requested_host, const std::string& requested_path) const; // return true iff plugin "name" is mentioned in config bool PluginIsConfigured(const std::string& name) const; diff --git a/http.cpp b/http.cpp index eeff552..714bcc4 100644 --- a/http.cpp +++ b/http.cpp @@ -71,8 +71,7 @@ template< class Send> void handle_request( - const Config& config, - const Socket& socket, + Server& server, http::request>&& req, Send&& send) { @@ -115,9 +114,17 @@ handle_request( return res; }; - std::string res_data; try { - res_data = generate_response(req, config, socket); + http::response res{http::status::ok, req.version()}; + res.set(http::field::server, VersionString); + res.set(http::field::content_type, mime_type(extend_index_html(std::string(req.target())))); + res.keep_alive(req.keep_alive()); + std::string res_data = generate_response(req, res, server); + if (req.method() != http::verb::head) { + res.body() = res_data; + res.content_length(res_data.size()); + } + return send(std::move(res)); } catch(const bad_request_exception& ex) { return send(bad_request(ex.what())); } catch(const not_found_exception& ex) { @@ -126,14 +133,6 @@ handle_request( return send(server_error(ex.what())); } - http::response res{http::status::ok, req.version()}; - res.set(http::field::server, VersionString); - res.set(http::field::content_type, mime_type(extend_index_html(std::string(req.target())))); - res.content_length(res_data.size()); - res.keep_alive(req.keep_alive()); - if (req.method() != http::verb::head) - res.body() = res_data; - return send(std::move(res)); } //------------------------------------------------------------------------------ @@ -187,8 +186,7 @@ class session : public std::enable_shared_from_this beast::tcp_stream stream_; beast::flat_buffer buffer_; - const Config& m_config; - const Socket& m_socket; + Server& m_server; http::request req_; std::shared_ptr res_; send_lambda lambda_; @@ -197,11 +195,9 @@ public: // Take ownership of the stream session( tcp::socket&& socket, - const Config& config, - const Socket& config_socket) + Server& server) : stream_(std::move(socket)) - , m_config(config) - , m_socket(config_socket) + , m_server(server) , lambda_(*this) { } @@ -252,7 +248,7 @@ public: return fail(ec, "read"); // Send the response - handle_request(m_config, m_socket , std::move(req_), lambda_); + handle_request(m_server , std::move(req_), lambda_); } void @@ -298,19 +294,16 @@ class listener : public std::enable_shared_from_this { net::io_context& ioc_; tcp::acceptor acceptor_; - const Config& m_config; - const Socket& m_socket; + Server& m_server; public: listener( net::io_context& ioc, tcp::endpoint endpoint, - const Config& config, - const Socket& socket) + Server& server) : ioc_(ioc) , acceptor_(net::make_strand(ioc)) - , m_config(config) - , m_socket(socket) + , m_server(server) { beast::error_code ec; @@ -379,8 +372,7 @@ private: // Create the session and run it std::make_shared( std::move(socket), - m_config, - m_socket)->run(); + m_server)->run(); } // Accept another connection @@ -394,7 +386,8 @@ private: namespace HTTP { - Server::Server(Config& config, boost::asio::io_context& ioc, const Socket& socket): ::Server(config, ioc), m_socket(socket) + Server::Server(Config& config, boost::asio::io_context& ioc, const Socket& socket, plugins_container_type& plugins) + : ::Server(config, ioc, socket, plugins) { } @@ -411,8 +404,7 @@ namespace HTTP { std::make_shared( m_ioc, tcp::endpoint{address, port}, - m_config, - m_socket)->run(); + *this)->run(); return EXIT_SUCCESS; } diff --git a/http.h b/http.h index d98170d..c8f27a3 100644 --- a/http.h +++ b/http.h @@ -10,9 +10,8 @@ namespace HTTP { class Server: public ::Server { - const Socket& m_socket; public: - Server(Config& config, boost::asio::io_context& ioc, const Socket& socket); + Server(Config& config, boost::asio::io_context& ioc, const Socket& socket, plugins_container_type& plugins); virtual ~Server(); int start() override; }; diff --git a/https.cpp b/https.cpp index 161efad..f312295 100644 --- a/https.cpp +++ b/https.cpp @@ -84,8 +84,7 @@ template< class Send> void handle_request( - const Config& config, - const Socket& socket, + ::Server& server, http::request>&& req, Send&& send) { @@ -128,9 +127,17 @@ handle_request( return res; }; - std::string res_data; try { - res_data = generate_response(req, config, socket); + http::response res{http::status::ok, req.version()}; + res.set(http::field::server, VersionString); + res.set(http::field::content_type, mime_type(extend_index_html(std::string(req.target())))); + res.keep_alive(req.keep_alive()); + std::string res_data = generate_response(req, res, server); + if (req.method() != http::verb::head) { + res.body() = res_data; + res.content_length(res_data.size()); + } + return send(std::move(res)); } catch(const bad_request_exception& ex) { return send(bad_request(ex.what())); } catch(const not_found_exception& ex) { @@ -138,15 +145,6 @@ handle_request( } catch(const server_error_exception& ex) { return send(server_error(ex.what())); } - - http::response res{http::status::ok, req.version()}; - res.set(http::field::server, VersionString); - res.set(http::field::content_type, mime_type(extend_index_html(std::string(req.target())))); - res.content_length(res_data.size()); - res.keep_alive(req.keep_alive()); - if (req.method() != http::verb::head) - res.body() = res_data; - return send(std::move(res)); } //------------------------------------------------------------------------------ @@ -220,8 +218,7 @@ class session : public std::enable_shared_from_this beast::ssl_stream stream_; beast::flat_buffer buffer_; - const Config& m_config; - const Socket& m_socket; + Server& m_server; http::request req_; std::shared_ptr res_; send_lambda lambda_; @@ -232,11 +229,9 @@ public: session( tcp::socket&& socket, ssl::context& ctx, - const Config& config, - const Socket& config_socket) + Server& server) : stream_(std::move(socket), ctx) - , m_config(config) - , m_socket(config_socket) + , m_server(server) , lambda_(*this) { } @@ -310,7 +305,7 @@ public: return fail(ec, "read"); // Send the response - handle_request(m_config, m_socket, std::move(req_), lambda_); + handle_request(m_server, std::move(req_), lambda_); } void @@ -369,21 +364,18 @@ class listener : public std::enable_shared_from_this net::io_context& ioc_; ssl::context& ctx_; tcp::acceptor acceptor_; - const Config& m_config; - const Socket& m_socket; + ::Server& m_server; public: listener( net::io_context& ioc, ssl::context& ctx, tcp::endpoint endpoint, - const Config& config, - const Socket& socket) + Server& server) : ioc_(ioc) , ctx_(ctx) , acceptor_(ioc) - , m_config(config) - , m_socket(socket) + , m_server(server) { beast::error_code ec; @@ -453,8 +445,7 @@ private: std::make_shared( std::move(socket), ctx_, - m_config, - m_socket)->run(); + m_server)->run(); } // Accept another connection @@ -597,7 +588,8 @@ int servername_callback(SSL *s, int *al, void *arg) namespace HTTPS { -Server::Server(Config& config, boost::asio::io_context& ioc, const Socket& socket): ::Server(config, ioc), m_socket(socket) +Server::Server(Config& config, boost::asio::io_context& ioc, const Socket& socket, plugins_container_type& plugins) + : ::Server(config, ioc, socket, plugins) { for (const auto& serve_site: socket.serve_sites) { for (const auto& site: config.Sites()) { @@ -637,8 +629,7 @@ int Server::start() m_ioc, m_ctx_dummy, tcp::endpoint{address, port}, - m_config, - m_socket)->run(); + *this)->run(); return EXIT_SUCCESS; } diff --git a/https.h b/https.h index e009b78..1362e59 100644 --- a/https.h +++ b/https.h @@ -27,10 +27,8 @@ private: ctx_type m_ctx; ssl::context m_ctx_dummy{tls_method}; // Initial use, will be replaced by host specific context (with specific certificate) - const Socket& m_socket; - public: - Server(Config& config, boost::asio::io_context& ioc, const Socket& socket); + Server(Config& config, boost::asio::io_context& ioc, const Socket& socket, plugins_container_type& plugins); virtual ~Server(); int start() override; diff --git a/plugin.cpp b/plugin.cpp index 1da89cd..8e4182c 100644 --- a/plugin.cpp +++ b/plugin.cpp @@ -73,3 +73,8 @@ bool PluginLoader::validate_config() return true; } +plugins_container_type& PluginLoader::get_plugins() +{ + return m_plugins; +} + diff --git a/plugin.h b/plugin.h index cc68524..18e5aca 100644 --- a/plugin.h +++ b/plugin.h @@ -8,14 +8,18 @@ #include #include +typedef boost::shared_ptr plugin_type; +typedef std::unordered_map plugins_container_type; + class PluginLoader { Config& m_config; - std::unordered_map> m_plugins; + plugins_container_type m_plugins; public: PluginLoader(Config& config); void load_plugins(); // Load all plugins from configured paths bool validate_config(); // Check if all configured plugins exist + plugins_container_type& get_plugins(); }; diff --git a/plugin_interface.h b/plugin_interface.h index e67d20a..9fc2085 100644 --- a/plugin_interface.h +++ b/plugin_interface.h @@ -3,13 +3,30 @@ #include #include +#include + +typedef std::string(*plugin_interface_getter_type)(const std::string& key); +typedef void(*plugin_interface_setter_type)(const std::string& key, const std::string& value); class BOOST_SYMBOL_VISIBLE webserver_plugin_interface { public: static const int interface_version {1}; virtual int version() { return interface_version; } + + // + // The Interface to be implemented by plugins + // + // + virtual std::string name() = 0; - virtual std::string generate_page(std::string path) = 0; - virtual ~webserver_plugin_interface(){} + + // returns result page without headers + virtual std::string generate_page( + std::function& GetServerParam, + std::function& GetRequestParam, // request including body (POST...) + std::function& SetResponseHeader // to be added to result string + ) = 0; + + virtual ~webserver_plugin_interface(){} // optional }; diff --git a/plugins/static-files/static-files.cpp b/plugins/static-files/static-files.cpp index 287c975..358e239 100644 --- a/plugins/static-files/static-files.cpp +++ b/plugins/static-files/static-files.cpp @@ -19,8 +19,16 @@ static_files_plugin::~static_files_plugin() //std::cout << "Plugin destructor" << std::endl; } -std::string static_files_plugin::generate_page(std::string path) +std::string static_files_plugin::generate_page( + std::function& GetServerParam, + std::function& GetRequestParam, // request including body (POST...) + std::function& SetResponseHeader // to be added to result string +) { - return "Static Files "s + path; + try { + return "Static Files "s + GetServerParam("path"s); + } catch (const std::exception& ex) { + return "Error: "s + ex.what(); + } } diff --git a/plugins/static-files/static-files.h b/plugins/static-files/static-files.h index 7ea5500..ff35e92 100644 --- a/plugins/static-files/static-files.h +++ b/plugins/static-files/static-files.h @@ -7,8 +7,14 @@ class static_files_plugin: public webserver_plugin_interface public: static_files_plugin(); ~static_files_plugin(); + std::string name(); - std::string generate_page(std::string path); + std::string generate_page( + std::function& GetServerParam, + std::function& GetRequestParam, // request including body (POST...) + std::function& SetResponseHeader // to be added to result string + ); + }; extern "C" BOOST_SYMBOL_EXPORT static_files_plugin webserver_plugin; diff --git a/response.cpp b/response.cpp index 78368f6..507b2d7 100644 --- a/response.cpp +++ b/response.cpp @@ -1,6 +1,10 @@ #include "response.h" #include "file.h" +#include + +using namespace std::placeholders; + namespace { // Append an HTTP rel-path to a local filesystem path. @@ -60,8 +64,39 @@ std::string extend_index_html(std::string path) return path; } -std::string generate_response(http::request& req, const Config& config, const Socket& socket) +namespace { + +std::string GetServerParam(const std::string& key, Server& server) { + return ""; +} + +std::string GetRequestParam(const std::string& key, http::request& req) +{ + return ""; +} + +void SetResponseHeader(const std::string& key, const std::string& value) +{ +} + +} + +std::string generate_response(http::request& req, http::response& res, Server& server) +{ +#if 0 + std::string host{req["host"]}; // TODO: just use string_view + std::string target{req.target()}; + std::string plugin_name { server.GetConfig().GetPlugin(server.GetSocket(), host, target)}; + plugin_type plugin{server.GetPlugin(plugin_name)}; + + auto GetServerParamFunction {std::function(std::bind(GetServerParam, _1, std::ref(server)))}; + auto GetRequestParamFunction {std::function(std::bind(GetRequestParam, _1, req))}; + auto SetResponseHeaderFunction{std::function(SetResponseHeader)}; + + return plugin->generate_page(GetServerParamFunction, GetRequestParamFunction, SetResponseHeaderFunction); + +#else // Make sure we can handle the method if( req.method() != http::verb::get && req.method() != http::verb::head) @@ -76,7 +111,7 @@ std::string generate_response(http::request& req, const Confi // Build the path to the requested file std::string host{req["host"]}; // TODO: just use string_view std::string target{req.target()}; - std::string path = path_cat(config.DocRoot(socket, host, target), extend_index_html(std::string(req.target()))); + std::string path = path_cat(server.GetConfig().DocRoot(server.GetSocket(), host, target), extend_index_html(std::string(req.target()))); std::string result; try { @@ -88,5 +123,6 @@ std::string generate_response(http::request& req, const Confi } return result; +#endif } diff --git a/response.h b/response.h index a093320..a877944 100644 --- a/response.h +++ b/response.h @@ -1,6 +1,6 @@ #pragma once -#include "config.h" +#include "server.h" #include @@ -37,4 +37,4 @@ public: }; std::string extend_index_html(std::string path); -std::string generate_response(http::request& req, const Config& config, const Socket& socket); +std::string generate_response(http::request& req, http::response& res, Server& server); diff --git a/server.cpp b/server.cpp index 91ee9e8..47c50ac 100644 --- a/server.cpp +++ b/server.cpp @@ -21,7 +21,11 @@ namespace net = boost::asio; // from namespace ssl = boost::asio::ssl; // from using tcp = boost::asio::ip::tcp; // from -Server::Server(Config& config, boost::asio::io_context& ioc): m_config(config), m_ioc(ioc) +Server::Server(Config& config, boost::asio::io_context& ioc, const Socket& socket, plugins_container_type& plugins) + : m_config(config) + , m_ioc(ioc) + , m_socket(socket) + , m_plugins(plugins) { } @@ -29,7 +33,7 @@ Server::~Server() { } -int server(Config& config) +int run_server(Config& config, plugins_container_type& plugins) { auto const threads = std::max(1, config.Threads()); @@ -40,9 +44,9 @@ int server(Config& config) const auto& sockets {config.Sockets()}; for (const auto& socket: sockets) { if (socket.protocol == SocketProtocol::HTTP) { - servers.push_back(std::make_shared(config, ioc, socket)); + servers.push_back(std::make_shared(config, ioc, socket, plugins)); } else { - servers.push_back(std::make_shared(config, ioc, socket)); + servers.push_back(std::make_shared(config, ioc, socket, plugins)); } servers.back()->start(); } @@ -64,3 +68,18 @@ int server(Config& config) return EXIT_SUCCESS; } +Config& Server::GetConfig() +{ + return m_config; +} + +const Socket& Server::GetSocket() +{ + return m_socket; +} + +plugin_type Server::GetPlugin(const std::string& name) +{ + return m_plugins.at(name); // Config validation made sure that we will find it here. For safety, a thrown exception will be caught in webserver.cpp +} + diff --git a/server.h b/server.h index 5e5343f..c2b6d1c 100644 --- a/server.h +++ b/server.h @@ -3,6 +3,7 @@ #include #include "config.h" +#include "plugin.h" using namespace std::string_literals; @@ -13,11 +14,19 @@ class Server protected: Config& m_config; boost::asio::io_context& m_ioc; + const Socket& m_socket; + plugins_container_type& m_plugins; public: - Server(Config& config, boost::asio::io_context& ioc); + Server(Config& config, boost::asio::io_context& ioc, const Socket& socket, plugins_container_type& m_plugins); + virtual ~Server(); virtual int start() = 0; + + // Getters + Config& GetConfig(); + const Socket& GetSocket(); + plugin_type GetPlugin(const std::string& name); }; -int server(Config& config); +int run_server(Config& config, plugins_container_type& plugins); diff --git a/webserver.conf b/webserver.conf index 7749739..0981c0d 100644 --- a/webserver.conf +++ b/webserver.conf @@ -14,6 +14,7 @@ antcom.de www.antcom.de + static-files /home/ernie/homepage/test