Skip to content

Commit ee03484

Browse files
author
Dolan Halbrook
authored
Merge branch 'nodejs:main' into main
2 parents bff0813 + 14e62db commit ee03484

5 files changed

Lines changed: 111 additions & 6 deletions

File tree

.github/workflows/scorecard.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
persist-credentials: false
3535

3636
- name: "Run analysis"
37-
uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0
37+
uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1
3838
with:
3939
results_file: results.sarif
4040
results_format: sarif

lib/interceptor/cache.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@ const { AbortError } = require('../core/errors.js')
2020
*/
2121
function needsRevalidation (result, cacheControlDirectives) {
2222
if (cacheControlDirectives?.['no-cache']) {
23-
// Always revalidate requests with the no-cache directive
23+
// Always revalidate requests with the no-cache request directive
24+
return true
25+
}
26+
27+
if (result.cacheControlDirectives?.['no-cache'] && !Array.isArray(result.cacheControlDirectives['no-cache'])) {
28+
// Always revalidate requests with unqualified no-cache response directive
2429
return true
2530
}
2631

@@ -233,7 +238,7 @@ function handleResult (
233238
}
234239

235240
let headers = {
236-
...normaliseHeaders(opts),
241+
...opts.headers,
237242
'if-modified-since': new Date(result.cachedAt).toUTCString()
238243
}
239244

@@ -319,6 +324,11 @@ module.exports = (opts = {}) => {
319324
return dispatch(opts, handler)
320325
}
321326

327+
opts = {
328+
...opts,
329+
headers: normaliseHeaders(opts)
330+
}
331+
322332
const reqCacheControl = opts.headers?.['cache-control']
323333
? parseCacheControlHeader(opts.headers['cache-control'])
324334
: undefined

lib/util/cache.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,11 @@ function makeCacheKey (opts) {
1212
throw new Error('opts.origin is undefined')
1313
}
1414

15-
const headers = normaliseHeaders(opts)
16-
1715
return {
1816
origin: opts.origin.toString(),
1917
method: opts.method,
2018
path: opts.path,
21-
headers
19+
headers: opts.headers
2220
}
2321
}
2422

test/interceptors/cache.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,45 @@ describe('Cache Interceptor', () => {
128128
}
129129
})
130130

131+
test('revalidates reponses with no-cache directive, regardless of cacheByDefault', async () => {
132+
let requestCount = 0
133+
const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {
134+
++requestCount
135+
res.setHeader('Vary', 'Accept-Encoding')
136+
res.setHeader('cache-control', 'no-cache')
137+
res.end(`Request count: ${requestCount}`)
138+
}).listen(0)
139+
140+
after(async () => {
141+
server.close()
142+
143+
await once(server, 'close')
144+
})
145+
146+
await once(server, 'listening')
147+
148+
const client = new Client(`http://localhost:${server.address().port}`)
149+
.compose(interceptors.cache({
150+
cacheByDefault: 1000
151+
}))
152+
153+
const request = {
154+
origin: 'localhost',
155+
method: 'GET',
156+
path: '/'
157+
}
158+
159+
const res1 = await client.request(request)
160+
const body1 = await res1.body.text()
161+
strictEqual(body1, 'Request count: 1')
162+
strictEqual(requestCount, 1)
163+
164+
const res2 = await client.request(request)
165+
const body2 = await res2.body.text()
166+
strictEqual(body2, 'Request count: 2')
167+
strictEqual(requestCount, 2)
168+
})
169+
131170
test('stale responses are revalidated before deleteAt (if-modified-since)', async () => {
132171
const clock = FakeTimers.install({
133172
shouldClearNativeTimers: true

test/issue-3904.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
const { describe, test, after } = require('node:test')
2+
const assert = require('node:assert')
3+
const { createServer } = require('node:http')
4+
const { once } = require('node:events')
5+
const MemoryCacheStore = require('../lib/cache/memory-cache-store.js')
6+
const { Agent, interceptors, request, setGlobalDispatcher } = require('..')
7+
8+
describe('Cache with cache-control: no-store request header', () => {
9+
[
10+
'CACHE-CONTROL',
11+
'cache-control',
12+
'Cache-Control'
13+
].forEach(headerName => {
14+
test(`should not cache response for request with header: "${headerName}: no-store`, async () => {
15+
const store = new MemoryCacheStore()
16+
let requestCount = 0
17+
const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {
18+
++requestCount
19+
res.setHeader('Vary', 'Accept-Encoding')
20+
res.setHeader('Cache-Control', 'max-age=60')
21+
res.end(`Request count: ${requestCount}`)
22+
})
23+
24+
after(async () => {
25+
server.close()
26+
27+
await once(server, 'close')
28+
})
29+
30+
await new Promise(resolve => server.listen(0, resolve))
31+
const { port } = server.address()
32+
const url = `http://localhost:${port}`
33+
34+
const agent = new Agent()
35+
setGlobalDispatcher(
36+
agent.compose(
37+
interceptors.cache({
38+
store,
39+
cacheByDefault: 1000,
40+
methods: ['GET']
41+
})
42+
)
43+
)
44+
45+
const res1 = await request(url, { headers: { [headerName]: 'no-store' } })
46+
const body1 = await res1.body.text()
47+
assert.strictEqual(body1, 'Request count: 1')
48+
assert.strictEqual(requestCount, 1)
49+
50+
const res2 = await request(url)
51+
const body2 = await res2.body.text()
52+
assert.strictEqual(body2, 'Request count: 2')
53+
assert.strictEqual(requestCount, 2)
54+
55+
await new Promise(resolve => server.close(resolve))
56+
})
57+
})
58+
})

0 commit comments

Comments
 (0)