1 #pragma once 2 3 #include <experimental/filesystem> 4 #include <fstream> 5 #include <string> 6 #include <crow/app.h> 7 #include <crow/http_request.h> 8 #include <crow/http_response.h> 9 #include <crow/routing.h> 10 #include <boost/algorithm/string/replace.hpp> 11 #include <boost/container/flat_set.hpp> 12 13 namespace crow { 14 namespace webassets { 15 16 namespace filesystem = std::experimental::filesystem; 17 static const char* gzip_string = "gzip"; 18 static const char* none_string = "none"; 19 static const char* if_none_match_string = "If-None-Match"; 20 static const char* content_encoding_string = "Content-Encoding"; 21 static const char* content_type_string = "Content-Type"; 22 static const char* etag_string = "ETag"; 23 24 static boost::container::flat_set<std::string> routes; 25 26 template <typename... Middlewares> 27 void request_routes(Crow<Middlewares...>& app) { 28 const static boost::container::flat_map<const char*, const char*> content_types{ 29 {{".css", "text/css;charset=UTF-8"}, 30 {".html", "text/html;charset=UTF-8"}, 31 {".js", "text/html;charset=UTF-8"}, 32 {".png", "image/png;charset=UTF-8"}, 33 {".woff", "application/x-font-woff"}, 34 {".woff2", "application/x-font-woff2"}, 35 {".ttf", "application/x-font-ttf"}, 36 {".svg", "image/svg+xml"}, 37 {".eot", "application/vnd.ms-fontobject"}, 38 // dev tools don't care about map type, setting to json causes 39 // browser to show as text 40 // https://stackoverflow.com/questions/19911929/what-mime-type-should-i-use-for-javascript-source-map-files 41 {".map", "application/json"}}}; 42 auto rootpath = filesystem::path("/usr/share/www/"); 43 auto dir_iter = filesystem::recursive_directory_iterator(rootpath); 44 for (auto& dir : dir_iter) { 45 auto absolute_path = dir.path(); 46 auto relative_path = filesystem::path( 47 absolute_path.string().substr(rootpath.string().size() - 1)); 48 // make sure we don't recurse into certain directories 49 // note: maybe check for is_directory() here as well... 50 if (filesystem::is_directory(dir)) { 51 // don't recurse into hidden directories or symlinks 52 if (boost::starts_with(dir.path().filename().string(), ".") || 53 filesystem::is_symlink(dir)) { 54 dir_iter.disable_recursion_pending(); 55 } 56 } else if (filesystem::is_regular_file(dir)) { 57 auto webpath = relative_path; 58 bool is_gzip = false; 59 if (relative_path.extension() == ".gz") { 60 webpath = webpath.replace_extension(""); 61 is_gzip = true; 62 } 63 64 if (webpath.filename() == "index.html") { 65 webpath = webpath.parent_path(); 66 } 67 68 routes.insert(webpath.string()); 69 70 std::string absolute_path_str = absolute_path.string(); 71 const char* content_type = nullptr; 72 auto content_type_it = 73 content_types.find(relative_path.extension().c_str()); 74 if (content_type_it != content_types.end()) { 75 content_type = content_type_it->second; 76 } 77 app.route_dynamic(std::string(webpath.string()))( 78 [is_gzip, absolute_path_str, content_type](const crow::request& req, 79 crow::response& res) { 80 if (is_gzip) { 81 res.add_header(content_encoding_string, gzip_string); 82 } else { 83 res.add_header(content_encoding_string, none_string); 84 } 85 // std::string sha1("a576dc96a5c605b28afb032f3103630d61ac1068"); 86 // res.add_header(etag_string, sha1); 87 88 // if (req.get_header_value(if_none_match_string) == sha1) { 89 // res.code = 304; 90 //} else { 91 // res.code = 200; 92 // TODO, if you have a browser from the dark ages that doesn't 93 // support 94 // gzip, unzip it before sending based on Accept-Encoding header 95 // res.add_header(content_encoding_string, gzip_string); 96 if (content_type != nullptr) { 97 res.add_header(content_type_string, content_type); 98 } 99 // res.set_header("Cache-Control", "public, max-age=86400"); 100 std::ifstream inf(absolute_path_str); 101 if (!inf) { 102 CROW_LOG_DEBUG << "failed to read file"; 103 res.code = 400; 104 res.end(); 105 return; 106 } 107 108 std::string body{std::istreambuf_iterator<char>(inf), 109 std::istreambuf_iterator<char>()}; 110 111 res.body = body; 112 res.end(); 113 }); 114 } 115 } 116 } 117 } // namespace webassets 118 } // namespace crow