diff --git a/tools/server/CMakeLists.txt b/tools/server/CMakeLists.txt index 17109fddbd307..b27a5ee8b2c07 100644 --- a/tools/server/CMakeLists.txt +++ b/tools/server/CMakeLists.txt @@ -15,7 +15,7 @@ set(TARGET_SRCS httplib.h ) set(PUBLIC_ASSETS - index.html.gz + index.html.br loading.html ) diff --git a/tools/server/README.md b/tools/server/README.md index 0b84966ae86d7..7289bdbcf0a77 100644 --- a/tools/server/README.md +++ b/tools/server/README.md @@ -270,11 +270,11 @@ npm i # to run the dev server npm run dev -# to build the public/index.html.gz +# to build the public/index.html.br npm run build ``` -After `public/index.html.gz` has been generated we need to generate the c++ -headers (like build/tools/server/index.html.gz.hpp) that will be included +After `public/index.html.br` has been generated we need to generate the c++ +headers (like build/tools/server/index.html.br.hpp) that will be included by server.cpp. This is done by building `llama-server` as described in the [build](#build) section above. diff --git a/tools/server/public/index.html.br b/tools/server/public/index.html.br new file mode 100644 index 0000000000000..e42a3ab99bac5 Binary files /dev/null and b/tools/server/public/index.html.br differ diff --git a/tools/server/public/index.html.gz b/tools/server/public/index.html.gz deleted file mode 100644 index 02fb00339ec8d..0000000000000 Binary files a/tools/server/public/index.html.gz and /dev/null differ diff --git a/tools/server/server.cpp b/tools/server/server.cpp index 348588a2cb224..b3f0e1ee0c351 100644 --- a/tools/server/server.cpp +++ b/tools/server/server.cpp @@ -16,7 +16,7 @@ #define MIMETYPE_JSON "application/json; charset=utf-8" // auto generated files (see README.md for details) -#include "index.html.gz.hpp" +#include "index.html.br.hpp" #include "loading.html.hpp" #include @@ -4701,14 +4701,14 @@ int main(int argc, char ** argv) { } else { // using embedded static index.html svr->Get("/", [](const httplib::Request & req, httplib::Response & res) { - if (req.get_header_value("Accept-Encoding").find("gzip") == std::string::npos) { - res.set_content("Error: gzip is not supported by this browser", "text/plain"); + if (req.get_header_value("Accept-Encoding").find("br") == std::string::npos) { + res.set_content("Error: brotli is not supported by this browser", "text/plain"); } else { - res.set_header("Content-Encoding", "gzip"); + res.set_header("Content-Encoding", "br"); // COEP and COOP headers, required by pyodide (python interpreter) res.set_header("Cross-Origin-Embedder-Policy", "require-corp"); res.set_header("Cross-Origin-Opener-Policy", "same-origin"); - res.set_content(reinterpret_cast(index_html_gz), index_html_gz_len, "text/html; charset=utf-8"); + res.set_content(reinterpret_cast(index_html_br), index_html_br_len, "text/html; charset=utf-8"); } return false; }); diff --git a/tools/server/webui/package-lock.json b/tools/server/webui/package-lock.json index a05cbcfe5c392..79088c3545b15 100644 --- a/tools/server/webui/package-lock.json +++ b/tools/server/webui/package-lock.json @@ -45,7 +45,6 @@ "eslint": "^9.17.0", "eslint-plugin-react-hooks": "^5.0.0", "eslint-plugin-react-refresh": "^0.4.16", - "fflate": "^0.8.2", "globals": "^15.14.0", "prettier": "^3.4.2", "sass-embedded": "^1.83.4", @@ -3006,13 +3005,6 @@ "reusify": "^1.0.4" } }, - "node_modules/fflate": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", - "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", - "dev": true, - "license": "MIT" - }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", diff --git a/tools/server/webui/package.json b/tools/server/webui/package.json index 8076840324d49..28bf4c39f462e 100644 --- a/tools/server/webui/package.json +++ b/tools/server/webui/package.json @@ -48,7 +48,6 @@ "eslint": "^9.17.0", "eslint-plugin-react-hooks": "^5.0.0", "eslint-plugin-react-refresh": "^0.4.16", - "fflate": "^0.8.2", "globals": "^15.14.0", "prettier": "^3.4.2", "sass-embedded": "^1.83.4", diff --git a/tools/server/webui/vite.config.ts b/tools/server/webui/vite.config.ts index 910c286e0c40c..358a91f4b7bed 100644 --- a/tools/server/webui/vite.config.ts +++ b/tools/server/webui/vite.config.ts @@ -3,7 +3,7 @@ import react from '@vitejs/plugin-react'; import { viteSingleFile } from 'vite-plugin-singlefile'; import path from 'node:path'; import fs from 'node:fs'; -import * as fflate from 'fflate'; +import zlib from 'node:zlib'; /* eslint-disable */ @@ -36,19 +36,15 @@ const BUILD_PLUGINS = [ let content = GUIDE_FOR_FRONTEND + '\n' + fs.readFileSync(outputIndexHtml, 'utf-8'); content = content.replace(/\r/g, ''); // remove windows-style line endings - const compressed = fflate.gzipSync(Buffer.from(content, 'utf-8'), { - level: 9, + const compressed = zlib.brotliCompressSync(Buffer.from(content), { + params: { + [zlib.constants.BROTLI_PARAM_MODE]: zlib.constants.BROTLI_MODE_TEXT, + [zlib.constants.BROTLI_PARAM_QUALITY]: + zlib.constants.BROTLI_MAX_QUALITY, + [zlib.constants.BROTLI_PARAM_SIZE_HINT]: content.length, + }, }); - // because gzip header contains machine-specific info, we must remove these data from the header - // timestamp - compressed[0x4] = 0; - compressed[0x5] = 0; - compressed[0x6] = 0; - compressed[0x7] = 0; - // OS - compressed[0x9] = 0; - if (compressed.byteLength > MAX_BUNDLE_SIZE) { throw new Error( `Bundle size is too large (${Math.ceil(compressed.byteLength / 1024)} KB).\n` + @@ -58,7 +54,7 @@ const BUILD_PLUGINS = [ const targetOutputFile = path.join( config.build.outDir, - '../../public/index.html.gz' + '../../public/index.html.br' ); fs.writeFileSync(targetOutputFile, compressed); },