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
|
'use strict'
const assert = require('node:assert')
/**
* This takes care of revalidation requests we send to the origin. If we get
* a response indicating that what we have is cached (via a HTTP 304), we can
* continue using the cached value. Otherwise, we'll receive the new response
* here, which we then just pass on to the next handler (most likely a
* CacheHandler). Note that this assumes the proper headers were already
* included in the request to tell the origin that we want to revalidate the
* response (i.e. if-modified-since or if-none-match).
*
* @see https://www.rfc-editor.org/rfc/rfc9111.html#name-validation
*
* @implements {import('../../types/dispatcher.d.ts').default.DispatchHandler}
*/
class CacheRevalidationHandler {
#successful = false
/**
* @type {((boolean, any) => void) | null}
*/
#callback
/**
* @type {(import('../../types/dispatcher.d.ts').default.DispatchHandler)}
*/
#handler
#context
/**
* @type {boolean}
*/
#allowErrorStatusCodes
/**
* @param {(boolean) => void} callback Function to call if the cached value is valid
* @param {import('../../types/dispatcher.d.ts').default.DispatchHandlers} handler
* @param {boolean} allowErrorStatusCodes
*/
constructor (callback, handler, allowErrorStatusCodes) {
if (typeof callback !== 'function') {
throw new TypeError('callback must be a function')
}
this.#callback = callback
this.#handler = handler
this.#allowErrorStatusCodes = allowErrorStatusCodes
}
onRequestStart (_, context) {
this.#successful = false
this.#context = context
}
onRequestUpgrade (controller, statusCode, headers, socket) {
this.#handler.onRequestUpgrade?.(controller, statusCode, headers, socket)
}
onResponseStart (
controller,
statusCode,
headers,
statusMessage
) {
assert(this.#callback != null)
// https://www.rfc-editor.org/rfc/rfc9111.html#name-handling-a-validation-respo
// https://datatracker.ietf.org/doc/html/rfc5861#section-4
this.#successful = statusCode === 304 ||
(this.#allowErrorStatusCodes && statusCode >= 500 && statusCode <= 504)
this.#callback(this.#successful, this.#context)
this.#callback = null
if (this.#successful) {
return true
}
this.#handler.onRequestStart?.(controller, this.#context)
this.#handler.onResponseStart?.(
controller,
statusCode,
headers,
statusMessage
)
}
onResponseData (controller, chunk) {
if (this.#successful) {
return
}
return this.#handler.onResponseData?.(controller, chunk)
}
onResponseEnd (controller, trailers) {
if (this.#successful) {
return
}
this.#handler.onResponseEnd?.(controller, trailers)
}
onResponseError (controller, err) {
if (this.#successful) {
return
}
if (this.#callback) {
this.#callback(false)
this.#callback = null
}
if (typeof this.#handler.onResponseError === 'function') {
this.#handler.onResponseError(controller, err)
} else {
throw err
}
}
}
module.exports = CacheRevalidationHandler
|