Public: anyone can read this; the crew can change it.

Every compressed response says Vary: Accept-Encoding: /llms.txt and /sitemap.xml come back br-encoded without it #8340

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

torque/forge:torque/8328-every-compressed-response-says-vary-acce → main 1 commit fork

  • Merged. torque/8328-every-compressed-response-says-vary-acce was merged into main as a1df712.

Review

Sign in to review this change.
3 files changed +34 −2
src/http/compress.ts +9 −1
@@ -1,6 +1,6 @@
11 /**
22 * Content-Encoding for the bodies the forge writes itself (#2915 check 7): pages,
3 * stylesheets, scripts and JSON, as br or gzip when `Accept-Encoding` allows.
3 * stylesheets, scripts, JSON, text and XML, as br or gzip when `Accept-Encoding` allows.
44 * Both come from the runtime (`Bun.gzipSync`, `node:zlib` brotli); no
55 * dependency. A body under MIN_BYTES is left alone, and so is anything that
66 * is not on the type list: SSE, git streams, log follows and tarballs keep
@@ -25,6 +25,14 @@ const TYPES = new Set([
2525 "application/json",
2626 "application/manifest+json",
2727 "image/svg+xml",
28 // The site's words for crawlers and agents: /llms.txt, /robots.txt, the
29 // sitemap and the feeds. Left off, the edge encoded them with no Vary (#8328).
30 "text/markdown",
31 "text/plain",
32 "text/xml",
33 "application/xml",
34 "application/atom+xml",
35 "application/rss+xml",
2836 ]);
2937
3038 /** The encoding to use: br over gzip, a `q=0` or absent coding refused. */
test/compress-serve.test.ts +24
@@ -2,7 +2,9 @@ import { afterAll, beforeAll, expect, test } from "bun:test";
22 import { mkdtemp, rm } from "node:fs/promises";
33 import { tmpdir } from "node:os";
44 import { join } from "node:path";
5 import { brotliDecompressSync } from "node:zlib";
56 import { createApp, type App } from "../src/app.ts";
7 import { MIN_BYTES } from "../src/http/compress.ts";
68 import { serve, type Servers } from "../src/http/server.ts";
79 import { adminCookie } from "./helpers/admin.ts";
810 import { quietLogs } from "./helpers/globals.ts";
@@ -53,3 +55,25 @@ test("a live event stream is never encoded, even when br is asked for", async ()
5355 expect(res.headers.get("content-encoding")).toBe(null);
5456 await res.body?.cancel();
5557 });
58
59 // #8328: the edge br-encoded these with no Vary, because the forge left them alone.
60 for (const path of ["/llms.txt", "/sitemap.xml"]) {
61 test(`${path} says Vary: Accept-Encoding once, plain or br, and is encoded only when asked`, async () => {
62 const plain = await fetch(`${servers.url}${path}`, { headers: { "accept-encoding": "identity" } });
63 expect(plain.status).toBe(200);
64 expect(plain.headers.get("content-encoding")).toBe(null);
65 expect(plain.headers.get("vary")?.match(/accept-encoding/gi)).toHaveLength(1);
66 const text = await plain.text();
67
68 const br = await fetch(`${servers.url}${path}`, { headers: { "accept-encoding": "br" }, decompress: false });
69 expect(br.status).toBe(200);
70 expect(br.headers.get("vary")?.match(/accept-encoding/gi)).toHaveLength(1);
71 expect(br.headers.get("x-content-type-options")).toBe("nosniff");
72 if (new TextEncoder().encode(text).length >= MIN_BYTES) {
73 expect(br.headers.get("content-encoding")).toBe("br");
74 expect(brotliDecompressSync(new Uint8Array(await br.arrayBuffer())).toString("utf8")).toBe(text);
75 } else {
76 expect(br.headers.get("content-encoding")).toBe(null);
77 }
78 });
79 }
test/feeds.test.ts +1 −1
@@ -184,7 +184,7 @@ describe("a repository's feed", () => {
184184 const first = await fetch(url);
185185 const etag = first.headers.get("etag")!;
186186 expect(etag).toMatch(/^W\//);
187 expect(first.headers.get("vary")).toBe("authorization, cookie");
187 expect(first.headers.get("vary")).toBe("authorization, cookie, Accept-Encoding");
188188 await first.text();
189189 const again = await fetch(url, { headers: { "if-none-match": etag } });
190190 expect(again.status).toBe(304);

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs