diff options
| -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; | 
