const COMPRESSIBLE = /^(text\/(?!event-stream)|application\/(javascript|json|xml|manifest\+json|ld\+json|rss\+xml|atom\+xml)|image\/svg\+xml)/i; /** Smaller bodies aren't worth the gzip overhead. */ const MIN_SIZE = 1024; /** * Gzips `res` when the client accepts gzip and the body is compressible * text. Streams (no buffering), so it is safe for large and streamed * responses. Responses that are already encoded, ranged, bodiless or * event streams pass through untouched. */ export default function compressResponse(req, res) { if (!res.body || req.method === "HEAD") return res; if (res.status < 200 || res.status === 204 || res.status === 206) { return res; } if (res.headers.has("Content-Encoding")) return res; if (!/\bgzip\b/i.test(req.headers.get("Accept-Encoding") || "")) { return res; } if (!COMPRESSIBLE.test(res.headers.get("Content-Type") || "")) return res; if (/\bno-transform\b/i.test(res.headers.get("Cache-Control") || "")) { return res; } const length = Number(res.headers.get("Content-Length")); if (length && length < MIN_SIZE) return res; // Copy: headers of fetch()/Response.redirect responses are immutable const headers = new Headers(res.headers); headers.set("Content-Encoding", "gzip"); headers.delete("Content-Length"); const vary = headers.get("Vary"); if (!vary) { headers.set("Vary", "Accept-Encoding"); } else if (!/\b(accept-encoding|\*)\b/i.test(vary)) { headers.set("Vary", `${vary}, Accept-Encoding`); } return new Response(res.body.pipeThrough(new CompressionStream("gzip")), { status: res.status, statusText: res.statusText, headers, }); }