server, ui : Add support for HTTP ETags in llama-server (#23701)

* allow caching of ui elements in llama-server

* use fnv_hash

* Update tools/server/server-http.cpp

etag has to be set always

Co-authored-by: Xuan-Son Nguyen <thichthat@gmail.com>

---------

Co-authored-by: Xuan-Son Nguyen <thichthat@gmail.com>
This commit is contained in:
Markus Tavenrath 2026-05-28 20:21:24 +10:00 committed by GitHub
parent e8d2567429
commit d205df6812
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 29 additions and 4 deletions

View file

@ -318,12 +318,19 @@ bool server_http_context::init(const common_params & params) {
} 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");

View file

@ -9,6 +9,19 @@
#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);
@ -95,6 +108,7 @@ int main(int argc, char ** argv) {
" 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";
@ -110,14 +124,18 @@ int main(int argc, char ** argv) {
}
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";