Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

return no-cache on 404 #2609

Merged
merged 4 commits into from
Feb 12, 2025
Merged
Changes from 2 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
18 changes: 14 additions & 4 deletions backend/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,16 @@ const route = new Proxy({}, {
}
})

// helper function that returns 404 and prevents client from caching the 404 response
// which can sometimes break things: https://github.com/okTurtles/group-income/issues/2608
function notFoundNoCache (h) {
return h.response()
.code(404)
.header('Cache-Control', 'no-store, must-revalidate, proxy-revalidate')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

*-revalidate is redundant as well, since no-store already means that the response should not be put in a cache to begin with.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I think the issue here is this, since by default 404 responses will not be cached.

Most commonly, caches store the successful result of a retrieval request: i.e., a 200 (OK) response to a GET request, which contains a representation of the target resource (Section 9.3.1 of [HTTP]). However, it is also possible to store redirects, negative results (e.g., 404 (Not Found)), incomplete results (e.g., 206 (Partial Content)), and responses to methods other than GET if the method's definition allows such caching and defines something suitable for use as a cache key.

  cache: {
    // Do not set other cache options here, to make sure the 'otherwise' option
    // will be used so that the 'immutable' directive gets included.
    otherwise: 'public,max-age=31536000,immutable'
  }

I'm not too familiar with HAPI, and not with the old version used in GI, but, instead of setting a cache policy and then 'undoing it' with this new notFoundNoCache wouldn't it be better to simply tell HAPI not to apply the immutable (or a custom) cache policy to errors?

.header('Pragma', 'no-cache')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pragma: no-cache has never been standard for responses, and is also largely irrelevant for clients that understand cache-control.

.header('Expires', '0')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expires is supposed to have a timestamp, so 0 isn't a valid value. However, it's not really relevant if cache-control is present, except for clients that don't support HTTP/1.1. I don't think any such clients exist (today) to any significant, and even if they did, they can be ignored.

}

// RESTful API routes

// TODO: Update this regex once `chel` uses prefixed manifests
Expand Down Expand Up @@ -277,7 +287,7 @@ route.GET('/latestHEADinfo/{contractID}', {
const HEADinfo = await sbp('chelonia/db/latestHEADinfo', contractID)
if (!HEADinfo) {
console.warn(`[backend] latestHEADinfo not found for ${contractID}`)
return Boom.notFound()
return notFoundNoCache(h)
}
return HEADinfo
} catch (err) {
Expand Down Expand Up @@ -473,7 +483,7 @@ route.GET('/file/{hash}', {

const blobOrString = await sbp('chelonia/db/get', `any:${hash}`)
if (!blobOrString) {
return Boom.notFound()
return notFoundNoCache(h)
}
return h.response(blobOrString).etag(hash)
})
Expand Down Expand Up @@ -675,7 +685,7 @@ route.GET('/kv/{contractID}/{key}', {

const result = await sbp('chelonia/db/get', `_private_kv_${contractID}_${key}`)
if (!result) {
return Boom.notFound()
return notFoundNoCache(h)
}

return h.response(result).etag(createCID(result))
Expand Down Expand Up @@ -804,7 +814,7 @@ route.GET('/zkpp/{name}/auth_hash', {
try {
const challenge = await getChallenge(req.params['name'], req.query['b'])

return challenge || Boom.notFound()
return challenge || notFoundNoCache(h)
} catch (e) {
e.ip = req.headers['x-real-ip'] || req.info.remoteAddress
console.error(e, 'Error at GET /zkpp/{name}/auth_hash: ' + e.message)
Expand Down