UNPKG

@fly/cdn

Version:
84 lines 9.6 kB
/** @module Middleware */ import { Image } from "@fly/v8env/lib/fly/image"; import responseCache from "@fly/v8env/lib/fly/cache/response"; /** * Automatically encodes images as webp for supported clients. * This would apply to requests with 'image/webp' in the accept header * and 'image/jpeg' or 'image/png' in the response type from origin. * * Example: * * ```typescript * import { origin } from "./src/backends"; * import { autoWebp } from "./src/middleware/auto-webp"; * * const backend = origin({ * origin: "https://fly.io/", * headers: {host: "fly.io"} * }) * * const images = autoWebp(backend); * * fly.http.respondWith(images); * ``` */ export function autoWebp(fetch) { return async function imageConversions(req, init) { if (typeof req === "string") { req = new Request(req, init); init = undefined; } // pass through if client doesn't support webp if (!webpAllowed(req)) { return fetch(req, init); } // generate a cache key const key = `webp:${req.url}`; // get response from responseCache and serve it (if available) let resp = await responseCache.get(key); if (resp) { resp.headers.set("Fly-Image-Cache", "HIT"); return resp; } resp = await fetch(req, init); // check if the req is a png or jpeg image if (!isImage(resp)) { return resp; } if (req.method === "GET") { let img = await loadImage(resp); img = img.webp({ force: true, quality: 60 }); resp.headers.set("content-type", "image/webp"); const body = await img.toBuffer(); resp = new Response(body.data, resp); resp.headers.set("content-length", body.data.byteLength.toString()); // put webp in responseCache for an hour await responseCache.set(key, resp, { tags: [req.url], ttl: 3600 }); resp.headers.set("Fly-Image-Cache", "MISS"); } return resp; }; } async function loadImage(resp) { if (!isImage(resp)) { throw new Error("Response wasn't an image"); } const raw = await resp.arrayBuffer(); const img = new Image(raw); return img; } function isImage(resp) { const contentType = resp.headers.get("Content-Type") || ""; if (contentType.includes("image/png") || contentType.includes("image/jpeg")) { return true; } return false; } export function webpAllowed(req) { const accept = req.headers.get("accept") || ""; if (accept.includes("image/webp")) { return true; } return false; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXV0by13ZWJwLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL21pZGRsZXdhcmUvYXV0by13ZWJwLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLHlCQUF5QjtBQUN6QixPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDakQsT0FBTyxhQUFhLE1BQU0sbUNBQW1DLENBQUM7QUFHOUQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBb0JHO0FBRUgsTUFBTSxVQUFVLFFBQVEsQ0FBQyxLQUFvQjtJQUMzQyxPQUFPLEtBQUssVUFBVSxnQkFBZ0IsQ0FBQyxHQUFnQixFQUFFLElBQWtCO1FBQ3pFLElBQUksT0FBTyxHQUFHLEtBQUssUUFBUSxFQUFDO1lBQzFCLEdBQUcsR0FBRyxJQUFJLE9BQU8sQ0FBQyxHQUFHLEVBQUUsSUFBSSxDQUFDLENBQUM7WUFDN0IsSUFBSSxHQUFHLFNBQVMsQ0FBQztTQUNsQjtRQUVELDhDQUE4QztRQUM5QyxJQUFHLENBQUMsV0FBVyxDQUFDLEdBQUcsQ0FBQyxFQUFDO1lBQ25CLE9BQU8sS0FBSyxDQUFDLEdBQUcsRUFBRSxJQUFJLENBQUMsQ0FBQztTQUN6QjtRQUVELHVCQUF1QjtRQUN2QixNQUFNLEdBQUcsR0FBRyxRQUFRLEdBQUcsQ0FBQyxHQUFHLEVBQUUsQ0FBQztRQUU5Qiw4REFBOEQ7UUFDOUQsSUFBSSxJQUFJLEdBQWEsTUFBTSxhQUFhLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFBO1FBQ2pELElBQUcsSUFBSSxFQUFDO1lBQ04sSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsaUJBQWlCLEVBQUUsS0FBSyxDQUFDLENBQUE7WUFDMUMsT0FBTyxJQUFJLENBQUE7U0FDWjtRQUVELElBQUksR0FBRyxNQUFNLEtBQUssQ0FBQyxHQUFHLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFFOUIsMENBQTBDO1FBQzFDLElBQUcsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLEVBQUU7WUFDakIsT0FBTyxJQUFJLENBQUE7U0FDWjtRQUVELElBQUksR0FBRyxDQUFDLE1BQU0sS0FBSyxLQUFLLEVBQUM7WUFDdkIsSUFBSSxHQUFHLEdBQUcsTUFBTSxTQUFTLENBQUMsSUFBSSxDQUFDLENBQUE7WUFDL0IsR0FBRyxHQUFHLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFDO1lBQzdDLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLGNBQWMsRUFBRSxZQUFZLENBQUMsQ0FBQztZQUUvQyxNQUFNLElBQUksR0FBRyxNQUFNLEdBQUcsQ0FBQyxRQUFRLEVBQUUsQ0FBQTtZQUNqQyxJQUFJLEdBQUcsSUFBSSxRQUFRLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsQ0FBQTtZQUNwQyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxnQkFBZ0IsRUFBRSxJQUFJLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQUFBO1lBRW5FLHdDQUF3QztZQUN4QyxNQUFNLGFBQWEsQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLElBQUksRUFBRSxFQUFFLElBQUksRUFBRSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsRUFBRSxHQUFHLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQTtZQUNsRSxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxpQkFBaUIsRUFBRSxNQUFNLENBQUMsQ0FBQTtTQUM1QztRQUNELE9BQU8sSUFBSSxDQUFBO0lBQ2IsQ0FBQyxDQUFBO0FBQ0gsQ0FBQztBQUVELEtBQUssVUFBVSxTQUFTLENBQUMsSUFBYztJQUNyQyxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxFQUFFO1FBQ2xCLE1BQU0sSUFBSSxLQUFLLENBQUMsMEJBQTBCLENBQUMsQ0FBQTtLQUM1QztJQUNELE1BQU0sR0FBRyxHQUFHLE1BQU0sSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFBO0lBQ3BDLE1BQU0sR0FBRyxHQUFHLElBQUksS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFBO0lBRTFCLE9BQU8sR0FBRyxDQUFBO0FBQ1osQ0FBQztBQUVELFNBQVMsT0FBTyxDQUFDLElBQWM7SUFDN0IsTUFBTSxXQUFXLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsY0FBYyxDQUFDLElBQUksRUFBRSxDQUFBO0lBQzFELElBQUksV0FBVyxDQUFDLFFBQVEsQ0FBQyxXQUFXLENBQUMsSUFBSSxXQUFXLENBQUMsUUFBUSxDQUFDLFlBQVksQ0FBQyxFQUFFO1FBQzNFLE9BQU8sSUFBSSxDQUFBO0tBQ1o7SUFDRCxPQUFPLEtBQUssQ0FBQTtBQUNkLENBQUM7QUFFRCxNQUFNLFVBQVUsV0FBVyxDQUFDLEdBQVk7SUFDdEMsTUFBTSxNQUFNLEdBQUcsR0FBRyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDLElBQUksRUFBRSxDQUFBO0lBQzlDLElBQ0UsTUFBTSxDQUFDLFFBQVEsQ0FBQyxZQUFZLENBQUMsRUFDOUI7UUFDQyxPQUFPLElBQUksQ0FBQTtLQUNaO0lBQ0QsT0FBTyxLQUFLLENBQUE7QUFDZCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqIEBtb2R1bGUgTWlkZGxld2FyZSAqL1xuaW1wb3J0IHsgSW1hZ2UgfSBmcm9tIFwiQGZseS92OGVudi9saWIvZmx5L2ltYWdlXCI7XG5pbXBvcnQgcmVzcG9uc2VDYWNoZSBmcm9tIFwiQGZseS92OGVudi9saWIvZmx5L2NhY2hlL3Jlc3BvbnNlXCI7XG5pbXBvcnQgeyBGZXRjaEZ1bmN0aW9uIH0gZnJvbSBcIi4uL2ZldGNoXCI7XG5cbi8qKlxuICogQXV0b21hdGljYWxseSBlbmNvZGVzIGltYWdlcyBhcyB3ZWJwIGZvciBzdXBwb3J0ZWQgY2xpZW50cy5cbiAqIFRoaXMgd291bGQgYXBwbHkgdG8gcmVxdWVzdHMgd2l0aCAnaW1hZ2Uvd2VicCcgaW4gdGhlIGFjY2VwdCBoZWFkZXIgXG4gKiBhbmQgJ2ltYWdlL2pwZWcnIG9yICdpbWFnZS9wbmcnIGluIHRoZSByZXNwb25zZSB0eXBlIGZyb20gb3JpZ2luLlxuICogXG4gKiBFeGFtcGxlOiBcbiAqIFxuICogYGBgdHlwZXNjcmlwdFxuICogaW1wb3J0IHsgb3JpZ2luIH0gZnJvbSBcIi4vc3JjL2JhY2tlbmRzXCI7XG4gKiBpbXBvcnQgeyBhdXRvV2VicCB9IGZyb20gXCIuL3NyYy9taWRkbGV3YXJlL2F1dG8td2VicFwiO1xuICogXG4gKiBjb25zdCBiYWNrZW5kID0gb3JpZ2luKHtcbiAqICBvcmlnaW46IFwiaHR0cHM6Ly9mbHkuaW8vXCIsXG4gKiAgaGVhZGVyczoge2hvc3Q6IFwiZmx5LmlvXCJ9XG4gKiB9KVxuICogXG4gKiBjb25zdCBpbWFnZXMgPSBhdXRvV2VicChiYWNrZW5kKTtcbiAqIFxuICogZmx5Lmh0dHAucmVzcG9uZFdpdGgoaW1hZ2VzKTtcbiAqIGBgYFxuICovXG5cbmV4cG9ydCBmdW5jdGlvbiBhdXRvV2VicChmZXRjaDogRmV0Y2hGdW5jdGlvbik6IEZldGNoRnVuY3Rpb24ge1xuICByZXR1cm4gYXN5bmMgZnVuY3Rpb24gaW1hZ2VDb252ZXJzaW9ucyhyZXE6IFJlcXVlc3RJbmZvLCBpbml0PzogUmVxdWVzdEluaXQpIHtcbiAgICBpZiAodHlwZW9mIHJlcSA9PT0gXCJzdHJpbmdcIil7XG4gICAgICByZXEgPSBuZXcgUmVxdWVzdChyZXEsIGluaXQpO1xuICAgICAgaW5pdCA9IHVuZGVmaW5lZDtcbiAgICB9XG5cbiAgICAvLyBwYXNzIHRocm91Z2ggaWYgY2xpZW50IGRvZXNuJ3Qgc3VwcG9ydCB3ZWJwXG4gICAgaWYoIXdlYnBBbGxvd2VkKHJlcSkpe1xuICAgICAgcmV0dXJuIGZldGNoKHJlcSwgaW5pdCk7XG4gICAgfVxuXG4gICAgLy8gZ2VuZXJhdGUgYSBjYWNoZSBrZXlcbiAgICBjb25zdCBrZXkgPSBgd2VicDoke3JlcS51cmx9YDtcblxuICAgIC8vIGdldCByZXNwb25zZSBmcm9tIHJlc3BvbnNlQ2FjaGUgYW5kIHNlcnZlIGl0IChpZiBhdmFpbGFibGUpXG4gICAgbGV0IHJlc3A6IFJlc3BvbnNlID0gYXdhaXQgcmVzcG9uc2VDYWNoZS5nZXQoa2V5KVxuICAgIGlmKHJlc3Ape1xuICAgICAgcmVzcC5oZWFkZXJzLnNldChcIkZseS1JbWFnZS1DYWNoZVwiLCBcIkhJVFwiKVxuICAgICAgcmV0dXJuIHJlc3BcbiAgICB9XG5cbiAgICByZXNwID0gYXdhaXQgZmV0Y2gocmVxLCBpbml0KTtcblxuICAgIC8vIGNoZWNrIGlmIHRoZSByZXEgaXMgYSBwbmcgb3IganBlZyBpbWFnZVxuICAgIGlmKCFpc0ltYWdlKHJlc3ApKSB7XG4gICAgICByZXR1cm4gcmVzcFxuICAgIH1cblxuICAgIGlmIChyZXEubWV0aG9kID09PSBcIkdFVFwiKXtcbiAgICAgIGxldCBpbWcgPSBhd2FpdCBsb2FkSW1hZ2UocmVzcClcbiAgICAgIGltZyA9IGltZy53ZWJwKHsgZm9yY2U6IHRydWUsIHF1YWxpdHk6IDYwIH0pO1xuICAgICAgcmVzcC5oZWFkZXJzLnNldChcImNvbnRlbnQtdHlwZVwiLCBcImltYWdlL3dlYnBcIik7XG5cbiAgICAgIGNvbnN0IGJvZHkgPSBhd2FpdCBpbWcudG9CdWZmZXIoKVxuICAgICAgcmVzcCA9IG5ldyBSZXNwb25zZShib2R5LmRhdGEsIHJlc3ApXG4gICAgICByZXNwLmhlYWRlcnMuc2V0KFwiY29udGVudC1sZW5ndGhcIiwgYm9keS5kYXRhLmJ5dGVMZW5ndGgudG9TdHJpbmcoKSlcblxuICAgICAgLy8gcHV0IHdlYnAgaW4gcmVzcG9uc2VDYWNoZSBmb3IgYW4gaG91clxuICAgICAgYXdhaXQgcmVzcG9uc2VDYWNoZS5zZXQoa2V5LCByZXNwLCB7IHRhZ3M6IFtyZXEudXJsXSwgdHRsOiAzNjAwIH0pXG4gICAgICByZXNwLmhlYWRlcnMuc2V0KFwiRmx5LUltYWdlLUNhY2hlXCIsIFwiTUlTU1wiKVxuICAgIH1cbiAgICByZXR1cm4gcmVzcFxuICB9XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGxvYWRJbWFnZShyZXNwOiBSZXNwb25zZSk6IFByb21pc2U8SW1hZ2U+IHtcbiAgaWYgKCFpc0ltYWdlKHJlc3ApKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFwiUmVzcG9uc2Ugd2Fzbid0IGFuIGltYWdlXCIpXG4gIH1cbiAgY29uc3QgcmF3ID0gYXdhaXQgcmVzcC5hcnJheUJ1ZmZlcigpXG4gIGNvbnN0IGltZyA9IG5ldyBJbWFnZShyYXcpXG5cbiAgcmV0dXJuIGltZ1xufVxuXG5mdW5jdGlvbiBpc0ltYWdlKHJlc3A6IFJlc3BvbnNlKTogYm9vbGVhbntcbiAgY29uc3QgY29udGVudFR5cGUgPSByZXNwLmhlYWRlcnMuZ2V0KFwiQ29udGVudC1UeXBlXCIpIHx8IFwiXCJcbiAgaWYgKGNvbnRlbnRUeXBlLmluY2x1ZGVzKFwiaW1hZ2UvcG5nXCIpIHx8IGNvbnRlbnRUeXBlLmluY2x1ZGVzKFwiaW1hZ2UvanBlZ1wiKSkge1xuICAgIHJldHVybiB0cnVlXG4gIH1cbiAgcmV0dXJuIGZhbHNlXG59XG5cbmV4cG9ydCBmdW5jdGlvbiB3ZWJwQWxsb3dlZChyZXE6IFJlcXVlc3Qpe1xuICBjb25zdCBhY2NlcHQgPSByZXEuaGVhZGVycy5nZXQoXCJhY2NlcHRcIikgfHwgXCJcIlxuICBpZihcbiAgICBhY2NlcHQuaW5jbHVkZXMoXCJpbWFnZS93ZWJwXCIpXG4gICl7XG4gICAgcmV0dXJuIHRydWVcbiAgfVxuICByZXR1cm4gZmFsc2Vcbn0iXX0=