75 lines
2.4 KiB
C++
75 lines
2.4 KiB
C++
#include "StaticFiles.hpp"
|
|||
|
|
|
||
|
|
#include <cstdio>
|
||
|
|
#include <fstream>
|
||
|
|
#include <sstream>
|
||
|
|
|
||
|
|
namespace molo::example::fw {
|
||
|
|
|
||
|
|
bool path_is_safe(const std::string& request_path) {
|
||
|
|
if (request_path.find("..") != std::string::npos) return false;
|
||
|
|
if (request_path.find('\\') != std::string::npos) return false;
|
||
|
|
if (request_path.empty() || request_path.front() != '/') return false;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
namespace {
|
||
|
|
|
||
|
|
std::string content_type_for(const std::string& name) {
|
||
|
|
auto ends = [&](const char* ext) {
|
||
|
|
std::string e(ext);
|
||
|
|
return name.size() >= e.size() &&
|
||
|
|
name.compare(name.size() - e.size(), e.size(), e) == 0;
|
||
|
|
};
|
||
|
|
if (ends(".html")) return "text/html; charset=utf-8";
|
||
|
|
if (ends(".css")) return "text/css; charset=utf-8";
|
||
|
|
if (ends(".js")) return "text/javascript; charset=utf-8";
|
||
|
|
if (ends(".json")) return "application/json";
|
||
|
|
if (ends(".svg")) return "image/svg+xml";
|
||
|
|
if (ends(".png")) return "image/png";
|
||
|
|
return "application/octet-stream";
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace
|
||
|
|
|
||
|
|
std::string etag_for(const std::string& body) {
|
||
|
|
unsigned long long h = 1469598103934665603ULL;
|
||
|
|
for (unsigned char c : body) {
|
||
|
|
h ^= c;
|
||
|
|
h *= 1099511628211ULL;
|
||
|
|
}
|
||
|
|
char buf[32];
|
||
|
|
std::snprintf(buf, sizeof(buf), "\"%llx-%zu\"",
|
||
|
|
(unsigned long long)h, body.size());
|
||
|
|
return buf;
|
||
|
|
}
|
||
|
|
|
||
|
|
Response serve_disk_file(const std::string& root,
|
||
|
|
const std::string& subpath) {
|
||
|
|
std::string full = "/" + subpath;
|
||
|
|
if (!path_is_safe(full)) return TextResponse("unsafe path", 403);
|
||
|
|
std::ifstream in(root + full, std::ios::binary);
|
||
|
|
if (!in) return TextResponse("not found", 404);
|
||
|
|
std::ostringstream ss;
|
||
|
|
ss << in.rdbuf();
|
||
|
|
Response res(200, ss.str(), content_type_for(full));
|
||
|
|
res.headers.set("ETag", etag_for(res.body));
|
||
|
|
res.headers.set("Cache-Control", "public, max-age=3600");
|
||
|
|
return res;
|
||
|
|
}
|
||
|
|
|
||
|
|
Response serve_embedded(const std::map<std::string, std::string>& assets,
|
||
|
|
const std::string& subpath) {
|
||
|
|
std::string full = "/" + subpath;
|
||
|
|
if (!path_is_safe(full)) return TextResponse("unsafe path", 403);
|
||
|
|
auto it = assets.find(full);
|
||
|
|
if (it == assets.end()) return TextResponse("not found", 404);
|
||
|
|
Response res(200, it->second, content_type_for(full));
|
||
|
|
res.headers.set("ETag", etag_for(res.body));
|
||
|
|
res.headers.set("Cache-Control", "public, max-age=3600");
|
||
|
|
res.headers.set("X-Asset-Source", "embedded");
|
||
|
|
return res;
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace molo::example::fw
|