Skip to content

Commit 60402ad

Browse files
committed
fix: add Last-Modified header to GET and HEAD responses
Only sent when access is granted (200 path), using the resource's mtime from fs.stat, as required by the Solid protocol. Fixes #1793
1 parent e0ee057 commit 60402ad

3 files changed

Lines changed: 28 additions & 4 deletions

File tree

‎lib/handlers/get.mjs‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,19 +76,22 @@ export default async function handler (req, res, next) {
7676
let container
7777
let contentRange
7878
let chunksize
79+
let mtime
7980

8081
if (ret) {
8182
stream = ret.stream
8283
contentType = ret.contentType
8384
container = ret.container
8485
contentRange = ret.contentRange
8586
chunksize = ret.chunksize
87+
mtime = ret.mtime
8688
}
8789

8890
// Till here it must exist
8991
if (!includeBody) {
9092
debug('HEAD only')
9193
res.setHeader('Content-Type', ret.contentType)
94+
if (mtime) res.setHeader('Last-Modified', mtime.toUTCString())
9295
return res.status(200).send('OK')
9396
}
9497

@@ -118,7 +121,8 @@ export default async function handler (req, res, next) {
118121
// If request accepts the content-type we found
119122
if (stream && negotiator.mediaType([contentType])) {
120123
let headers = {
121-
'Content-Type': contentType
124+
'Content-Type': contentType,
125+
...(mtime && { 'Last-Modified': mtime.toUTCString() })
122126
}
123127

124128
if (contentRange) {
@@ -160,6 +164,9 @@ export default async function handler (req, res, next) {
160164
headers
161165
})) return
162166
res.setHeader('Content-Type', possibleRDFType)
167+
if (mtime) {
168+
res.setHeader('Last-Modified', mtime.toUTCString())
169+
}
163170
res.send(data)
164171
return next()
165172
} catch (err) {

‎lib/ldp.mjs‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ class LDP {
451451
}
452452

453453
if (!options.includeBody) {
454-
return { stream: stats, contentType, container: stats.isDirectory() }
454+
return { stream: stats, contentType, container: stats.isDirectory(), mtime: stats.mtime }
455455
}
456456

457457
if (stats.isDirectory()) {
@@ -465,7 +465,7 @@ class LDP {
465465
throw err
466466
}
467467
const stream = stringToStream(data)
468-
return { stream, contentType, container: true }
468+
return { stream, contentType, container: true, mtime: stats.mtime }
469469
} else {
470470
let chunksize, contentRange, start, end
471471
if (options.range) {
@@ -487,7 +487,7 @@ class LDP {
487487
})
488488
.on('open', function () {
489489
debug.handlers(`GET -- Reading ${pathLocal}`)
490-
return resolve({ stream, contentType, container: false, contentRange, chunksize })
490+
return resolve({ stream, contentType, container: false, contentRange, chunksize, mtime: stats.mtime })
491491
})
492492
}))
493493
}

‎test/integration/header-test.mjs‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,23 @@ describe('Header handler', () => {
6868
})
6969
})
7070

71+
describe('Last-Modified', () => {
72+
describeHeaderTest('read/append for the public', {
73+
resource: '/public-ra',
74+
headers: {
75+
'Last-Modified': /^\w{3}, \d{2} \w{3} \d{4} \d{2}:\d{2}:\d{2} GMT$/
76+
}
77+
})
78+
79+
describe('on a HEAD request', () => {
80+
it('has a Last-Modified header', async () => {
81+
const { headers } = await request.head('/public-ra')
82+
expect(headers).to.have.property('last-modified')
83+
expect(headers['last-modified']).to.match(/^\w{3}, \d{2} \w{3} \d{4} \d{2}:\d{2}:\d{2} GMT$/)
84+
})
85+
})
86+
})
87+
7188
function describeHeaderTest (label, { resource, headers }) {
7289
describe(`a resource that is ${label}`, () => {
7390
// Retrieve the response headers

0 commit comments

Comments
 (0)