diff options
author | Roland Reichwein <mail@reichwein.it> | 2020-04-09 18:30:32 +0200 |
---|---|---|
committer | Roland Reichwein <mail@reichwein.it> | 2020-04-09 18:30:32 +0200 |
commit | 0d157fb407a35f8afe6d6f0f4c2cc5cd5d5a1933 (patch) | |
tree | 86ccea82ebbe29197eacb9a85e8ec7548c5ae38c | |
parent | 2f42619303627db401e469e2fd65123cd794a378 (diff) |
Prepared generate_page for static-files plugin
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | config.cpp | 30 | ||||
-rw-r--r-- | config.h | 1 | ||||
-rw-r--r-- | http.cpp | 52 | ||||
-rw-r--r-- | http.h | 3 | ||||
-rw-r--r-- | https.cpp | 53 | ||||
-rw-r--r-- | https.h | 4 | ||||
-rw-r--r-- | plugin.cpp | 5 | ||||
-rw-r--r-- | plugin.h | 6 | ||||
-rw-r--r-- | plugin_interface.h | 21 | ||||
-rw-r--r-- | plugins/static-files/static-files.cpp | 12 | ||||
-rw-r--r-- | plugins/static-files/static-files.h | 8 | ||||
-rw-r--r-- | response.cpp | 40 | ||||
-rw-r--r-- | response.h | 4 | ||||
-rw-r--r-- | server.cpp | 27 | ||||
-rw-r--r-- | server.h | 13 | ||||
-rw-r--r-- | webserver.conf | 1 | ||||
-rw-r--r-- | webserver.cpp | 2 |
19 files changed, 202 insertions, 84 deletions
@@ -1,5 +1,7 @@ *.o *.d +*.so +*.swp default.profraw test-webserver webserver @@ -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 @@ -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) { @@ -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; @@ -71,8 +71,7 @@ template< class Send> void handle_request( - const Config& config, - const Socket& socket, + Server& server, http::request<Body, http::basic_fields<Allocator>>&& 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<http::string_body> 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<http::string_body> 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<session> beast::tcp_stream stream_; beast::flat_buffer buffer_; - const Config& m_config; - const Socket& m_socket; + Server& m_server; http::request<http::string_body> req_; std::shared_ptr<void> 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<listener> { 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<session>( 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<listener>( m_ioc, tcp::endpoint{address, port}, - m_config, - m_socket)->run(); + *this)->run(); return EXIT_SUCCESS; } @@ -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; }; @@ -84,8 +84,7 @@ template< class Send> void handle_request( - const Config& config, - const Socket& socket, + ::Server& server, http::request<Body, http::basic_fields<Allocator>>&& 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<http::string_body> 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<http::string_body> 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<session> beast::ssl_stream<beast::tcp_stream> stream_; beast::flat_buffer buffer_; - const Config& m_config; - const Socket& m_socket; + Server& m_server; http::request<http::string_body> req_; std::shared_ptr<void> 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<listener> 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<session>( 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; } @@ -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; @@ -73,3 +73,8 @@ bool PluginLoader::validate_config() return true; } +plugins_container_type& PluginLoader::get_plugins() +{ + return m_plugins; +} + @@ -8,14 +8,18 @@ #include <memory> #include <unordered_map> +typedef boost::shared_ptr<webserver_plugin_interface> plugin_type; +typedef std::unordered_map<std::string, plugin_type> plugins_container_type; + class PluginLoader { Config& m_config; - std::unordered_map<std::string, boost::shared_ptr<webserver_plugin_interface>> 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 <boost/config.hpp> #include <string> +#include <functional> + +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<std::string(const std::string& key)>& GetServerParam, + std::function<std::string(const std::string& key)>& GetRequestParam, // request including body (POST...) + std::function<void(const std::string& key, const std::string& value)>& 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<std::string(const std::string& key)>& GetServerParam, + std::function<std::string(const std::string& key)>& GetRequestParam, // request including body (POST...) + std::function<void(const std::string& key, const std::string& value)>& 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<std::string(const std::string& key)>& GetServerParam, + std::function<std::string(const std::string& key)>& GetRequestParam, // request including body (POST...) + std::function<void(const std::string& key, const std::string& value)>& 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 <functional> + +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<http::string_body>& 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<http::string_body>& req) +{ + return ""; +} + +void SetResponseHeader(const std::string& key, const std::string& value) +{ +} + +} + +std::string generate_response(http::request<http::string_body>& req, http::response<http::string_body>& 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::string(const std::string& key)>(std::bind(GetServerParam, _1, std::ref(server)))}; + auto GetRequestParamFunction {std::function<std::string(const std::string& key)>(std::bind(GetRequestParam, _1, req))}; + auto SetResponseHeaderFunction{std::function<void(const std::string& key, const std::string& value)>(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<http::string_body>& 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<http::string_body>& req, const Confi } return result; +#endif } @@ -1,6 +1,6 @@ #pragma once -#include "config.h" +#include "server.h" #include <boost/beast/http.hpp> @@ -37,4 +37,4 @@ public: }; std::string extend_index_html(std::string path); -std::string generate_response(http::request<http::string_body>& req, const Config& config, const Socket& socket); +std::string generate_response(http::request<http::string_body>& req, http::response<http::string_body>& res, Server& server); @@ -21,7 +21,11 @@ namespace net = boost::asio; // from <boost/asio.hpp> namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp> using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> -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<int>(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<HTTP::Server>(config, ioc, socket)); + servers.push_back(std::make_shared<HTTP::Server>(config, ioc, socket, plugins)); } else { - servers.push_back(std::make_shared<HTTPS::Server>(config, ioc, socket)); + servers.push_back(std::make_shared<HTTPS::Server>(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 +} + @@ -3,6 +3,7 @@ #include <boost/asio/io_context.hpp> #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 @@ <host>antcom.de</host> <host>www.antcom.de</host> <path requested="/" type="files"> + <plugin>static-files</plugin> <target>/home/ernie/homepage/test</target> </path> <!-- diff --git a/webserver.cpp b/webserver.cpp index dd06021..71829bb 100644 --- a/webserver.cpp +++ b/webserver.cpp @@ -40,7 +40,7 @@ int main(int argc, char* argv[]) if (!plugin_loader.validate_config()) throw std::runtime_error("Couldn't find all configured plugins."); - return server(config); + return run_server(config, plugin_loader.get_plugins()); } catch (const std::exception& ex) { std::cout << "Error: " << ex.what() << std::endl; return 1; |