Dev Build / cleanup (push) Canceled after 0s
Dev Build / typecheck (push) Canceled after 0s
Dev Build / build (linux-arm64, ubuntu-24.04-arm, linux-arm64) (push) Canceled after 0s
Dev Build / build (linux-x64, ubuntu-latest, linux-x64) (push) Canceled after 0s
Dev Build / build (win-x64, windows-latest, win32-x64) (push) Canceled after 0s
Dev Build / dispatch-dev-docker (push) Skipped
80 lines
3.0 KiB
Plaintext
80 lines
3.0 KiB
Plaintext
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
const require_middleware_etag_digest = require("./digest.js");
|
|
//#region src/middleware/etag/index.ts
|
|
/**
|
|
* Default headers to pass through on 304 responses. From the spec:
|
|
* > The response must not contain a body and must include the headers that
|
|
* > would have been sent in an equivalent 200 OK response: Cache-Control,
|
|
* > Content-Location, Date, ETag, Expires, and Vary.
|
|
*/
|
|
const RETAINED_304_HEADERS = [
|
|
"cache-control",
|
|
"content-location",
|
|
"date",
|
|
"etag",
|
|
"expires",
|
|
"vary"
|
|
];
|
|
const stripWeak = (tag) => tag.replace(/^W\//, "");
|
|
function etagMatches(etag, ifNoneMatch) {
|
|
return ifNoneMatch != null && ifNoneMatch.split(",").some((t) => stripWeak(t.trim()) === stripWeak(etag));
|
|
}
|
|
function initializeGenerator(generator) {
|
|
if (!generator) {
|
|
if (crypto && crypto.subtle) generator = (body) => crypto.subtle.digest({ name: "SHA-1" }, body);
|
|
}
|
|
return generator;
|
|
}
|
|
/**
|
|
* ETag Middleware for Hono.
|
|
*
|
|
* @see {@link https://hono.dev/docs/middleware/builtin/etag}
|
|
*
|
|
* @param {ETagOptions} [options] - The options for the ETag middleware.
|
|
* @param {boolean} [options.weak=false] - Define using or not using a weak validation. If true is set, then `W/` is added to the prefix of the value.
|
|
* @param {string[]} [options.retainedHeaders=RETAINED_304_HEADERS] - The headers that you want to retain in the 304 Response.
|
|
* @param {function(Uint8Array): ArrayBuffer | Promise<ArrayBuffer>} [options.generateDigest] -
|
|
* A custom digest generation function. By default, it uses 'SHA-1'
|
|
* This function is called with the response body as a `Uint8Array` and should return a hash as an `ArrayBuffer` or a Promise of one.
|
|
* @returns {MiddlewareHandler} The middleware handler function.
|
|
*
|
|
* @example
|
|
* ```ts
|
|
* const app = new Hono()
|
|
*
|
|
* app.use('/etag/*', etag())
|
|
* app.get('/etag/abc', (c) => {
|
|
* return c.text('Hono is hot')
|
|
* })
|
|
* ```
|
|
*/
|
|
const etag = (options) => {
|
|
const retainedHeaders = new Set((options?.retainedHeaders ?? RETAINED_304_HEADERS).map((header) => header.toLowerCase()));
|
|
const weak = options?.weak ?? false;
|
|
const generator = initializeGenerator(options?.generateDigest);
|
|
return async function etag(c, next) {
|
|
const ifNoneMatch = c.req.header("If-None-Match") ?? null;
|
|
await next();
|
|
if (!(c.req.method === "GET" || c.req.method === "HEAD" || c.req.method === "QUERY") || !c.res.ok) return;
|
|
const res = c.res;
|
|
let etag = res.headers.get("ETag");
|
|
if (!etag) {
|
|
if (!generator) return;
|
|
const hash = await require_middleware_etag_digest.generateDigest(res.clone().body, generator);
|
|
if (hash === null) return;
|
|
etag = weak ? `W/"${hash}"` : `"${hash}"`;
|
|
}
|
|
if (ifNoneMatch === "*" || etagMatches(etag, ifNoneMatch)) {
|
|
c.res = new Response(null, {
|
|
status: 304,
|
|
statusText: "Not Modified",
|
|
headers: { ETag: etag }
|
|
});
|
|
for (const key of Array.from(c.res.headers.keys())) if (!retainedHeaders.has(key.toLowerCase())) c.res.headers.delete(key);
|
|
} else c.res.headers.set("ETag", etag);
|
|
};
|
|
};
|
|
//#endregion
|
|
exports.RETAINED_304_HEADERS = RETAINED_304_HEADERS;
|
|
exports.etag = etag;
|