v4: the API's JSON and the page are sent compressed — a page of the feed went out as it was, twenty kilobytes where a fifth does: only the built frontend's files were compressed, at build time, and Deno's server leaves alone a response whose length Oak has set. compressMiddleware (api/middleware/compress.ts), installed after the error middleware and around everything that sets a body, sends an object or array, and a string whose type its route set, in brotli or gzip as the request's Accept-Encoding takes, a coding refused with q=0 not being taken, from a kilobyte up, the threshold the build compresses from. Brotli is at quality 5, the frontend's files, compressed once, staying at 11. Files, streams and bytes, a response already encoded, a HEAD and one marked no-transform go out as they are, and a compressible answer says Vary: Accept-Encoding whether compressed or not.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
khannurien
2026-10-10 19:01:06 +00:00
parent 1bd5c924b0
commit 6ab398e687
4 changed files with 205 additions and 1 deletions

View File

@@ -0,0 +1,90 @@
import { BASE_URL } from "../helpers.ts";
import { assertEquals } from "@std/assert";
import { Application } from "@oak/oak";
import { brotliDecompressSync, gunzipSync } from "node:zlib";
import { compressMiddleware } from "../../middleware/compress.ts";
const BIG = { success: true, data: { items: Array(200).fill("dump title") } };
function makeApp(): Application {
const app = new Application();
app.use(compressMiddleware);
app.use((ctx) => {
const path = ctx.request.url.pathname;
if (path === "/big") ctx.response.body = BIG;
if (path === "/small") ctx.response.body = { success: true };
if (path === "/html") {
ctx.response.type = "text/html";
ctx.response.body = `<!doctype html>${"<p>gerbeur</p>".repeat(200)}`;
}
if (path === "/bytes") ctx.response.body = new Uint8Array(4096);
if (path === "/encoded") {
ctx.response.headers.set("Content-Encoding", "br");
ctx.response.type = "text/css";
ctx.response.body = "x".repeat(4096);
}
});
return app;
}
async function get(path: string, acceptEncoding?: string) {
const res = await makeApp().handle(
new Request(`${BASE_URL}${path}`, {
headers: acceptEncoding ? { "Accept-Encoding": acceptEncoding } : {},
}),
);
if (!res) throw new Error("no response");
return { res, bytes: new Uint8Array(await res.arrayBuffer()) };
}
const text = (bytes: Uint8Array) => new TextDecoder().decode(bytes);
Deno.test("compressMiddleware: JSON goes out in brotli, or gzip for a client without it", async () => {
const br = await get("/big", "gzip, deflate, br");
assertEquals(br.res.headers.get("Content-Encoding"), "br");
assertEquals(
br.res.headers.get("Content-Type"),
"application/json; charset=UTF-8",
);
assertEquals(br.res.headers.get("Vary"), "Accept-Encoding");
assertEquals(JSON.parse(text(brotliDecompressSync(br.bytes))), BIG);
const gzip = await get("/big", "gzip");
assertEquals(gzip.res.headers.get("Content-Encoding"), "gzip");
assertEquals(JSON.parse(text(gunzipSync(gzip.bytes))), BIG);
const refused = await get("/big", "br;q=0, gzip;q=0.5");
assertEquals(refused.res.headers.get("Content-Encoding"), "gzip");
});
Deno.test("compressMiddleware: a client that accepts no coding gets the JSON as it is", async () => {
for (const accept of [undefined, "identity", "br;q=0"]) {
const { res, bytes } = await get("/big", accept);
assertEquals(res.headers.get("Content-Encoding"), null);
// A cache still has to tell this answer from the compressed one.
assertEquals(res.headers.get("Vary"), "Accept-Encoding");
assertEquals(JSON.parse(text(bytes)), BIG);
}
});
Deno.test("compressMiddleware: the HTML page is compressed too", async () => {
const { res, bytes } = await get("/html", "br");
assertEquals(res.headers.get("Content-Encoding"), "br");
assertEquals(res.headers.get("Content-Type"), "text/html; charset=UTF-8");
assertEquals(text(brotliDecompressSync(bytes)).startsWith("<!doctype"), true);
});
Deno.test("compressMiddleware: short answers, bytes and encoded files are left alone", async () => {
const small = await get("/small", "br");
assertEquals(small.res.headers.get("Content-Encoding"), null);
assertEquals(small.res.headers.get("Vary"), null);
assertEquals(JSON.parse(text(small.bytes)), { success: true });
const bytes = await get("/bytes", "br");
assertEquals(bytes.res.headers.get("Content-Encoding"), null);
assertEquals(bytes.bytes.length, 4096);
const encoded = await get("/encoded", "br");
assertEquals(encoded.res.headers.get("Content-Encoding"), "br");
assertEquals(text(encoded.bytes), "x".repeat(4096));
});