-
Notifications
You must be signed in to change notification settings - Fork 89
/
Copy pathmetaphysics-cdn.ts
129 lines (109 loc) · 3.97 KB
/
metaphysics-cdn.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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/// <reference types="@cloudflare/workers-types" />
export default {
async fetch(request, env, ctx) {
const cacheKeyGeneration = env.CACHE_KEY_GENERATION ?? "v1"
const defaultMaxAge = env.DEFAULT_MAX_AGE ?? 30
// Returns a SHA-256 digest of provided string.
async function sha256(message) {
// encode as UTF-8
const msgBuffer = await new TextEncoder().encode(message)
// hash the message
const hashBuffer = await crypto.subtle.digest("SHA-256", msgBuffer)
// convert bytes to hex string
return [...new Uint8Array(hashBuffer)]
.map((b) => b.toString(16).padStart(2, "0"))
.join("")
}
// Construct a GET request with digest appended to path, to serve as cache key.
async function getCacheKey(request) {
const body = await request.clone().text()
const hash = await sha256(body)
const cacheUrl = new URL(request.url)
cacheUrl.pathname = `${cacheUrl.pathname}_post_${cacheKeyGeneration}${hash}`
console.log(
`Using key ${cacheUrl.pathname} for ${body.substring(0, 100)}...`
)
return new Request(cacheUrl.toString(), {
headers: request.headers,
method: "GET",
})
}
function getMaxAgeFromRequest(request) {
try {
const cacheControl = request.headers.get("cache-control")
if (cacheControl?.includes("max-age=")) {
const directives = cacheControl
.toLowerCase()
.split(",")
.map(function (directive) {
return directive.trim().split("=")
})
return directives
?.find(function (arr) {
return arr[0] == "max-age"
})
?.at(1)
}
return null
} catch (e) {
return null
}
}
try {
if (
request.method.toUpperCase() === "OPTIONS" &&
request.headers.get("access-control-request-method") &&
request.headers.get("origin")?.match(/\.artsy\.net$/)
) {
// Handle preflight requests
const allowedHeaders =
request.headers.get("access-control-request-headers") ??
"cache-control,content-type,user-agent,x-access-token,x-original-session-id,x-timezone,x-user-id"
return new Response(null, {
status: 204,
headers: {
"Access-Control-Allow-Headers": allowedHeaders,
"Access-Control-Allow-Methods": "GET,POST,OPTIONS",
"Access-Control-Allow-Origin": "*",
"Access-Control-Max-Age": "600",
"Content-Length": "0",
Vary: "Origin,Access-Control-Request-Headers",
},
})
}
if (
!request.headers.get("cache-control")?.includes("no-cache") &&
!request.headers.has("x-access-token") &&
request.method.toUpperCase() === "POST"
) {
const cacheKey = await getCacheKey(request)
// @ts-ignore
const cache = caches.default
// Find the cache key in the cache
const cachedResponse = await cache.match(cacheKey)
const maxAge = getMaxAgeFromRequest(request) || defaultMaxAge
let response
if (cachedResponse) {
response = new Response(cachedResponse.body, cachedResponse)
response.headers.set("Cache-Control", `max-age=${maxAge}`)
} else {
console.log("Cache miss. Fetching from origin...") // DEBUG
const originResponse = await fetch(request)
if (
originResponse.status >= 300 ||
originResponse.headers.get("cache-control")?.includes("no-cache")
) {
return originResponse
}
response = new Response(originResponse.body, originResponse)
response.headers.set("Cache-Control", `max-age=${maxAge}`)
ctx.waitUntil(cache.put(cacheKey, response.clone()))
}
return response
}
return fetch(request)
} catch (e) {
return new Response("Error thrown " + e.message)
}
},
}