Skip to content

Commit c65844d

Browse files
committed
followup http-cache
1 parent 2d40ade commit c65844d

File tree

3 files changed

+53
-20
lines changed

3 files changed

+53
-20
lines changed

lib/handler/cache-handler.js

+29-19
Original file line numberDiff line numberDiff line change
@@ -2,53 +2,63 @@
22

33
const util = require('../core/util')
44
const DecoratorHandler = require('../handler/decorator-handler')
5-
const { parseCacheControlHeader, parseVaryHeader, UNSAFE_METHODS, assertCacheStoreType } = require('../util/cache')
5+
const {
6+
assertCacheStoreType,
7+
parseCacheControlHeader,
8+
parseVaryHeader,
9+
UNSAFE_METHODS
10+
} = require('../util/cache')
611

712
/**
813
* Writes a response to a CacheStore and then passes it on to the next handler
914
*/
1015
class CacheHandler extends DecoratorHandler {
1116
/**
12-
* @type {import('../../types/cache-interceptor.d.ts').default.CacheOptions | null}
13-
*/
14-
#opts = null
17+
* @type {import('../../types/cache-interceptor.d.ts').default.CacheStore}
18+
*/
19+
#store
20+
1521
/**
16-
* @type {import('../../types/dispatcher.d.ts').default.RequestOptions | null}
22+
* @type {import('../../types/dispatcher.d.ts').default.RequestOptions}
1723
*/
18-
#req = null
24+
#requestOptions
25+
1926
/**
20-
* @type {import('../../types/dispatcher.d.ts').default.DispatchHandlers | null}
27+
* @type {import('../../types/dispatcher.d.ts').default.DispatchHandlers}
2128
*/
22-
#handler = null
29+
#handler
30+
2331
/**
2432
* @type {import('../../types/cache-interceptor.d.ts').default.CacheStoreWriteable | undefined}
2533
*/
2634
#writeStream
2735

2836
/**
2937
* @param {import('../../types/cache-interceptor.d.ts').default.CacheOptions} opts
30-
* @param {import('../../types/dispatcher.d.ts').default.RequestOptions} req
38+
* @param {import('../../types/dispatcher.d.ts').default.RequestOptions} requestOptions
3139
* @param {import('../../types/dispatcher.d.ts').default.DispatchHandlers} handler
3240
*/
33-
constructor (opts, req, handler) {
41+
constructor (opts, requestOptions, handler) {
3442
super(handler)
3543

3644
if (typeof opts !== 'object') {
3745
throw new TypeError(`expected opts to be an object, got type ${typeof opts}`)
3846
}
3947

40-
assertCacheStoreType(opts.store)
48+
const { store } = opts
49+
50+
assertCacheStoreType(store)
4151

42-
if (typeof req !== 'object') {
52+
if (typeof requestOptions !== 'object') {
4353
throw new TypeError(`expected req to be an object, got type ${typeof opts}`)
4454
}
4555

4656
if (typeof handler !== 'object') {
4757
throw new TypeError(`expected handler to be an object, got type ${typeof opts}`)
4858
}
4959

50-
this.#opts = opts
51-
this.#req = req
60+
this.#store = store
61+
this.#requestOptions = requestOptions
5262
this.#handler = handler
5363
}
5464

@@ -75,14 +85,14 @@ class CacheHandler extends DecoratorHandler {
7585
)
7686

7787
if (
78-
UNSAFE_METHODS.includes(this.#req.method) &&
88+
UNSAFE_METHODS.includes(this.#requestOptions.method) &&
7989
statusCode >= 200 &&
8090
statusCode <= 399
8191
) {
8292
// https://www.rfc-editor.org/rfc/rfc9111.html#name-invalidating-stored-respons
8393
// Try/catch for if it's synchronous
8494
try {
85-
const result = this.#opts.store.deleteByOrigin(this.#req.origin)
95+
const result = this.#store.deleteByOrigin(this.#requestOptions.origin)
8696
if (
8797
result &&
8898
typeof result.catch === 'function' &&
@@ -103,7 +113,7 @@ class CacheHandler extends DecoratorHandler {
103113
const cacheControlHeader = headers['cache-control']
104114
const contentLengthHeader = headers['content-length']
105115

106-
if (!cacheControlHeader || !contentLengthHeader || this.#opts.store.isFull) {
116+
if (!cacheControlHeader || !contentLengthHeader || this.#store.isFull) {
107117
// Don't have the headers we need, can't cache
108118
return downstreamOnHeaders()
109119
}
@@ -122,7 +132,7 @@ class CacheHandler extends DecoratorHandler {
122132
const staleAt = determineStaleAt(now, headers, cacheControlDirectives)
123133
if (staleAt) {
124134
const varyDirectives = headers.vary
125-
? parseVaryHeader(headers.vary, this.#req.headers)
135+
? parseVaryHeader(headers.vary, this.#requestOptions.headers)
126136
: undefined
127137
const deleteAt = determineDeleteAt(now, cacheControlDirectives, staleAt)
128138

@@ -132,7 +142,7 @@ class CacheHandler extends DecoratorHandler {
132142
cacheControlDirectives
133143
)
134144

135-
this.#writeStream = this.#opts.store.createWriteStream(this.#req, {
145+
this.#writeStream = this.#store.createWriteStream(this.#requestOptions, {
136146
statusCode,
137147
statusMessage,
138148
rawHeaders: strippedHeaders,

lib/util/cache.js

+21
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
'use strict'
22

3+
const SAFE_METHODS = /** @type {const} */ ([
4+
'GET', 'HEAD', 'OPTIONS', 'TRACE'
5+
])
6+
37
const UNSAFE_METHODS = /** @type {const} */ ([
48
'POST', 'PUT', 'PATCH', 'DELETE'
59
])
@@ -196,10 +200,27 @@ function assertCacheStoreType (store) {
196200
throw new TypeError(`CacheStore needs a isFull getter with type boolean, current type: ${typeof store.isFull}`)
197201
}
198202
}
203+
/**
204+
* @param {unknown} methods
205+
* @returns {asserts methods is import('../../types/cache-interceptor.d.ts').default.CacheMethods[]}
206+
*/
207+
function assertCacheMethods (methods) {
208+
if (!Array.isArray(methods)) {
209+
throw new TypeError(`expected type to be an array, got ${typeof methods}`)
210+
}
211+
212+
for (const method of methods) {
213+
if (!UNSAFE_METHODS.includes(method)) {
214+
throw new TypeError(`CacheMethods needs to be one of ${UNSAFE_METHODS.join(', ')}, got ${method}`)
215+
}
216+
}
217+
}
199218

200219
module.exports = {
220+
SAFE_METHODS,
201221
UNSAFE_METHODS,
202222
parseCacheControlHeader,
203223
parseVaryHeader,
224+
assertCacheMethods,
204225
assertCacheStoreType
205226
}

types/cache-interceptor.d.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import Dispatcher from './dispatcher'
44
export default CacheHandler
55

66
declare namespace CacheHandler {
7+
export type CacheMethods = 'GET' | 'HEAD' | 'OPTIONS' | 'TRACE'
8+
79
export interface CacheOptions {
810
store?: CacheStore
911

@@ -14,7 +16,7 @@ declare namespace CacheHandler {
1416
* @see https://www.rfc-editor.org/rfc/rfc9111.html#name-invalidating-stored-respons
1517
* @see https://www.rfc-editor.org/rfc/rfc9110#section-9.2.1
1618
*/
17-
methods?: ('GET' | 'HEAD' | 'OPTIONS' | 'TRACE')[]
19+
methods?: CacheMethods[]
1820
}
1921

2022
/**

0 commit comments

Comments
 (0)