From 5330a835484f16318b3b074f8cf8890203eb3393 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Sun, 12 Feb 2023 18:57:09 +0100 Subject: Convert from webserver API to FCGI --- weblog.cpp | 206 ++++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 121 insertions(+), 85 deletions(-) (limited to 'weblog.cpp') diff --git a/weblog.cpp b/weblog.cpp index 7842ac3..2005484 100644 --- a/weblog.cpp +++ b/weblog.cpp @@ -1,9 +1,12 @@ #include "weblog.h" +#include "libreichwein/file.h" #include "libreichwein/mime.h" #include "libreichwein/stringhelper.h" #include "libreichwein/url.h" +#include + #include #include #include @@ -19,6 +22,7 @@ using namespace std::string_literals; namespace fs = std::filesystem; namespace pt = boost::property_tree; +using namespace Reichwein; using namespace Reichwein::Mime; using namespace Reichwein::Stringhelper; @@ -28,29 +32,12 @@ namespace { const std::string article_filename{"article.data"}; // Used to return errors by generating response page and HTTP status code - std::string HttpStatus(std::string status, std::string message, std::function& SetResponseHeader) + void HttpStatus(std::string status, std::string message, FCGX_Request& request) { - SetResponseHeader("status", status); - SetResponseHeader("content_type", "text/html"); - return status + " " + message; - } + FCGX_FPrintF(request.out, "Status: %s Error\r\n", status.c_str()); + FCGX_PutS("Content-Type: text/plain\r\n\r\n", request.out); - std::string getFile(const fs::path& filename) - { - std::ifstream file(filename.string(), std::ios::in | std::ios::binary | std::ios::ate); - - if (file.is_open()) { - std::ifstream::pos_type fileSize = file.tellg(); - file.seekg(0, std::ios::beg); - - std::string bytes(fileSize, ' '); - file.read(reinterpret_cast(bytes.data()), fileSize); - - return bytes; - - } else { - throw std::runtime_error("Opening "s + filename.string() + " for reading"); - } + FCGX_FPrintF(request.out, "%s %s", status.c_str(), message.c_str()); } bool is_index_page(std::string& rel_target) @@ -180,7 +167,7 @@ namespace { // returns teaser of article in plain text std::string shortVersion(const fs::path& path) { - std::string article {getFile(path / article_filename)}; + std::string article {File::getFile(path / article_filename)}; size_t pos0 {article.find("\n\n")}; if (pos0 == article.npos) return ""; @@ -207,25 +194,27 @@ namespace { class HtmlPage { - std::function& mGetRequestParam; + Config& m_config; std::string mContents; std::string mHeader; const std::string mFooter; public: - HtmlPage(std::function& GetRequestParam, - std::string s = ""s) - : mGetRequestParam(GetRequestParam) - , mContents(s) - , mHeader("" - "" - "" - "" + GetRequestParam("WEBLOG_NAME") + "" - "" - "" - "" - "") + HtmlPage(FCGX_Request& request, Config& config, + std::string s = ""s): + m_config{config}, + mContents(s), + mHeader("" + "" + "" + "" + m_config.getName() + "" + "" + "" + "" + "") { } @@ -241,18 +230,18 @@ namespace { } }; - std::string generateIndexPage(fs::path& path, - std::function& GetRequestParam, - std::function& SetResponseHeader, - size_t page) + void generateIndexPage(fs::path& path, + FCGX_Request& request, + Config& config, + size_t page) { try { if (page > std::numeric_limits::max()) throw std::runtime_error("Bad page index: "s + std::to_string(page)); - HtmlPage htmlPage{GetRequestParam, "

"s + GetRequestParam("WEBLOG_NAME") + "

"s}; + HtmlPage htmlPage{request, config, "

"s + config.getName() + "

"s}; - fs::path link{ GetRequestParam("plugin_path")}; + fs::path link{ FCGX_GetParam("PATH_INFO", request.envp)}; auto list{getArticleList(path, page)}; if (list.empty()) @@ -277,21 +266,20 @@ namespace { htmlPage += " older>>"s; htmlPage += "
"; } - SetResponseHeader("cache_control", "no-store"); - return htmlPage; + FCGX_FPrintF(request.out, "Cache-Control: no-store\r\n\r\n"); + std::string data{htmlPage}; + FCGX_PutStr(data.c_str(), data.size(), request.out); } catch (const std::exception& ex) { - return HttpStatus("500", "Reading Index page: "s + ex.what(), SetResponseHeader); + HttpStatus("500", "Reading Index page: "s + ex.what(), request); } } - std::string generateArticlePage(fs::path& path, - std::function& GetRequestParam, - std::function& SetResponseHeader) + void generateArticlePage(fs::path& path, FCGX_Request& request, Config& config) { try { auto metaData{getMetaData(path)}; - std::string data { getFile(path / article_filename)}; + std::string data { File::getFile(path / article_filename)}; size_t pos {data.find("\n\n")}; if (pos == data.npos) @@ -303,23 +291,25 @@ namespace { if (it != metaData.end() && it->second == "text/plain") data = verbatimText(data); - HtmlPage htmlPage{GetRequestParam, "

"s + metaData.at("Subject") + "

" + HtmlPage htmlPage{request, config, "

"s + metaData.at("Subject") + "

" "
" + metaData.at("Date") + "
" "

"s + data + "
□"}; - return htmlPage; + data = htmlPage; + FCGX_PutStr(data.c_str(), data.size(), request.out); } catch (const std::exception& ex) { - return HttpStatus("500", "Reading Article: "s + ex.what(), SetResponseHeader); + return HttpStatus("500", "Reading Article: "s + ex.what(), request); } } - std::string generateStaticFile(fs::path& path, std::function& SetResponseHeader) + void generateStaticFile(fs::path& path, FCGX_Request& request) { try { - SetResponseHeader("content_type", mime_type(path.string())); - return getFile(path); + FCGX_FPrintF(request.out, "Content-Type: %s\r\n\r\n", mime_type(path.string()).c_str()); + std::string data{File::getFile(path)}; + FCGX_PutStr(data.c_str(), data.size(), request.out); } catch (const std::exception& ex) { - return HttpStatus("500", "Reading Article file: "s + ex.what(), SetResponseHeader); + HttpStatus("500", "Reading Article file: "s + ex.what(), request); } } @@ -343,53 +333,63 @@ namespace { return result; } + void usage() + { + std::cout << "usage: weblog [-c ]" << std::endl; + } + + fs::path getConfigPath(int argc, char* argv[]) + { + if (argc == 2 && argv[1] == "-h"s) { + usage(); + exit(0); + } + if (argc == 3 && argv[1] == "-c"s) + return argv[2]; + return {}; + } + } // anonymous namespace -Weblog::Weblog() +Weblog::Weblog(int argc, char* argv[]): m_config{getConfigPath(argc, argv).empty() ? Config{} : Config{getConfigPath(argc, argv)}} { - //std::cout << "Plugin constructor" << std::endl; } Weblog::~Weblog() { - //std::cout << "Plugin destructor" << std::endl; } -std::string Weblog::generate_page( - std::function& GetServerParam, - std::function& GetRequestParam, // request including body (POST...) - std::function& SetResponseHeader // to be added to result string -) +namespace { + +void generate_page(FCGX_Request& request, Config& config) { try { // Make sure we can handle the method - std::string method {GetRequestParam("method")}; + std::string method {FCGX_GetParam("REQUEST_METHOD", request.envp)}; if (method != "GET" && method != "HEAD") - return HttpStatus("400", "Unknown HTTP method", SetResponseHeader); + return HttpStatus("400", "Unknown HTTP method", request); // Request path must not contain "..". - std::string rel_target{GetRequestParam("rel_target")}; - std::string target{GetRequestParam("target")}; + std::string rel_target{FCGX_GetParam("PATH_INFO", request.envp)}; + std::string target{FCGX_GetParam("SCRIPT_NAME", request.envp)}; if (rel_target.find("..") != std::string::npos) { - return HttpStatus("400", "Illegal request: "s + target, SetResponseHeader); + return HttpStatus("400", "Illegal request: "s + target, request); } std::unordered_map query { SplitQueryString(rel_target) }; // Build the path to the requested file - std::string doc_root{GetRequestParam("doc_root")}; + std::string path_translated{FCGX_GetParam("PATH_TRANSLATED", request.envp)}; if (rel_target.size() >= 4 && std::all_of(rel_target.begin(), rel_target.begin() + 4, isdigit)) { rel_target = rel_target.substr(0, 4) + "/" + rel_target; } - fs::path path {fs::path{doc_root} / rel_target}; + fs::path path {path_translated}; 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); + std::string location{FCGX_GetParam("REQUEST_URI", request.envp) + "/"s}; + FCGX_FPrintF(request.out, "Location: %s\r\n", location.c_str()); + return HttpStatus("301", "Correcting directory path", request); } - SetResponseHeader("content_type", "text/html"); - size_t page {0}; auto it {query.find("page")}; if (it != query.end()) { @@ -400,24 +400,60 @@ std::string Weblog::generate_page( } } - if (is_index_page(rel_target)) - return generateIndexPage(path, GetRequestParam, SetResponseHeader, page); + if (is_index_page(rel_target)) { + FCGX_PutS("Content-Type: text/html\r\n", request.out); + return generateIndexPage(path, request, config, page); + } - if (is_article_page(rel_target, path)) - return generateArticlePage(path, GetRequestParam, SetResponseHeader); + if (is_article_page(rel_target, path)) { + FCGX_PutS("Content-Type: text/html\r\n", request.out); + return generateArticlePage(path, request, config); + } - if (is_index_file(rel_target, path) || is_article_file(rel_target, path)) - return generateStaticFile(path, SetResponseHeader); + if (is_index_file(rel_target, path) || is_article_file(rel_target, path)) { + FCGX_PutS("Content-Type: text/html\r\n", request.out); + return generateStaticFile(path, request); + } - return HttpStatus("404", "Bad path specification: "s + rel_target, SetResponseHeader); + return HttpStatus("404", "Bad path specification: "s + rel_target, request); } catch (const std::exception& ex) { - return HttpStatus("500", "Unknown Error: "s + ex.what(), SetResponseHeader); + return HttpStatus("500", "Unknown Error: "s + ex.what(), request); } } -void Weblog::run(int argc, char* argv[]) +} // namespace + +int Weblog::run() { + int result = FCGX_Init(); + if (result != 0) { // error on init + std::cerr << "Error: FCGX_Init()" << std::endl; + return 1; + } + + result = FCGX_IsCGI(); + if (result) { + std::cerr << "Error: No FCGI environment available" << std::endl; + return 1; + } + + FCGX_Request request; + result = FCGX_InitRequest(&request, 0, 0); + if (result != 0) { + std::cerr << "Error: FCGX_InitRequest()" << std::endl; + return 1; + } + + while (FCGX_Accept_r(&request) >= 0) { + try { + generate_page(request, m_config); + } catch (const std::exception& ex) { + FCGX_PutS("Status: 500 Internal Server Error\r\n", request.out); + FCGX_PutS("Content-Type: text/plain\r\n\r\n", request.out); + FCGX_FPrintF(request.out, "Error: %s\r\n", ex.what()); + } + } return 0; } -- cgit v1.2.3