summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Makefile2
-rw-r--r--config.cpp30
-rw-r--r--config.h1
-rw-r--r--http.cpp52
-rw-r--r--http.h3
-rw-r--r--https.cpp53
-rw-r--r--https.h4
-rw-r--r--plugin.cpp5
-rw-r--r--plugin.h6
-rw-r--r--plugin_interface.h21
-rw-r--r--plugins/static-files/static-files.cpp12
-rw-r--r--plugins/static-files/static-files.h8
-rw-r--r--response.cpp40
-rw-r--r--response.h4
-rw-r--r--server.cpp27
-rw-r--r--server.h13
-rw-r--r--webserver.conf1
-rw-r--r--webserver.cpp2
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<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;
}
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<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;
}
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 <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
}
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 <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);
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 <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
+}
+
diff --git a/server.h b/server.h
index 5e5343f..c2b6d1c 100644
--- a/server.h
+++ b/server.h
@@ -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;