#include "response.h" #include "file.h" #include #include #include using namespace std::placeholders; namespace { std::string extend_index_html(std::string path) { if (path.size() && path.back() == '/') path.append("index.html"); return path; } std::string GetServerParam(const std::string& key, Server& server) { // following are the supported fields: // ... throw std::runtime_error("Unsupported server param: "s + key); } std::unordered_map> GetRequestParamFunctions{ // following are the supported fields: {"target", [](request_type& req, Server& server){return std::string{req.target()};}}, {"rel_target", [](request_type& req, Server& server){ std::string host{req["host"]}; std::string target{req.target()}; return server.GetConfig().GetRelativePath(server.GetSocket(), host, target); }}, {"doc_root", [](request_type& req, Server& server) { std::string host{req["host"]}; std::string target{req.target()}; return server.GetConfig().DocRoot(server.GetSocket(), host, target); }}, {"method", [](request_type& req, Server& server){ if (req.method() == http::verb::get) return "GET"; else if (req.method() == http::verb::post) return "POST"; else if (req.method() == http::verb::head) return "HEAD"; else return ""; }}, }; std::string GetRequestParam(const std::string& key, request_type& req, Server& server) { auto it = GetRequestParamFunctions.find(key); if (it != GetRequestParamFunctions.end()) return it->second(req, server); throw std::runtime_error("Unsupported request param: "s + key); } void SetResponseHeader(const std::string& key, const std::string& value, response_type& res) { // following are the supported fields: if (key == "status") { // HTTP Status, e.g. "200" (OK) res.result(unsigned(stoul(value))); } else if (key == "server") { // Server name/version string res.set(http::field::server, value); } else if (key == "content_type") { // e.g. text/html res.set(http::field::content_type, value); } else throw std::runtime_error("Unsupported response field: "s + key); } // Return a reasonable mime type based on the extension of a file. beast::string_view mime_type(beast::string_view path) { using beast::iequals; auto const ext = [&path] { auto const pos = path.rfind("."); if (pos == beast::string_view::npos) return beast::string_view{}; return path.substr(pos); }(); if(iequals(ext, ".htm")) return "text/html"; if(iequals(ext, ".html")) return "text/html"; if(iequals(ext, ".php")) return "text/html"; if(iequals(ext, ".css")) return "text/css"; if(iequals(ext, ".txt")) return "text/plain"; if(iequals(ext, ".js")) return "application/javascript"; if(iequals(ext, ".json")) return "application/json"; if(iequals(ext, ".xml")) return "application/xml"; if(iequals(ext, ".swf")) return "application/x-shockwave-flash"; if(iequals(ext, ".flv")) return "video/x-flv"; if(iequals(ext, ".png")) return "image/png"; if(iequals(ext, ".jpe")) return "image/jpeg"; if(iequals(ext, ".jpeg")) return "image/jpeg"; if(iequals(ext, ".jpg")) return "image/jpeg"; if(iequals(ext, ".gif")) return "image/gif"; if(iequals(ext, ".bmp")) return "image/bmp"; if(iequals(ext, ".ico")) return "image/vnd.microsoft.icon"; if(iequals(ext, ".tiff")) return "image/tiff"; if(iequals(ext, ".tif")) return "image/tiff"; if(iequals(ext, ".svg")) return "image/svg+xml"; if(iequals(ext, ".svgz")) return "image/svg+xml"; return "application/text"; } } // anonymous namespace response_type generate_response(request_type& req, Server& server) { response_type res{http::status::ok, req.version()}; res.set(http::field::server, VersionString); res.set(http::field::content_type, mime_type(extend_index_html(std::string(req.target())))); res.keep_alive(req.keep_alive()); std::string host{req["host"]}; std::string target{req.target()}; std::string plugin_name { server.GetConfig().GetPlugin(server.GetSocket(), host, target)}; plugin_type plugin{server.GetPlugin(plugin_name)}; auto GetServerParamFunction {std::function(std::bind(GetServerParam, _1, std::ref(server)))}; auto GetRequestParamFunction {std::function(std::bind(GetRequestParam, _1, std::ref(req), std::ref(server)))}; auto SetResponseHeaderFunction{std::function(std::bind(SetResponseHeader, _1, _2, std::ref(res)))}; std::string res_data { plugin->generate_page(GetServerParamFunction, GetRequestParamFunction, SetResponseHeaderFunction)}; if (req.method() == http::verb::head) { res.content_length(res_data.size()); } else { res.body() = res_data; res.prepare_payload(); } return res; }