} else {
#if defined(LLAMA_UI_HAS_ASSETS)
auto serve_asset = [](const std::string & name, const char * mime, bool with_isolation_headers) {
- return [name, mime, with_isolation_headers](const httplib::Request & /*req*/, httplib::Response & res) {
+ return [name, mime, with_isolation_headers](const httplib::Request & req, httplib::Response & res) {
const llama_ui_asset * a = llama_ui_find_asset(name.c_str());
if (!a) {
res.status = 404;
return false;
}
+ res.set_header("ETag", a->etag);
+ // Check If-None-Match for conditional GET (304 Not Modified)
+ if (const std::string & inm = req.get_header_value("If-None-Match");
+ !inm.empty() && inm == a->etag) {
+ res.status = 304;
+ return false;
+ }
if (with_isolation_headers) {
// COEP and COOP headers, required by pyodide (python interpreter)
res.set_header("Cross-Origin-Embedder-Policy", "require-corp");
#include <fstream>
#include <string>
#include <vector>
+#include <cstdint>
+
+// Computes FNV-1a hash of the data
+static uint64_t fnv_hash(const uint8_t * data, size_t len) {
+ const uint64_t fnv_prime = 0x100000001b3ULL;
+ uint64_t hash = 0xcbf29ce484222325ULL;
+
+ for (size_t i = 0; i < len; ++i) {
+ hash ^= data[i];
+ hash *= fnv_prime;
+ }
+ return hash;
+}
static bool read_file(const std::string & path, std::vector<unsigned char> & out) {
std::ifstream f(path, std::ios::binary | std::ios::ate);
" const char * name;\n"
" const unsigned char * data;\n"
" size_t size;\n"
+ " const char * etag;\n"
"};\n\n"
"const llama_ui_asset * llama_ui_find_asset(const char * name);\n";
}
cpp += fmt("static const unsigned char asset_%d_data[] = {", i);
append_bytes_hex(cpp, bytes);
- cpp += fmt("};\nstatic const size_t asset_%d_size = %lu;\n\n",
+ const auto hash = fnv_hash(bytes.data(), bytes.size());
+
+ cpp += fmt("};\nstatic const size_t asset_%d_size = %lu;\n",
i, static_cast<unsigned long>(bytes.size()));
+ cpp += fmt("static const char asset_%d_etag[] = \"\\\"0x%016lx\\\"\";\n\n",
+ i, static_cast<unsigned long>(hash));
}
cpp += "static const llama_ui_asset g_assets[] = {\n";
for (int i = 0; i < n_assets; i++) {
- const char * name = argv[3 + i * 2];
- cpp += fmt(" { \"%s\", asset_%d_data, asset_%d_size },\n", name, i, i);
+ cpp += fmt(" { \"%s\", asset_%d_data, asset_%d_size, asset_%d_etag },\n",
+ argv[3 + i * 2], i, i, i);
}
cpp += "};\n\n";