Skip to content

Commit bb502d9

Browse files
committed
tmp
Signed-off-by: flakey5 <[email protected]>
1 parent 9f467e7 commit bb502d9

File tree

8 files changed

+93
-57
lines changed

8 files changed

+93
-57
lines changed

docs/docs/api/CacheStore.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,14 @@ The store must implement the following functions:
2525
Optional. This tells the cache interceptor if the store is full or not. If this is true,
2626
the cache interceptor will not attempt to cache the response.
2727

28-
### Function: `getValueByKey`
28+
### Function: `get`
2929

3030
Parameters:
3131

3232
* **req** `Dispatcher.RequestOptions` - Incoming request
3333

34-
Returns: `GetValueByKeyResult | Promise<GetValueByKeyResult | undefined> | undefined` - If the request is cached, a readable for the body is returned. Otherwise, `undefined` is returned.
34+
Returns: `GetResult | Promise<GetResult | undefined> | undefined` - If the request is cached, the cached response is returned. If the request's method is anything other than HEAD, the response is also returned.
35+
If the request isn't cached, `undefined` is returned.
3536

3637
Response properties:
3738

lib/cache/memory-cache-store.js

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,9 @@ class MemoryCacheStore {
6363

6464
/**
6565
* @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} key
66-
* @returns {import('../../types/cache-interceptor.d.ts').default.GetValueByKeyResult | undefined}
66+
* @returns {import('../../types/cache-interceptor.d.ts').default.GetResult | undefined}
6767
*/
68-
getValueByKey (key) {
68+
get (key) {
6969
if (typeof key !== 'object') {
7070
throw new TypeError(`expected key to be object, got ${typeof key}`)
7171
}
@@ -121,9 +121,10 @@ class MemoryCacheStore {
121121
const values = this.#getValuesForRequest(key, true)
122122

123123
/**
124-
* @type {number}
124+
* @type {(MemoryStoreValue & { index: number }) | undefined}
125125
*/
126126
let value = this.#findValue(key, values)
127+
let valueIndex = value?.index
127128
if (!value) {
128129
// The value doesn't already exist, meaning we haven't cached this
129130
// response before. Let's assign it a value and insert it into our data
@@ -150,9 +151,11 @@ class MemoryCacheStore {
150151
// Our value is either the only response for this path or our deleteAt
151152
// time is sooner than all the other responses
152153
values.push(value)
154+
valueIndex = values.length - 1
153155
} else if (opts.deleteAt >= values[0].deleteAt) {
154156
// Our deleteAt is later than everyone elses
155157
values.unshift(value)
158+
valueIndex = 0
156159
} else {
157160
// We're neither in the front or the end, let's just binary search to
158161
// find our stop we need to be in
@@ -168,6 +171,7 @@ class MemoryCacheStore {
168171
const middleValue = values[middleIndex]
169172
if (opts.deleteAt === middleIndex) {
170173
values.splice(middleIndex, 0, value)
174+
valueIndex = middleIndex
171175
break
172176
} else if (opts.deleteAt > middleValue.opts.deleteAt) {
173177
endIndex = middleIndex
@@ -215,7 +219,7 @@ class MemoryCacheStore {
215219
if (currentSize >= maxEntrySize) {
216220
body = null
217221
this.end()
218-
// TODO remove element from values
222+
shiftAtIndex(values, valueIndex)
219223
return callback()
220224
}
221225

@@ -276,7 +280,7 @@ class MemoryCacheStore {
276280
* to respond with.
277281
* @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} req
278282
* @param {MemoryStoreValue[]} values
279-
* @returns {MemoryStoreValue | undefined}
283+
* @returns {(MemoryStoreValue & { index: number }) | undefined}
280284
*/
281285
#findValue (req, values) {
282286
/**
@@ -311,7 +315,10 @@ class MemoryCacheStore {
311315
}
312316

313317
if (matches) {
314-
value = current
318+
value = {
319+
current,
320+
index: i
321+
}
315322
break
316323
}
317324
}
@@ -320,4 +327,16 @@ class MemoryCacheStore {
320327
}
321328
}
322329

330+
/**
331+
* @param {any[]} array Array to modify
332+
* @param {number} idx Index to delete
333+
*/
334+
function shiftAtIndex (array, idx) {
335+
for (let i = idx + 1; idx < array.length; i++) {
336+
array[i - 1] = array[i]
337+
}
338+
339+
array.length--
340+
}
341+
323342
module.exports = MemoryCacheStore

lib/handler/cache-handler.js

Lines changed: 42 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -135,31 +135,43 @@ class CacheHandler extends DecoratorHandler {
135135
cacheControlDirectives
136136
)
137137

138-
this.#writeStream = this.#store.createWriteStream(this.#cacheKey, {
139-
statusCode,
140-
statusMessage,
141-
rawHeaders: strippedHeaders,
142-
vary: varyDirectives,
143-
cachedAt: now,
144-
staleAt,
145-
deleteAt
146-
})
147-
148-
if (this.#writeStream) {
149-
const handler = this
150-
this.#writeStream
151-
.on('drain', resume)
152-
.on('error', function () {
138+
if (this.#cacheKey.method === 'HEAD') {
139+
this.#store.createWriteStream(this.#cacheKey, {
140+
statusCode,
141+
statusMessage,
142+
rawHeaders: strippedHeaders,
143+
vary: varyDirectives,
144+
cachedAt: now,
145+
staleAt,
146+
deleteAt
147+
})
148+
} else {
149+
this.#writeStream = this.#store.createWriteStream(this.#cacheKey, {
150+
statusCode,
151+
statusMessage,
152+
rawHeaders: strippedHeaders,
153+
vary: varyDirectives,
154+
cachedAt: now,
155+
staleAt,
156+
deleteAt
157+
})
158+
159+
if (this.#writeStream) {
160+
const handler = this
161+
this.#writeStream
162+
.on('drain', resume)
163+
.on('error', function () {
153164
// TODO (fix): Make error somehow observable?
154-
})
155-
.on('close', function () {
156-
if (handler.#writeStream === this) {
157-
handler.#writeStream = undefined
158-
}
159-
160-
// TODO (fix): Should we resume even if was paused downstream?
161-
resume()
162-
})
165+
})
166+
.on('close', function () {
167+
if (handler.#writeStream === this) {
168+
handler.#writeStream = undefined
169+
}
170+
171+
// TODO (fix): Should we resume even if was paused downstream?
172+
resume()
173+
})
174+
}
163175
}
164176
}
165177

@@ -175,7 +187,7 @@ class CacheHandler extends DecoratorHandler {
175187
onData (chunk) {
176188
let paused = false
177189

178-
if (this.#writeStream && this.#cacheKey.method !== 'HEAD') {
190+
if (this.#writeStream) {
179191
paused ||= this.#writeStream.write(chunk) === false
180192
}
181193

@@ -234,7 +246,6 @@ function canCacheResponse (statusCode, headers, cacheControlDirectives) {
234246
}
235247

236248
if (
237-
!cacheControlDirectives.public ||
238249
cacheControlDirectives.private === true ||
239250
cacheControlDirectives['no-cache'] === true ||
240251
cacheControlDirectives['no-store']
@@ -249,7 +260,7 @@ function canCacheResponse (statusCode, headers, cacheControlDirectives) {
249260

250261
// https://www.rfc-editor.org/rfc/rfc9111.html#name-storing-responses-to-authen
251262
if (headers.authorization) {
252-
if (typeof headers.authorization !== 'string') {
263+
if (!cacheControlDirectives.public || typeof headers.authorization !== 'string') {
253264
return false
254265
}
255266

@@ -299,7 +310,10 @@ function determineStaleAt (now, headers, cacheControlDirectives) {
299310

300311
if (headers.expire && typeof headers.expire === 'string') {
301312
// https://www.rfc-editor.org/rfc/rfc9111.html#section-5.3
302-
return now + (Date.now() - new Date(headers.expire).getTime())
313+
const expiresDate = new Date(headers.expire)
314+
if (expiresDate instanceof Date && !isNaN(expiresDate)) {
315+
return now + (Date.now() - expiresDate.getTime())
316+
}
303317
}
304318

305319
return undefined

lib/interceptor/cache.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ module.exports = (opts = {}) => {
5555
// TODO (perf): For small entries support returning a Buffer instead of a stream.
5656
// Maybe store should return { staleAt, headers, body, etc... } instead of a stream + stream.value?
5757
// Where body can be a Buffer, string, stream or blob?
58-
const result = store.getValueByKey(cacheKey)
58+
const result = store.get(cacheKey)
5959
if (!result) {
6060
// Request isn't cached
6161
return dispatch(opts, new CacheHandler(globalOpts, cacheKey, handler))
@@ -114,6 +114,8 @@ module.exports = (opts = {}) => {
114114
if (typeof handler.onComplete === 'function') {
115115
handler.onComplete([])
116116
}
117+
118+
stream?.destroy()
117119
} else {
118120
stream.on('data', function (chunk) {
119121
if (typeof handler.onData === 'function' && !handler.onData(chunk)) {
@@ -127,7 +129,7 @@ module.exports = (opts = {}) => {
127129
}
128130

129131
/**
130-
* @param {import('../../types/cache-interceptor.d.ts').default.GetValueByKeyResult} result
132+
* @param {import('../../types/cache-interceptor.d.ts').default.GetResult} result
131133
*/
132134
const handleStream = (result) => {
133135
const { response: value, body: stream } = result

lib/util/cache.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ function assertCacheStore (store, name = 'CacheStore') {
208208
throw new TypeError(`expected type of ${name} to be a CacheStore, got ${store === null ? 'null' : typeof store}`)
209209
}
210210

211-
for (const fn of ['getValueByKey', 'createWriteStream', 'deleteByKey']) {
211+
for (const fn of ['get', 'createWriteStream', 'deleteByKey']) {
212212
if (typeof store[fn] !== 'function') {
213213
throw new TypeError(`${name} needs to have a \`${fn}()\` function`)
214214
}

test/cache-interceptor/cache-stores.js

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ function cacheStoreTests (CacheStore) {
1515
test('matches interface', async () => {
1616
const store = new CacheStore()
1717
equal(typeof store.isFull, 'boolean')
18-
equal(typeof store.getValueByKey, 'function')
18+
equal(typeof store.get, 'function')
1919
equal(typeof store.createWriteStream, 'function')
2020
equal(typeof store.deleteByKey, 'function')
2121
})
@@ -44,15 +44,15 @@ function cacheStoreTests (CacheStore) {
4444
const store = new CacheStore()
4545

4646
// Sanity check
47-
equal(store.getValueByKey(request), undefined)
47+
equal(store.get(request), undefined)
4848

4949
// Write the response to the store
5050
let writeStream = store.createWriteStream(request, requestValue)
5151
notEqual(writeStream, undefined)
5252
writeResponse(writeStream, requestBody)
5353

5454
// Now try fetching it with a deep copy of the original request
55-
let readResult = store.getValueByKey(structuredClone(request))
55+
let readResult = store.get(structuredClone(request))
5656
notEqual(readResult, undefined)
5757

5858
deepStrictEqual(await readResponse(readResult), {
@@ -79,7 +79,7 @@ function cacheStoreTests (CacheStore) {
7979

8080
// We haven't cached this one yet, make sure it doesn't confuse it with
8181
// another request
82-
equal(store.getValueByKey(anotherRequest), undefined)
82+
equal(store.get(anotherRequest), undefined)
8383

8484
// Now let's cache it
8585
writeStream = store.createWriteStream(anotherRequest, {
@@ -89,7 +89,7 @@ function cacheStoreTests (CacheStore) {
8989
notEqual(writeStream, undefined)
9090
writeResponse(writeStream, anotherBody)
9191

92-
readResult = store.getValueByKey(anotherRequest)
92+
readResult = store.get(anotherRequest)
9393
notEqual(readResult, undefined)
9494
deepStrictEqual(await readResponse(readResult), {
9595
...anotherValue,
@@ -123,7 +123,7 @@ function cacheStoreTests (CacheStore) {
123123
notEqual(writeStream, undefined)
124124
writeResponse(writeStream, requestBody)
125125

126-
const readResult = store.getValueByKey(request)
126+
const readResult = store.get(request)
127127
deepStrictEqual(await readResponse(readResult), {
128128
...requestValue,
129129
body: requestBody
@@ -155,7 +155,7 @@ function cacheStoreTests (CacheStore) {
155155
notEqual(writeStream, undefined)
156156
writeResponse(writeStream, requestBody)
157157

158-
equal(store.getValueByKey(request), undefined)
158+
equal(store.get(request), undefined)
159159
})
160160

161161
test('respects vary directives', async () => {
@@ -186,13 +186,13 @@ function cacheStoreTests (CacheStore) {
186186
const store = new CacheStore()
187187

188188
// Sanity check
189-
equal(store.getValueByKey(request), undefined)
189+
equal(store.get(request), undefined)
190190

191191
const writeStream = store.createWriteStream(request, requestValue)
192192
notEqual(writeStream, undefined)
193193
writeResponse(writeStream, requestBody)
194194

195-
const readStream = store.getValueByKey(structuredClone(request))
195+
const readStream = store.get(structuredClone(request))
196196
notEqual(readStream, undefined)
197197
deepStrictEqual(await readResponse(readStream), {
198198
...requestValue,
@@ -207,7 +207,7 @@ function cacheStoreTests (CacheStore) {
207207
'some-header': 'another-value'
208208
}
209209
}
210-
equal(store.getValueByKey(nonMatchingRequest), undefined)
210+
equal(store.get(nonMatchingRequest), undefined)
211211
})
212212
})
213213
}
@@ -236,14 +236,14 @@ test('MemoryCacheStore locks values properly', async () => {
236236

237237
// Value should now be locked, we shouldn't be able to create a readable or
238238
// another writable to it until the first one finishes
239-
equal(store.getValueByKey(request), undefined)
239+
equal(store.get(request), undefined)
240240
equal(store.createWriteStream(request, requestValue), undefined)
241241

242242
// Close the writable, this should unlock it
243243
writeResponse(writable, ['asd'])
244244

245245
// Stream is now closed, let's lock any new write streams
246-
const result = store.getValueByKey(request)
246+
const result = store.get(request)
247247
notEqual(result, undefined)
248248

249249
// Consume & close the result, this should lift the write lock
@@ -265,8 +265,8 @@ function writeResponse (stream, body) {
265265
}
266266

267267
/**
268-
* @param {import('../../types/cache-interceptor.d.ts').default.GetValueByKeyResult} result
269-
* @returns {Promise<import('../../types/cache-interceptor.d.ts').default.GetValueByKeyResult | { body: Buffer[] }>}
268+
* @param {import('../../types/cache-interceptor.d.ts').default.GetResult} result
269+
* @returns {Promise<import('../../types/cache-interceptor.d.ts').default.GetResult | { body: Buffer[] }>}
270270
*/
271271
async function readResponse ({ response, body: stream }) {
272272
/**

test/types/cache-interceptor.test-d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import CacheInterceptor from '../../types/cache-interceptor'
55
const store: CacheInterceptor.CacheStore = {
66
isFull: false,
77

8-
getValue (_: CacheInterceptor.CacheKey): CacheInterceptor.GetValueByKeyResult | Promise<CacheInterceptor.GetValueByKeyResult | undefined> | undefined {
8+
getValue (_: CacheInterceptor.CacheKey): CacheInterceptor.GetResult | Promise<CacheInterceptor.GetResult | undefined> | undefined {
99
throw new Error('stub')
1010
},
1111

0 commit comments

Comments
 (0)