#include "statistics.h" #include #include #include #include #include #include #include #include #include #include using namespace std::string_literals; namespace bp = boost::process; namespace fs = std::filesystem; namespace { // Used to return errors by generating response page and HTTP status code std::string HttpStatus(std::string status, std::string message, std::function& SetResponseHeader) { SetResponseHeader("status", status); SetResponseHeader("content_type", "text/html"); return status + " " + message; } // returns sum over specified column uint64_t getSum(const std::string& stats, size_t column) { uint64_t result{0}; std::istringstream is{stats}; std::string line; while (std::getline(is, line) && !is.eof()) { std::vector elements; boost::algorithm::split(elements, line, [](char c){ return c == ','; }); if (column >= elements.size()) { std::cerr << "Error: No column " << column << " found." << std::endl; return 0; } result += stoull(elements[column]); } return result; } } // anonymous namespace std::string statistics_plugin::name() { return "statistics"; } statistics_plugin::statistics_plugin() { //std::cout << "Plugin constructor" << std::endl; } statistics_plugin::~statistics_plugin() { //std::cout << "Plugin destructor" << std::endl; } std::string statistics_plugin::generate_page( std::function& GetServerParam, std::function& GetRequestParam, // request including body (POST...) std::function& SetResponseHeader // to be added to result string ) { try { // Request path must not contain "..". std::string rel_target{GetRequestParam("rel_target")}; size_t query_pos{rel_target.find("?")}; if (query_pos != rel_target.npos) rel_target = rel_target.substr(0, query_pos); std::string target{GetRequestParam("target")}; if (rel_target.find("..") != std::string::npos) { return HttpStatus("400", "Illegal request: "s + target, SetResponseHeader); } // Build the path to the requested file std::string doc_root{GetRequestParam("doc_root")}; fs::path path {fs::path{doc_root} / rel_target}; if (target.size() && target.back() != '/' && fs::is_directory(path)) { std::string location{GetRequestParam("location") + "/"s}; SetResponseHeader("location", location); return HttpStatus("301", "Correcting directory path", SetResponseHeader); } try { SetResponseHeader("content_type", "text/html"); std::string header {"" "" "Webserver Statistics" ""}; std::string footer{"


"}; std::string result{header}; std::string statistics{GetServerParam("statistics")}; double ipv6_fraction_by_requests {double(getSum(statistics, 5)) / getSum(statistics, 1)}; double ipv6_fraction_by_bytes {double(getSum(statistics, 7) + getSum(statistics, 8)) / (getSum(statistics, 3) + getSum(statistics, 4))}; double https_fraction_by_requests {double(getSum(statistics, 9)) / getSum(statistics, 1)}; double https_fraction_by_bytes {double(getSum(statistics, 11) + getSum(statistics, 12)) / (getSum(statistics, 3) + getSum(statistics, 4))}; result += "

Webserver Statistics

"; result += "

Host uptime: "s + GetServerParam("uptime_host") + "

"; result += "

Host webserver: "s + GetServerParam("uptime_webserver") + "

"; result += "

IPv6 fraction by requests: "s + std::to_string(ipv6_fraction_by_requests * 100) + "

"; result += "

IPv6 fraction by bytes: "s + std::to_string(ipv6_fraction_by_bytes * 100) + "

"; result += "

HTTPS fraction by requests: "s + std::to_string(https_fraction_by_requests * 100) + "

"; result += "

HTTPS fraction by bytes: "s + std::to_string(https_fraction_by_bytes * 100) + "

"; result += "
"s + statistics + "
"s; result += footer; return result; } catch (const std::exception& ex) { return HttpStatus("500", "Statistics error: "s + ex.what(), SetResponseHeader); } } catch (const std::exception& ex) { return HttpStatus("500", "Unknown Error: "s + ex.what(), SetResponseHeader); } }