Skip to content

Commit 7339979

Browse files
committed
Serve precompressed static assets with gzip headers
1 parent ff74385 commit 7339979

1 file changed

Lines changed: 94 additions & 3 deletions

File tree

src/api/server.cpp

Lines changed: 94 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66
#include <chrono>
77
#include <cstring>
88
#include <filesystem>
9+
#include <fstream>
910
#include <httplib.h>
1011
#include <atomic>
1112
#include <cctype>
13+
#include <unordered_map>
1214
#include <mutex>
1315
#include <nlohmann/json.hpp>
1416

@@ -20,6 +22,64 @@ std::string make_error_json(const std::string& message) {
2022
return nlohmann::json{{"error", message}}.dump();
2123
}
2224

25+
std::string get_mime_type_for_path(const std::filesystem::path& path) {
26+
static const std::unordered_map<std::string, std::string> kMimeByExtension{
27+
{".css", "text/css"},
28+
{".csv", "text/csv"},
29+
{".gif", "image/gif"},
30+
{".htm", "text/html"},
31+
{".html", "text/html"},
32+
{".ico", "image/x-icon"},
33+
{".jpeg", "image/jpeg"},
34+
{".jpg", "image/jpeg"},
35+
{".js", "application/javascript"},
36+
{".json", "application/json"},
37+
{".map", "application/json"},
38+
{".mjs", "application/javascript"},
39+
{".png", "image/png"},
40+
{".svg", "image/svg+xml"},
41+
{".txt", "text/plain"},
42+
{".wasm", "application/wasm"},
43+
{".webp", "image/webp"},
44+
{".woff", "font/woff"},
45+
{".woff2", "font/woff2"},
46+
{".xml", "application/xml"},
47+
};
48+
49+
const auto ext = path.extension().string();
50+
const auto it = kMimeByExtension.find(ext);
51+
if (it != kMimeByExtension.end()) {
52+
return it->second;
53+
}
54+
return "application/octet-stream";
55+
}
56+
57+
bool read_file(const std::filesystem::path& path, std::string& output) {
58+
std::ifstream file(path, std::ios::binary);
59+
if (!file.is_open()) {
60+
return false;
61+
}
62+
output.assign(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
63+
return !file.bad();
64+
}
65+
66+
bool serve_file_response(httplib::Response& res,
67+
const std::filesystem::path& path,
68+
const std::filesystem::path& mime_from_path,
69+
bool gzip_encoded) {
70+
std::string body;
71+
if (!read_file(path, body)) {
72+
return false;
73+
}
74+
75+
res.set_content(body, get_mime_type_for_path(mime_from_path));
76+
if (gzip_encoded) {
77+
res.set_header("Content-Encoding", "gzip");
78+
res.set_header("Vary", "Accept-Encoding");
79+
}
80+
return true;
81+
}
82+
2383
} // namespace
2484

2585
struct ApiServer::Impl {
@@ -162,16 +222,47 @@ bool ApiServer::register_static_root(const std::string& frontend_root) {
162222
}
163223

164224
fs::path requested = root / relative;
225+
fs::path requested_gzip = requested;
226+
requested_gzip += ".gz";
227+
228+
if (fs::is_regular_file(requested_gzip)) {
229+
if (serve_file_response(res, requested_gzip, requested, true)) {
230+
return;
231+
}
232+
res.status = 500;
233+
res.set_content(make_error_json("failed to read static file"), "application/json");
234+
return;
235+
}
236+
165237
if (fs::is_regular_file(requested)) {
166-
res.set_file_content(requested.string());
238+
if (serve_file_response(res, requested, requested, false)) {
239+
return;
240+
}
241+
res.status = 500;
242+
res.set_content(make_error_json("failed to read static file"), "application/json");
167243
return;
168244
}
169245

170246
if (!fs::is_regular_file(index_path)) {
171247
res.status = 404;
172248
} else {
173-
res.set_file_content(index_path.string());
174-
res.status = 200;
249+
fs::path index_gzip = index_path;
250+
index_gzip += ".gz";
251+
if (fs::is_regular_file(index_gzip)) {
252+
if (serve_file_response(res, index_gzip, index_path, true)) {
253+
res.status = 200;
254+
return;
255+
}
256+
res.status = 500;
257+
res.set_content(make_error_json("failed to read static file"), "application/json");
258+
return;
259+
}
260+
if (serve_file_response(res, index_path, index_path, false)) {
261+
res.status = 200;
262+
} else {
263+
res.status = 500;
264+
res.set_content(make_error_json("failed to read static file"), "application/json");
265+
}
175266
}
176267
});
177268

0 commit comments

Comments
 (0)