Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,18 @@ also be specified to enable caching. The `immutable` directive will prevent
supported clients from making conditional requests during the life of the
`maxAge` option to check if the file has changed.

When enabled, the `immutable` directive is sent as its own `Cache-Control`
field-line instead of being appended to the `max-age` value, for example:

```
Cache-Control: public, max-age=31536000
Cache-Control: immutable
```

HTTP caches combine repeated field-lines, so the effective set of directives is
unchanged. Splitting the value keeps the `public, max-age=<seconds>` field-line
matchable against the HTTP/3 QPACK static table.

##### index

By default send supports "index.html" files, to disable this
Expand Down
8 changes: 6 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -747,10 +747,14 @@ SendStream.prototype.setHeader = function setHeader (path, stat) {
var cacheControl = 'public, max-age=' + Math.floor(this._maxage / 1000)

if (this._immutable) {
cacheControl += ', immutable'
// Send the `immutable` directive as its own field-line so the
// `public, max-age=<seconds>` value can still be matched against the
// HTTP/3 QPACK static table. Caches combine repeated field-lines, so
// the effective directives are unchanged.
cacheControl = [cacheControl, 'immutable']
}

debug('cache-control %s', cacheControl)
debug('cache-control %o', cacheControl)
res.setHeader('Cache-Control', cacheControl)
}

Expand Down
30 changes: 30 additions & 0 deletions test/send.js
Original file line number Diff line number Diff line change
Expand Up @@ -1100,6 +1100,20 @@ describe('send(file, options)', function () {
.get('/name.txt')
.expect('Cache-Control', 'public, max-age=3600, immutable', done)
})

it('should send the immutable directive as a separate field-line', function (done) {
request(createServer({ immutable: true, maxAge: '1h', root: fixtures }))
.get('/name.txt')
.expect(shouldHaveHeaderValues('Cache-Control', ['public, max-age=3600', 'immutable']))
.expect(200, done)
})

it('should send a single field-line when disabled', function (done) {
request(createServer({ immutable: false, maxAge: '1h', root: fixtures }))
.get('/name.txt')
.expect(shouldHaveHeaderValues('Cache-Control', ['public, max-age=3600']))
.expect(200, done)
})
})

describe('maxAge', function () {
Expand Down Expand Up @@ -1317,6 +1331,22 @@ function createServer (opts, fn) {
})
}

function shouldHaveHeaderValues (header, values) {
return function (res) {
var lower = header.toLowerCase()
var raw = res.res.rawHeaders
var actual = []

for (var i = 0; i < raw.length; i += 2) {
if (raw[i].toLowerCase() === lower) {
actual.push(raw[i + 1])
}
}

assert.deepStrictEqual(actual, values, 'should have ' + header + ' field-lines ' + JSON.stringify(values))
}
}

function shouldNotHaveBody () {
return function (res) {
assert.ok(res.text === '' || res.text === undefined)
Expand Down