forked from oakserver/oak
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathetag.ts
83 lines (77 loc) · 2.34 KB
/
etag.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
// Copyright 2018-2023 the oak authors. All rights reserved. MIT license.
/**
* A collection of oak specific APIs for management of ETags.
*
* @module
*/
import type { State } from "./application.ts";
import type { Context } from "./context.ts";
import { calculate, type ETagOptions } from "./deps.ts";
import type { Middleware } from "./middleware.ts";
import { BODY_TYPES, isAsyncIterable, isReader } from "./util.ts";
// re-exports to maintain backwards compatibility
export {
calculate,
type ETagOptions,
type FileInfo,
ifMatch,
ifNoneMatch,
} from "./deps.ts";
function fstat(file: Deno.FsFile): Promise<Deno.FileInfo | undefined> {
if ("fstat" in Deno) {
// deno-lint-ignore no-explicit-any
return (Deno as any).fstat(file.rid);
}
return Promise.resolve(undefined);
}
/** For a given Context, try to determine the response body entity that an ETag
* can be calculated from. */
// deno-lint-ignore no-explicit-any
export function getEntity<S extends State = Record<string, any>>(
context: Context<S>,
): Promise<string | Uint8Array | Deno.FileInfo | undefined> {
const { body } = context.response;
if (body instanceof Deno.FsFile) {
return fstat(body);
}
if (body instanceof Uint8Array) {
return Promise.resolve(body);
}
if (BODY_TYPES.includes(typeof body)) {
return Promise.resolve(String(body));
}
if (isAsyncIterable(body) || isReader(body)) {
return Promise.resolve(undefined);
}
if (typeof body === "object" && body !== null) {
try {
const bodyText = JSON.stringify(body);
return Promise.resolve(bodyText);
} catch {
// We don't really care about errors here
}
}
return Promise.resolve(undefined);
}
/**
* Create middleware that will attempt to decode the response.body into
* something that can be used to generate an `ETag` and add the `ETag` header to
* the response.
*/
// deno-lint-ignore no-explicit-any
export function factory<S extends State = Record<string, any>>(
options?: ETagOptions,
): Middleware<S> {
return async function etag(context: Context<S>, next) {
await next();
if (!context.response.headers.has("ETag")) {
const entity = await getEntity(context);
if (entity) {
const etag = await calculate(entity, options);
if (etag) {
context.response.headers.set("ETag", etag);
}
}
}
};
}