From 48908fb0bba69404dfd86d1af3b9ace1e0d598c9 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Fri, 15 May 2020 18:59:19 +0200 Subject: Speedup GetPath() --- TODO | 1 - config.cpp | 88 +++++++++++++++++++++++++++++++++++++------------------------- config.h | 9 ++++--- https.cpp | 14 +++++----- plugin.cpp | 7 ++--- 5 files changed, 68 insertions(+), 51 deletions(-) diff --git a/TODO b/TODO index 584b690..76149ec 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,3 @@ -Speed up config.GetPath weblog: link consistency check (cron?) Integrate into Debian: WNPP support alternative SSL libs: mbedtls, gnutls, wolfssl, botan, (matrixssl, libressl, cryptlib: not in debian) diff --git a/config.cpp b/config.cpp index 2ece1ef..ed28ea9 100644 --- a/config.cpp +++ b/config.cpp @@ -56,15 +56,16 @@ void Config::readConfigfile(std::string filename) for (const auto& site: element.second) { if (site.first != "site"s) throw std::runtime_error(" expected in "); + std::string site_name; Site site_struct; for (const auto& x: site.second) { if (x.first == "name"s) { - if (site_struct.name == "") - site_struct.name = x.second.data(); + if (site_name == "") + site_name = x.second.data(); else throw std::runtime_error("Found double site name: "s + x.second.data()); } else if (x.first == "host"s) { - if (std::find(site_struct.hosts.begin(), site_struct.hosts.end(), x.second.data()) == site_struct.hosts.end()) + if (site_struct.hosts.find(x.second.data()) == site_struct.hosts.end()) site_struct.hosts.insert(x.second.data()); else throw std::runtime_error("Found double site host element: "s + x.second.data()); @@ -110,10 +111,12 @@ void Config::readConfigfile(std::string filename) } else throw std::runtime_error("Unknown element: "s + x.first); } - if (std::find_if(m_sites.begin(), m_sites.end(), [&](const Site& site){return site.name == site_struct.name;}) == m_sites.end()) - m_sites.push_back(site_struct); + if (site_name.empty()) + throw std::runtime_error("Empty site name"); + if (m_sites.find(site_name) == m_sites.end()) + m_sites[site_name] = site_struct; else - throw std::runtime_error("Found double site spec: "s + site_struct.name); + throw std::runtime_error("Found double site spec: "s + site_name); } } else if (element.first == "sockets"s) { for (const auto& socket: element.second) { @@ -140,10 +143,10 @@ void Config::readConfigfile(std::string filename) throw std::runtime_error("Unknown protocol: "s + x.second.data()); } else if (x.first == "site"s) { std::string site {x.second.data()}; - if (std::find(socket_struct.serve_sites.begin(), socket_struct.serve_sites.end(), site) == socket_struct.serve_sites.end()) { - socket_struct.serve_sites.push_back(site); + if (socket_struct.serve_sites.find(site) == socket_struct.serve_sites.end()) { + socket_struct.serve_sites.insert(site); } else { - throw std::runtime_error("Site "s + site + " already defined for "s + socket_struct.address + " port " + socket_struct.port); + throw std::runtime_error("Site "s + site + " already defined for "s + socket_struct.address + ", port " + socket_struct.port); } } else throw std::runtime_error("Unknown element: "s + x.first); @@ -161,10 +164,26 @@ void Config::readConfigfile(std::string filename) for (auto& socket: m_sockets) { if (socket.serve_sites.empty()) { for (const auto& site: m_sites) { - socket.serve_sites.push_back(site.name); + socket.serve_sites.insert(site.first); } } } + + validate(); +} + +// just throws on inconsistency +void Config::validate() +{ + // make sure all m_sockets.serve_sites are configured in m_sites + + for (auto& socket: m_sockets) { + for (auto& serve_site: socket.serve_sites) { + if (m_sites.find(serve_site) == m_sites.end()) + throw std::runtime_error("Found serve_site "s + serve_site + " without configured site"s); + } + } + } Config::Config(const std::string& filename) @@ -193,7 +212,7 @@ const std::vector& Config::PluginDirectories() const return m_plugin_directories; } -const std::vector& Config::Sites() const +const std::unordered_map& Config::Sites() const { return m_sites; } @@ -217,21 +236,21 @@ void Config::dump() const std::cout << std::endl; for (const auto& site: m_sites) { - std::cout << "Site: " << site.name << ":"; - for (const auto& host: site.hosts) + std::cout << "Site: " << site.first << ":"; + for (const auto& host: site.second.hosts) std::cout << " " << host; std::cout << std::endl; - if (site.paths.size() == 0) + if (site.second.paths.size() == 0) std::cout << " Warning: No paths configured." << std::endl; - for (const auto& path: site.paths) { + for (const auto& path: site.second.paths) { std::cout << " Path: " << path.requested << std::endl; for (const auto& param: path.params) { std::cout << " " << param.first << ": " << param.second << std::endl; } } - if (site.key_path != ""s) { - std::cout << " Key: " << site.key_path.generic_string() << std::endl; - std::cout << " Cert: " << site.cert_path.generic_string() << std::endl; + if (site.second.key_path != ""s) { + std::cout << " Key: " << site.second.key_path.generic_string() << std::endl; + std::cout << " Cert: " << site.second.cert_path.generic_string() << std::endl; } } @@ -251,29 +270,26 @@ const Path& Config::GetPath(const Socket& socket, const std::string& requested_h { //boost::timer::auto_cpu_timer t; - // TODO: speed this up std::string host{requested_host}; const Path* result{nullptr}; size_t path_len{0}; // find longest matching prefix RemovePortFromHostname(host); - 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) && - ("/?"s.find(requested_path[path.requested.size()]) != std::string::npos || - requested_path[path.requested.size()] == 0 || - requested_path[path.requested.size() - 1] == '/' - ) && - path.requested.size() > path_len) - { - path_len = path.requested.size(); - result = &path; - } - } + for (const auto& site_name: socket.serve_sites) { + const Site& site {m_sites.at(site_name)}; // omit error check: validation previously made sure this exists + + if (site.hosts.find(host) != site.hosts.end()) { + for (const auto& path: site.paths) { + if (boost::starts_with(requested_path, path.requested) && + ("/?"s.find(requested_path[path.requested.size()]) != std::string::npos || + requested_path[path.requested.size()] == 0 || + requested_path[path.requested.size() - 1] == '/' + ) && + path.requested.size() > path_len) + { + path_len = path.requested.size(); + result = &path; } } } @@ -288,7 +304,7 @@ const Path& Config::GetPath(const Socket& socket, const std::string& requested_h bool Config::PluginIsConfigured(const std::string& name) const { for (const auto& site: m_sites) { - for (const auto& path: site.paths) { + for (const auto& path: site.second.paths) { auto it{path.params.find("plugin")}; if (it != path.params.end() && it->second == name) return true; diff --git a/config.h b/config.h index 46cd267..e60bba5 100644 --- a/config.h +++ b/config.h @@ -18,7 +18,7 @@ struct Path struct Site { - std::string name; + // std::string name; is the index in the m_sites map std::unordered_set hosts; std::vector paths; fs::path cert_path; @@ -36,7 +36,7 @@ struct Socket std::string address; std::string port; SocketProtocol protocol; - std::vector serve_sites; // if empty, automatically expand to all configured sites + std::unordered_set serve_sites; // if empty, automatically expand to all configured sites }; class Config @@ -44,12 +44,13 @@ class Config const std::string default_filename{"/etc/webserver.conf"}; void readConfigfile(std::string filename); + void validate(); std::string m_user; std::string m_group; int m_threads; std::vector m_plugin_directories; - std::vector m_sites; + std::unordered_map m_sites; std::vector m_sockets; public: @@ -62,7 +63,7 @@ class Config int Threads() const; const std::vector& PluginDirectories() const; - const std::vector& Sites() const; + const std::unordered_map& Sites() const; const std::vector& Sockets() const; // diff --git a/https.cpp b/https.cpp index 508ece9..a5aa118 100644 --- a/https.cpp +++ b/https.cpp @@ -660,15 +660,15 @@ void Server::load_certificates() // import the real certificates for (const auto& serve_site: m_socket.serve_sites) { for (const auto& site: m_config.Sites()) { - if (site.name == serve_site) { + if (site.first == serve_site) { std::shared_ptr ctx {std::make_shared(tls_method)}; std::cout << "Creating SSL context/cert for site " << serve_site << " on port " << m_socket.port << std::endl; - load_server_certificate(*ctx, site.cert_path, site.key_path); + load_server_certificate(*ctx, site.second.cert_path, site.second.key_path); SSL_CTX_set_client_hello_cb(ctx->native_handle(), servername_callback, &m_ctx); - for (const auto& host: site.hosts) { + for (const auto& host: site.second.hosts) { std::cout << " Adding Host " << host << std::endl; m_ctx.emplace(host, ctx); } @@ -681,19 +681,19 @@ void Server::reload_certificates() { for (const auto& serve_site: m_socket.serve_sites) { for (const auto& site: m_config.Sites()) { - if (site.name == serve_site) { + if (site.first == serve_site) { std::cout << "Updating SSL context/cert for site " << serve_site << " on port " << m_socket.port << std::endl; - auto it_host {site.hosts.begin()}; - if (it_host == site.hosts.end()) { + auto it_host {site.second.hosts.begin()}; + if (it_host == site.second.hosts.end()) { std::cout << " Warning: No configured host found." << std::endl; } else { auto it_ctx {m_ctx.find(*it_host)}; if (it_ctx == m_ctx.end()) { std::cout << " Warning: No context found for configured host." << std::endl; } else { - load_server_certificate(*it_ctx->second, site.cert_path, site.key_path); + load_server_certificate(*it_ctx->second, site.second.cert_path, site.second.key_path); } } } diff --git a/plugin.cpp b/plugin.cpp index 28fdb62..afcbdb3 100644 --- a/plugin.cpp +++ b/plugin.cpp @@ -64,22 +64,23 @@ void PluginLoader::load_plugins() } } +// validate config regarding plugins bool PluginLoader::validate_config() { const auto& sites{m_config.Sites()}; for (const auto& site: sites) { - for (const auto& path: site.paths) { + for (const auto& path: site.second.paths) { // path must contain target and plugin auto it {path.params.find("target")}; if (it == path.params.end()) { - std::cout << "Path " << path.requested << " for site " << site.name << " is missing target specification." << std::endl; + std::cout << "Path " << path.requested << " for site " << site.first << " is missing target specification." << std::endl; return false; } it = path.params.find("plugin"); if (it == path.params.end()) { - std::cout << "Path " << path.requested << " for site " << site.name << " is missing plugin specification." << std::endl; + std::cout << "Path " << path.requested << " for site " << site.first << " is missing plugin specification." << std::endl; return false; } -- cgit v1.2.3