@bbc/http-transport-cache
Version:
Caching middleware
68 lines (57 loc) • 2.15 kB
JavaScript
;
const _ = require('lodash');
const parseCacheControl = require('./parseCacheControl');
const getFromCache = require('./cache').getFromCache;
const storeInCache = require('./cache').storeInCache;
const toResponse = require('./toResponse');
const isCachable = require('./isCachable');
const events = require('./events');
const directives = require('./directives');
const startCacheConnection = require('./cacheConnection').startCacheConnection;
const STALE_IF_ERROR = 'stale-if-error';
const MAX_AGE = 'max-age';
const CACHE_CONTROL = 'cache-control';
const SEGMENT = 'stale';
class HttpError extends Error { }
module.exports = (cache, opts) => {
return async (ctx, next) => {
if (!cache.isReady()) {
try {
const timeout = _.get(opts, 'connectionTimeout');
await startCacheConnection({ cache, timeout });
} catch (err) {
events.emitCacheEvent('connection_error', opts, ctx, null, err);
if (_.get(opts, 'ignoreCacheErrors', false)) return next();
throw err;
}
}
let cached;
try {
await next();
if (ctx.res.statusCode >= 500) throw new HttpError();
} catch (err) {
try {
cached = await getFromCache(cache, SEGMENT, ctx, opts);
} catch (cacheErr) {
if (_.get(opts, 'ignoreCacheErrors', false)) throw err;
throw cacheErr;
}
if (cached) {
ctx.isStale = true;
ctx.res = toResponse(cached);
events.emitCacheEvent('stale', opts, ctx);
return;
}
if (err instanceof HttpError) return;
return Promise.reject(err);
}
const cacheControl = parseCacheControl(ctx.res.headers[CACHE_CONTROL]);
if (isCachable(cacheControl, directives.STALE_IF_ERROR, opts?.defaultTTL) && !ctx.res.fromCache) {
const maxAgeMilliseconds = (cacheControl[MAX_AGE] || opts?.defaultTTL || 0) * 1000;
const staleIfErrorMilliseconds = (cacheControl[STALE_IF_ERROR] || 0) * 1000;
const ttl = maxAgeMilliseconds + staleIfErrorMilliseconds;
storeInCache(cache, SEGMENT, ctx, ctx.res.toJSON(), ttl, opts);
return ctx.res.toJSON();
}
};
};