blob: 7845d712ad95603a0c86539e130166b2f3c43a35 [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;
81 }
82 app.route_dynamic(std::string(webpath.string()))(
83 [is_gzip, absolute_path_str, content_type](const crow::request& req,
84 crow::response& res) {
Ed Tanousba9f9a62017-10-11 16:40:35 -070085 static const char* content_type_string = "Content-Type";
Ed Tanous911ac312017-08-15 09:37:42 -070086 // std::string sha1("a576dc96a5c605b28afb032f3103630d61ac1068");
Ed Tanousba9f9a62017-10-11 16:40:35 -070087 // res.add_header("ETag", sha1);
Ed Tanous911ac312017-08-15 09:37:42 -070088
Ed Tanousba9f9a62017-10-11 16:40:35 -070089 // if (req.get_header_value("If-None-Match") == sha1) {
Ed Tanous911ac312017-08-15 09:37:42 -070090 // res.code = 304;
91 //} else {
92 // res.code = 200;
93 // TODO, if you have a browser from the dark ages that doesn't
94 // support
95 // gzip, unzip it before sending based on Accept-Encoding header
Ed Tanousba9f9a62017-10-11 16:40:35 -070096 // res.add_header("Content-Encoding", gzip_string);
Ed Tanous911ac312017-08-15 09:37:42 -070097 if (content_type != nullptr) {
98 res.add_header(content_type_string, content_type);
99 }
Ed Tanousba9f9a62017-10-11 16:40:35 -0700100
101 if (is_gzip) {
102 res.add_header("Content-Encoding", "gzip");
103 }
104
Ed Tanous911ac312017-08-15 09:37:42 -0700105 // res.set_header("Cache-Control", "public, max-age=86400");
106 std::ifstream inf(absolute_path_str);
107 if (!inf) {
108 CROW_LOG_DEBUG << "failed to read file";
109 res.code = 400;
110 res.end();
111 return;
112 }
113
114 std::string body{std::istreambuf_iterator<char>(inf),
115 std::istreambuf_iterator<char>()};
116
117 res.body = body;
118 res.end();
119 });
120 }
121 }
122}
123} // namespace webassets
124} // namespace crow