#include "http_server.h" #include "common/debug.h" #include "common/hostname.h" #include "global/global_init.h" #include "global/global_context.h" #include "exporter/DaemonMetricCollector.h" #include #include #include #include #include #include #include #include #include #include #include #include #define dout_context g_ceph_context #define dout_subsys ceph_subsys_ceph_exporter namespace beast = boost::beast; // from namespace http = beast::http; // from namespace net = boost::asio; // from using tcp = boost::asio::ip::tcp; // from class http_connection : public std::enable_shared_from_this { public: http_connection(tcp::socket socket) : socket_(std::move(socket)) {} // Initiate the asynchronous operations associated with the connection. void start() { read_request(); check_deadline(); } private: tcp::socket socket_; beast::flat_buffer buffer_{8192}; http::request request_; http::response response_; net::steady_timer deadline_{socket_.get_executor(), std::chrono::seconds(60)}; // Asynchronously receive a complete request message. void read_request() { auto self = shared_from_this(); http::async_read(socket_, buffer_, request_, [self](beast::error_code ec, std::size_t bytes_transferred) { boost::ignore_unused(bytes_transferred); if (ec) { dout(1) << "ERROR: " << ec.message() << dendl; return; } else { self->process_request(); } }); } // Determine what needs to be done with the request message. void process_request() { response_.version(request_.version()); response_.keep_alive(request_.keep_alive()); switch (request_.method()) { case http::verb::get: response_.result(http::status::ok); create_response(); break; default: // We return responses indicating an error if // we do not recognize the request method. response_.result(http::status::method_not_allowed); response_.set(http::field::content_type, "text/plain"); std::string body("Invalid request-method '" + std::string(request_.method_string()) + "'"); response_.body() = body; break; } write_response(); } // Construct a response message based on the program state. void create_response() { if (request_.target() == "/") { response_.set(http::field::content_type, "text/html; charset=utf-8"); std::string body("\n" "Ceph Exporter\n" "\n" "

Ceph Exporter

\n" "

Metrics

" "\n" "\n"); response_.body() = body; } else if (request_.target() == "/metrics") { response_.set(http::field::content_type, "text/plain; charset=utf-8"); DaemonMetricCollector &collector = collector_instance(); std::string metrics = collector.get_metrics(); response_.body() = metrics; } else { response_.result(http::status::method_not_allowed); response_.set(http::field::content_type, "text/plain"); response_.body() = "File not found \n"; } } // Asynchronously transmit the response message. void write_response() { auto self = shared_from_this(); response_.prepare_payload(); http::async_write(socket_, response_, [self](beast::error_code ec, std::size_t) { self->socket_.shutdown(tcp::socket::shutdown_send, ec); self->deadline_.cancel(); if (ec) { dout(1) << "ERROR: " << ec.message() << dendl; return; } }); } // Check whether we have spent enough time on this connection. void check_deadline() { auto self = shared_from_this(); deadline_.async_wait([self](beast::error_code ec) { if (!ec) { // Close socket to cancel any outstanding operation. self->socket_.close(ec); } }); } }; // "Loop" forever accepting new connections. void http_server(tcp::acceptor &acceptor, tcp::socket &socket) { acceptor.async_accept(socket, [&](beast::error_code ec) { if (!ec) std::make_shared(std::move(socket))->start(); http_server(acceptor, socket); }); } void http_server_thread_entrypoint() { try { std::string exporter_addr = g_conf().get_val("exporter_addr"); auto const address = net::ip::make_address(exporter_addr); unsigned short port = g_conf().get_val("exporter_http_port"); net::io_context ioc{1}; tcp::acceptor acceptor{ioc, {address, port}}; tcp::socket socket{ioc}; http_server(acceptor, socket); dout(1) << "Http server running on " << exporter_addr << ":" << port << dendl; ioc.run(); } catch (std::exception const &e) { dout(1) << "Error: " << e.what() << dendl; exit(EXIT_FAILURE); } }