91 lines
3.6 KiB
TypeScript
91 lines
3.6 KiB
TypeScript
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));
|
|
});
|