2018-10-23 23:45:42 +08:00
|
|
|
'use strict';
|
|
|
|
|
2019-10-15 18:48:50 +08:00
|
|
|
const ONE_MINUTE_IN_SECONDS = 60;
|
|
|
|
const THREE_MINUTE_IN_SECONDS = 60 * 3;
|
|
|
|
const FIVE_MINUTES_IN_SECONDS = ONE_MINUTE_IN_SECONDS * 5;
|
|
|
|
const TEN_MINUTES_IN_SECONDS = ONE_MINUTE_IN_SECONDS * 10;
|
|
|
|
const FIFTEEN_MINUTES_IN_SECONDS = ONE_MINUTE_IN_SECONDS * 15;
|
|
|
|
const THIRTY_MINUTES_IN_SECONDS = ONE_MINUTE_IN_SECONDS * 30;
|
|
|
|
const ONE_HOUR_IN_SECONDS = ONE_MINUTE_IN_SECONDS * 60;
|
|
|
|
const ONE_YEAR_IN_SECONDS = ONE_HOUR_IN_SECONDS * 24 * 365;
|
|
|
|
|
2019-07-03 23:16:09 +08:00
|
|
|
const FALLBACK_TTL = global.environment.varnish.fallbackTtl || FIVE_MINUTES_IN_SECONDS;
|
2018-03-21 23:48:21 +08:00
|
|
|
|
2019-10-15 18:48:50 +08:00
|
|
|
const validFallbackTTL = [
|
|
|
|
ONE_MINUTE_IN_SECONDS,
|
|
|
|
THREE_MINUTE_IN_SECONDS,
|
|
|
|
FIVE_MINUTES_IN_SECONDS,
|
|
|
|
TEN_MINUTES_IN_SECONDS,
|
|
|
|
FIFTEEN_MINUTES_IN_SECONDS,
|
|
|
|
THIRTY_MINUTES_IN_SECONDS,
|
|
|
|
ONE_HOUR_IN_SECONDS
|
|
|
|
];
|
|
|
|
|
2019-07-03 23:15:14 +08:00
|
|
|
module.exports = function setCacheControlHeader ({
|
|
|
|
ttl = ONE_YEAR_IN_SECONDS,
|
|
|
|
fallbackTtl = FALLBACK_TTL,
|
|
|
|
revalidate = false
|
|
|
|
} = {}) {
|
2019-10-15 18:48:50 +08:00
|
|
|
if (!validFallbackTTL.includes(fallbackTtl)) {
|
2019-10-15 19:27:40 +08:00
|
|
|
const message = [
|
|
|
|
'Invalid fallback TTL value for Cache-Control header.',
|
|
|
|
`Got ${fallbackTtl}, expected ${validFallbackTTL.join(', ')}`
|
|
|
|
].join(' ');
|
|
|
|
|
|
|
|
throw new Error(message);
|
2019-10-15 18:48:50 +08:00
|
|
|
}
|
|
|
|
|
2018-03-21 23:38:37 +08:00
|
|
|
return function setCacheControlHeaderMiddleware (req, res, next) {
|
|
|
|
if (req.method !== 'GET') {
|
|
|
|
return next();
|
|
|
|
}
|
|
|
|
|
2020-06-02 15:00:45 +08:00
|
|
|
const { mapConfigProvider = { getAffectedTables: callback => callback() }, logger } = res.locals;
|
2018-03-21 23:38:37 +08:00
|
|
|
|
2019-07-03 23:15:14 +08:00
|
|
|
mapConfigProvider.getAffectedTables((err, affectedTables) => {
|
|
|
|
if (err) {
|
2020-06-03 23:32:16 +08:00
|
|
|
err.message = `Error generating Cache Control Header. ${err.message}`;
|
|
|
|
logger.warn({ error: err });
|
2019-07-03 23:15:14 +08:00
|
|
|
return next();
|
|
|
|
}
|
|
|
|
|
2019-10-22 01:07:24 +08:00
|
|
|
const directives = ['public'];
|
2019-07-03 23:15:14 +08:00
|
|
|
|
2019-07-09 21:44:34 +08:00
|
|
|
if (everyAffectedTableCanBeInvalidated(affectedTables)) {
|
2019-07-03 23:15:14 +08:00
|
|
|
directives.push(`max-age=${ttl}`);
|
2019-07-04 22:24:17 +08:00
|
|
|
} else {
|
2019-10-15 18:48:50 +08:00
|
|
|
directives.push(`max-age=${computeNextTTL({ ttlInSeconds: fallbackTtl })}`);
|
2019-07-03 23:15:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (revalidate) {
|
|
|
|
directives.push('must-revalidate');
|
|
|
|
}
|
2018-03-21 23:38:37 +08:00
|
|
|
|
2019-07-03 23:15:14 +08:00
|
|
|
res.set('Cache-Control', directives.join(','));
|
2018-03-21 23:38:37 +08:00
|
|
|
|
2019-07-03 23:15:14 +08:00
|
|
|
next();
|
|
|
|
});
|
2018-03-21 23:38:37 +08:00
|
|
|
};
|
2018-03-21 23:43:34 +08:00
|
|
|
};
|
2019-07-04 21:58:39 +08:00
|
|
|
|
2019-07-09 21:44:34 +08:00
|
|
|
function everyAffectedTableCanBeInvalidated (affectedTables) {
|
2019-07-09 00:05:30 +08:00
|
|
|
const skipNotUpdatedAtTables = false;
|
|
|
|
const skipAnalysisCachedTables = true;
|
|
|
|
|
|
|
|
return affectedTables &&
|
|
|
|
affectedTables.getTables(skipNotUpdatedAtTables, skipAnalysisCachedTables)
|
|
|
|
.every(table => table.updated_at !== null);
|
2019-07-04 21:58:39 +08:00
|
|
|
}
|
2019-10-15 18:48:50 +08:00
|
|
|
|
|
|
|
function computeNextTTL ({ ttlInSeconds } = {}) {
|
|
|
|
const nowInSeconds = Math.ceil(Date.now() / 1000);
|
|
|
|
const secondsAfterPreviousTTLStep = nowInSeconds % ttlInSeconds;
|
|
|
|
const secondsToReachTheNextTTLStep = ttlInSeconds - secondsAfterPreviousTTLStep;
|
|
|
|
|
|
|
|
return secondsToReachTheNextTTLStep;
|
|
|
|
}
|