blob: 6fd0d60f6b91cc0c24b3897b2ee4fa4eb5e96fbe [file] [log] [blame]
Ed Tanous911ac312017-08-15 09:37:42 -07001#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
13namespace crow {
14namespace webassets {
15
16namespace filesystem = std::experimental::filesystem;
Ed Tanousba9f9a62017-10-11 16:40:35 -070017
18struct cmp_str {
19 bool operator()(const char* a, const char* b) const {
20 return std::strcmp(a, b) < 0;
21 }
22};
Ed Tanous911ac312017-08-15 09:37:42 -070023
24static boost::container::flat_set<std::string> routes;
25
26template <typename... Middlewares>
27void request_routes(Crow<Middlewares...>& app) {
Ed Tanousba9f9a62017-10-11 16:40:35 -070028 const static boost::container::flat_map<const char*, const char*, cmp_str>
29 content_types{
30 {{".css", "text/css;charset=UTF-8"},
31 {".html", "text/html;charset=UTF-8"},
32 {".js", "text/html;charset=UTF-8"},
33 {".png", "image/png;charset=UTF-8"},
34 {".woff", "application/x-font-woff"},
35 {".woff2", "application/x-font-woff2"},
36 {".ttf", "application/x-font-ttf"},
37 {".svg", "image/svg+xml"},
38 {".eot", "application/vnd.ms-fontobject"},
39 {".xml", "application/xml"},
40 // dev tools don't care about map type, setting to json causes
41 // browser to show as text
42 // https://stackoverflow.com/questions/19911929/what-mime-type-should-i-use-for-javascript-source-map-files
43 {".map", "application/json"}}};
Ed Tanous911ac312017-08-15 09:37:42 -070044 auto rootpath = filesystem::path("/usr/share/www/");
45 auto dir_iter = filesystem::recursive_directory_iterator(rootpath);
46 for (auto& dir : dir_iter) {
47 auto absolute_path = dir.path();
Ed Tanousba9f9a62017-10-11 16:40:35 -070048 auto absolute_path_str = dir.path().string();
Ed Tanous911ac312017-08-15 09:37:42 -070049 auto relative_path = filesystem::path(
50 absolute_path.string().substr(rootpath.string().size() - 1));
51 // make sure we don't recurse into certain directories
52 // note: maybe check for is_directory() here as well...
53 if (filesystem::is_directory(dir)) {
54 // don't recurse into hidden directories or symlinks
55 if (boost::starts_with(dir.path().filename().string(), ".") ||
56 filesystem::is_symlink(dir)) {
57 dir_iter.disable_recursion_pending();
58 }
59 } else if (filesystem::is_regular_file(dir)) {
60 auto webpath = relative_path;
61 bool is_gzip = false;
62 if (relative_path.extension() == ".gz") {
63 webpath = webpath.replace_extension("");
64 is_gzip = true;
65 }
66
67 if (webpath.filename() == "index.html") {
68 webpath = webpath.parent_path();
Ed Tanousba9f9a62017-10-11 16:40:35 -070069 if (webpath.string() != "/") {
70 webpath += "/";
71 }
Ed Tanous911ac312017-08-15 09:37:42 -070072 }
73
74 routes.insert(webpath.string());
75
76 std::string absolute_path_str = absolute_path.string();
77 const char* content_type = nullptr;
Ed Tanousba9f9a62017-10-11 16:40:35 -070078 auto content_type_it = content_types.find(webpath.extension().c_str());
Ed Tanous911ac312017-08-15 09:37:42 -070079 if (content_type_it != content_types.end()) {
80 content_type = content_type_it->second;
Ed Tanous710adfc2017-10-24 17:04:52 -070081 } else {
82 if (webpath.string() == "$metadata"){
83 content_type_it = content_types.find(".xml");
84 // should always be true
85 if (content_type_it != content_types.end()) {
86 content_type = content_type_it->second;
87 }
88 }
Ed Tanous911ac312017-08-15 09:37:42 -070089 }
Ed Tanous746b22a2017-11-07 15:32:12 -080090 app.route_dynamic(webpath.string())(
Ed Tanous911ac312017-08-15 09:37:42 -070091 [is_gzip, absolute_path_str, content_type](const crow::request& req,
92 crow::response& res) {
Ed Tanousba9f9a62017-10-11 16:40:35 -070093 static const char* content_type_string = "Content-Type";
Ed Tanous911ac312017-08-15 09:37:42 -070094 // std::string sha1("a576dc96a5c605b28afb032f3103630d61ac1068");
Ed Tanousba9f9a62017-10-11 16:40:35 -070095 // res.add_header("ETag", sha1);
Ed Tanous911ac312017-08-15 09:37:42 -070096
Ed Tanousba9f9a62017-10-11 16:40:35 -070097 // if (req.get_header_value("If-None-Match") == sha1) {
Ed Tanous911ac312017-08-15 09:37:42 -070098 // res.code = 304;
99 //} else {
100 // res.code = 200;
101 // TODO, if you have a browser from the dark ages that doesn't
102 // support
103 // gzip, unzip it before sending based on Accept-Encoding header
Ed Tanousba9f9a62017-10-11 16:40:35 -0700104 // res.add_header("Content-Encoding", gzip_string);
Ed Tanous911ac312017-08-15 09:37:42 -0700105 if (content_type != nullptr) {
106 res.add_header(content_type_string, content_type);
107 }
Ed Tanousba9f9a62017-10-11 16:40:35 -0700108
109 if (is_gzip) {
110 res.add_header("Content-Encoding", "gzip");
111 }
112
Ed Tanous911ac312017-08-15 09:37:42 -0700113 // res.set_header("Cache-Control", "public, max-age=86400");
114 std::ifstream inf(absolute_path_str);
115 if (!inf) {
116 CROW_LOG_DEBUG << "failed to read file";
117 res.code = 400;
118 res.end();
119 return;
120 }
121
122 std::string body{std::istreambuf_iterator<char>(inf),
123 std::istreambuf_iterator<char>()};
124
125 res.body = body;
126 res.end();
127 });
128 }
129 }
130}
131} // namespace webassets
132} // namespace crow