From 25763e2e90006bef8548ba3e6598f88715591025 Mon Sep 17 00:00:00 2001 From: Gregor Jasny Date: Mon, 7 Dec 2020 08:49:34 +0100 Subject: [PATCH] pull: Use civetweb C API due to packaging issues in Debian --- cmake/civetweb-3rdparty-config.cmake | 2 -- pull/BUILD.bazel | 2 +- pull/CMakeLists.txt | 2 +- pull/include/prometheus/exposer.h | 4 +-- pull/src/basic_auth.cc | 17 +++++++--- pull/src/basic_auth.h | 23 ++++++++++---- pull/src/endpoint.cc | 12 +++++--- pull/src/endpoint.h | 6 ++-- pull/src/exposer.cc | 46 ++++++++++++++++++++++++---- pull/src/handler.cc | 15 ++++++++- pull/src/handler.h | 8 +++-- 11 files changed, 103 insertions(+), 34 deletions(-) diff --git a/cmake/civetweb-3rdparty-config.cmake b/cmake/civetweb-3rdparty-config.cmake index d684361d..d759f145 100644 --- a/cmake/civetweb-3rdparty-config.cmake +++ b/cmake/civetweb-3rdparty-config.cmake @@ -11,9 +11,7 @@ set_and_check(CIVETWEB_INCLUDE_DIR ${_IMPORT_PREFIX}/include) set(CIVETWEB_INCLUDE_DIRS "${CIVETWEB_INCLUDE_DIR}") add_library(civetweb OBJECT - ${_IMPORT_PREFIX}/include/CivetServer.h ${_IMPORT_PREFIX}/include/civetweb.h - ${_IMPORT_PREFIX}/src/CivetServer.cpp ${_IMPORT_PREFIX}/src/civetweb.c ${_IMPORT_PREFIX}/src/handle_form.inl ${_IMPORT_PREFIX}/src/md5.inl diff --git a/pull/BUILD.bazel b/pull/BUILD.bazel index 1e6e9250..b582a853 100644 --- a/pull/BUILD.bazel +++ b/pull/BUILD.bazel @@ -22,7 +22,7 @@ cc_library( visibility = ["//visibility:public"], deps = [ "//core", - "@civetweb", + "@civetweb//:libcivetweb", "@net_zlib_zlib//:z", ], ) diff --git a/pull/CMakeLists.txt b/pull/CMakeLists.txt index 6dfa6a2f..73f98b83 100644 --- a/pull/CMakeLists.txt +++ b/pull/CMakeLists.txt @@ -40,7 +40,7 @@ target_link_libraries(pull ${PROJECT_NAME}::core PRIVATE Threads::Threads - $,${PROJECT_NAME}::civetweb,civetweb::civetweb-cpp> + $,${PROJECT_NAME}::civetweb,civetweb::civetweb> $<$,$>>:rt> $<$:ZLIB::ZLIB> ) diff --git a/pull/include/prometheus/exposer.h b/pull/include/prometheus/exposer.h index 1f3b0775..06523c97 100644 --- a/pull/include/prometheus/exposer.h +++ b/pull/include/prometheus/exposer.h @@ -10,7 +10,7 @@ #include "prometheus/detail/pull_export.h" #include "prometheus/registry.h" -class CivetServer; +extern "C" struct mg_context; namespace prometheus { @@ -38,7 +38,7 @@ class PROMETHEUS_CPP_PULL_EXPORT Exposer { private: detail::Endpoint& GetEndpointForUri(const std::string& uri); - std::unique_ptr server_; + std::shared_ptr server_; std::vector> endpoints_; }; diff --git a/pull/src/basic_auth.cc b/pull/src/basic_auth.cc index 7bae3ca4..d2bab6c3 100644 --- a/pull/src/basic_auth.cc +++ b/pull/src/basic_auth.cc @@ -1,6 +1,6 @@ #include "basic_auth.h" -#include "CivetServer.h" +#include "civetweb.h" #include "detail/base64.h" #include "prometheus/detail/future_std.h" @@ -9,15 +9,24 @@ namespace prometheus { BasicAuthHandler::BasicAuthHandler(AuthFunc callback, std::string realm) : callback_(std::move(callback)), realm_(std::move(realm)) {} -bool BasicAuthHandler::authorize(CivetServer* server, mg_connection* conn) { - if (!AuthorizeInner(server, conn)) { +int BasicAuthHandler::authHandler(struct mg_connection* conn, void* cbdata) { + auto* handler = reinterpret_cast(cbdata); + if (!handler) { + return 0; // No handler found + } + + return handler->Authorize(conn) ? 1 : 0; +} + +bool BasicAuthHandler::Authorize(mg_connection* conn) { + if (!AuthorizeInner(conn)) { WriteUnauthorizedResponse(conn); return false; } return true; } -bool BasicAuthHandler::AuthorizeInner(CivetServer*, mg_connection* conn) { +bool BasicAuthHandler::AuthorizeInner(mg_connection* conn) { const char* authHeader = mg_get_header(conn, "Authorization"); if (authHeader == nullptr) { diff --git a/pull/src/basic_auth.h b/pull/src/basic_auth.h index 895f90ea..8d41feef 100644 --- a/pull/src/basic_auth.h +++ b/pull/src/basic_auth.h @@ -4,7 +4,7 @@ #include #include -#include "CivetServer.h" +#include "civetweb.h" #include "prometheus/detail/pull_export.h" namespace prometheus { @@ -12,11 +12,23 @@ namespace prometheus { /** * Handler for HTTP Basic authentication for Endpoints. */ -class PROMETHEUS_CPP_PULL_EXPORT BasicAuthHandler : public CivetAuthHandler { +class BasicAuthHandler { public: using AuthFunc = std::function; - explicit BasicAuthHandler(AuthFunc callback, std::string realm); + BasicAuthHandler(AuthFunc callback, std::string realm); + /** + * authHandler(struct mg_connection *, void *cbdata) + * + * Handles the authorization requests. + * + * @param conn - the connection information + * @param cbdata - pointer to the CivetAuthHandler instance. + * @returns 1 if authorized, 0 otherwise + */ + static int authHandler(struct mg_connection* conn, void* cbdata); + + private: /** * Implements civetweb authorization interface. * @@ -26,10 +38,9 @@ class PROMETHEUS_CPP_PULL_EXPORT BasicAuthHandler : public CivetAuthHandler { * If handler returns false, or the Auth header is absent, * rejects the request with 401 Unauthorized. */ - bool authorize(CivetServer* server, mg_connection* conn) override; + bool Authorize(mg_connection* conn); - private: - bool AuthorizeInner(CivetServer* server, mg_connection* conn); + bool AuthorizeInner(mg_connection* conn); void WriteUnauthorizedResponse(mg_connection* conn); AuthFunc callback_; diff --git a/pull/src/endpoint.cc b/pull/src/endpoint.cc index 274e17c5..374bb6b5 100644 --- a/pull/src/endpoint.cc +++ b/pull/src/endpoint.cc @@ -7,19 +7,20 @@ namespace prometheus { namespace detail { -Endpoint::Endpoint(CivetServer& server, std::string uri) +Endpoint::Endpoint(mg_context* server, std::string uri) : server_(server), uri_(std::move(uri)), endpoint_registry_(std::make_shared()), metrics_handler_( detail::make_unique(*endpoint_registry_)) { RegisterCollectable(endpoint_registry_); - server_.addHandler(uri_, metrics_handler_.get()); + mg_set_request_handler(server_, uri_.c_str(), MetricsHandler::requestHandler, + metrics_handler_.get()); } Endpoint::~Endpoint() { - server_.removeHandler(uri_); - server_.removeAuthHandler(uri_); + mg_set_request_handler(server_, uri_.c_str(), nullptr, nullptr); + mg_set_auth_handler(server_, uri_.c_str(), nullptr, nullptr); } void Endpoint::RegisterCollectable( @@ -32,7 +33,8 @@ void Endpoint::RegisterAuth( const std::string& realm) { auth_handler_ = detail::make_unique(std::move(authCB), realm); - server_.addAuthHandler(uri_, auth_handler_.get()); + mg_set_auth_handler(server_, uri_.c_str(), BasicAuthHandler::authHandler, + auth_handler_.get()); } const std::string& Endpoint::GetURI() const { return uri_; } diff --git a/pull/src/endpoint.h b/pull/src/endpoint.h index af64d5f8..1316550a 100644 --- a/pull/src/endpoint.h +++ b/pull/src/endpoint.h @@ -9,7 +9,7 @@ #include "prometheus/collectable.h" #include "prometheus/registry.h" -class CivetServer; +extern "C" struct mg_context; namespace prometheus { namespace detail { @@ -17,7 +17,7 @@ class MetricsHandler; class Endpoint { public: - explicit Endpoint(CivetServer& server, std::string uri); + Endpoint(mg_context* server, std::string uri); ~Endpoint(); void RegisterCollectable(const std::weak_ptr& collectable); @@ -28,7 +28,7 @@ class Endpoint { const std::string& GetURI() const; private: - CivetServer& server_; + mg_context* server_; const std::string uri_; // registry for "meta" metrics about the endpoint itself std::shared_ptr endpoint_registry_; diff --git a/pull/src/exposer.cc b/pull/src/exposer.cc index 4326c349..8d10eb3a 100644 --- a/pull/src/exposer.cc +++ b/pull/src/exposer.cc @@ -1,10 +1,12 @@ #include "prometheus/exposer.h" +#include #include +#include +#include #include -#include -#include "CivetServer.h" +#include "civetweb.h" #include "endpoint.h" #include "handler.h" #include "prometheus/client_metric.h" @@ -17,8 +19,21 @@ Exposer::Exposer(const std::string& bind_address, const std::size_t num_threads) "num_threads", std::to_string(num_threads)}) {} -Exposer::Exposer(std::vector options) - : server_(detail::make_unique(std::move(options))) {} +Exposer::Exposer(std::vector options) { + // create NULL-terminated option list + std::vector pointers; + pointers.reserve(options.size() + 1u); + std::transform(options.begin(), options.end(), std::back_inserter(pointers), + [](const std::string& o) { return o.c_str(); }); + pointers.push_back(nullptr); + + server_.reset(mg_start(nullptr, this, pointers.data()), mg_stop); + if (!server_) { + throw std::runtime_error( + "null context when constructing civetweb server. " + "Possible problem binding to port."); + } +} Exposer::~Exposer() = default; @@ -36,7 +51,25 @@ void Exposer::RegisterAuth( } std::vector Exposer::GetListeningPorts() const { - return server_->getListeningPorts(); + std::vector server_ports(8); + for (;;) { + int size = mg_get_server_ports(server_.get(), + static_cast(server_ports.size()), + server_ports.data()); + if (size < static_cast(server_ports.size())) { + server_ports.resize(size < 0 ? 0 : size); + break; + } + server_ports.resize(server_ports.size() * 2); + } + + std::vector ports; + ports.reserve(server_ports.size()); + std::transform(server_ports.begin(), server_ports.end(), + std::back_inserter(ports), + [](const mg_server_port& sp) { return sp.port; }); + + return ports; } detail::Endpoint& Exposer::GetEndpointForUri(const std::string& uri) { @@ -48,7 +81,8 @@ detail::Endpoint& Exposer::GetEndpointForUri(const std::string& uri) { return *it->get(); } - endpoints_.emplace_back(detail::make_unique(*server_, uri)); + endpoints_.emplace_back( + detail::make_unique(server_.get(), uri)); return *endpoints_.back().get(); } diff --git a/pull/src/handler.cc b/pull/src/handler.cc index 69e6d9d1..9aed1a41 100644 --- a/pull/src/handler.cc +++ b/pull/src/handler.cc @@ -119,7 +119,20 @@ void MetricsHandler::RegisterCollectable( collectables_.push_back(collectable); } -bool MetricsHandler::handleGet(CivetServer*, struct mg_connection* conn) { +int MetricsHandler::requestHandler(struct mg_connection* conn, void* cbdata) { + auto* handler = reinterpret_cast(cbdata); + auto* request_info = mg_get_request_info(conn); + + if (handler && request_info) { + if (std::strcmp(request_info->request_method, "GET") == 0) { + return handler->handleGet(conn) ? 1 : 0; + } + } + + return 0; // No handler found +} + +bool MetricsHandler::handleGet(struct mg_connection* conn) { auto start_time_of_request = std::chrono::steady_clock::now(); std::vector metrics; diff --git a/pull/src/handler.h b/pull/src/handler.h index 79ddc4a9..71caa131 100644 --- a/pull/src/handler.h +++ b/pull/src/handler.h @@ -4,22 +4,24 @@ #include #include -#include "CivetServer.h" +#include "civetweb.h" #include "prometheus/counter.h" #include "prometheus/registry.h" #include "prometheus/summary.h" namespace prometheus { namespace detail { -class MetricsHandler : public CivetHandler { +class MetricsHandler { public: explicit MetricsHandler(Registry& registry); void RegisterCollectable(const std::weak_ptr& collectable); - bool handleGet(CivetServer* server, struct mg_connection* conn) override; + static int requestHandler(struct mg_connection* conn, void* cbdata); private: + bool handleGet(struct mg_connection* conn); + std::mutex collectables_mutex_; std::vector> collectables_; Family& bytes_transferred_family_;