Skip to content

Commit 3596e82

Browse files
authored
fix: cache (nodejs#3804)
* fix: cache * WIP * WIP * WIP * WIP * WIP * WIP * WIP * WIP * WIP * WIP * WIP * WIP * WIP * fixuP * WIP * WIP
1 parent 45c3259 commit 3596e82

File tree

4 files changed

+188
-128
lines changed

4 files changed

+188
-128
lines changed

lib/cache/memory-cache-store.js

+1
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ class MemoryStoreReadableStream extends Readable {
294294
* @type {MemoryStoreValue}
295295
*/
296296
#value
297+
297298
/**
298299
* @type {Buffer[]}
299300
*/

lib/handler/cache-handler.js

+51-38
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ const {
77
parseVaryHeader
88
} = require('../util/cache')
99

10+
function noop () {}
11+
1012
/**
1113
* Writes a response to a CacheStore and then passes it on to the next handler
1214
*/
@@ -46,6 +48,17 @@ class CacheHandler extends DecoratorHandler {
4648
this.#handler = handler
4749
}
4850

51+
onConnect (abort) {
52+
if (this.#writeStream) {
53+
this.#writeStream.destroy()
54+
this.#writeStream = undefined
55+
}
56+
57+
if (typeof this.#handler.onConnect === 'function') {
58+
this.#handler.onConnect(abort)
59+
}
60+
}
61+
4962
/**
5063
* @see {DispatchHandlers.onHeaders}
5164
*
@@ -61,49 +74,46 @@ class CacheHandler extends DecoratorHandler {
6174
resume,
6275
statusMessage
6376
) {
64-
const downstreamOnHeaders = () => this.#handler.onHeaders(
65-
statusCode,
66-
rawHeaders,
67-
resume,
68-
statusMessage
69-
)
77+
const downstreamOnHeaders = () => {
78+
if (typeof this.#handler.onHeaders === 'function') {
79+
return this.#handler.onHeaders(
80+
statusCode,
81+
rawHeaders,
82+
resume,
83+
statusMessage
84+
)
85+
} else {
86+
return true
87+
}
88+
}
7089

7190
if (
7291
!util.safeHTTPMethods.includes(this.#requestOptions.method) &&
7392
statusCode >= 200 &&
7493
statusCode <= 399
7594
) {
76-
// https://www.rfc-editor.org/rfc/rfc9111.html#name-invalidating-stored-respons
77-
// Try/catch for if it's synchronous
95+
// https://www.rfc-editor.org/rfc/rfc9111.html#name-invalidating-stored-response
7896
try {
79-
const result = this.#store.deleteByOrigin(this.#requestOptions.origin)
80-
if (
81-
result &&
82-
typeof result.catch === 'function' &&
83-
typeof this.#handler.onError === 'function'
84-
) {
85-
// Fail silently
86-
result.catch(_ => {})
87-
}
88-
} catch (err) {
97+
this.#store.deleteByOrigin(this.#requestOptions.origin.toString())?.catch?.(noop)
98+
} catch {
8999
// Fail silently
90100
}
91-
92101
return downstreamOnHeaders()
93102
}
94103

95104
const headers = util.parseHeaders(rawHeaders)
96105

97106
const cacheControlHeader = headers['cache-control']
98-
const contentLengthHeader = headers['content-length']
99-
100-
if (!cacheControlHeader || !contentLengthHeader || this.#store.isFull) {
101-
// Don't have the headers we need, can't cache
107+
if (!cacheControlHeader || typeof cacheControlHeader !== 'string') {
108+
// Don't have cache-control, can't cache.
102109
return downstreamOnHeaders()
103110
}
104111

105-
const contentLength = Number(contentLengthHeader)
112+
const contentLengthHeader = headers['content-length']
113+
const contentLength = contentLengthHeader ? Number(contentLengthHeader) : null
106114
if (!Number.isInteger(contentLength)) {
115+
// Don't know the final size, don't cache.
116+
// TODO (fix): Why not cache?
107117
return downstreamOnHeaders()
108118
}
109119

@@ -137,18 +147,24 @@ class CacheHandler extends DecoratorHandler {
137147
})
138148

139149
if (this.#writeStream) {
140-
this.#writeStream.on('drain', resume)
141-
this.#writeStream.on('error', () => {
142-
this.#writeStream = undefined
143-
resume()
144-
})
150+
const handler = this
151+
this.#writeStream
152+
.on('drain', resume)
153+
.on('error', function () {
154+
// TODO (fix): Make error somehow observable?
155+
})
156+
.on('close', function () {
157+
if (handler.#writeStream === this) {
158+
handler.#writeStream = undefined
159+
}
160+
161+
// TODO (fix): Should we resume even if was paused downstream?
162+
resume()
163+
})
145164
}
146165
}
147166

148-
if (typeof this.#handler.onHeaders === 'function') {
149-
return downstreamOnHeaders()
150-
}
151-
return false
167+
return downstreamOnHeaders()
152168
}
153169

154170
/**
@@ -178,10 +194,7 @@ class CacheHandler extends DecoratorHandler {
178194
*/
179195
onComplete (rawTrailers) {
180196
if (this.#writeStream) {
181-
if (rawTrailers) {
182-
this.#writeStream.rawTrailers = rawTrailers
183-
}
184-
197+
this.#writeStream.rawTrailers = rawTrailers ?? []
185198
this.#writeStream.end()
186199
}
187200

@@ -332,7 +345,7 @@ function stripNecessaryHeaders (rawHeaders, parsedHeaders, cacheControlDirective
332345
for (let i = 0; i < headerNames.length; i++) {
333346
const header = headerNames[i]
334347

335-
if (headersToRemove.indexOf(header) !== -1) {
348+
if (headersToRemove.includes(header)) {
336349
// We have a at least one header we want to remove
337350
if (!strippedHeaders) {
338351
// This is the first header we want to remove, let's create the object

lib/handler/cache-revalidation-handler.js

+44-13
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
'use strict'
22

3+
const assert = require('node:assert')
34
const DecoratorHandler = require('../handler/decorator-handler')
45

56
/**
@@ -19,29 +20,35 @@ const DecoratorHandler = require('../handler/decorator-handler')
1920
class CacheRevalidationHandler extends DecoratorHandler {
2021
#successful = false
2122
/**
22-
* @type {(() => void)}
23+
* @type {((boolean) => void) | null}
2324
*/
24-
#successCallback
25+
#callback
2526
/**
2627
* @type {(import('../../types/dispatcher.d.ts').default.DispatchHandlers)}
2728
*/
2829
#handler
2930

31+
#abort
3032
/**
31-
* @param {() => void} successCallback Function to call if the cached value is valid
33+
* @param {(boolean) => void} callback Function to call if the cached value is valid
3234
* @param {import('../../types/dispatcher.d.ts').default.DispatchHandlers} handler
3335
*/
34-
constructor (successCallback, handler) {
35-
if (typeof successCallback !== 'function') {
36-
throw new TypeError('successCallback must be a function')
36+
constructor (callback, handler) {
37+
if (typeof callback !== 'function') {
38+
throw new TypeError('callback must be a function')
3739
}
3840

3941
super(handler)
4042

41-
this.#successCallback = successCallback
43+
this.#callback = callback
4244
this.#handler = handler
4345
}
4446

47+
onConnect (abort) {
48+
this.#successful = false
49+
this.#abort = abort
50+
}
51+
4552
/**
4653
* @see {DispatchHandlers.onHeaders}
4754
*
@@ -57,13 +64,21 @@ class CacheRevalidationHandler extends DecoratorHandler {
5764
resume,
5865
statusMessage
5966
) {
67+
assert(this.#callback != null)
68+
6069
// https://www.rfc-editor.org/rfc/rfc9111.html#name-handling-a-validation-respo
61-
if (statusCode === 304) {
62-
this.#successful = true
63-
this.#successCallback()
70+
this.#successful = statusCode === 304
71+
this.#callback(this.#successful)
72+
this.#callback = null
73+
74+
if (this.#successful) {
6475
return true
6576
}
6677

78+
if (typeof this.#handler.onConnect === 'function') {
79+
this.#handler.onConnect(this.#abort)
80+
}
81+
6782
if (typeof this.#handler.onHeaders === 'function') {
6883
return this.#handler.onHeaders(
6984
statusCode,
@@ -72,7 +87,8 @@ class CacheRevalidationHandler extends DecoratorHandler {
7287
statusMessage
7388
)
7489
}
75-
return false
90+
91+
return true
7692
}
7793

7894
/**
@@ -90,7 +106,7 @@ class CacheRevalidationHandler extends DecoratorHandler {
90106
return this.#handler.onData(chunk)
91107
}
92108

93-
return false
109+
return true
94110
}
95111

96112
/**
@@ -99,7 +115,11 @@ class CacheRevalidationHandler extends DecoratorHandler {
99115
* @param {string[] | null} rawTrailers
100116
*/
101117
onComplete (rawTrailers) {
102-
if (!this.#successful && typeof this.#handler.onComplete === 'function') {
118+
if (this.#successful) {
119+
return
120+
}
121+
122+
if (typeof this.#handler.onComplete === 'function') {
103123
this.#handler.onComplete(rawTrailers)
104124
}
105125
}
@@ -110,8 +130,19 @@ class CacheRevalidationHandler extends DecoratorHandler {
110130
* @param {Error} err
111131
*/
112132
onError (err) {
133+
if (this.#successful) {
134+
return
135+
}
136+
137+
if (this.#callback) {
138+
this.#callback(false)
139+
this.#callback = null
140+
}
141+
113142
if (typeof this.#handler.onError === 'function') {
114143
this.#handler.onError(err)
144+
} else {
145+
throw err
115146
}
116147
}
117148
}

0 commit comments

Comments
 (0)